new EncryptActivity to handle message and file encryption all in one

This commit is contained in:
Thialfihar 2010-05-09 19:51:21 +00:00
parent 6e3f182291
commit 5e73b696d1
20 changed files with 1239 additions and 924 deletions

View File

@ -61,22 +61,12 @@
android:configChanges="keyboardHidden|orientation|keyboard"/>
<activity
android:name=".EncryptMessageActivity"
android:label="@string/title_encryptMessage"
android:name=".EncryptActivity"
android:label="@string/title_encrypt"
android:configChanges="keyboardHidden|orientation|keyboard">
<intent-filter>
<action android:name="org.thialfihar.android.apg.intent.ENCRYPT" />
</intent-filter>
</activity>
<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>

20
res/anim/push_left_in.xml Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2007 The Android Open Source Project
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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="100%p" android:toXDelta="0" android:duration="500"/>
<alpha android:fromAlpha="1.0" android:toAlpha="1.0" android:duration="500" />
</set>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2007 The Android Open Source Project
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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0" android:toXDelta="-100%p" android:duration="500"/>
<alpha android:fromAlpha="1.0" android:toAlpha="1.0" android:duration="500" />
</set>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2007 The Android Open Source Project
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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="-100%p" android:toXDelta="0" android:duration="500"/>
<alpha android:fromAlpha="1.0" android:toAlpha="1.0" android:duration="500" />
</set>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2007 The Android Open Source Project
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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0" android:toXDelta="100%p" android:duration="500"/>
<alpha android:fromAlpha="1.0" android:toAlpha="1.0" android:duration="500" />
</set>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 916 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

359
res/layout/encrypt.xml Normal file
View File

@ -0,0 +1,359 @@
<?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">
<ScrollView
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:fillViewport="true">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:layout_marginLeft="5dip">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/source_previous"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_previous"/>
<TextView
android:id="@+id/source_label"
android:layout_width="0dip"
android:layout_height="fill_parent"
android:layout_weight="1"
android:text="@string/label_message"
android:textAppearance="?android:attr/textAppearanceMedium"
android:gravity="center_horizontal|center_vertical"
android:textColor="#ffffffff"/>
<ImageView
android:id="@+id/source_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_next"/>
</LinearLayout>
<ViewFlipper
android:id="@+id/source"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1">
<LinearLayout
android:id="@+id/source_message"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<EditText
android:id="@+id/message"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="top"
android:inputType="text|textCapSentences|textMultiLine|textLongMessage"/>
</LinearLayout>
<LinearLayout
android:id="@+id/source_file"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<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>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/label_delete_after_encryption"
android:text="@string/label_delete_after_encryption"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_gravity="center_vertical"
android:paddingRight="10dip"
android:layout_height="wrap_content"
android:layout_width="0dip"
android:layout_weight="1"/>
<CheckBox
android:id="@+id/delete_after_encryption"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"/>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/label_ascii_armour"
android:text="@string/label_ascii_armour"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_gravity="center_vertical"
android:paddingRight="10dip"
android:layout_height="wrap_content"
android:layout_width="0dip"
android:layout_weight="1"/>
<CheckBox
android:id="@+id/ascii_armour"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"/>
</LinearLayout>
</LinearLayout>
</ViewFlipper>
<View
android:layout_width="fill_parent"
android:layout_height="1dip"
android:background="?android:attr/listDivider"
android:layout_marginBottom="5dip"/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/mode_previous"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_previous"/>
<TextView
android:id="@+id/mode_label"
android:layout_width="0dip"
android:layout_height="fill_parent"
android:layout_weight="1"
android:text="@string/label_asymmetric"
android:textAppearance="?android:attr/textAppearanceMedium"
android:gravity="center_horizontal|center_vertical"
android:textColor="#ffffffff"/>
<ImageView
android:id="@+id/mode_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_next"/>
</LinearLayout>
<ViewFlipper
android:id="@+id/mode"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/mode_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:orientation="horizontal">
<TextView
android:id="@+id/label_sign"
android:text="@string/label_sign"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"/>
<LinearLayout
android:orientation="vertical"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
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>
<CheckBox
android:id="@+id/sign"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"/>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingBottom="3dip"
android:orientation="horizontal">
<TextView
android:id="@+id/label_select_public_keys"
android:text="@string/label_select_public_keys"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_height="wrap_content"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_gravity="center_vertical"/>
<Button
android:text="@string/btn_selectEncryptKeys"
android:id="@+id/btn_selectEncryptKeys"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"/>
</LinearLayout>
</LinearLayout>
<TableLayout
android:id="@+id/mode_symmetric"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:stretchColumns="1">
<TableRow>
<TextView
android:id="@+id/label_pass_phrase"
android:text="@string/label_pass_phrase"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
android:paddingRight="10dip"/>
<EditText
android:id="@+id/pass_phrase"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:inputType="textPassword"/>
</TableRow>
<TableRow>
<TextView
android:id="@+id/label_pass_phrase_again"
android:text="@string/label_pass_phrase_again"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
android:paddingRight="10dip"/>
<EditText
android:id="@+id/pass_phrase_again"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:inputType="textPassword"/>
</TableRow>
</TableLayout>
</ViewFlipper>
<View
android:layout_width="fill_parent"
android:layout_height="1dip"
android:background="?android:attr/listDivider"
android:layout_marginBottom="5dip"/>
</LinearLayout>
</ScrollView>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="@android:style/ButtonBar">
<Button
android:id="@+id/btn_encrypt_to_clipboard"
android:text="@string/btn_encrypt_to_clipboard"
android:layout_weight="1"
android:layout_width="0dip"
android:layout_height="wrap_content"/>
<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

@ -149,7 +149,7 @@
<RadioButton
android:id="@+id/use_asymmetric"
android:text="@string/use_asymmetric"
android:text="@string/label_asymmetric"
android:layout_height="wrap_content"
android:layout_width="0dip"
android:layout_weight="1"
@ -157,7 +157,7 @@
<RadioButton
android:id="@+id/use_symmetric"
android:text="@string/use_symmetric"
android:text="@string/label_symmetric"
android:layout_height="wrap_content"
android:layout_width="0dip"
android:layout_weight="1"

View File

@ -21,7 +21,7 @@
<string name="title_manageSecretKeys">Manage Secret Keys</string>
<string name="title_selectRecipients">Select Recipients</string>
<string name="title_selectSignature">Select Signature</string>
<string name="title_encryptMessage">Encrypt Message</string>
<string name="title_encrypt">Encrypt</string>
<string name="title_decryptMessage">Decrypt Message</string>
<string name="title_encryptFile">Encrypt File</string>
<string name="title_decryptFile">Decrypt File</string>
@ -60,6 +60,7 @@
<string name="none">&lt;none&gt;</string>
<string name="label_sign">Sign</string>
<string name="label_message">Message</string>
<string name="label_file">File</string>
<string name="label_pass_phrase">Pass Phrase</string>
<string name="label_pass_phrase_again">Again</string>
@ -71,8 +72,8 @@
<string name="label_encryption_algorithm">Encryption Algorithm</string>
<string name="label_hash_algorithm">Hash Algorithm</string>
<string name="use_asymmetric">Public Key</string>
<string name="use_symmetric">Pass Phrase</string>
<string name="label_asymmetric">Public Key</string>
<string name="label_symmetric">Pass Phrase</string>
<string name="section_defaults">Defaults</string>

View File

@ -167,7 +167,7 @@ public class DecryptMessageActivity extends BaseActivity {
}
private void replyClicked() {
Intent intent = new Intent(this, EncryptMessageActivity.class);
Intent intent = new Intent(this, EncryptActivity.class);
intent.setAction(Apg.Intent.ENCRYPT);
String data = mMessage.getText().toString();
data = data.replaceAll("(?m)^", "> ");

View File

@ -0,0 +1,783 @@
/*
* 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.util.Vector;
import org.bouncycastle2.openpgp.PGPException;
import org.bouncycastle2.openpgp.PGPPublicKey;
import org.bouncycastle2.openpgp.PGPPublicKeyRing;
import org.bouncycastle2.openpgp.PGPSecretKey;
import org.bouncycastle2.openpgp.PGPSecretKeyRing;
import org.bouncycastle2.util.Strings;
import org.openintents.intents.FileManager;
import org.thialfihar.android.apg.Apg.GeneralException;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Message;
import android.text.ClipboardManager;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewFlipper;
public class EncryptActivity extends BaseActivity {
private String mSubject = null;
private String mSendTo = null;
private long mEncryptionKeyIds[] = null;
private EditText mMessage = null;
private Button mSelectKeysButton = null;
private Button mEncryptButton = null;
private Button mEncryptToClipboardButton = null;
private CheckBox mSign = null;
private TextView mMainUserId = null;
private TextView mMainUserIdRest = null;
private ViewFlipper mSource = null;
private TextView mSourceLabel = null;
private ImageView mSourcePrevious = null;
private ImageView mSourceNext = null;
private ViewFlipper mMode = null;
private TextView mModeLabel = null;
private ImageView mModePrevious = null;
private ImageView mModeNext = null;
private int mEncryptTarget;
private EditText mPassPhrase = null;
private EditText mPassPhraseAgain = null;
private CheckBox mAsciiArmour = null;
private EditText mFilename = null;
private CheckBox mDeleteAfter = null;
private ImageButton mBrowse = null;
private String mInputFilename = null;
private String mOutputFilename = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.encrypt);
mSource = (ViewFlipper) findViewById(R.id.source);
mSourceLabel = (TextView) findViewById(R.id.source_label);
mSourcePrevious = (ImageView) findViewById(R.id.source_previous);
mSourceNext = (ImageView) findViewById(R.id.source_next);
mSourcePrevious.setClickable(true);
mSourcePrevious.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mSource.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
R.anim.push_right_in));
mSource.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
R.anim.push_right_out));
mSource.showPrevious();
updateSource();
}
});
mSourceNext.setClickable(true);
OnClickListener nextSourceClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
mSource.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
R.anim.push_left_in));
mSource.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
R.anim.push_left_out));
mSource.showNext();
updateSource();
}
};
mSourceNext.setOnClickListener(nextSourceClickListener);
mSourceLabel.setClickable(true);
mSourceLabel.setOnClickListener(nextSourceClickListener);
mMode = (ViewFlipper) findViewById(R.id.mode);
mModeLabel = (TextView) findViewById(R.id.mode_label);
mModePrevious = (ImageView) findViewById(R.id.mode_previous);
mModeNext = (ImageView) findViewById(R.id.mode_next);
mModePrevious.setClickable(true);
mModePrevious.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mMode.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
R.anim.push_right_in));
mMode.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
R.anim.push_right_out));
mMode.showPrevious();
updateMode();
}
});
OnClickListener nextModeClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
mMode.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
R.anim.push_left_in));
mMode.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
R.anim.push_left_out));
mMode.showNext();
updateMode();
}
};
mModeNext.setOnClickListener(nextModeClickListener);
mModeLabel.setClickable(true);
mModeLabel.setOnClickListener(nextModeClickListener);
mMessage = (EditText) findViewById(R.id.message);
mSelectKeysButton = (Button) findViewById(R.id.btn_selectEncryptKeys);
mEncryptButton = (Button) findViewById(R.id.btn_encrypt);
mEncryptToClipboardButton = (Button) findViewById(R.id.btn_encrypt_to_clipboard);
mSign = (CheckBox) findViewById(R.id.sign);
mMainUserId = (TextView) findViewById(R.id.main_user_id);
mMainUserIdRest = (TextView) findViewById(R.id.main_user_id_rest);
mPassPhrase = (EditText) findViewById(R.id.pass_phrase);
mPassPhraseAgain = (EditText) findViewById(R.id.pass_phrase_again);
// measure the height of the source_file view and set the message view's min height to that,
// so it fills mSource fully... bit of a hack.
View tmp = findViewById(R.id.source_file);
tmp.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
int height = tmp.getMeasuredHeight();
mMessage.setMinimumHeight(height);
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();
}
});
mDeleteAfter = (CheckBox) findViewById(R.id.delete_after_encryption);
mAsciiArmour = (CheckBox) findViewById(R.id.ascii_armour);
mAsciiArmour.setChecked(getDefaultAsciiArmour());
mAsciiArmour.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
guessOutputFilename();
}
});
mEncryptToClipboardButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
encryptToClipboardClicked();
}
});
mEncryptButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
encryptClicked();
}
});
mSelectKeysButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
selectPublicKeys();
}
});
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();
}
}
});
Intent intent = getIntent();
if (intent.getAction() != null &&
(intent.getAction().equals(Apg.Intent.ENCRYPT) ||
intent.getAction().equals(Apg.Intent.ENCRYPT_FILE))) {
Bundle extras = intent.getExtras();
if (extras == null) {
extras = new Bundle();
}
String data = extras.getString("data");
mSendTo = extras.getString("sendTo");
mSubject = extras.getString("subject");
long signatureKeyId = extras.getLong("signatureKeyId");
long encryptionKeyIds[] = extras.getLongArray("encryptionKeyIds");
if (signatureKeyId != 0) {
PGPSecretKeyRing keyRing = Apg.findSecretKeyRing(signatureKeyId);
PGPSecretKey masterKey = null;
if (keyRing != null) {
masterKey = Apg.getMasterKey(keyRing);
if (masterKey != null) {
Vector<PGPSecretKey> signKeys = Apg.getUsableSigningKeys(keyRing);
if (signKeys.size() > 0) {
setSecretKeyId(masterKey.getKeyID());
}
}
}
}
if (encryptionKeyIds != null) {
Vector<Long> goodIds = new Vector<Long>();
for (int i = 0; i < encryptionKeyIds.length; ++i) {
PGPPublicKeyRing keyRing = Apg.findPublicKeyRing(encryptionKeyIds[i]);
PGPPublicKey masterKey = null;
if (keyRing == null) {
continue;
}
masterKey = Apg.getMasterKey(keyRing);
if (masterKey == null) {
continue;
}
Vector<PGPPublicKey> encryptKeys = Apg.getUsableEncryptKeys(keyRing);
if (encryptKeys.size() == 0) {
continue;
}
goodIds.add(masterKey.getKeyID());
}
if (goodIds.size() > 0) {
mEncryptionKeyIds = new long[goodIds.size()];
for (int i = 0; i < goodIds.size(); ++i) {
mEncryptionKeyIds[i] = goodIds.get(i);
}
}
}
if (intent.getAction().equals(Apg.Intent.ENCRYPT)) {
if (data != null) {
mMessage.setText(data);
}
mSource.setInAnimation(null);
mSource.setOutAnimation(null);
while (mSource.getCurrentView().getId() != R.id.source_message) {
mSource.showNext();
}
} else if (intent.getAction().equals(Apg.Intent.ENCRYPT_FILE)) {
mSource.setInAnimation(null);
mSource.setOutAnimation(null);
while (mSource.getCurrentView().getId() != R.id.source_file) {
mSource.showNext();
}
}
}
updateView();
updateSource();
updateMode();
}
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 guessOutputFilename() {
mInputFilename = mFilename.getText().toString();
File file = new File(mInputFilename);
String ending = (mAsciiArmour.isChecked() ? ".asc" : ".gpg");
mOutputFilename = Constants.path.app_dir + "/" + file.getName() + ending;
}
private void updateSource() {
switch (mSource.getCurrentView().getId()) {
case R.id.source_file: {
mSourceLabel.setText(R.string.label_file);
mEncryptButton.setText(R.string.btn_encrypt);
mEncryptToClipboardButton.setEnabled(false);
break;
}
case R.id.source_message: {
mSourceLabel.setText(R.string.label_message);
mEncryptButton.setText(R.string.btn_send);
mEncryptToClipboardButton.setEnabled(true);
break;
}
default: {
break;
}
}
}
private void updateMode() {
switch (mMode.getCurrentView().getId()) {
case R.id.mode_asymmetric: {
mModeLabel.setText(R.string.label_asymmetric);
break;
}
case R.id.mode_symmetric: {
mModeLabel.setText(R.string.label_symmetric);
break;
}
default: {
break;
}
}
}
private void encryptToClipboardClicked() {
mEncryptTarget = Id.target.clipboard;
initiateEncryption();
}
private void encryptClicked() {
if (mSource.getCurrentView().getId() == R.id.source_file) {
mEncryptTarget = Id.target.file;
} else {
mEncryptTarget = Id.target.email;
}
initiateEncryption();
}
private void initiateEncryption() {
if (mEncryptTarget == Id.target.file) {
String currentFilename = mFilename.getText().toString();
if (mInputFilename == null || !mInputFilename.equals(currentFilename)) {
guessOutputFilename();
}
if (mInputFilename.equals("")) {
Toast.makeText(this, "Select a file first.", Toast.LENGTH_SHORT).show();
return;
}
File file = new File(mInputFilename);
if (!file.exists() || !file.isFile()) {
Toast.makeText(this, "Error: file not found", Toast.LENGTH_SHORT).show();
return;
}
}
// symmetric encryption
if (mMode.getCurrentView().getId() == R.id.mode_symmetric) {
boolean gotPassPhrase = false;
String passPhrase = mPassPhrase.getText().toString();
String passPhraseAgain = mPassPhraseAgain.getText().toString();
if (!passPhrase.equals(passPhraseAgain)) {
Toast.makeText(this, "Pass phrases don't match.", Toast.LENGTH_SHORT).show();
return;
}
gotPassPhrase = (passPhrase.length() != 0);
if (!gotPassPhrase) {
Toast.makeText(this, "Enter a pass phrase.", Toast.LENGTH_SHORT).show();
return;
}
} else {
boolean encryptIt = mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0;
// for now require at least one form of encryption for files
if (!encryptIt && mEncryptTarget == Id.target.file) {
Toast.makeText(this, "Select at least one encryption key.",
Toast.LENGTH_SHORT).show();
return;
}
if (!encryptIt && getSecretKeyId() == 0) {
Toast.makeText(this, "Select at least one encryption key or a signature key.",
Toast.LENGTH_SHORT).show();
return;
}
if (getSecretKeyId() != 0 && Apg.getPassPhrase() == null) {
showDialog(Id.dialog.pass_phrase);
return;
}
}
if (mEncryptTarget == Id.target.file) {
askForOutputFilename();
} else {
encryptStart();
}
}
private void askForOutputFilename() {
showDialog(Id.dialog.output_filename);
}
@Override
public void passPhraseCallback(String passPhrase) {
super.passPhraseCallback(passPhrase);
if (mEncryptTarget == Id.target.file) {
askForOutputFilename();
} else {
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;
OutputStream out;
long size;
boolean useAsciiArmour = true;
long encryptionKeyIds[] = null;
long signatureKeyId = 0;
boolean signOnly = false;
String passPhrase = null;
if (mMode.getCurrentView().getId() == R.id.mode_symmetric) {
passPhrase = mPassPhrase.getText().toString();
if (passPhrase.length() == 0) {
passPhrase = null;
}
} else {
encryptionKeyIds = mEncryptionKeyIds;
signatureKeyId = getSecretKeyId();
signOnly = mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0;
}
if (mEncryptTarget == Id.target.file) {
if (mInputFilename.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath()) ||
mOutputFilename.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
throw new GeneralException("external storage not ready");
}
}
in = new FileInputStream(mInputFilename);
out = new FileOutputStream(mOutputFilename);
File file = new File(mInputFilename);
size = file.length();
useAsciiArmour = mAsciiArmour.isChecked();
} else {
String message = mMessage.getText().toString();
if (signOnly) {
// 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
message = message.replaceAll(" +\n", "\n");
message = message.replaceAll("\n\n+", "\n\n");
message = message.replaceFirst("^\n+", "");
// make sure there'll be exactly one newline at the end
message = message.replaceFirst("\n*$", "\n");
}
byte[] byteData = Strings.toUTF8ByteArray(message);
in = new ByteArrayInputStream(byteData);
out = new ByteArrayOutputStream();
size = byteData.length;
useAsciiArmour = true;
}
if (signOnly) {
Apg.signText(in, out, getSecretKeyId(),
Apg.getPassPhrase(), getDefaultHashAlgorithm(), this);
} else {
Apg.encrypt(in, out, size, useAsciiArmour,
encryptionKeyIds, signatureKeyId,
Apg.getPassPhrase(), this,
getDefaultEncryptionAlgorithm(), getDefaultHashAlgorithm(),
passPhrase);
}
out.close();
if (mEncryptTarget != Id.target.file) {
data.putString("message", new String(((ByteArrayOutputStream)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);
}
private void updateView() {
if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) {
mSelectKeysButton.setText(R.string.no_keys_selected);
} else if (mEncryptionKeyIds.length == 1) {
mSelectKeysButton.setText(R.string.one_key_selected);
} else {
mSelectKeysButton.setText("" + mEncryptionKeyIds.length + " " +
getResources().getString(R.string.n_keys_selected));
}
if (getSecretKeyId() == 0) {
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.setChecked(true);
}
}
private void selectPublicKeys() {
Intent intent = new Intent(this, SelectPublicKeyListActivity.class);
intent.putExtra("selection", mEncryptionKeyIds);
startActivityForResult(intent, Id.request.public_keys);
}
private void selectSecretKey() {
Intent intent = new Intent(this, SelectSecretKeyListActivity.class);
startActivityForResult(intent, Id.request.secret_keys);
}
@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.output_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;
}
case Id.request.secret_keys: {
if (resultCode == RESULT_OK) {
super.onActivityResult(requestCode, resultCode, data);
}
updateView();
break;
}
case Id.request.public_keys: {
if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
mEncryptionKeyIds = bundle.getLongArray("selection");
}
updateView();
break;
}
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(EncryptActivity.this,
"Error: " + data.getString("error"),
Toast.LENGTH_SHORT).show();
return;
} else {
String message = data.getString("message");
switch (mEncryptTarget) {
case Id.target.clipboard: {
ClipboardManager clip = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
clip.setText(message);
Toast.makeText(this, "Successfully encrypted to clipboard.",
Toast.LENGTH_SHORT).show();
break;
}
case Id.target.email: {
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("text/plain; charset=utf-8");
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message);
if (mSubject != null) {
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,
mSubject);
}
if (mSendTo != null) {
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL,
new String[] { mSendTo });
}
EncryptActivity.this.
startActivity(Intent.createChooser(emailIntent, "Send mail..."));
}
case Id.target.file: {
Toast.makeText(this, "Successfully encrypted.", Toast.LENGTH_SHORT).show();
if (mDeleteAfter.isChecked()) {
setDeleteFile(mInputFilename);
showDialog(Id.dialog.delete_file);
}
break;
}
default: {
// shouldn't happen
break;
}
}
}
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case Id.dialog.output_filename: {
return FileDialog.build(this, "Encrypt to file",
"Please specify which file to encrypt to.\n" +
"WARNING! File will be overwritten if it exists.",
mOutputFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(Id.dialog.output_filename);
mOutputFilename = filename;
encryptStart();
}
@Override
public void onCancelClick() {
removeDialog(Id.dialog.output_filename);
}
},
getString(R.string.filemanager_title_save),
getString(R.string.filemanager_btn_save),
Id.request.output_filename);
}
default: {
break;
}
}
return super.onCreateDialog(id);
}
}

View File

@ -1,520 +0,0 @@
/*
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thialfihar.android.apg;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import org.bouncycastle2.openpgp.PGPEncryptedData;
import org.bouncycastle2.openpgp.PGPException;
import org.bouncycastle2.openpgp.PGPSecretKey;
import org.bouncycastle2.openpgp.PGPSecretKeyRing;
import org.openintents.intents.FileManager;
import org.thialfihar.android.apg.Apg.GeneralException;
import org.thialfihar.android.apg.utils.Choice;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Message;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.RadioGroup;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.RadioGroup.OnCheckedChangeListener;
public class EncryptFileActivity extends BaseActivity {
private EditText mFilename = null;
private CheckBox mDeleteAfter = null;
private ImageButton mBrowse = null;
private CheckBox mSign = null;
private TextView mMainUserId = null;
private TextView mMainUserIdRest = null;
private Spinner mAlgorithm = null;
private EditText mPassPhrase = null;
private EditText mPassPhraseAgain = null;
private CheckBox mAsciiArmour = null;
private RadioGroup mEncryptionMode = null;
private ViewGroup mAsymmetricLayout = null;
private ViewGroup mSymmetricLayout = null;
private Button mEncryptButton = null;
private Button mSelectKeysButton = null;
private long mEncryptionKeyIds[] = null;
private String mInputFilename = null;
private String mOutputFilename = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.encrypt_file);
mAsciiArmour = (CheckBox) findViewById(R.id.ascii_armour);
mAsciiArmour.setChecked(getDefaultAsciiArmour());
mAsciiArmour.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
guessOutputFilename();
}
});
// asymmetric tab
mSelectKeysButton = (Button) findViewById(R.id.btn_selectEncryptKeys);
mSelectKeysButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
selectPublicKeys();
}
});
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();
}
});
mDeleteAfter = (CheckBox) findViewById(R.id.delete_after_encryption);
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();
}
}
});
// symmetric tab
mAlgorithm = (Spinner) findViewById(R.id.algorithm);
Choice choices[] = {
new Choice(PGPEncryptedData.AES_128, "AES 128"),
new Choice(PGPEncryptedData.AES_192, "AES 192"),
new Choice(PGPEncryptedData.AES_256, "AES 256"),
new Choice(PGPEncryptedData.BLOWFISH, "Blowfish"),
new Choice(PGPEncryptedData.TWOFISH, "Twofish"),
new Choice(PGPEncryptedData.CAST5, "CAST5"),
new Choice(PGPEncryptedData.DES, "DES"),
new Choice(PGPEncryptedData.TRIPLE_DES, "Triple DES"),
new Choice(PGPEncryptedData.IDEA, "IDEA"),
};
ArrayAdapter<Choice> adapter =
new ArrayAdapter<Choice>(this, android.R.layout.simple_spinner_item, choices);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mAlgorithm.setAdapter(adapter);
for (int i = 0; i < choices.length; ++i) {
if (choices[i].getId() == getDefaultEncryptionAlgorithm()) {
mAlgorithm.setSelection(i);
break;
}
}
mEncryptionMode = (RadioGroup) findViewById(R.id.encryption_mode);
mAsymmetricLayout = (ViewGroup) findViewById(R.id.layout_asymmetric);
mSymmetricLayout = (ViewGroup) findViewById(R.id.layout_symmetric);
mPassPhrase = (EditText) findViewById(R.id.pass_phrase);
mPassPhraseAgain = (EditText) findViewById(R.id.pass_phrase_again);
mEncryptionMode.check(R.id.use_asymmetric);
mEncryptionMode.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
if (checkedId == R.id.use_symmetric) {
mAsymmetricLayout.setVisibility(ViewGroup.GONE);
mSymmetricLayout.setVisibility(ViewGroup.VISIBLE);
mEncryptionKeyIds = null;
setSecretKeyId(0);
} else {
mAsymmetricLayout.setVisibility(ViewGroup.VISIBLE);
mSymmetricLayout.setVisibility(ViewGroup.GONE);
mPassPhrase.setText("");
mPassPhraseAgain.setText("");
}
updateView();
}
});
if (mEncryptionMode.getCheckedRadioButtonId() == R.id.use_symmetric) {
mAsymmetricLayout.setVisibility(ViewGroup.GONE);
mSymmetricLayout.setVisibility(ViewGroup.VISIBLE);
} else {
mAsymmetricLayout.setVisibility(ViewGroup.VISIBLE);
mSymmetricLayout.setVisibility(ViewGroup.GONE);
}
mEncryptButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
encryptClicked();
}
});
updateView();
}
private void updateView() {
if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) {
mSelectKeysButton.setText(R.string.no_keys_selected);
} else if (mEncryptionKeyIds.length == 1) {
mSelectKeysButton.setText(R.string.one_key_selected);
} else {
mSelectKeysButton.setText("" + mEncryptionKeyIds.length + " " +
getResources().getString(R.string.n_keys_selected));
}
if (getSecretKeyId() == 0) {
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.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 selectPublicKeys() {
Intent intent = new Intent(this, SelectPublicKeyListActivity.class);
intent.putExtra("selection", mEncryptionKeyIds);
startActivityForResult(intent, Id.request.public_keys);
}
private void guessOutputFilename() {
mInputFilename = mFilename.getText().toString();
File file = new File(mInputFilename);
String ending = (mAsciiArmour.isChecked() ? ".asc" : ".gpg");
mOutputFilename = Constants.path.app_dir + "/" + file.getName() + ending;
}
private void encryptClicked() {
String currentFilename = mFilename.getText().toString();
if (mInputFilename == null || !mInputFilename.equals(currentFilename)) {
guessOutputFilename();
}
if (mInputFilename.equals("")) {
Toast.makeText(this, "Select a file first.", Toast.LENGTH_SHORT).show();
return;
}
File file = new File(mInputFilename);
if (!file.exists() || !file.isFile()) {
Toast.makeText(this, "Error: file not found", Toast.LENGTH_SHORT).show();
return;
}
// symmetric encryption
if (mEncryptionMode.getCheckedRadioButtonId() == R.id.use_symmetric) {
boolean gotPassPhrase = false;
String passPhrase = mPassPhrase.getText().toString();
String passPhraseAgain = mPassPhraseAgain.getText().toString();
if (!passPhrase.equals(passPhraseAgain)) {
Toast.makeText(this, "Pass phrases don't match.",
Toast.LENGTH_SHORT).show();
return;
}
gotPassPhrase = (passPhrase.length() != 0);
if (!gotPassPhrase) {
Toast.makeText(this, "Enter a pass phrase.",
Toast.LENGTH_SHORT).show();
return;
}
} else {
boolean encryptIt = mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0;
// for now require at least one form of encryption
if (!encryptIt) {
Toast.makeText(this, "Select at least one encryption key.",
Toast.LENGTH_SHORT).show();
return;
}
if (getSecretKeyId() != 0 && Apg.getPassPhrase() == null) {
showDialog(Id.dialog.pass_phrase);
return;
}
}
askForOutputFilename();
}
@Override
public void passPhraseCallback(String passPhrase) {
super.passPhraseCallback(passPhrase);
askForOutputFilename();
}
private void askForOutputFilename() {
showDialog(Id.dialog.output_filename);
}
private void encryptStart() {
showDialog(Id.dialog.encrypting);
startThread();
}
@Override
public void run() {
String error = null;
Bundle data = new Bundle();
Message msg = new Message();
try {
if (mInputFilename.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath()) ||
mOutputFilename.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
throw new GeneralException("external storage not ready");
}
}
InputStream in = new FileInputStream(mInputFilename);
OutputStream out = new FileOutputStream(mOutputFilename);
String passPhrase = null;
if (mEncryptionMode.getCheckedRadioButtonId() == R.id.use_symmetric) {
passPhrase = mPassPhrase.getText().toString();
if (passPhrase.length() == 0) {
passPhrase = null;
}
}
File file = new File(mInputFilename);
long fileSize = file.length();
Apg.encrypt(in, out, fileSize, mAsciiArmour.isChecked(),
mEncryptionKeyIds, getSecretKeyId(),
Apg.getPassPhrase(), this,
((Choice) mAlgorithm.getSelectedItem()).getId(),
getDefaultHashAlgorithm(),
passPhrase);
out.close();
} catch (FileNotFoundException e) {
error = "file not found: " + e.getMessage();
}
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 Dialog onCreateDialog(int id) {
switch (id) {
case Id.dialog.output_filename: {
return FileDialog.build(this, "Encrypt to file",
"Please specify which file to encrypt to.\n" +
"WARNING! File will be overwritten if it exists.",
mOutputFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(Id.dialog.output_filename);
mOutputFilename = filename;
encryptStart();
}
@Override
public void onCancelClick() {
removeDialog(Id.dialog.output_filename);
}
},
getString(R.string.filemanager_title_save),
getString(R.string.filemanager_btn_save),
Id.request.output_filename);
}
default: {
break;
}
}
return super.onCreateDialog(id);
}
@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.output_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;
}
case Id.request.secret_keys: {
if (resultCode == RESULT_OK) {
super.onActivityResult(requestCode, resultCode, data);
}
updateView();
break;
}
case Id.request.public_keys: {
if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
mEncryptionKeyIds = bundle.getLongArray("selection");
}
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();
if (mDeleteAfter.isChecked()) {
setDeleteFile(mInputFilename);
showDialog(Id.dialog.delete_file);
}
}
}
}

View File

@ -1,379 +0,0 @@
/*
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thialfihar.android.apg;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.util.Vector;
import org.bouncycastle2.openpgp.PGPException;
import org.bouncycastle2.openpgp.PGPPublicKey;
import org.bouncycastle2.openpgp.PGPPublicKeyRing;
import org.bouncycastle2.openpgp.PGPSecretKey;
import org.bouncycastle2.openpgp.PGPSecretKeyRing;
import org.bouncycastle2.util.Strings;
import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
import android.text.ClipboardManager;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class EncryptMessageActivity extends BaseActivity {
private String mSubject = null;
private String mSendTo = null;
private long mEncryptionKeyIds[] = null;
private EditText mMessage = null;
private Button mSelectKeysButton = null;
private Button mEncryptButton = null;
private Button mSendButton = null;
private CheckBox mSign = null;
private TextView mMainUserId = null;
private TextView mMainUserIdRest = null;
private int mEncryptTarget;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.encrypt_message);
mMessage = (EditText) findViewById(R.id.message);
mSelectKeysButton = (Button) findViewById(R.id.btn_selectEncryptKeys);
mEncryptButton = (Button) findViewById(R.id.btn_encrypt_to_clipboard);
mSendButton = (Button) findViewById(R.id.btn_send);
mSign = (CheckBox) findViewById(R.id.sign);
mMainUserId = (TextView) findViewById(R.id.main_user_id);
mMainUserIdRest = (TextView) findViewById(R.id.main_user_id_rest);
Intent intent = getIntent();
if (intent.getAction() != null &&
intent.getAction().equals(Apg.Intent.ENCRYPT)) {
String data = intent.getExtras().getString("data");
mSendTo = intent.getExtras().getString("sendTo");
mSubject = intent.getExtras().getString("subject");
long signatureKeyId = intent.getExtras().getLong("signatureKeyId");
long encryptionKeyIds[] = intent.getExtras().getLongArray("encryptionKeyIds");
if (signatureKeyId != 0) {
PGPSecretKeyRing keyRing = Apg.findSecretKeyRing(signatureKeyId);
PGPSecretKey masterKey = null;
if (keyRing != null) {
masterKey = Apg.getMasterKey(keyRing);
if (masterKey != null) {
Vector<PGPSecretKey> signKeys = Apg.getUsableSigningKeys(keyRing);
if (signKeys.size() > 0) {
setSecretKeyId(masterKey.getKeyID());
}
}
}
}
if (encryptionKeyIds != null) {
Vector<Long> goodIds = new Vector<Long>();
for (int i = 0; i < encryptionKeyIds.length; ++i) {
PGPPublicKeyRing keyRing = Apg.findPublicKeyRing(encryptionKeyIds[i]);
PGPPublicKey masterKey = null;
if (keyRing == null) {
continue;
}
masterKey = Apg.getMasterKey(keyRing);
if (masterKey == null) {
continue;
}
Vector<PGPPublicKey> encryptKeys = Apg.getUsableEncryptKeys(keyRing);
if (encryptKeys.size() == 0) {
continue;
}
goodIds.add(masterKey.getKeyID());
}
if (goodIds.size() > 0) {
mEncryptionKeyIds = new long[goodIds.size()];
for (int i = 0; i < goodIds.size(); ++i) {
mEncryptionKeyIds[i] = goodIds.get(i);
}
}
}
if (data != null) {
mMessage.setText(data);
}
}
mEncryptButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
encryptClicked();
}
});
mSendButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
sendClicked();
}
});
mSelectKeysButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
selectPublicKeys();
}
});
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();
}
}
});
updateView();
}
private void encryptClicked() {
mEncryptTarget = Id.target.clipboard;
if (getSecretKeyId() != 0 && Apg.getPassPhrase() == null) {
showDialog(Id.dialog.pass_phrase);
} else {
encryptStart();
}
}
private void sendClicked() {
mEncryptTarget = Id.target.email;
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 {
boolean encryptIt = mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0;
if (getSecretKeyId() == 0 && !encryptIt) {
throw new Apg.GeneralException("no signature key or encryption key selected");
}
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
message = message.replaceAll(" +\n", "\n");
message = message.replaceAll("\n\n+", "\n\n");
message = message.replaceFirst("^\n+", "");
// make sure there'll be exactly one newline at the end
message = message.replaceFirst("\n*$", "\n");
}
byte[] byteData = Strings.toUTF8ByteArray(message);
ByteArrayInputStream in = new ByteArrayInputStream(byteData);
ByteArrayOutputStream out = new ByteArrayOutputStream();
if (encryptIt) {
Apg.encrypt(in, out, byteData.length, true, mEncryptionKeyIds, getSecretKeyId(),
Apg.getPassPhrase(), this,
getDefaultEncryptionAlgorithm(), getDefaultHashAlgorithm(),
null);
} else {
Apg.signText(in, out, getSecretKeyId(),
Apg.getPassPhrase(), getDefaultHashAlgorithm(), this);
}
out.close();
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);
}
private void updateView() {
if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) {
mSelectKeysButton.setText(R.string.no_keys_selected);
} else if (mEncryptionKeyIds.length == 1) {
mSelectKeysButton.setText(R.string.one_key_selected);
} else {
mSelectKeysButton.setText("" + mEncryptionKeyIds.length + " " +
getResources().getString(R.string.n_keys_selected));
}
if (getSecretKeyId() == 0) {
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.setChecked(true);
}
}
private void selectPublicKeys() {
Intent intent = new Intent(this, SelectPublicKeyListActivity.class);
intent.putExtra("selection", mEncryptionKeyIds);
startActivityForResult(intent, Id.request.public_keys);
}
private void selectSecretKey() {
Intent intent = new Intent(this, SelectSecretKeyListActivity.class);
startActivityForResult(intent, Id.request.secret_keys);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case Id.request.secret_keys: {
if (resultCode == RESULT_OK) {
super.onActivityResult(requestCode, resultCode, data);
}
updateView();
break;
}
case Id.request.public_keys: {
if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
mEncryptionKeyIds = bundle.getLongArray("selection");
}
updateView();
break;
}
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");
switch (mEncryptTarget) {
case Id.target.clipboard: {
ClipboardManager clip = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
clip.setText(message);
Toast.makeText(this, "Successfully encrypted to clipboard.",
Toast.LENGTH_SHORT).show();
break;
}
case Id.target.email: {
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("text/plain; charset=utf-8");
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message);
if (mSubject != null) {
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,
mSubject);
}
if (mSendTo != null) {
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL,
new String[] { mSendTo });
}
EncryptMessageActivity.this.
startActivity(Intent.createChooser(emailIntent, "Send mail..."));
}
default: {
// shouldn't happen
break;
}
}
}
}
}

View File

@ -107,5 +107,6 @@ public final class Id {
public static final class target {
public static final int clipboard = 0x21070001;
public static final int email = 0x21070002;
public static final int file = 0x21070003;
}
}

View File

@ -24,14 +24,10 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
@ -45,7 +41,6 @@ import android.widget.Button;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;
@ -67,7 +62,9 @@ public class MainActivity extends BaseActivity {
encryptMessageButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, EncryptMessageActivity.class));
Intent intent = new Intent(MainActivity.this, EncryptActivity.class);
intent.setAction(Apg.Intent.ENCRYPT);
startActivity(intent);
}
});
@ -81,7 +78,9 @@ public class MainActivity extends BaseActivity {
encryptFileButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, EncryptFileActivity.class));
Intent intent = new Intent(MainActivity.this, EncryptActivity.class);
intent.setAction(Apg.Intent.ENCRYPT_FILE);
startActivity(intent);
}
});
@ -212,6 +211,7 @@ public class MainActivity extends BaseActivity {
message.setText("Read the warnings!\n\n" +
"Changes:\n" +
"* encrypt to clipboard\n" +
"* new encrypt GUI to handle everything\n" +
"\n" +
"WARNING: be careful editing your existing keys, as they " +
"WILL be stripped of certificates right now.\n" +