major restructuring, moving dialog, message, menu, option menu, task, type IDs into Id in a similar structure as the generated R, also introducing a BaseActivity class that almost all activities derive from, which generates some common dialogs, handles the progress update, thread management, and thread communication

also adding first draft of encrypt file activity, not very functional yet
This commit is contained in:
Thialfihar 2010-04-19 02:12:13 +00:00
parent 09741b0286
commit d5c5a2c43b
44 changed files with 2367 additions and 1754 deletions

8
.classpath Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry combineaccessrules="false" exported="true" kind="src" path="/BouncyCastlePort"/>
<classpathentry kind="output" path="bin"/>
</classpath>

33
.project Normal file
View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>APG 0.9.x</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@ -14,71 +14,98 @@
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="org.thialfihar.android.apg"
android:versionName="0.9.0" android:versionCode="5">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".MainActivity"
<application
android:icon="@drawable/icon"
android:label="@string/app_name">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboardHidden|orientation|keyboard">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".PublicKeyListActivity"
<activity
android:name=".PublicKeyListActivity"
android:label="@string/title_managePublicKeys"
android:configChanges="keyboardHidden|orientation|keyboard">
</activity>
android:configChanges="keyboardHidden|orientation|keyboard" />
<activity android:name=".SecretKeyListActivity"
<activity
android:name=".SecretKeyListActivity"
android:label="@string/title_manageSecretKeys"
android:configChanges="keyboardHidden|orientation|keyboard">
</activity>
android:configChanges="keyboardHidden|orientation|keyboard"/>
<activity android:name=".EditKeyActivity"
<activity
android:name=".EditKeyActivity"
android:label="@string/title_editKey"
android:configChanges="keyboardHidden|orientation|keyboard">
</activity>
android:configChanges="keyboardHidden|orientation|keyboard"/>
<activity android:name=".SelectPublicKeyListActivity"
<activity
android:name=".SelectPublicKeyListActivity"
android:label="@string/title_selectRecipients"
android:configChanges="keyboardHidden|orientation|keyboard">
</activity>
android:configChanges="keyboardHidden|orientation|keyboard"/>
<activity android:name=".SelectSecretKeyListActivity"
<activity
android:name=".SelectSecretKeyListActivity"
android:label="@string/title_selectSignature"
android:configChanges="keyboardHidden|orientation|keyboard">
</activity>
android:configChanges="keyboardHidden|orientation|keyboard"/>
<activity android:name=".EncryptMessageActivity"
<activity
android:name=".EncryptMessageActivity"
android:label="@string/title_encryptMessage"
android:configChanges="keyboardHidden|orientation|keyboard">
<intent-filter>
<action android:name="org.thialfihar.android.apg.intent.ENCRYPT" />
</intent-filter>
</activity>
<activity android:name=".DecryptMessageActivity"
<activity
android:name=".EncryptFileActivity"
android:label="@string/title_encryptFile"
android:configChanges="keyboardHidden|orientation|keyboard">
<intent-filter>
<action android:name="org.thialfihar.android.apg.intent.ENCRYPT_FILE" />
</intent-filter>
</activity>
<activity
android:name=".DecryptMessageActivity"
android:label="@string/title_decryptMessage"
android:configChanges="keyboardHidden|orientation|keyboard">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/*"/>
</intent-filter>
<intent-filter>
<action android:name="org.thialfihar.android.apg.intent.DECRYPT" />
</intent-filter>
</activity>
<activity android:name=".MailListActivity"
<activity
android:name=".MailListActivity"
android:label="@string/title_mailInbox"
android:configChanges="keyboardHidden|orientation|keyboard">
</activity>
android:configChanges="keyboardHidden|orientation|keyboard"/>
<provider android:name="org.thialfihar.android.apg.provider.DataProvider"
<provider
android:name="org.thialfihar.android.apg.provider.DataProvider"
android:authorities="org.thialfihar.android.apg.provider" />
</application>

BIN
bin/APG 0.9.x.apk Normal file

Binary file not shown.

BIN
bin/classes.dex Normal file

Binary file not shown.

BIN
bin/resources.ap_ Normal file

Binary file not shown.

View File

@ -18,35 +18,47 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
<TableLayout
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:stretchColumns="1"
android:layout_marginRight="?android:attr/scrollbarSize"
android:paddingLeft="6dip">
<TableRow>
<TextView android:id="@+id/label_algorithm"
android:text="Algorithm"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
android:paddingRight="10dip"/>
<Spinner
android:id="@+id/algorithm"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</TableRow>
<TableRow>
<TextView android:id="@+id/label_size"
android:text="Key Size"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
android:paddingRight="10dip"/>
<EditText android:id="@+id/size"
android:text="1024"
android:layout_height="wrap_content"
android:layout_width="fill_parent" android:gravity="right" android:numeric="integer"/>
android:layout_width="fill_parent"
android:gravity="right"
android:numeric="integer"/>
</TableRow>
</TableLayout>
</ScrollView>

View File

@ -46,6 +46,7 @@
<RelativeLayout
android:layout_height="wrap_content"
android:layout_width="wrap_content">
<ImageView
android:id="@+id/ic_signature"
android:layout_width="wrap_content"
@ -79,9 +80,11 @@
android:text="Main User Id Rest"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_gravity="left"/>
android:layout_height="wrap_content"
android:layout_gravity="left"/>
</LinearLayout>
</LinearLayout>
<Button
@ -94,4 +97,3 @@
</LinearLayout>
</LinearLayout>

View File

@ -26,12 +26,14 @@
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1">
<LinearLayout
android:id="@+id/container"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:layout_marginRight="?android:attr/scrollbarSize"/>
</ScrollView>
<LinearLayout
@ -53,6 +55,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/btn_doNotSave"/>
</LinearLayout>
</LinearLayout>

View File

@ -31,18 +31,24 @@
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="horizontal">
<TableLayout
android:layout_height="wrap_content"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_marginLeft="16dip"
android:stretchColumns="1">
<TableRow>
<TextView android:id="@+id/label_key_id" android:text="Key ID"
<TextView
android:id="@+id/label_key_id"
android:text="Key ID"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
android:paddingRight="10dip"/>
<TextView
android:id="@+id/key_id"
android:text="00000000 00000000"
@ -50,56 +56,79 @@
android:layout_width="wrap_content"
android:paddingRight="5dip"
android:typeface="monospace"/>
</TableRow>
<TableRow>
<TextView android:id="@+id/label_algorithm"
<TextView
android:id="@+id/label_algorithm"
android:text="Algorithm"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
android:paddingRight="10dip"/>
<TextView android:id="@+id/algorithm"
<TextView
android:id="@+id/algorithm"
android:text="Name"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:paddingRight="5dip"/>
</TableRow>
<TableRow>
<TextView android:id="@+id/label_creation"
<TextView
android:id="@+id/label_creation"
android:text="Creation"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
android:paddingRight="10dip"/>
<TextView
android:id="@+id/creation"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</TableRow>
<TableRow>
<TextView android:id="@+id/label_expiry"
<TextView
android:id="@+id/label_expiry"
android:text="Expiry"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
android:paddingRight="10dip"/>
<Button
android:id="@+id/expiry"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</TableRow>
<TableRow>
<TextView android:id="@+id/label_usage"
<TextView
android:id="@+id/label_usage"
android:text="Usage"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
android:paddingRight="10dip"/>
<Spinner
android:id="@+id/usage"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</TableRow>
</TableLayout>
<ImageButton
@ -110,4 +139,5 @@
android:layout_gravity="center_vertical"/>
</LinearLayout>
</org.thialfihar.android.apg.ui.widget.KeyEditor>

View File

@ -28,20 +28,25 @@
android:background="?android:attr/listDivider"/>
<RadioButton
android:id="@+id/is_main_user_id" android:text="Main User ID"
android:layout_height="wrap_content" android:layout_width="wrap_content"
android:id="@+id/is_main_user_id"
android:text="Main User ID"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginLeft="20dip"/>
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="horizontal">
<TableLayout
android:layout_height="wrap_content"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_marginLeft="16dip">
<TableRow>
<TextView
android:id="@+id/name_label"
android:text="Name"
@ -49,13 +54,17 @@
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:paddingRight="5dip"/>
<EditText
android:id="@+id/name"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_width="fill_parent"/>
</TableRow>
<TableRow>
<TextView
android:id="@+id/email_label"
android:text="Email"
@ -63,13 +72,17 @@
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:paddingRight="5dip"/>
<EditText
android:id="@+id/email"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_width="fill_parent"/>
</TableRow>
<TableRow>
<TextView
android:id="@+id/comment_label"
android:text="Comment"
@ -77,12 +90,15 @@
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:paddingRight="5dip"/>
<EditText
android:id="@+id/comment"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_width="fill_parent"/>
</TableRow>
</TableLayout>
<ImageButton
@ -93,4 +109,5 @@
android:layout_gravity="center_vertical"/>
</LinearLayout>
</org.thialfihar.android.apg.ui.widget.UserIdEditor>

166
res/layout/encrypt_file.xml Normal file
View File

@ -0,0 +1,166 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:paddingTop="5dip">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingLeft="5dip"
android:paddingRight="5dip">
<TextView
android:id="@+id/label_filename"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="50dip"
android:layout_height="wrap_content"
android:text="@string/label_file"/>
<EditText
android:id="@+id/filename"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<ImageButton
android:id="@+id/btn_browse"
android:src="@drawable/ic_launcher_folder_small"
android:layout_height="wrap_content"
android:layout_width="wrap_content"/>
</LinearLayout>
<TabHost
android:id="@+id/tab_host"
android:layout_weight="1"
android:layout_width="fill_parent"
android:layout_height="0dip">
<TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="65dip"/>
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingTop="65dip">
<LinearLayout
android:id="@+id/tab_asymmetric"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingBottom="3dip"
android:orientation="horizontal">
<CheckBox
android:text="@string/sign"
android:id="@+id/sign"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"/>
<LinearLayout
android:orientation="vertical"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_gravity="center_vertical"
android:paddingRight="5dip">
<TextView
android:id="@+id/main_user_id"
android:text="Main User Id"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"/>
<TextView
android:id="@+id/main_user_id_rest"
android:text="Main User Id Rest"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"/>
</LinearLayout>
</LinearLayout>
<View
android:id="@+id/separator"
android:layout_width="fill_parent"
android:layout_height="1dip"
android:background="?android:attr/listDivider"/>
<TextView
android:id="@+id/label_encrypt_keys"
android:text="@string/label_encrypt_keys"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ListView
android:id="@+id/public_key_list"
android:choiceMode="multipleChoice"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"/>
</LinearLayout>
<LinearLayout
android:id="@+id/tab_symmetric"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
</LinearLayout>
</FrameLayout>
</TabHost>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="@android:style/ButtonBar">
<Button
android:id="@+id/btn_encrypt"
android:text="@string/btn_encrypt"
android:layout_weight="1"
android:layout_width="0dip"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>

View File

@ -27,12 +27,12 @@
android:layout_height="0dip"
android:layout_weight="1"
android:gravity="top"
android:inputType="text|textCapSentences|textMultiLine|textLongMessage">
</EditText>
android:inputType="text|textCapSentences|textMultiLine|textLongMessage"/>
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="fill_parent" android:paddingBottom="3dip">
android:layout_width="fill_parent"
android:paddingBottom="3dip">
<CheckBox
android:text="@string/sign"
@ -54,18 +54,18 @@
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right">
</TextView>
android:layout_gravity="right"/>
<TextView
android:id="@+id/main_user_id_rest"
android:text="Main User Id Rest"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_gravity="right">
</TextView>
android:layout_height="wrap_content"
android:layout_gravity="right"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
@ -91,5 +91,3 @@
</LinearLayout>
</LinearLayout>

View File

@ -18,18 +18,20 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingLeft="5dip"
android:paddingRight="5dip"
>
android:paddingRight="5dip">
<EditText
android:id="@+id/input"
android:layout_width="wrap_content"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
android:layout_weight="1"/>
<ImageButton
android:id="@+id/btn_browse"
android:src="@drawable/ic_launcher_folder_small"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher_folder_small" android:layout_width="wrap_content"/>
android:layout_width="wrap_content"/>
</LinearLayout>

28
res/layout/key_list.xml Normal file
View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true">
<ExpandableListView
android:id="@+id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</ScrollView>

View File

@ -19,7 +19,8 @@
android:singleLine="true"
android:paddingLeft="10dip"
android:layout_marginRight="?android:attr/scrollbarSize"
android:layout_height="?android:attr/listPreferredItemHeight" android:layout_width="fill_parent">
android:layout_height="?android:attr/listPreferredItemHeight"
android:layout_width="fill_parent">
<LinearLayout
android:layout_height="wrap_content"
@ -32,14 +33,17 @@
android:src="@drawable/key_small"
android:paddingRight="6dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_gravity="center_vertical"/>
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"/>
<TextView
android:id="@+id/key_id"
android:text="Key ID"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:paddingRight="5dip" android:typeface="monospace"/>
android:layout_height="wrap_content"
android:paddingRight="5dip"
android:typeface="monospace"/>
<TextView
android:id="@+id/key_details"
@ -48,11 +52,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<LinearLayout
android:gravity="right"
android:orientation="horizontal"
android:layout_width="fill_parent" android:paddingBottom="2dip" android:paddingTop="2dip" android:layout_height="fill_parent" android:layout_gravity="center_vertical">
android:paddingBottom="2dip"
android:paddingTop="2dip"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center_vertical">
<ImageView
android:id="@+id/ic_encrypt_key"
@ -69,4 +76,5 @@
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -33,7 +33,9 @@
android:text="Key ID"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:paddingRight="5dip" android:typeface="monospace"/>
android:layout_height="wrap_content"
android:paddingRight="5dip"
android:typeface="monospace"/>
<TextView
android:id="@+id/key_details"
@ -42,7 +44,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<LinearLayout
android:gravity="right"
android:orientation="horizontal"
@ -67,4 +68,5 @@
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -24,7 +24,8 @@
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content" android:paddingLeft="36dip">
android:layout_height="wrap_content"
android:paddingLeft="36dip">
<TextView
android:id="@+id/main_user_id"

View File

@ -32,17 +32,21 @@
<ListView
android:id="@+id/account_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</ListView>
android:layout_height="fill_parent"/>
</ScrollView>
<LinearLayout
android:orientation="horizontal"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="@android:style/ButtonBar">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/btn_encryptMessage"
android:text="@string/btn_encryptMessage"
@ -59,4 +63,27 @@
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/btn_encryptFile"
android:text="@string/btn_encryptFile"
android:layout_width="wrap_content"
android:layout_weight="1"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/btn_decryptFile"
android:text="@string/btn_decryptFile"
android:layout_width="wrap_content"
android:layout_weight="1"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -26,8 +26,7 @@
android:choiceMode="multipleChoice"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1">
</ListView>
android:layout_weight="1"/>
<LinearLayout
android:orientation="horizontal"

View File

@ -40,7 +40,7 @@
<TextView
android:id="@+id/main_user_id"
android:text="Main User ID"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
@ -50,6 +50,7 @@
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
@ -64,7 +65,7 @@
<TextView
android:id="@+id/key_id"
android:text="BBBBBBBB"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textAppearance="?android:attr/textAppearanceSmall"
android:typeface="monospace"
android:layout_width="wrap_content"
android:layout_height="fill_parent"/>

View File

@ -24,7 +24,6 @@
<ListView
android:id="@+id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</ListView>
android:layout_height="fill_parent"/>
</LinearLayout>

View File

@ -33,7 +33,7 @@
<TextView
android:id="@+id/main_user_id"
android:text="Main User ID"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
@ -43,6 +43,7 @@
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
@ -56,7 +57,7 @@
<TextView
android:id="@+id/key_id"
android:text="BBBBBBBB"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textAppearance="?android:attr/textAppearanceSmall"
android:typeface="monospace"
android:layout_width="wrap_content"
android:layout_height="fill_parent"/>

View File

@ -23,6 +23,8 @@
<string name="title_selectSignature">Select Signature</string>
<string name="title_encryptMessage">Encrypt Message</string>
<string name="title_decryptMessage">Decrypt Message</string>
<string name="title_encryptFile">Encrypt File</string>
<string name="title_decryptFile">Decrypt File</string>
<string name="title_authentification">Authentification</string>
<string name="title_createKey">Create Key</string>
<string name="title_editKey">Edit Key</string>
@ -31,15 +33,21 @@
<string name="section_keys">Keys</string>
<string name="btn_send">Send via Email</string>
<string name="btn_encrypt">Encrypt</string>
<string name="btn_decrypt">Decrypt</string>
<string name="btn_verify">Verify</string>
<string name="btn_selectEncryptKeys">Select Recipients</string>
<string name="btn_reply">Reply</string>
<string name="btn_encryptMessage">Encrypt Message</string>
<string name="btn_decryptMessage">Decrypt Message</string>
<string name="btn_encryptFile">Encrypt File</string>
<string name="btn_decryptFile">Decrypt File</string>
<string name="btn_save">Save</string>
<string name="btn_doNotSave">Cancel</string>
<string name="tab_symmetric">Symmetric</string>
<string name="tab_asymmetric">Asymmetric</string>
<string name="menu_about">About</string>
<string name="menu_addAccount">Add GMail Account</string>
<string name="menu_managePublicKeys">Manage Public Keys</string>
@ -53,6 +61,9 @@
<string name="unknown_user_id">&lt;unknown&gt;</string>
<string name="none">&lt;none&gt;</string>
<string name="label_file">File:</string>
<string name="label_encrypt_keys">Encrypt with:</string>
<string name="sign_only">Sign only</string>
<string name="encrypt_only">Encrypt only</string>
<string name="sign_and_encrypt">Sign and Encrypt</string>

View File

@ -98,6 +98,8 @@ public class Apg {
public static class Intent {
public static final String DECRYPT = "org.thialfihar.android.apg.intent.DECRYPT";
public static final String ENCRYPT = "org.thialfihar.android.apg.intent.ENCRYPT";
public static final String DECRYPT_FILE = "org.thialfihar.android.apg.intent.DECRYPT_FILE";
public static final String ENCRYPT_FILE = "org.thialfihar.android.apg.intent.ENCRYPT_FILE";
}
public static String VERSION = "0.9.0";
@ -121,8 +123,8 @@ public class Apg {
CompressionAlgorithmTags.BZIP2,
CompressionAlgorithmTags.ZIP };
protected static Vector<PGPPublicKeyRing> mPublicKeyRings;
protected static Vector<PGPSecretKeyRing> mSecretKeyRings;
protected static Vector<PGPPublicKeyRing> mPublicKeyRings = new Vector<PGPPublicKeyRing>();
protected static Vector<PGPSecretKeyRing> mSecretKeyRings = new Vector<PGPSecretKeyRing>();
public static Pattern PGP_MESSAGE =
Pattern.compile(".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*",
@ -139,9 +141,6 @@ public class Apg {
protected static final int RETURN_OK = 0;
protected static final int RETURN_UPDATED = 1;
protected static final int TYPE_PUBLIC = 0;
protected static final int TYPE_SECRET = 1;
protected static HashMap<Long, Integer> mSecretKeyIdToIdMap;
protected static HashMap<Long, PGPSecretKeyRing> mSecretKeyIdToKeyRingMap;
protected static HashMap<Long, Integer> mPublicKeyIdToIdMap;
@ -180,13 +179,12 @@ public class Apg {
}
public static void initialize(Activity context) {
setPassPhrase(null);
if (mInitialized) {
return;
}
loadKeyRings(context, TYPE_PUBLIC);
loadKeyRings(context, TYPE_SECRET);
loadKeyRings(context, Id.type.public_key);
loadKeyRings(context, Id.type.secret_key);
mInitialized = true;
}
@ -546,8 +544,8 @@ public class Apg {
saveKeyRing(context, secretKeyRing);
saveKeyRing(context, publicKeyRing);
loadKeyRings(context, TYPE_PUBLIC);
loadKeyRings(context, TYPE_SECRET);
loadKeyRings(context, Id.type.public_key);
loadKeyRings(context, Id.type.secret_key);
progress.setProgress("done.", 100, 100);
}
@ -617,7 +615,7 @@ public class Apg {
Bundle returnData = new Bundle();
PGPObjectFactory objectFactors = null;
if (type == TYPE_SECRET) {
if (type == Id.type.secret_key) {
progress.setProgress("importing secret keys...", 0, 100);
} else {
progress.setProgress("importing public keys...", 0, 100);
@ -647,7 +645,7 @@ public class Apg {
PGPSecretKeyRing secretKeyRing;
int retValue;
if (type == TYPE_SECRET) {
if (type == Id.type.secret_key) {
if (!(obj instanceof PGPSecretKeyRing)) {
continue;
}
@ -729,7 +727,7 @@ public class Apg {
private static void loadKeyRings(Activity context, int type) {
Cursor cursor;
if (type == TYPE_SECRET) {
if (type == Id.type.secret_key) {
mSecretKeyRings.clear();
mSecretKeyIdToIdMap.clear();
mSecretKeyIdToKeyRingMap.clear();
@ -757,7 +755,7 @@ public class Apg {
long keyId = cursor.getLong(keyIdIndex);
try {
if (type == TYPE_SECRET) {
if (type == Id.type.secret_key) {
PGPSecretKeyRing key = new PGPSecretKeyRing(keyData);
mSecretKeyRings.add(key);
mSecretKeyIdToIdMap.put(keyId, id);
@ -775,7 +773,7 @@ public class Apg {
}
}
if (type == TYPE_SECRET) {
if (type == Id.type.secret_key) {
Collections.sort(mSecretKeyRings, new SecretKeySorter());
} else {
Collections.sort(mPublicKeyRings, new PublicKeySorter());
@ -1059,14 +1057,14 @@ public class Apg {
PGPPublicKey masterKey = getMasterKey(keyRing);
Uri uri = Uri.withAppendedPath(PublicKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
context.getContentResolver().delete(uri, null, null);
loadKeyRings(context, TYPE_PUBLIC);
loadKeyRings(context, Id.type.public_key);
}
public static void deleteKey(Activity context, PGPSecretKeyRing keyRing) {
PGPSecretKey masterKey = getMasterKey(keyRing);
Uri uri = Uri.withAppendedPath(SecretKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
context.getContentResolver().delete(uri, null, null);
loadKeyRings(context, TYPE_SECRET);
loadKeyRings(context, Id.type.secret_key);
}
public static PGPPublicKey findPublicKey(long keyId) {

View File

@ -35,8 +35,6 @@ import android.widget.LinearLayout;
import android.widget.Toast;
public class AskForSecretKeyPassPhrase {
public static final int DIALOG_PASS_PHRASE = 12345;
public static interface PassPhraseCallbackInterface {
void passPhraseCallback(String passPhrase);
}
@ -91,7 +89,7 @@ public class AskForSecretKeyPassPhrase {
alert.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
activity.removeDialog(DIALOG_PASS_PHRASE);
activity.removeDialog(Id.dialog.pass_phrase);
String passPhrase = "" + input.getText();
try {
secretKey.extractPrivateKey(passPhrase.toCharArray(),
@ -109,7 +107,7 @@ public class AskForSecretKeyPassPhrase {
alert.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
activity.removeDialog(DIALOG_PASS_PHRASE);
activity.removeDialog(Id.dialog.pass_phrase);
}
});

View File

@ -0,0 +1,235 @@
/*
* 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 android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
public class BaseActivity extends Activity
implements Runnable, ProgressDialogUpdater,
AskForSecretKeyPassPhrase.PassPhraseCallbackInterface {
private ProgressDialog mProgressDialog = null;
private Thread mRunningThread = null;
private long mSecretKeyId = 0;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
handlerCallback(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Apg.initialize(this);
}
@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("initializing...");
return mProgressDialog;
}
case Id.dialog.decrypting: {
mProgressDialog.setMessage("initializing...");
return mProgressDialog;
}
case Id.dialog.saving: {
mProgressDialog.setMessage("saving...");
return mProgressDialog;
}
case Id.dialog.importing: {
mProgressDialog.setMessage("importing...");
return mProgressDialog;
}
case Id.dialog.exporting: {
mProgressDialog.setMessage("exporting...");
return mProgressDialog;
}
default: {
break;
}
}
mProgressDialog = null;
switch (id) {
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("Error");
alert.setMessage("The pass phrases didn't match.");
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("Error");
alert.setMessage("Empty pass phrases are not supported.");
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();
}
default: {
break;
}
}
return super.onCreateDialog(id);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
default: {
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
@Override
public void setProgress(int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
data.putInt("type", Id.message.progress_update);
data.putInt("progress", progress);
data.putInt("max", max);
msg.setData(data);
mHandler.sendMessage(msg);
}
@Override
public void setProgress(String message, int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
data.putInt("type", Id.message.progress_update);
data.putString("message", message);
data.putInt("progress", progress);
data.putInt("max", max);
msg.setData(data);
mHandler.sendMessage(msg);
}
public void handlerCallback(Message msg) {
Bundle data = msg.getData();
if (data == null) {
return;
}
int type = data.getInt("type");
switch (type) {
case Id.message.progress_update: {
String message = data.getString("message");
if (mProgressDialog != null) {
if (message != null) {
mProgressDialog.setMessage(message);
}
mProgressDialog.setMax(data.getInt("max"));
mProgressDialog.setProgress(data.getInt("progress"));
}
break;
}
case Id.message.import_done: // intentionall no break
case Id.message.export_done: // intentionall no break
case Id.message.done: {
mProgressDialog = null;
doneCallback(msg);
break;
}
}
}
public void doneCallback(Message msg) {
}
public void passPhraseCallback(String passPhrase) {
Log.e("oink", "setting pass phrase to " + passPhrase);
Apg.setPassPhrase(passPhrase);
}
public void sendMessage(Message msg) {
mHandler.sendMessage(msg);
}
public void startThread() {
mRunningThread = new Thread(this);
mRunningThread.start();
}
public void run() {
}
public void setSecretKeyId(long id) {
mSecretKeyId = id;
}
public long getSecretKeyId() {
return mSecretKeyId;
}
}

View File

@ -29,13 +29,9 @@ import org.bouncycastle2.jce.provider.BouncyCastleProvider;
import org.bouncycastle2.openpgp.PGPException;
import org.bouncycastle2.util.Strings;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.ClipboardManager;
import android.view.View;
@ -47,27 +43,13 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
public class DecryptMessageActivity extends Activity
implements Runnable, ProgressDialogUpdater,
AskForSecretKeyPassPhrase.PassPhraseCallbackInterface {
static final int GET_PUCLIC_KEYS = 1;
static final int GET_SECRET_KEY = 2;
static final int DIALOG_DECRYPTING = 1;
static final int MESSAGE_PROGRESS_UPDATE = 1;
static final int MESSAGE_DONE = 2;
private long mDecryptionKeyId = 0;
public class DecryptMessageActivity extends BaseActivity {
private long mSignatureKeyId = 0;
private String mReplyTo = null;
private String mSubject = null;
private boolean mSignedOnly = false;
private ProgressDialog mProgressDialog = null;
private Thread mRunningThread = null;
private EditText mMessage = null;
private LinearLayout mSignatureLayout = null;
private ImageView mSignatureStatusImage = null;
@ -75,90 +57,11 @@ public class DecryptMessageActivity extends Activity
private TextView mUserIdRest = null;
private Button mDecryptButton = null;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Bundle data = msg.getData();
if (data != null) {
int type = data.getInt("type");
switch (type) {
case MESSAGE_PROGRESS_UPDATE: {
String message = data.getString("message");
if (mProgressDialog != null) {
if (message != null) {
mProgressDialog.setMessage(message);
}
mProgressDialog.setMax(data.getInt("max"));
mProgressDialog.setProgress(data.getInt("progress"));
}
break;
}
case MESSAGE_DONE: {
removeDialog(DIALOG_DECRYPTING);
mProgressDialog = null;
mSignatureKeyId = 0;
String error = data.getString("error");
String decryptedMessage = data.getString("decryptedMessage");
if (error != null) {
Toast.makeText(DecryptMessageActivity.this,
"Error: " + data.getString("error"),
Toast.LENGTH_SHORT).show();
}
mSignatureLayout.setVisibility(View.INVISIBLE);
if (decryptedMessage != null) {
mMessage.setText(decryptedMessage);
mDecryptButton.setText(R.string.btn_reply);
mDecryptButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
replyClicked();
}
});
if (data.getBoolean("signature")) {
String userId = data.getString("signatureUserId");
mSignatureKeyId = data.getLong("signatureKeyId");
mUserIdRest.setText("id: " +
Long.toHexString(mSignatureKeyId & 0xffffffffL));
if (userId == null) {
userId =
getResources()
.getString(
R.string.unknown_user_id);
}
String chunks[] = userId.split(" <", 2);
userId = chunks[0];
if (chunks.length > 1) {
mUserIdRest.setText("<" + chunks[1]);
}
mUserId.setText(userId);
if (data.getBoolean("signatureSuccess")) {
mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
} else if (data.getBoolean("signatureUnknown")) {
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
} else {
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
}
mSignatureLayout.setVisibility(View.VISIBLE);
}
}
break;
}
}
}
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.decrypt_message);
Apg.initialize(this);
mMessage = (EditText) findViewById(R.id.message);
mDecryptButton = (Button) findViewById(R.id.btn_decrypt);
mSignatureLayout = (LinearLayout) findViewById(R.id.layout_signature);
@ -232,48 +135,6 @@ public class DecryptMessageActivity extends Activity
}
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_DECRYPTING: {
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setMessage("initializing...");
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(false);
return mProgressDialog;
}
case AskForSecretKeyPassPhrase.DIALOG_PASS_PHRASE: {
return AskForSecretKeyPassPhrase.createDialog(this, mDecryptionKeyId, this);
}
}
return super.onCreateDialog(id);
}
@Override
public void setProgress(int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
data.putInt("type", MESSAGE_PROGRESS_UPDATE);
data.putInt("progress", progress);
data.putInt("max", max);
msg.setData(data);
mHandler.sendMessage(msg);
}
@Override
public void setProgress(String message, int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
data.putInt("type", MESSAGE_PROGRESS_UPDATE);
data.putString("message", message);
data.putInt("progress", progress);
data.putInt("max", max);
msg.setData(data);
mHandler.sendMessage(msg);
}
private void decryptClicked() {
String error = null;
String messageData = mMessage.getText().toString();
@ -289,8 +150,8 @@ public class DecryptMessageActivity extends Activity
ByteArrayInputStream in =
new ByteArrayInputStream(messageData.getBytes());
try {
mDecryptionKeyId = Apg.getDecryptionKeyId(in);
showDialog(AskForSecretKeyPassPhrase.DIALOG_PASS_PHRASE);
setSecretKeyId(Apg.getDecryptionKeyId(in));
showDialog(Id.dialog.pass_phrase);
} catch (IOException e) {
error = e.getLocalizedMessage();
} catch (Apg.GeneralException e) {
@ -311,22 +172,23 @@ public class DecryptMessageActivity extends Activity
intent.putExtra("subject", "Re: " + mSubject);
intent.putExtra("sendTo", mReplyTo);
intent.putExtra("eyId", mSignatureKeyId);
intent.putExtra("signatureKeyId", mDecryptionKeyId);
intent.putExtra("signatureKeyId", getSecretKeyId());
intent.putExtra("encryptionKeyIds", new long[] { mSignatureKeyId });
startActivity(intent);
}
@Override
public void passPhraseCallback(String passPhrase) {
Apg.setPassPhrase(passPhrase);
super.passPhraseCallback(passPhrase);
decryptStart();
}
private void decryptStart() {
showDialog(DIALOG_DECRYPTING);
mRunningThread = new Thread(this);
mRunningThread.start();
showDialog(Id.dialog.decrypting);
startThread();
}
@Override
public void run() {
String error = null;
Security.addProvider(new BouncyCastleProvider());
@ -356,7 +218,7 @@ public class DecryptMessageActivity extends Activity
error = e.getMessage();
}
data.putInt("type", MESSAGE_DONE);
data.putInt("type", Id.message.done);
if (error != null) {
data.putString("error", error);
@ -365,6 +227,61 @@ public class DecryptMessageActivity extends Activity
}
msg.setData(data);
mHandler.sendMessage(msg);
sendMessage(msg);
}
@Override
public void doneCallback(Message msg) {
super.doneCallback(msg);
Bundle data = msg.getData();
removeDialog(Id.dialog.decrypting);
mSignatureKeyId = 0;
String error = data.getString("error");
String decryptedMessage = data.getString("decryptedMessage");
if (error != null) {
Toast.makeText(DecryptMessageActivity.this,
"Error: " + data.getString("error"),
Toast.LENGTH_SHORT).show();
}
mSignatureLayout.setVisibility(View.INVISIBLE);
if (decryptedMessage != null) {
mMessage.setText(decryptedMessage);
mDecryptButton.setText(R.string.btn_reply);
mDecryptButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
replyClicked();
}
});
if (data.getBoolean("signature")) {
String userId = data.getString("signatureUserId");
mSignatureKeyId = data.getLong("signatureKeyId");
mUserIdRest.setText("id: " +
Long.toHexString(mSignatureKeyId & 0xffffffffL));
if (userId == null) {
userId =
getResources()
.getString(
R.string.unknown_user_id);
}
String chunks[] = userId.split(" <", 2);
userId = chunks[0];
if (chunks.length > 1) {
mUserIdRest.setText("<" + chunks[1]);
}
mUserId.setText(userId);
if (data.getBoolean("signatureSuccess")) {
mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
} else if (data.getBoolean("signatureUnknown")) {
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
} else {
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
}
mSignatureLayout.setVisibility(View.VISIBLE);
}
}
}
}

View File

@ -27,18 +27,16 @@ import org.bouncycastle2.openpgp.PGPSecretKeyRing;
import org.thialfihar.android.apg.ui.widget.SectionView;
import org.thialfihar.android.apg.utils.IterableIterator;
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.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.InputType;
import android.text.method.PasswordTransformationMethod;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
@ -50,17 +48,7 @@ import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Toast;
public class EditKeyActivity extends Activity
implements OnClickListener, ProgressDialogUpdater, Runnable {
static final int OPTION_MENU_NEW_PASS_PHRASE = 1;
static final int DIALOG_NEW_PASS_PHRASE = 1;
static final int DIALOG_PASS_PHRASES_DO_NOT_MATCH = 2;
static final int DIALOG_NO_PASS_PHRASE = 3;
static final int DIALOG_SAVING = 4;
static final int MESSAGE_PROGRESS_UPDATE = 1;
static final int MESSAGE_DONE = 2;
public class EditKeyActivity extends BaseActivity implements OnClickListener {
private PGPSecretKeyRing mKeyRing = null;
@ -70,56 +58,8 @@ public class EditKeyActivity extends Activity
private Button mSaveButton;
private Button mDiscardButton;
private ProgressDialog mProgressDialog = null;
private Thread mRunningThread = null;
private String mNewPassPhrase = null;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Bundle data = msg.getData();
if (data != null) {
int type = data.getInt("type");
switch (type) {
case MESSAGE_PROGRESS_UPDATE: {
String message = data.getString("message");
if (mProgressDialog != null) {
if (message != null) {
mProgressDialog.setMessage(message);
}
mProgressDialog.setMax(data.getInt("max"));
mProgressDialog.setProgress(data.getInt("progress"));
}
break;
}
case MESSAGE_DONE: {
removeDialog(DIALOG_SAVING);
mProgressDialog = null;
String error = data.getString("error");
if (error != null) {
Toast.makeText(EditKeyActivity.this,
"Error: " + data.getString("error"),
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(EditKeyActivity.this, R.string.key_saved,
Toast.LENGTH_SHORT).show();
setResult(RESULT_OK);
finish();
}
break;
}
default: {
break;
}
}
}
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -176,13 +116,14 @@ public class EditKeyActivity extends Activity
}
public boolean havePassPhrase() {
Log.e("oink", "password is " + Apg.getPassPhrase());
return (Apg.getPassPhrase() != null && !Apg.getPassPhrase().equals("")) ||
(mNewPassPhrase != null && mNewPassPhrase.equals(""));
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, OPTION_MENU_NEW_PASS_PHRASE, 0,
menu.add(0, Id.menu.option.new_pass_phrase, 0,
(havePassPhrase() ? "Change Pass Phrase" : "Set Pass Phrase"))
.setIcon(android.R.drawable.ic_menu_add);
return true;
@ -191,8 +132,8 @@ public class EditKeyActivity extends Activity
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case OPTION_MENU_NEW_PASS_PHRASE: {
showDialog(DIALOG_NEW_PASS_PHRASE);
case Id.menu.option.new_pass_phrase: {
showDialog(Id.dialog.new_pass_phrase);
return true;
}
@ -206,15 +147,7 @@ public class EditKeyActivity extends Activity
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_SAVING: {
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setMessage("saving...");
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(false);
return mProgressDialog;
}
case DIALOG_NEW_PASS_PHRASE: {
case Id.dialog.new_pass_phrase: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
if (havePassPhrase()) {
@ -252,17 +185,17 @@ public class EditKeyActivity extends Activity
alert.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(DIALOG_NEW_PASS_PHRASE);
removeDialog(Id.dialog.new_pass_phrase);
String passPhrase1 = "" + input1.getText();
String passPhrase2 = "" + input2.getText();
if (!passPhrase1.equals(passPhrase2)) {
showDialog(DIALOG_PASS_PHRASES_DO_NOT_MATCH);
showDialog(Id.dialog.pass_phrases_do_not_match);
return;
}
if (passPhrase1.equals("")) {
showDialog(DIALOG_NO_PASS_PHRASE);
showDialog(Id.dialog.no_pass_phrase);
return;
}
@ -273,49 +206,13 @@ public class EditKeyActivity extends Activity
alert.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(DIALOG_NEW_PASS_PHRASE);
removeDialog(Id.dialog.new_pass_phrase);
}
});
return alert.create();
}
case DIALOG_PASS_PHRASES_DO_NOT_MATCH: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setIcon(android.R.drawable.ic_dialog_alert);
alert.setTitle("Error");
alert.setMessage("The pass phrases didn't match.");
alert.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(DIALOG_PASS_PHRASES_DO_NOT_MATCH);
}
});
alert.setCancelable(false);
return alert.create();
}
case DIALOG_NO_PASS_PHRASE: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setIcon(android.R.drawable.ic_dialog_alert);
alert.setTitle("Error");
alert.setMessage("Empty pass phrases are not supported.");
alert.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(DIALOG_NO_PASS_PHRASE);
}
});
alert.setCancelable(false);
return alert.create();
}
default: {
break;
}
@ -334,16 +231,15 @@ public class EditKeyActivity extends Activity
}
private void saveClicked() {
if ((Apg.getPassPhrase() == null || Apg.getPassPhrase().equals("")) &&
(mNewPassPhrase == null || mNewPassPhrase.equals(""))) {
if (!havePassPhrase()) {
Toast.makeText(this, R.string.set_a_pass_phrase, Toast.LENGTH_SHORT).show();
return;
}
showDialog(DIALOG_SAVING);
mRunningThread = new Thread(this);
mRunningThread.start();
showDialog(Id.dialog.saving);
startThread();
}
@Override
public void run() {
String error = null;
Bundle data = new Bundle();
@ -368,34 +264,32 @@ public class EditKeyActivity extends Activity
error = e.getMessage();
}
data.putInt("type", MESSAGE_DONE);
data.putInt("type", Id.message.done);
if (error != null) {
data.putString("error", error);
}
msg.setData(data);
mHandler.sendMessage(msg);
sendMessage(msg);
}
public void setProgress(int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
data.putInt("type", MESSAGE_PROGRESS_UPDATE);
data.putInt("progress", progress);
data.putInt("max", max);
msg.setData(data);
mHandler.sendMessage(msg);
}
@Override
public void doneCallback(Message msg) {
super.doneCallback(msg);
public void setProgress(String message, int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
data.putInt("type", MESSAGE_PROGRESS_UPDATE);
data.putString("message", message);
data.putInt("progress", progress);
data.putInt("max", max);
msg.setData(data);
mHandler.sendMessage(msg);
Bundle data = msg.getData();
removeDialog(Id.dialog.saving);
String error = data.getString("error");
if (error != null) {
Toast.makeText(EditKeyActivity.this,
"Error: " + data.getString("error"),
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(EditKeyActivity.this, R.string.key_saved, Toast.LENGTH_SHORT).show();
setResult(RESULT_OK);
finish();
}
}
}

View File

@ -0,0 +1,304 @@
/*
* 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.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.util.Collections;
import java.util.Vector;
import org.bouncycastle2.bcpg.HashAlgorithmTags;
import org.bouncycastle2.openpgp.PGPException;
import org.bouncycastle2.openpgp.PGPPublicKeyRing;
import org.bouncycastle2.openpgp.PGPSecretKey;
import org.bouncycastle2.openpgp.PGPSecretKeyRing;
import org.openintents.intents.FileManager;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.TabHost;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.TabHost.TabSpec;
public class EncryptFileActivity extends BaseActivity {
private EditText mFilename = null;
private ImageButton mBrowse = null;
private CheckBox mSign = null;
private TextView mMainUserId = null;
private TextView mMainUserIdRest = null;
private ListView mList;
private Button mEncryptButton = null;
private long mEncryptionKeyIds[] = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.encrypt_file);
TabHost tabHost = (TabHost) findViewById(R.id.tab_host);
tabHost.setup();
TabSpec ts1 = tabHost.newTabSpec("TAB_ASYMMETRIC");
ts1.setIndicator(getString(R.string.tab_asymmetric),
getResources().getDrawable(R.drawable.key));
ts1.setContent(R.id.tab_asymmetric);
tabHost.addTab(ts1);
TabSpec ts2 = tabHost.newTabSpec("TAB_SYMMETRIC");
ts2.setIndicator(getString(R.string.tab_symmetric),
getResources().getDrawable(R.drawable.encrypted));
ts2.setContent(R.id.tab_symmetric);
tabHost.addTab(ts2);
tabHost.setCurrentTab(0);
Vector<PGPPublicKeyRing> keyRings =
(Vector<PGPPublicKeyRing>) Apg.getPublicKeyRings().clone();
Collections.sort(keyRings, new Apg.PublicKeySorter());
mList = (ListView) findViewById(R.id.public_key_list);
mList.setAdapter(new SelectPublicKeyListAdapter(mList, keyRings));
mFilename = (EditText) findViewById(R.id.filename);
mBrowse = (ImageButton) findViewById(R.id.btn_browse);
mBrowse.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
openFile();
}
});
mEncryptButton = (Button) findViewById(R.id.btn_encrypt);
mSign = (CheckBox) findViewById(R.id.sign);
mMainUserId = (TextView) findViewById(R.id.main_user_id);
mMainUserIdRest = (TextView) findViewById(R.id.main_user_id_rest);
mSign.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
CheckBox checkBox = (CheckBox) v;
if (checkBox.isChecked()) {
selectSecretKey();
} else {
setSecretKeyId(0);
Apg.setPassPhrase(null);
updateView();
}
}
});
mEncryptButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
encryptClicked();
}
});
updateView();
}
private void updateView() {
if (getSecretKeyId() == 0) {
mSign.setText(R.string.sign);
mSign.setChecked(false);
mMainUserId.setText("");
mMainUserIdRest.setText("");
} else {
String uid = getResources().getString(R.string.unknown_user_id);
String uidExtra = "";
PGPSecretKeyRing keyRing = Apg.getSecretKeyRing(getSecretKeyId());
if (keyRing != null) {
PGPSecretKey key = Apg.getMasterKey(keyRing);
if (key != null) {
String userId = Apg.getMainUserIdSafe(this, key);
String chunks[] = userId.split(" <", 2);
uid = chunks[0];
if (chunks.length > 1) {
uidExtra = "<" + chunks[1];
}
}
}
mMainUserId.setText(uid);
mMainUserIdRest.setText(uidExtra);
mSign.setText(R.string.sign_as);
mSign.setChecked(true);
}
}
private void openFile() {
String filename = mFilename.getText().toString();
Intent intent = new Intent(FileManager.ACTION_PICK_FILE);
intent.setData(Uri.parse("file://" + filename));
intent.putExtra(FileManager.EXTRA_TITLE, "Select file to encrypt...");
intent.putExtra(FileManager.EXTRA_BUTTON_TEXT, "Open");
try {
startActivityForResult(intent, Id.request.filename);
} catch (ActivityNotFoundException e) {
// No compatible file manager was found.
Toast.makeText(this, R.string.no_filemanager_installed, Toast.LENGTH_SHORT).show();
}
}
private void selectSecretKey() {
Intent intent = new Intent(this, SelectSecretKeyListActivity.class);
startActivityForResult(intent, Id.request.secret_keys);
}
private void encryptClicked() {
if (getSecretKeyId() != 0 && Apg.getPassPhrase() == null) {
showDialog(Id.dialog.pass_phrase);
} else {
encryptStart();
}
}
@Override
public void passPhraseCallback(String passPhrase) {
super.passPhraseCallback(passPhrase);
encryptStart();
}
private void encryptStart() {
showDialog(Id.dialog.encrypting);
startThread();
}
@Override
public void run() {
String error = null;
Bundle data = new Bundle();
Message msg = new Message();
try {
InputStream in = new FileInputStream(mFilename.getText().toString());
ByteArrayOutputStream out = new ByteArrayOutputStream();
if (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) {
Apg.encrypt(in, out, true, mEncryptionKeyIds, getSecretKeyId(),
Apg.getPassPhrase(), this);
data.putString("message", new String(out.toByteArray()));
} else {
Apg.signText(in, out, getSecretKeyId(),
Apg.getPassPhrase(), HashAlgorithmTags.SHA256, this);
data.putString("message", new String(out.toByteArray()));
}
} catch (IOException e) {
error = e.getMessage();
} catch (PGPException e) {
error = e.getMessage();
} catch (NoSuchProviderException e) {
error = e.getMessage();
} catch (NoSuchAlgorithmException e) {
error = e.getMessage();
} catch (SignatureException e) {
error = e.getMessage();
} catch (Apg.GeneralException e) {
error = e.getMessage();
}
data.putInt("type", Id.message.done);
if (error != null) {
data.putString("error", error);
}
msg.setData(data);
sendMessage(msg);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case Id.request.filename: {
if (resultCode == RESULT_OK && data != null) {
String filename = data.getDataString();
if (filename != null) {
// Get rid of URI prefix:
if (filename.startsWith("file://")) {
filename = filename.substring(7);
}
// replace %20 and so on
filename = Uri.decode(filename);
mFilename.setText(filename);
}
}
return;
}
case Id.request.secret_keys: {
if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
long newId = bundle.getLong("selectedKeyId");
if (getSecretKeyId() != newId) {
Apg.setPassPhrase(null);
}
setSecretKeyId(newId);
} else {
setSecretKeyId(0);
Apg.setPassPhrase(null);
}
updateView();
break;
}
default: {
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
@Override
public void doneCallback(Message msg) {
super.doneCallback(msg);
Bundle data = msg.getData();
removeDialog(Id.dialog.encrypting);
String error = data.getString("error");
if (error != null) {
Toast.makeText(EncryptFileActivity.this,
"Error: " + data.getString("error"),
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(EncryptFileActivity.this,
"Successfully encrypted.",
Toast.LENGTH_SHORT).show();
}
}
}

View File

@ -32,12 +32,8 @@ import org.bouncycastle2.openpgp.PGPSecretKey;
import org.bouncycastle2.openpgp.PGPSecretKeyRing;
import org.bouncycastle2.util.Strings;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
@ -47,25 +43,11 @@ import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class EncryptMessageActivity extends Activity
implements Runnable, ProgressDialogUpdater,
AskForSecretKeyPassPhrase.PassPhraseCallbackInterface {
static final int GET_PUCLIC_KEYS = 1;
static final int GET_SECRET_KEY = 2;
static final int DIALOG_ENCRYPTING = 1;
static final int MESSAGE_PROGRESS_UPDATE = 1;
static final int MESSAGE_DONE = 2;
public class EncryptMessageActivity extends BaseActivity {
private String mSubject = null;
private String mSendTo = null;
private long mEncryptionKeyIds[] = null;
private long mSignatureKeyId = 0;
private ProgressDialog mProgressDialog = null;
private Thread mRunningThread = null;
private EditText mMessage = null;
private Button mSelectKeysButton = null;
@ -74,92 +56,11 @@ public class EncryptMessageActivity extends Activity
private TextView mMainUserId = null;
private TextView mMainUserIdRest = null;
private Handler mhandler = new Handler() {
@Override
public void handleMessage(Message mSg) {
Bundle data = mSg.getData();
if (data != null) {
int type = data.getInt("type");
switch (type) {
case MESSAGE_PROGRESS_UPDATE: {
String message = data.getString("message");
if (mProgressDialog != null) {
if (message != null) {
mProgressDialog.setMessage(message);
}
mProgressDialog.setMax(data.getInt("max"));
mProgressDialog.setProgress(data.getInt("progress"));
}
break;
}
case MESSAGE_DONE: {
removeDialog(DIALOG_ENCRYPTING);
mProgressDialog = null;
String error = data.getString("error");
if (error != null) {
Toast.makeText(EncryptMessageActivity.this,
"Error: " + data.getString("error"),
Toast.LENGTH_SHORT).show();
return;
} else {
String message = data.getString("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 });
}
EncryptMessageActivity.this.
startActivity(Intent.createChooser(emailIntent, "Send mail..."));
}
break;
}
default: {
break;
}
}
}
}
};
@Override
public void setProgress(int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
data.putInt("type", MESSAGE_PROGRESS_UPDATE);
data.putInt("progress", progress);
data.putInt("max", max);
msg.setData(data);
mhandler.sendMessage(msg);
}
@Override
public void setProgress(String message, int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
data.putInt("type", MESSAGE_PROGRESS_UPDATE);
data.putString("message", message);
data.putInt("progress", progress);
data.putInt("max", max);
msg.setData(data);
mhandler.sendMessage(msg);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.encrypt_message);
Apg.initialize(this);
mMessage = (EditText) findViewById(R.id.message);
mSelectKeysButton = (Button) findViewById(R.id.btn_selectEncryptKeys);
mSendButton = (Button) findViewById(R.id.btn_send);
@ -183,7 +84,7 @@ public class EncryptMessageActivity extends Activity
if (masterKey != null) {
Vector<PGPSecretKey> signKeys = Apg.getUsableSigningKeys(keyRing);
if (signKeys.size() > 0) {
mSignatureKeyId = masterKey.getKeyID();
setSecretKeyId(masterKey.getKeyID());
}
}
}
@ -240,7 +141,7 @@ public class EncryptMessageActivity extends Activity
if (checkBox.isChecked()) {
selectSecretKey();
} else {
mSignatureKeyId = 0;
setSecretKeyId(0);
Apg.setPassPhrase(null);
updateView();
}
@ -250,49 +151,36 @@ public class EncryptMessageActivity extends Activity
updateView();
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_ENCRYPTING: {
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setMessage("initializing...");
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(false);
return mProgressDialog;
}
case AskForSecretKeyPassPhrase.DIALOG_PASS_PHRASE: {
return AskForSecretKeyPassPhrase.createDialog(this, mSignatureKeyId, this);
}
}
return super.onCreateDialog(id);
}
private void sendClicked() {
if (mSignatureKeyId != 0 && Apg.getPassPhrase() == null) {
showDialog(AskForSecretKeyPassPhrase.DIALOG_PASS_PHRASE);
if (getSecretKeyId() != 0 && Apg.getPassPhrase() == null) {
showDialog(Id.dialog.pass_phrase);
} else {
encryptStart();
}
}
@Override
public void passPhraseCallback(String passPhrase) {
Apg.setPassPhrase(passPhrase);
super.passPhraseCallback(passPhrase);
encryptStart();
}
private void encryptStart() {
showDialog(DIALOG_ENCRYPTING);
mRunningThread = new Thread(this);
mRunningThread.start();
showDialog(Id.dialog.encrypting);
startThread();
}
@Override
public void run() {
String error = null;
Bundle data = new Bundle();
Message msg = new Message();
try {
boolean encryptIt = mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0;
String message = mMessage.getText().toString();
if (!encryptIt) {
// fix the message a bit, trailing spaces and newlines break stuff,
// because GMail sends as HTML and such things fuck up the signature,
// TODO: things like "<" and ">" also fuck up the signature
@ -301,18 +189,18 @@ public class EncryptMessageActivity extends Activity
message = message.replaceFirst("^\n+", "");
// make sure there'll be exactly one newline at the end
message = message.replaceFirst("\n*$", "\n");
}
ByteArrayInputStream in =
new ByteArrayInputStream(Strings.toUTF8ByteArray(message));
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
if (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) {
Apg.encrypt(in, out, true, mEncryptionKeyIds, mSignatureKeyId,
if (encryptIt) {
Apg.encrypt(in, out, true, mEncryptionKeyIds, getSecretKeyId(),
Apg.getPassPhrase(), this);
data.putString("message", new String(out.toByteArray()));
} else {
Apg.signText(in, out, mSignatureKeyId,
Apg.signText(in, out, getSecretKeyId(),
Apg.getPassPhrase(), HashAlgorithmTags.SHA256, this);
data.putString("message", new String(out.toByteArray()));
}
@ -330,14 +218,14 @@ public class EncryptMessageActivity extends Activity
error = e.getMessage();
}
data.putInt("type", MESSAGE_DONE);
data.putInt("type", Id.message.done);
if (error != null) {
data.putString("error", error);
}
msg.setData(data);
mhandler.sendMessage(msg);
sendMessage(msg);
}
private void updateView() {
@ -350,7 +238,7 @@ public class EncryptMessageActivity extends Activity
getResources().getString(R.string.n_keys_selected));
}
if (mSignatureKeyId == 0) {
if (getSecretKeyId() == 0) {
mSign.setText(R.string.sign);
mSign.setChecked(false);
mMainUserId.setText("");
@ -358,7 +246,7 @@ public class EncryptMessageActivity extends Activity
} else {
String uid = getResources().getString(R.string.unknown_user_id);
String uidExtra = "";
PGPSecretKeyRing keyRing = Apg.getSecretKeyRing(mSignatureKeyId);
PGPSecretKeyRing keyRing = Apg.getSecretKeyRing(getSecretKeyId());
if (keyRing != null) {
PGPSecretKey key = Apg.getMasterKey(keyRing);
if (key != null) {
@ -380,18 +268,18 @@ public class EncryptMessageActivity extends Activity
private void selectPublicKeys() {
Intent intent = new Intent(this, SelectPublicKeyListActivity.class);
intent.putExtra("selection", mEncryptionKeyIds);
startActivityForResult(intent, GET_PUCLIC_KEYS);
startActivityForResult(intent, Id.request.public_keys);
}
private void selectSecretKey() {
Intent intent = new Intent(this, SelectSecretKeyListActivity.class);
startActivityForResult(intent, GET_SECRET_KEY);
startActivityForResult(intent, Id.request.secret_keys);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case GET_PUCLIC_KEYS: {
case Id.request.public_keys: {
if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
mEncryptionKeyIds = bundle.getLongArray("selection");
@ -400,26 +288,58 @@ public class EncryptMessageActivity extends Activity
break;
}
case GET_SECRET_KEY: {
case Id.request.secret_keys: {
if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
long newId = bundle.getLong("selectedKeyId");
if (mSignatureKeyId != newId) {
if (getSecretKeyId() != newId) {
Apg.setPassPhrase(null);
}
mSignatureKeyId = newId;
setSecretKeyId(newId);
} else {
mSignatureKeyId = 0;
setSecretKeyId(0);
Apg.setPassPhrase(null);
}
updateView();
break;
}
default:
default: {
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
@Override
public void doneCallback(Message msg) {
super.doneCallback(msg);
removeDialog(Id.dialog.encrypting);
Bundle data = msg.getData();
String error = data.getString("error");
if (error != null) {
Toast.makeText(EncryptMessageActivity.this,
"Error: " + data.getString("error"),
Toast.LENGTH_SHORT).show();
return;
} else {
String message = data.getString("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 });
}
EncryptMessageActivity.this.
startActivity(Intent.createChooser(emailIntent, "Send mail..."));
}
}
}

View File

@ -32,8 +32,7 @@ import android.widget.ImageButton;
import android.widget.Toast;
public class FileDialog {
public static final int REQUEST_CODE_PICK_FILE_OR_DIRECTORY = 12345;
private static EditText mInput;
private static EditText mFilename;
private static ImageButton mBrowse;
private static Activity mActivity;
private static String mFileManagerTitle;
@ -57,8 +56,8 @@ public class FileDialog {
View view = (View) inflater.inflate(R.layout.file_dialog, null);
mActivity = activity;
mInput = (EditText) view.findViewById(R.id.input);
mInput.setText(defaultFile);
mFilename = (EditText) view.findViewById(R.id.input);
mFilename.setText(defaultFile);
mBrowse = (ImageButton) view.findViewById(R.id.btn_browse);
mBrowse.setOnClickListener(new View.OnClickListener() {
@Override
@ -75,7 +74,7 @@ public class FileDialog {
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
clickListener.onOkClick(mInput.getText().toString());
clickListener.onOkClick(mFilename.getText().toString());
}
});
@ -89,8 +88,8 @@ public class FileDialog {
}
public static void setFilename(String filename) {
if (mInput != null) {
mInput.setText(filename);
if (mFilename != null) {
mFilename.setText(filename);
}
}
@ -98,17 +97,17 @@ public class FileDialog {
* Opens the file manager to select a file to open.
*/
private static void openFile() {
String fileName = mInput.getText().toString();
String filename = mFilename.getText().toString();
Intent intent = new Intent(FileManager.ACTION_PICK_FILE);
intent.setData(Uri.parse("file://" + fileName));
intent.setData(Uri.parse("file://" + filename));
intent.putExtra(FileManager.EXTRA_TITLE, mFileManagerTitle);
intent.putExtra(FileManager.EXTRA_BUTTON_TEXT, mFileManagerButton);
try {
mActivity.startActivityForResult(intent, REQUEST_CODE_PICK_FILE_OR_DIRECTORY);
mActivity.startActivityForResult(intent, Id.request.filename);
} catch (ActivityNotFoundException e) {
// No compatible file manager was found.
Toast.makeText(mActivity, R.string.no_filemanager_installed, Toast.LENGTH_SHORT).show();

View File

@ -0,0 +1,82 @@
/*
* 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;
public final class Id {
public static final class menu {
public static final int export = 0x21070001;
public static final int delete = 0x21070002;
public static final int edit = 0x21070003;
public static final class option {
public static final int new_pass_phrase = 0x21070001;
public static final int create = 0x21070002;
public static final int about = 0x21070003;
public static final int manage_public_keys = 0x21070004;
public static final int manage_secret_keys = 0x21070005;
public static final int import_keys = 0x21070006;
public static final int export_keys = 0x21070007;
}
}
public static final class message {
public static final int progress_update = 0x21070001;
public static final int done = 0x21070002;
public static final int import_keys = 0x21070003;
public static final int export_keys = 0x21070004;
public static final int import_done = 0x21070005;
public static final int export_done = 0x21070006;
public static final int create_key = 0x21070007;
public static final int edit_key = 0x21070008;
}
public static final class request {
public static final int public_keys = 0x21070001;
public static final int secret_keys = 0x21070002;
public static final int filename = 0x21070003;
}
public static final class dialog {
public static final int pass_phrase = 0x21070001;
public static final int encrypting = 0x21070002;
public static final int decrypting = 0x21070003;
public static final int new_pass_phrase = 0x21070004;
public static final int pass_phrases_do_not_match = 0x21070005;
public static final int no_pass_phrase = 0x21070006;
public static final int saving = 0x21070007;
public static final int delete_key = 0x21070008;
public static final int import_keys = 0x21070009;
public static final int importing = 0x2107000a;
public static final int export_key = 0x2107000b;
public static final int export_keys = 0x2107000c;
public static final int exporting = 0x2107000d;
public static final int new_account = 0x2107000e;
public static final int about = 0x2107000f;
public static final int change_log = 0x21070010;
}
public static final class task {
public static final int import_keys = 0x21070001;
public static final int export_keys = 0x21070002;
}
public static final class type {
public static final int public_key = 0x21070001;
public static final int secret_key = 0x21070002;
}
}

View File

@ -37,7 +37,7 @@ import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
public class MailListActivity extends ListActivity {
LayoutInflater minflater = null;
LayoutInflater mInflater = null;
private static class Conversation {
public long id;
@ -82,7 +82,7 @@ public class MailListActivity extends ListActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
minflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mconversations = new Vector<Conversation>();
mmessages = new Vector<Message>();
@ -191,7 +191,7 @@ public class MailListActivity extends ListActivity {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = minflater.inflate(R.layout.mailbox_message_item, null);
View view = mInflater.inflate(R.layout.mailbox_message_item, null);
Message message = (Message) getItem(position);

View File

@ -51,17 +51,7 @@ import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;
public class MainActivity extends Activity {
private static final int DIALOG_NEW_ACCOUNT = 1;
private static final int DIALOG_ABOUT = 2;
private static final int DIALOG_CHANGE_LOG = 3;
private static final int OPTION_MENU_ADD_ACCOUNT = 1;
private static final int OPTION_MENU_ABOUT = 2;
private static final int OPTION_MENU_MANAGE_PUBLIC_KEYS = 3;
private static final int OPTION_MENU_MANAGE_SECRET_KEYS = 4;
private static final int MENU_DELETE_ACCOUNT = 1;
public class MainActivity extends BaseActivity {
private static String PREF_SEEN_CHANGE_LOG = "seenChangeLogDialog" + Apg.VERSION;
@ -72,10 +62,10 @@ public class MainActivity extends Activity {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Apg.initialize(this);
Button encryptMessageButton = (Button) findViewById(R.id.btn_encryptMessage);
Button decryptMessageButton = (Button) findViewById(R.id.btn_decryptMessage);
Button encryptFileButton = (Button) findViewById(R.id.btn_encryptFile);
Button decryptFileButton = (Button) findViewById(R.id.btn_decryptFile);
mAccounts = (ListView) findViewById(R.id.account_list);
encryptMessageButton.setOnClickListener(new OnClickListener() {
@ -92,6 +82,20 @@ public class MainActivity extends Activity {
}
});
encryptFileButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startEncryptFileActivity();
}
});
decryptFileButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startDecryptFileActivity();
}
});
Cursor accountCursor = managedQuery(Accounts.CONTENT_URI, null, null, null, null);
mAccounts.setAdapter(new AccountListAdapter(this, accountCursor));
@ -113,14 +117,14 @@ public class MainActivity extends Activity {
SharedPreferences prefs = getPreferences(MODE_PRIVATE);
if (!prefs.getBoolean(PREF_SEEN_CHANGE_LOG, false)) {
showDialog(DIALOG_CHANGE_LOG);
showDialog(Id.dialog.change_log);
}
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_NEW_ACCOUNT: {
case Id.dialog.new_account: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle("Add Account");
@ -132,7 +136,7 @@ public class MainActivity extends Activity {
alert.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
MainActivity.this.removeDialog(DIALOG_NEW_ACCOUNT);
MainActivity.this.removeDialog(Id.dialog.new_account);
String accountName = "" + input.getText();
Cursor testCursor =
@ -165,14 +169,14 @@ public class MainActivity extends Activity {
alert.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
MainActivity.this.removeDialog(DIALOG_NEW_ACCOUNT);
MainActivity.this.removeDialog(Id.dialog.new_account);
}
});
return alert.create();
}
case DIALOG_ABOUT: {
case Id.dialog.about: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle("About " + Apg.FULL_VERSION);
@ -205,14 +209,14 @@ public class MainActivity extends Activity {
alert.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
MainActivity.this.removeDialog(DIALOG_ABOUT);
MainActivity.this.removeDialog(Id.dialog.about);
}
});
return alert.create();
}
case DIALOG_CHANGE_LOG: {
case Id.dialog.change_log: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle("Changes " + Apg.FULL_VERSION);
@ -246,7 +250,7 @@ public class MainActivity extends Activity {
alert.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
MainActivity.this.removeDialog(DIALOG_CHANGE_LOG);
MainActivity.this.removeDialog(Id.dialog.change_log);
SharedPreferences prefs = getPreferences(MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(PREF_SEEN_CHANGE_LOG, true);
@ -261,18 +265,19 @@ public class MainActivity extends Activity {
break;
}
}
return super.onCreateDialog(id);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, OPTION_MENU_MANAGE_PUBLIC_KEYS, 0, R.string.menu_managePublicKeys)
menu.add(0, Id.menu.option.manage_public_keys, 0, R.string.menu_managePublicKeys)
.setIcon(android.R.drawable.ic_menu_manage);
menu.add(0, OPTION_MENU_MANAGE_SECRET_KEYS, 1, R.string.menu_manageSecretKeys)
menu.add(0, Id.menu.option.manage_secret_keys, 1, R.string.menu_manageSecretKeys)
.setIcon(android.R.drawable.ic_menu_manage);
menu.add(1, OPTION_MENU_ADD_ACCOUNT, 2, R.string.menu_addAccount)
menu.add(1, Id.menu.option.create, 2, R.string.menu_addAccount)
.setIcon(android.R.drawable.ic_menu_add);
menu.add(1, OPTION_MENU_ABOUT, 3, R.string.menu_about)
menu.add(1, Id.menu.option.about, 3, R.string.menu_about)
.setIcon(android.R.drawable.ic_menu_info_details);
return true;
}
@ -280,22 +285,22 @@ public class MainActivity extends Activity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case OPTION_MENU_ADD_ACCOUNT: {
showDialog(DIALOG_NEW_ACCOUNT);
case Id.menu.option.create: {
showDialog(Id.dialog.new_account);
return true;
}
case OPTION_MENU_ABOUT: {
showDialog(DIALOG_ABOUT);
case Id.menu.option.about: {
showDialog(Id.dialog.about);
return true;
}
case OPTION_MENU_MANAGE_PUBLIC_KEYS: {
case Id.menu.option.manage_public_keys: {
startPublicKeyManager();
return true;
}
case OPTION_MENU_MANAGE_SECRET_KEYS: {
case Id.menu.option.manage_secret_keys: {
startSecretKeyManager();
return true;
}
@ -314,7 +319,7 @@ public class MainActivity extends Activity {
TextView nameTextView = (TextView) v.findViewById(R.id.account_name);
if (nameTextView != null) {
menu.setHeaderTitle(nameTextView.getText());
menu.add(0, MENU_DELETE_ACCOUNT, 0, "Delete Account");
menu.add(0, Id.menu.delete, 0, "Delete Account");
}
}
@ -324,7 +329,7 @@ public class MainActivity extends Activity {
(AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
switch (menuItem.getItemId()) {
case MENU_DELETE_ACCOUNT: {
case Id.menu.delete: {
Uri uri = Uri.withAppendedPath(Accounts.CONTENT_URI, "" + info.id);
this.getContentResolver().delete(uri, null, null);
return true;
@ -342,7 +347,6 @@ public class MainActivity extends Activity {
public void startSecretKeyManager() {
startActivity(new Intent(this, SecretKeyListActivity.class));
//startActivity(new Intent(this, EditKeyActivity.class));
}
public void startEncryptMessageActivity() {
@ -353,6 +357,14 @@ public class MainActivity extends Activity {
startActivity(new Intent(this, DecryptMessageActivity.class));
}
public void startEncryptFileActivity() {
startActivity(new Intent(this, EncryptFileActivity.class));
}
public void startDecryptFileActivity() {
//startActivity(new Intent(this, DecryptFileActivity.class));
}
public void startMailListActivity(String account) {
startActivity(new Intent(this, MailListActivity.class).putExtra("account", account));
}

View File

@ -27,17 +27,13 @@ import org.thialfihar.android.apg.utils.IterableIterator;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ExpandableListActivity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
@ -52,59 +48,280 @@ import android.widget.TextView;
import android.widget.Toast;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
public class PublicKeyListActivity extends ExpandableListActivity
implements Runnable, ProgressDialogUpdater {
static final int MENU_DELETE = 1;
static final int MENU_EXPORT = 2;
static final int OPTION_MENU_IMPORT_KEYS = 1;
static final int OPTION_MENU_EXPORT_KEYS = 2;
static final int MESSAGE_PROGRESS_UPDATE = 1;
static final int MESSAGE_IMPORT_DONE = 2;
static final int MESSAGE_EXPORT_DONE = 3;
static final int DIALOG_DELETE_KEY = 1;
static final int DIALOG_IMPORT_KEYS = 2;
static final int DIALOG_IMPORTING = 3;
static final int DIALOG_EXPORT_KEYS = 4;
static final int DIALOG_EXPORTING = 5;
static final int DIALOG_EXPORT_KEY = 6;
static final int TASK_IMPORT = 1;
static final int TASK_EXPORT = 2;
public class PublicKeyListActivity extends BaseActivity {
ExpandableListView mList;
protected int mSelectedItem = -1;
protected int mTask = 0;
private ProgressDialog mProgressDialog = null;
private Thread mRunningThread = null;
private String mImportFilename = Environment.getExternalStorageDirectory() + "/pubring.gpg";
private String mExportFilename = Environment.getExternalStorageDirectory() + "/pubexport.asc";
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.key_list);
mList = (ExpandableListView) findViewById(R.id.list);
mList.setAdapter(new PublicKeyListAdapter(this));
registerForContextMenu(mList);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, Id.menu.option.import_keys, 0, "Import Keys")
.setIcon(android.R.drawable.ic_menu_add);
menu.add(0, Id.menu.option.export_keys, 1, "Export Keys")
.setIcon(android.R.drawable.ic_menu_save);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case Id.menu.option.import_keys: {
showDialog(Id.dialog.import_keys);
return true;
}
case Id.menu.option.export_keys: {
showDialog(Id.dialog.export_keys);
return true;
}
default: {
break;
}
}
return false;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
ExpandableListView.ExpandableListContextMenuInfo info =
(ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(groupPosition);
String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
menu.setHeaderTitle(userId);
menu.add(0, Id.menu.export, 0, "Export Key");
menu.add(0, Id.menu.delete, 1, "Delete Key");
}
}
@Override
public boolean onContextItemSelected(MenuItem menuItem) {
ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuItem.getMenuInfo();
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
if (type != ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
return super.onContextItemSelected(menuItem);
}
switch (menuItem.getItemId()) {
case Id.menu.export: {
mSelectedItem = groupPosition;
showDialog(Id.dialog.export_key);
return true;
}
case Id.menu.delete: {
mSelectedItem = groupPosition;
showDialog(Id.dialog.delete_key);
return true;
}
default: {
return super.onContextItemSelected(menuItem);
}
}
}
@Override
protected Dialog onCreateDialog(int id) {
boolean singleKeyExport = false;
switch (id) {
case Id.dialog.delete_key: {
PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(mSelectedItem);
String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Warning ");
builder.setMessage("Do you really want to delete the key '" + userId + "'?\n" +
"You can't undo this!");
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setPositiveButton("Delete", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
deleteKey(mSelectedItem);
mSelectedItem = -1;
removeDialog(Id.dialog.delete_key);
}
});
builder.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mSelectedItem = -1;
removeDialog(Id.dialog.delete_key);
}
});
return builder.create();
}
case Id.dialog.import_keys: {
return FileDialog.build(this, "Import Keys",
"Please specify which file to import from.",
mImportFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(Id.dialog.import_keys);
mImportFilename = filename;
importKeys();
}
@Override
public void onCancelClick() {
removeDialog(Id.dialog.import_keys);
}
},
getString(R.string.filemanager_title_open),
getString(R.string.filemanager_btn_open));
}
case Id.dialog.export_key: {
singleKeyExport = true;
// break intentionally omitted, to use the Id.dialog.export_keys dialog
}
case Id.dialog.export_keys: {
String title = "Export Key";
if (!singleKeyExport) {
// plural "Keys"
title += "s";
}
final int thisDialogId = (singleKeyExport ? Id.dialog.export_key : Id.dialog.export_keys);
return FileDialog.build(this, title,
"Please specify which file to export to.\n" +
"WARNING! File will be overwritten if it exists.",
mExportFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(thisDialogId);
mExportFilename = filename;
exportKeys();
}
@Override
public void onCancelClick() {
removeDialog(thisDialogId);
}
},
getString(R.string.filemanager_title_save),
getString(R.string.filemanager_btn_save));
}
default: {
break;
}
}
return super.onCreateDialog(id);
}
public void importKeys() {
showDialog(Id.dialog.importing);
mTask = Id.task.import_keys;
startThread();
}
public void exportKeys() {
showDialog(Id.dialog.exporting);
mTask = Id.task.export_keys;
startThread();
}
@Override
public void run() {
String error = null;
Bundle data = new Bundle();
Message msg = new Message();
String filename = null;
if (mTask == Id.task.import_keys) {
filename = mImportFilename;
} else {
filename = mExportFilename;
}
try {
if (mTask == Id.task.import_keys) {
data = Apg.importKeyRings(this, Id.type.public_key, filename, this);
} else {
Vector<Object> keys = new Vector<Object>();
if (mSelectedItem == -1) {
for (PGPPublicKeyRing key : Apg.getPublicKeyRings()) {
keys.add(key);
}
} else {
keys.add(Apg.getPublicKeyRings().get(mSelectedItem));
}
data = Apg.exportKeyRings(this, keys, filename, this);
}
} catch (FileNotFoundException e) {
error = "file '" + filename + "' not found";
} catch (IOException e) {
error = e.getMessage();
} catch (PGPException e) {
error = e.getMessage();
} catch (Apg.GeneralException e) {
error = e.getMessage();
}
if (mTask == Id.task.import_keys) {
data.putInt("type", Id.message.import_done);
} else {
data.putInt("type", Id.message.export_done);
}
if (error != null) {
data.putString("error", error);
}
msg.setData(data);
sendMessage(msg);
}
private void deleteKey(int index) {
PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(index);
Apg.deleteKey(this, keyRing);
refreshList();
}
private void refreshList() {
((PublicKeyListAdapter) mList.getExpandableListAdapter()).notifyDataSetChanged();
}
@Override
public void doneCallback(Message msg) {
super.doneCallback(msg);
Bundle data = msg.getData();
if (data != null) {
int type = data.getInt("type");
switch (type) {
case MESSAGE_PROGRESS_UPDATE: {
String message = data.getString("message");
if (mProgressDialog != null) {
if (message != null) {
mProgressDialog.setMessage(message);
}
mProgressDialog.setMax(data.getInt("max"));
mProgressDialog.setProgress(data.getInt("progress"));
}
break;
}
case MESSAGE_IMPORT_DONE: {
removeDialog(DIALOG_IMPORTING);
mProgressDialog = null;
case Id.message.import_done: {
removeDialog(Id.dialog.importing);
String error = data.getString("error");
if (error != null) {
@ -132,9 +349,8 @@ public class PublicKeyListActivity extends ExpandableListActivity
break;
}
case MESSAGE_EXPORT_DONE: {
removeDialog(DIALOG_EXPORTING);
mProgressDialog = null;
case Id.message.export_done: {
removeDialog(Id.dialog.exporting);
String error = data.getString("error");
if (error != null) {
@ -163,295 +379,6 @@ public class PublicKeyListActivity extends ExpandableListActivity
}
}
}
};
public void setProgress(int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
data.putInt("type", MESSAGE_PROGRESS_UPDATE);
data.putInt("progress", progress);
data.putInt("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("type", MESSAGE_PROGRESS_UPDATE);
data.putString("message", message);
data.putInt("progress", progress);
data.putInt("max", max);
msg.setData(data);
mHandler.sendMessage(msg);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Apg.initialize(this);
setListAdapter(new PublicKeyListAdapter(this));
registerForContextMenu(getExpandableListView());
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, OPTION_MENU_IMPORT_KEYS, 0, "Import Keys")
.setIcon(android.R.drawable.ic_menu_add);
menu.add(0, OPTION_MENU_EXPORT_KEYS, 1, "Export Keys")
.setIcon(android.R.drawable.ic_menu_save);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case OPTION_MENU_IMPORT_KEYS: {
showDialog(DIALOG_IMPORT_KEYS);
return true;
}
case OPTION_MENU_EXPORT_KEYS: {
showDialog(DIALOG_EXPORT_KEYS);
return true;
}
default: {
break;
}
}
return false;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
ExpandableListView.ExpandableListContextMenuInfo info =
(ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(groupPosition);
String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
menu.setHeaderTitle(userId);
menu.add(0, MENU_EXPORT, 0, "Export Key");
menu.add(0, MENU_DELETE, 1, "Delete Key");
}
}
@Override
public boolean onContextItemSelected(MenuItem menuItem) {
ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuItem.getMenuInfo();
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
if (type != ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
return super.onContextItemSelected(menuItem);
}
switch (menuItem.getItemId()) {
case MENU_EXPORT: {
mSelectedItem = groupPosition;
showDialog(DIALOG_EXPORT_KEY);
return true;
}
case MENU_DELETE: {
mSelectedItem = groupPosition;
showDialog(DIALOG_DELETE_KEY);
return true;
}
default: {
return super.onContextItemSelected(menuItem);
}
}
}
@Override
protected Dialog onCreateDialog(int id) {
boolean singleKeyExport = false;
switch (id) {
case DIALOG_DELETE_KEY: {
PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(mSelectedItem);
String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Warning ");
builder.setMessage("Do you really want to delete the key '" + userId + "'?\n" +
"You can't undo this!");
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setPositiveButton("Delete", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
deleteKey(mSelectedItem);
mSelectedItem = -1;
removeDialog(DIALOG_DELETE_KEY);
}
});
builder.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mSelectedItem = -1;
removeDialog(DIALOG_DELETE_KEY);
}
});
return builder.create();
}
case DIALOG_IMPORT_KEYS: {
return FileDialog.build(this, "Import Keys",
"Please specify which file to import from.",
mImportFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(DIALOG_IMPORT_KEYS);
mImportFilename = filename;
importKeys();
}
@Override
public void onCancelClick() {
removeDialog(DIALOG_IMPORT_KEYS);
}
},
getString(R.string.filemanager_title_open),
getString(R.string.filemanager_btn_open));
}
case DIALOG_EXPORT_KEY: {
singleKeyExport = true;
// break intentionally omitted, to use the DIALOG_EXPORT_KEYS dialog
}
case DIALOG_EXPORT_KEYS: {
String title = "Export Key";
if (!singleKeyExport) {
// plural "Keys"
title += "s";
}
final int thisDialogId = (singleKeyExport ? DIALOG_EXPORT_KEY : DIALOG_EXPORT_KEYS);
return FileDialog.build(this, title,
"Please specify which file to export to.\n" +
"WARNING! File will be overwritten if it exists.",
mExportFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(thisDialogId);
mExportFilename = filename;
exportKeys();
}
@Override
public void onCancelClick() {
removeDialog(thisDialogId);
}
},
getString(R.string.filemanager_title_save),
getString(R.string.filemanager_btn_save));
}
case DIALOG_IMPORTING: {
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setMessage("importing...");
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(false);
return mProgressDialog;
}
case DIALOG_EXPORTING: {
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setMessage("exporting...");
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(false);
return mProgressDialog;
}
}
return super.onCreateDialog(id);
}
public void importKeys() {
showDialog(DIALOG_IMPORTING);
mTask = TASK_IMPORT;
mRunningThread = new Thread(this);
mRunningThread.start();
}
public void exportKeys() {
showDialog(DIALOG_EXPORTING);
mTask = TASK_EXPORT;
mRunningThread = new Thread(this);
mRunningThread.start();
}
public void run() {
String error = null;
Bundle data = new Bundle();
Message msg = new Message();
String filename = null;
if (mTask == TASK_IMPORT) {
filename = mImportFilename;
} else {
filename = mExportFilename;
}
try {
if (mTask == TASK_IMPORT) {
data = Apg.importKeyRings(this, Apg.TYPE_PUBLIC, filename, this);
} else {
Vector<Object> keys = new Vector<Object>();
if (mSelectedItem == -1) {
for (PGPPublicKeyRing key : Apg.getPublicKeyRings()) {
keys.add(key);
}
} else {
keys.add(Apg.getPublicKeyRings().get(mSelectedItem));
}
data = Apg.exportKeyRings(this, keys, filename, this);
}
} catch (FileNotFoundException e) {
error = "file '" + filename + "' not found";
} catch (IOException e) {
error = e.getMessage();
} catch (PGPException e) {
error = e.getMessage();
} catch (Apg.GeneralException e) {
error = e.getMessage();
}
if (mTask == TASK_IMPORT) {
data.putInt("type", MESSAGE_IMPORT_DONE);
} else {
data.putInt("type", MESSAGE_EXPORT_DONE);
}
if (error != null) {
data.putString("error", error);
}
msg.setData(data);
mHandler.sendMessage(msg);
}
private void deleteKey(int index) {
PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(index);
Apg.deleteKey(this, keyRing);
refreshList();
}
private void refreshList() {
((PublicKeyListAdapter) getExpandableListAdapter()).notifyDataSetChanged();
}
private static class PublicKeyListAdapter extends BaseExpandableListAdapter {
private LayoutInflater mInflater;
@ -633,7 +560,7 @@ public class PublicKeyListActivity extends ExpandableListActivity
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case FileDialog.REQUEST_CODE_PICK_FILE_OR_DIRECTORY: {
case Id.request.filename: {
if (resultCode == RESULT_OK && data != null) {
String filename = data.getDataString();
if (filename != null) {

View File

@ -27,15 +27,12 @@ import org.thialfihar.android.apg.utils.IterableIterator;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ExpandableListActivity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.ContextMenu;
import android.view.LayoutInflater;
@ -52,66 +49,362 @@ import android.widget.Toast;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
import android.widget.ExpandableListView.OnChildClickListener;
public class SecretKeyListActivity extends ExpandableListActivity
implements Runnable, ProgressDialogUpdater, OnChildClickListener,
AskForSecretKeyPassPhrase.PassPhraseCallbackInterface {
static final int CREATE_SECRET_KEY = 1;
static final int EDIT_SECRET_KEY = 2;
static final int MENU_EDIT = 1;
static final int MENU_EXPORT = 2;
static final int MENU_DELETE = 3;
static final int OPTION_MENU_IMPORT_KEYS = 1;
static final int OPTION_MENU_EXPORT_KEYS = 2;
static final int OPTION_MENU_CREATE_KEY = 3;
static final int MESSAGE_PROGRESS_UPDATE = 1;
static final int MESSAGE_DONE = 2;
static final int MESSAGE_IMPORT_DONE = 2;
static final int MESSAGE_EXPORT_DONE = 3;
static final int DIALOG_DELETE_KEY = 1;
static final int DIALOG_IMPORT_KEYS = 2;
static final int DIALOG_IMPORTING = 3;
static final int DIALOG_EXPORT_KEYS = 4;
static final int DIALOG_EXPORTING = 5;
static final int DIALOG_EXPORT_KEY = 6;
static final int TASK_IMPORT = 1;
static final int TASK_EXPORT = 2;
public class SecretKeyListActivity extends BaseActivity implements OnChildClickListener {
ExpandableListView mList;
protected int mSelectedItem = -1;
protected int mTask = 0;
private ProgressDialog mProgressDialog = null;
private Thread mRunningThread = null;
private String mImportFilename = Environment.getExternalStorageDirectory() + "/secring.gpg";
private String mExportFilename = Environment.getExternalStorageDirectory() + "/secexport.asc";
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Bundle data = msg.getData();
if (data != null) {
int type = data.getInt("type");
switch (type) {
case MESSAGE_PROGRESS_UPDATE: {
String message = data.getString("message");
if (mProgressDialog != null) {
if (message != null) {
mProgressDialog.setMessage(message);
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.key_list);
mList = (ExpandableListView) findViewById(R.id.list);
mList.setAdapter(new SecretKeyListAdapter(this));
registerForContextMenu(mList);
mList.setOnChildClickListener(this);
}
mProgressDialog.setMax(data.getInt("max"));
mProgressDialog.setProgress(data.getInt("progress"));
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, Id.menu.option.import_keys, 0, "Import Keys")
.setIcon(android.R.drawable.ic_menu_add);
menu.add(0, Id.menu.option.export_keys, 1, "Export Keys")
.setIcon(android.R.drawable.ic_menu_save);
menu.add(1, Id.menu.option.create, 2, "Create Key")
.setIcon(android.R.drawable.ic_menu_add);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case Id.menu.option.import_keys: {
showDialog(Id.dialog.import_keys);
return true;
}
case Id.menu.option.export_keys: {
showDialog(Id.dialog.export_keys);
return true;
}
case Id.menu.option.create: {
createKey();
return true;
}
default: {
break;
}
}
return false;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
ExpandableListView.ExpandableListContextMenuInfo info =
(ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(groupPosition);
String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
menu.setHeaderTitle(userId);
menu.add(0, Id.menu.edit, 0, "Edit Key");
menu.add(0, Id.menu.export, 1, "Export Key");
menu.add(0, Id.menu.delete, 2, "Delete Key");
}
}
@Override
public boolean onContextItemSelected(MenuItem menuItem) {
ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuItem.getMenuInfo();
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
if (type != ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
return super.onContextItemSelected(menuItem);
}
switch (menuItem.getItemId()) {
case Id.menu.edit: {
mSelectedItem = groupPosition;
showDialog(Id.dialog.pass_phrase);
return true;
}
case Id.menu.export: {
mSelectedItem = groupPosition;
showDialog(Id.dialog.export_key);
return true;
}
case Id.menu.delete: {
mSelectedItem = groupPosition;
showDialog(Id.dialog.delete_key);
return true;
}
default: {
return super.onContextItemSelected(menuItem);
}
}
}
@Override
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
int childPosition, long id) {
mSelectedItem = groupPosition;
showDialog(Id.dialog.pass_phrase);
return true;
}
@Override
protected Dialog onCreateDialog(int id) {
boolean singleKeyExport = false;
switch (id) {
case Id.dialog.delete_key: {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Warning ");
builder.setMessage("Do you really want to delete the key '" + userId + "'?\n" +
"You can't undo this!");
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setPositiveButton("Delete", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
deleteKey(mSelectedItem);
mSelectedItem = -1;
removeDialog(Id.dialog.delete_key);
}
});
builder.setNegativeButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mSelectedItem = -1;
removeDialog(Id.dialog.delete_key);
}
});
return builder.create();
}
case Id.dialog.import_keys: {
return FileDialog.build(this, "Import Keys",
"Please specify which file to import from.",
mImportFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(Id.dialog.import_keys);
mImportFilename = filename;
importKeys();
}
@Override
public void onCancelClick() {
removeDialog(Id.dialog.import_keys);
}
},
getString(R.string.filemanager_title_open),
getString(R.string.filemanager_btn_open));
}
case Id.dialog.export_key: {
singleKeyExport = true;
// break intentionally omitted, to use the Id.dialog.export_keys dialog
}
case Id.dialog.export_keys: {
String title = "Export Key";
if (!singleKeyExport) {
// plural "Keys"
title += "s";
}
final int thisDialogId = (singleKeyExport ? Id.dialog.delete_key : Id.dialog.export_keys);
return FileDialog.build(this, title,
"Please specify which file to export to.\n" +
"WARNING! You are about to export SECRET keys.\n" +
"WARNING! File will be overwritten if it exists.",
mExportFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(thisDialogId);
mExportFilename = filename;
exportKeys();
}
@Override
public void onCancelClick() {
removeDialog(thisDialogId);
}
},
getString(R.string.filemanager_title_save),
getString(R.string.filemanager_btn_save));
}
case Id.dialog.pass_phrase: {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
long keyId = keyRing.getSecretKey().getKeyID();
return AskForSecretKeyPassPhrase.createDialog(this, keyId, this);
}
}
return super.onCreateDialog(id);
}
@Override
public void passPhraseCallback(String passPhrase) {
super.passPhraseCallback(passPhrase);
editKey();
}
private void createKey() {
Intent intent = new Intent(this, EditKeyActivity.class);
startActivityForResult(intent, Id.message.create_key);
}
private void editKey() {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
long keyId = keyRing.getSecretKey().getKeyID();
Intent intent = new Intent(this, EditKeyActivity.class);
intent.putExtra("keyId", keyId);
startActivityForResult(intent, Id.message.edit_key);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case Id.message.create_key: // intentionally no break
case Id.message.edit_key: {
if (resultCode == RESULT_OK) {
refreshList();
}
break;
}
case MESSAGE_IMPORT_DONE: {
removeDialog(DIALOG_IMPORTING);
mProgressDialog = null;
case Id.request.filename: {
if (resultCode == RESULT_OK && data != null) {
String filename = data.getDataString();
if (filename != null) {
// Get rid of URI prefix:
if (filename.startsWith("file://")) {
filename = filename.substring(7);
}
// replace %20 and so on
filename = Uri.decode(filename);
FileDialog.setFilename(filename);
}
}
return;
}
default: {
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
public void importKeys() {
showDialog(Id.dialog.importing);
mTask = Id.task.import_keys;
startThread();
}
public void exportKeys() {
showDialog(Id.dialog.exporting);
mTask = Id.task.export_keys;
startThread();
}
@Override
public void run() {
String error = null;
Bundle data = new Bundle();
Message msg = new Message();
String filename = null;
if (mTask == Id.task.import_keys) {
filename = mImportFilename;
} else {
filename = mExportFilename;
}
try {
if (mTask == Id.task.import_keys) {
data = Apg.importKeyRings(this, Id.type.secret_key, filename, this);
} else {
Vector<Object> keys = new Vector<Object>();
if (mSelectedItem == -1) {
for (PGPSecretKeyRing key : Apg.getSecretKeyRings()) {
keys.add(key);
}
} else {
keys.add(Apg.getSecretKeyRings().get(mSelectedItem));
}
data = Apg.exportKeyRings(this, keys, filename, this);
}
} catch (FileNotFoundException e) {
error = "file '" + filename + "' not found";
} catch (IOException e) {
error = e.getMessage();
} catch (PGPException e) {
error = e.getMessage();
} catch (Apg.GeneralException e) {
error = e.getMessage();
}
if (mTask == Id.task.import_keys) {
data.putInt("type", Id.message.import_done);
} else {
data.putInt("type", Id.message.export_done);
}
if (error != null) {
data.putString("error", error);
}
msg.setData(data);
sendMessage(msg);
}
private void deleteKey(int index) {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(index);
Apg.deleteKey(this, keyRing);
refreshList();
}
private void refreshList() {
((SecretKeyListAdapter) mList.getExpandableListAdapter()).notifyDataSetChanged();
}
@Override
public void doneCallback(Message msg) {
super.doneCallback(msg);
Bundle data = msg.getData();
if (data != null) {
int type = data.getInt("type");
switch (type) {
case Id.message.import_done: {
removeDialog(Id.dialog.importing);
String error = data.getString("error");
if (error != null) {
@ -139,9 +432,8 @@ public class SecretKeyListActivity extends ExpandableListActivity
break;
}
case MESSAGE_EXPORT_DONE: {
removeDialog(DIALOG_EXPORTING);
mProgressDialog = null;
case Id.message.export_done: {
removeDialog(Id.dialog.exporting);
String error = data.getString("error");
if (error != null) {
@ -170,382 +462,6 @@ public class SecretKeyListActivity extends ExpandableListActivity
}
}
}
};
public void setProgress(int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
data.putInt("type", MESSAGE_PROGRESS_UPDATE);
data.putInt("progress", progress);
data.putInt("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("type", MESSAGE_PROGRESS_UPDATE);
data.putString("message", message);
data.putInt("progress", progress);
data.putInt("max", max);
msg.setData(data);
mHandler.sendMessage(msg);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Apg.initialize(this);
setListAdapter(new SecretKeyListAdapter(this));
registerForContextMenu(getExpandableListView());
getExpandableListView().setOnChildClickListener(this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, OPTION_MENU_IMPORT_KEYS, 0, "Import Keys")
.setIcon(android.R.drawable.ic_menu_add);
menu.add(0, OPTION_MENU_EXPORT_KEYS, 1, "Export Keys")
.setIcon(android.R.drawable.ic_menu_save);
menu.add(1, OPTION_MENU_CREATE_KEY, 2, "Create Key")
.setIcon(android.R.drawable.ic_menu_add);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case OPTION_MENU_IMPORT_KEYS: {
showDialog(DIALOG_IMPORT_KEYS);
return true;
}
case OPTION_MENU_EXPORT_KEYS: {
showDialog(DIALOG_EXPORT_KEYS);
return true;
}
case OPTION_MENU_CREATE_KEY: {
createKey();
return true;
}
default: {
break;
}
}
return false;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
ExpandableListView.ExpandableListContextMenuInfo info =
(ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(groupPosition);
String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
menu.setHeaderTitle(userId);
menu.add(0, MENU_EDIT, 0, "Edit Key");
menu.add(0, MENU_EXPORT, 1, "Export Key");
menu.add(0, MENU_DELETE, 2, "Delete Key");
}
}
@Override
public boolean onContextItemSelected(MenuItem menuItem) {
ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuItem.getMenuInfo();
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
if (type != ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
return super.onContextItemSelected(menuItem);
}
switch (menuItem.getItemId()) {
case MENU_EDIT: {
mSelectedItem = groupPosition;
showDialog(AskForSecretKeyPassPhrase.DIALOG_PASS_PHRASE);
return true;
}
case MENU_EXPORT: {
mSelectedItem = groupPosition;
showDialog(DIALOG_EXPORT_KEY);
return true;
}
case MENU_DELETE: {
mSelectedItem = groupPosition;
showDialog(DIALOG_DELETE_KEY);
return true;
}
default: {
return super.onContextItemSelected(menuItem);
}
}
}
@Override
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
int childPosition, long id) {
mSelectedItem = groupPosition;
showDialog(AskForSecretKeyPassPhrase.DIALOG_PASS_PHRASE);
return true;
}
@Override
protected Dialog onCreateDialog(int id) {
boolean singleKeyExport = false;
switch (id) {
case DIALOG_DELETE_KEY: {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Warning ");
builder.setMessage("Do you really want to delete the key '" + userId + "'?\n" +
"You can't undo this!");
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setPositiveButton("Delete", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
deleteKey(mSelectedItem);
mSelectedItem = -1;
removeDialog(DIALOG_DELETE_KEY);
}
});
builder.setNegativeButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mSelectedItem = -1;
removeDialog(DIALOG_DELETE_KEY);
}
});
return builder.create();
}
case DIALOG_IMPORT_KEYS: {
return FileDialog.build(this, "Import Keys",
"Please specify which file to import from.",
mImportFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(DIALOG_IMPORT_KEYS);
mImportFilename = filename;
importKeys();
}
@Override
public void onCancelClick() {
removeDialog(DIALOG_IMPORT_KEYS);
}
},
getString(R.string.filemanager_title_open),
getString(R.string.filemanager_btn_open));
}
case DIALOG_EXPORT_KEY: {
singleKeyExport = true;
// break intentionally omitted, to use the DIALOG_EXPORT_KEYS dialog
}
case DIALOG_EXPORT_KEYS: {
String title = "Export Key";
if (!singleKeyExport) {
// plural "Keys"
title += "s";
}
final int thisDialogId = (singleKeyExport ? DIALOG_DELETE_KEY : DIALOG_EXPORT_KEYS);
return FileDialog.build(this, title,
"Please specify which file to export to.\n" +
"WARNING! You are about to export SECRET keys.\n" +
"WARNING! File will be overwritten if it exists.",
mExportFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(thisDialogId);
mExportFilename = filename;
exportKeys();
}
@Override
public void onCancelClick() {
removeDialog(thisDialogId);
}
},
getString(R.string.filemanager_title_save),
getString(R.string.filemanager_btn_save));
}
case DIALOG_IMPORTING: {
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setMessage("importing...");
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(false);
return mProgressDialog;
}
case DIALOG_EXPORTING: {
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setMessage("exporting...");
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(false);
return mProgressDialog;
}
case AskForSecretKeyPassPhrase.DIALOG_PASS_PHRASE: {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
long keyId = keyRing.getSecretKey().getKeyID();
return AskForSecretKeyPassPhrase.createDialog(this, keyId, this);
}
}
return super.onCreateDialog(id);
}
public void passPhraseCallback(String passPhrase) {
Apg.setPassPhrase(passPhrase);
editKey();
}
private void createKey() {
Intent intent = new Intent(this, EditKeyActivity.class);
startActivityForResult(intent, CREATE_SECRET_KEY);
}
private void editKey() {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
long keyId = keyRing.getSecretKey().getKeyID();
Intent intent = new Intent(this, EditKeyActivity.class);
intent.putExtra("keyId", keyId);
startActivityForResult(intent, EDIT_SECRET_KEY);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case CREATE_SECRET_KEY: // intentionally no break
case EDIT_SECRET_KEY: {
if (resultCode == RESULT_OK) {
refreshList();
}
break;
}
case FileDialog.REQUEST_CODE_PICK_FILE_OR_DIRECTORY: {
if (resultCode == RESULT_OK && data != null) {
String filename = data.getDataString();
if (filename != null) {
// Get rid of URI prefix:
if (filename.startsWith("file://")) {
filename = filename.substring(7);
}
// replace %20 and so on
filename = Uri.decode(filename);
FileDialog.setFilename(filename);
}
}
return;
}
default:
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
public void importKeys() {
showDialog(DIALOG_IMPORTING);
mTask = TASK_IMPORT;
mRunningThread = new Thread(this);
mRunningThread.start();
}
public void exportKeys() {
showDialog(DIALOG_EXPORTING);
mTask = TASK_EXPORT;
mRunningThread = new Thread(this);
mRunningThread.start();
}
public void run() {
String error = null;
Bundle data = new Bundle();
Message msg = new Message();
String filename = null;
if (mTask == TASK_IMPORT) {
filename = mImportFilename;
} else {
filename = mExportFilename;
}
try {
if (mTask == TASK_IMPORT) {
data = Apg.importKeyRings(this, Apg.TYPE_SECRET, filename, this);
} else {
Vector<Object> keys = new Vector<Object>();
if (mSelectedItem == -1) {
for (PGPSecretKeyRing key : Apg.getSecretKeyRings()) {
keys.add(key);
}
} else {
keys.add(Apg.getSecretKeyRings().get(mSelectedItem));
}
data = Apg.exportKeyRings(this, keys, filename, this);
}
} catch (FileNotFoundException e) {
error = "file '" + filename + "' not found";
} catch (IOException e) {
error = e.getMessage();
} catch (PGPException e) {
error = e.getMessage();
} catch (Apg.GeneralException e) {
error = e.getMessage();
}
if (mTask == TASK_IMPORT) {
data.putInt("type", MESSAGE_IMPORT_DONE);
} else {
data.putInt("type", MESSAGE_EXPORT_DONE);
}
if (error != null) {
data.putString("error", error);
}
msg.setData(data);
mHandler.sendMessage(msg);
}
private void deleteKey(int index) {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(index);
Apg.deleteKey(this, keyRing);
refreshList();
}
private void refreshList() {
((SecretKeyListAdapter) getExpandableListAdapter())
.notifyDataSetChanged();
}
private static class SecretKeyListAdapter extends BaseExpandableListAdapter {
private LayoutInflater mInflater;

View File

@ -16,40 +16,28 @@
package org.thialfihar.android.apg;
import java.text.DateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.Vector;
import org.bouncycastle2.openpgp.PGPPublicKey;
import org.bouncycastle2.openpgp.PGPPublicKeyRing;
import org.thialfihar.android.apg.utils.IterableIterator;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ListView;
import android.widget.TextView;
public class SelectPublicKeyListActivity extends Activity {
protected Vector<PGPPublicKeyRing> mKeyRings;
protected LayoutInflater mInflater;
public class SelectPublicKeyListActivity extends BaseActivity {
protected Intent mIntent;
protected ListView mList;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
setContentView(R.layout.select_public_key);
// fill things
mIntent = getIntent();
@ -58,17 +46,15 @@ public class SelectPublicKeyListActivity extends Activity {
selectedKeyIds = mIntent.getExtras().getLongArray("selection");
}
Apg.initialize(this);
mKeyRings = (Vector<PGPPublicKeyRing>) Apg.getPublicKeyRings().clone();
Collections.sort(mKeyRings, new Apg.PublicKeySorter());
setContentView(R.layout.select_public_key);
mList = (ListView) findViewById(R.id.list);
mList.setAdapter(new PublicKeyListAdapter(this));
Vector<PGPPublicKeyRing> keyRings =
(Vector<PGPPublicKeyRing>) Apg.getPublicKeyRings().clone();
Collections.sort(keyRings, new Apg.PublicKeySorter());
mList.setAdapter(new SelectPublicKeyListAdapter(mList, keyRings));
if (selectedKeyIds != null) {
for (int i = 0; i < mKeyRings.size(); ++i) {
PGPPublicKeyRing keyRing = mKeyRings.get(i);
for (int i = 0; i < keyRings.size(); ++i) {
PGPPublicKeyRing keyRing = keyRings.get(i);
PGPPublicKey key = Apg.getMasterKey(keyRing);
if (key == null) {
continue;
@ -122,138 +108,4 @@ public class SelectPublicKeyListActivity extends Activity {
setResult(RESULT_OK, data);
finish();
}
private class PublicKeyListAdapter extends BaseAdapter {
public PublicKeyListAdapter(Context context) {
}
@Override
public boolean isEnabled(int position) {
PGPPublicKeyRing keyRing = mKeyRings.get(position);
if (Apg.getMasterKey(keyRing) == null) {
return false;
}
Vector<PGPPublicKey> encryptKeys = Apg.getUsableEncryptKeys(keyRing);
if (encryptKeys.size() == 0) {
return false;
}
return true;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public int getCount() {
return mKeyRings.size();
}
@Override
public Object getItem(int position) {
return mKeyRings.get(position);
}
@Override
public long getItemId(int position) {
PGPPublicKeyRing keyRing = mKeyRings.get(position);
PGPPublicKey key = Apg.getMasterKey(keyRing);
if (key != null) {
return key.getKeyID();
}
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = mInflater.inflate(R.layout.select_public_key_item, null);
boolean enabled = isEnabled(position);
PGPPublicKeyRing keyRing = mKeyRings.get(position);
PGPPublicKey key = null;
for (PGPPublicKey tKey : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
if (tKey.isMasterKey()) {
key = tKey;
break;
}
}
Vector<PGPPublicKey> encryptKeys = Apg.getEncryptKeys(keyRing);
Vector<PGPPublicKey> usableKeys = Apg.getUsableEncryptKeys(keyRing);
TextView mainUserId = (TextView) view.findViewById(R.id.main_user_id);
mainUserId.setText(R.string.unknown_user_id);
TextView mainUserIdRest = (TextView) view.findViewById(R.id.main_user_id_rest);
mainUserIdRest.setText("");
TextView keyId = (TextView) view.findViewById(R.id.key_id);
keyId.setText("<no key>");
TextView creation = (TextView) view.findViewById(R.id.creation);
creation.setText("-");
TextView expiry = (TextView) view.findViewById(R.id.expiry);
expiry.setText("no expire");
TextView status = (TextView) view.findViewById(R.id.status);
status.setText("???");
if (key != null) {
String userId = Apg.getMainUserId(key);
if (userId != null) {
String chunks[] = userId.split(" <", 2);
userId = chunks[0];
if (chunks.length > 1) {
mainUserIdRest.setText("<" + chunks[1]);
}
mainUserId.setText(userId);
}
keyId.setText("" + Long.toHexString(key.getKeyID() & 0xffffffffL));
}
if (mainUserIdRest.getText().length() == 0) {
mainUserIdRest.setVisibility(View.GONE);
}
PGPPublicKey timespanKey = key;
if (usableKeys.size() > 0) {
timespanKey = usableKeys.get(0);
status.setText("can encrypt");
} else if (encryptKeys.size() > 0) {
timespanKey = encryptKeys.get(0);
Date now = new Date();
if (now.compareTo(Apg.getCreationDate(timespanKey)) > 0) {
status.setText("not valid");
} else {
status.setText("expired");
}
} else {
status.setText("no key");
}
creation.setText(DateFormat.getDateInstance().format(Apg.getCreationDate(timespanKey)));
Date expiryDate = Apg.getExpiryDate(timespanKey);
if (expiryDate != null) {
expiry.setText(DateFormat.getDateInstance().format(expiryDate));
}
status.setText(status.getText() + " ");
CheckBox selected = (CheckBox) view.findViewById(R.id.selected);
selected.setChecked(mList.isItemChecked(position));
view.setEnabled(enabled);
mainUserId.setEnabled(enabled);
mainUserIdRest.setEnabled(enabled);
keyId.setEnabled(enabled);
creation.setEnabled(enabled);
expiry.setEnabled(enabled);
selected.setEnabled(enabled);
status.setEnabled(enabled);
return view;
}
}
}

View File

@ -0,0 +1,186 @@
/*
* 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.text.DateFormat;
import java.util.Date;
import java.util.Vector;
import org.bouncycastle2.openpgp.PGPPublicKey;
import org.bouncycastle2.openpgp.PGPPublicKeyRing;
import org.thialfihar.android.apg.utils.IterableIterator;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.ListView;
import android.widget.TextView;
public class SelectPublicKeyListAdapter extends BaseAdapter {
protected Vector<PGPPublicKeyRing> mKeyRings;
protected LayoutInflater mInflater;
protected ListView mParent;
public SelectPublicKeyListAdapter(ListView parent,
Vector<PGPPublicKeyRing> keyRings) {
setKeyRings(keyRings);
mParent = parent;
mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void setKeyRings(Vector<PGPPublicKeyRing> keyRings) {
mKeyRings = keyRings;
notifyDataSetChanged();
}
public Vector<PGPPublicKeyRing> getKeyRings() {
return mKeyRings;
}
@Override
public boolean isEnabled(int position) {
PGPPublicKeyRing keyRing = mKeyRings.get(position);
if (Apg.getMasterKey(keyRing) == null) {
return false;
}
Vector<PGPPublicKey> encryptKeys = Apg.getUsableEncryptKeys(keyRing);
if (encryptKeys.size() == 0) {
return false;
}
return true;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public int getCount() {
return mKeyRings.size();
}
@Override
public Object getItem(int position) {
return mKeyRings.get(position);
}
@Override
public long getItemId(int position) {
PGPPublicKeyRing keyRing = mKeyRings.get(position);
PGPPublicKey key = Apg.getMasterKey(keyRing);
if (key != null) {
return key.getKeyID();
}
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = mInflater.inflate(R.layout.select_public_key_item, null);
boolean enabled = isEnabled(position);
PGPPublicKeyRing keyRing = mKeyRings.get(position);
PGPPublicKey key = null;
for (PGPPublicKey tKey : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
if (tKey.isMasterKey()) {
key = tKey;
break;
}
}
Vector<PGPPublicKey> encryptKeys = Apg.getEncryptKeys(keyRing);
Vector<PGPPublicKey> usableKeys = Apg.getUsableEncryptKeys(keyRing);
TextView mainUserId = (TextView) view.findViewById(R.id.main_user_id);
mainUserId.setText(R.string.unknown_user_id);
TextView mainUserIdRest = (TextView) view.findViewById(R.id.main_user_id_rest);
mainUserIdRest.setText("");
TextView keyId = (TextView) view.findViewById(R.id.key_id);
keyId.setText("<no key>");
TextView creation = (TextView) view.findViewById(R.id.creation);
creation.setText("-");
TextView expiry = (TextView) view.findViewById(R.id.expiry);
expiry.setText("no expire");
TextView status = (TextView) view.findViewById(R.id.status);
status.setText("???");
if (key != null) {
String userId = Apg.getMainUserId(key);
if (userId != null) {
String chunks[] = userId.split(" <", 2);
userId = chunks[0];
if (chunks.length > 1) {
mainUserIdRest.setText("<" + chunks[1]);
}
mainUserId.setText(userId);
}
keyId.setText("" + Long.toHexString(key.getKeyID() & 0xffffffffL));
}
if (mainUserIdRest.getText().length() == 0) {
mainUserIdRest.setVisibility(View.GONE);
}
PGPPublicKey timespanKey = key;
if (usableKeys.size() > 0) {
timespanKey = usableKeys.get(0);
status.setText("can encrypt");
} else if (encryptKeys.size() > 0) {
timespanKey = encryptKeys.get(0);
Date now = new Date();
if (now.compareTo(Apg.getCreationDate(timespanKey)) > 0) {
status.setText("not valid");
} else {
status.setText("expired");
}
} else {
status.setText("no key");
}
creation.setText(DateFormat.getDateInstance().format(Apg.getCreationDate(timespanKey)));
Date expiryDate = Apg.getExpiryDate(timespanKey);
if (expiryDate != null) {
expiry.setText(DateFormat.getDateInstance().format(expiryDate));
}
status.setText(status.getText() + " ");
CheckBox selected = (CheckBox) view.findViewById(R.id.selected);
selected.setChecked(mParent.isItemChecked(position));
view.setEnabled(enabled);
mainUserId.setEnabled(enabled);
mainUserIdRest.setEnabled(enabled);
keyId.setEnabled(enabled);
creation.setEnabled(enabled);
expiry.setEnabled(enabled);
selected.setEnabled(enabled);
status.setEnabled(enabled);
return view;
}
}

View File

@ -38,7 +38,7 @@ import android.widget.ListView;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
public class SelectSecretKeyListActivity extends Activity {
public class SelectSecretKeyListActivity extends BaseActivity {
protected Vector<PGPSecretKeyRing> mKeyRings;
protected LayoutInflater mInflater;
protected Intent mIntent;
@ -55,8 +55,6 @@ public class SelectSecretKeyListActivity extends Activity {
// fill things
mIntent = getIntent();
Apg.initialize(this);
mKeyRings = (Vector<PGPSecretKeyRing>) Apg.getSecretKeyRings().clone();
Collections.sort(mKeyRings, new Apg.SecretKeySorter());