Merge remote-tracking branch 'origin/master' into wrapped-key-ring
Conflicts: OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java OpenKeychain/src/main/res/values/strings.xml
1
.gitignore
vendored
@ -31,3 +31,4 @@ pom.xml.*
|
||||
|
||||
#OS Specific
|
||||
[Tt]humbs.db
|
||||
.DS_Store
|
||||
|
6
.gitmodules
vendored
@ -19,3 +19,9 @@
|
||||
[submodule "extern/html-textview"]
|
||||
path = extern/html-textview
|
||||
url = https://github.com/open-keychain/html-textview.git
|
||||
[submodule "extern/openpgp-api-lib"]
|
||||
path = extern/openpgp-api-lib
|
||||
url = https://github.com/open-keychain/openpgp-api-lib.git
|
||||
[submodule "extern/openkeychain-api-lib"]
|
||||
path = extern/openkeychain-api-lib
|
||||
url = https://github.com/open-keychain/openkeychain-api-lib.git
|
||||
|
@ -1,3 +1,10 @@
|
||||
2.7
|
||||
* Purple! (Dominik, Vincent)
|
||||
* New key view design (Dominik, Vincent)
|
||||
* New flat Android buttons (Dominik, Vincent)
|
||||
* API fixes (Dominik)
|
||||
* Keybase.io import (Tim Bray)
|
||||
|
||||
2.6.1
|
||||
* some fixes for regression bugs
|
||||
|
||||
|
29
OpenKeychain-API/.gitignore
vendored
@ -1,29 +0,0 @@
|
||||
#Android specific
|
||||
bin
|
||||
gen
|
||||
obj
|
||||
lint.xml
|
||||
local.properties
|
||||
release.properties
|
||||
ant.properties
|
||||
*.class
|
||||
*.apk
|
||||
|
||||
#Gradle
|
||||
.gradle
|
||||
build
|
||||
gradle.properties
|
||||
|
||||
#Maven
|
||||
target
|
||||
pom.xml.*
|
||||
|
||||
#Eclipse
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
||||
.metadata
|
||||
|
||||
#IntelliJ IDEA
|
||||
.idea
|
||||
*.iml
|
@ -1,3 +0,0 @@
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '1.10'
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
// please leave this here, so this library builds on its own
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:0.10.0'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'android'
|
||||
|
||||
dependencies {
|
||||
compile 'com.android.support:support-v4:19.0.1'
|
||||
compile project(':libraries:openpgp-api-library')
|
||||
compile project(':libraries:openkeychain-api-library')
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 19
|
||||
buildToolsVersion "19.0.3"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 9
|
||||
targetSdkVersion 19
|
||||
}
|
||||
|
||||
/*
|
||||
* To sign release build, create file gradle.properties in ~/.gradle/ with this content:
|
||||
*
|
||||
* signingStoreLocation=/home/key.store
|
||||
* signingStorePassword=xxx
|
||||
* signingKeyAlias=alias
|
||||
* signingKeyPassword=xxx
|
||||
*/
|
||||
if (project.hasProperty('signingStoreLocation') &&
|
||||
project.hasProperty('signingStorePassword') &&
|
||||
project.hasProperty('signingKeyAlias') &&
|
||||
project.hasProperty('signingKeyPassword')) {
|
||||
println "Found sign properties in gradle.properties! Signing build…"
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
storeFile file(signingStoreLocation)
|
||||
storePassword signingStorePassword
|
||||
keyAlias signingKeyAlias
|
||||
keyPassword signingKeyPassword
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes.release.signingConfig = signingConfigs.release
|
||||
} else {
|
||||
buildTypes.release.signingConfig = null
|
||||
}
|
||||
|
||||
// Do not abort build if lint finds errors
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 23 KiB |
@ -1,33 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.sufficientlysecure.keychain.demo"
|
||||
android:versionCode="4"
|
||||
android:versionName="3">
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="9"
|
||||
android:targetSdkVersion="19" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="OpenKeychain API Demo">
|
||||
<activity
|
||||
android:name=".BaseActivity"
|
||||
android:label="OpenKeychain API Demo">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".OpenPgpProviderActivity"
|
||||
android:label="OpenPGP Provider"
|
||||
android:windowSoftInputMode="stateHidden" />
|
||||
<activity
|
||||
android:name=".IntentActivity"
|
||||
android:label="Intents" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* 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.sufficientlysecure.keychain.demo;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class BaseActivity extends PreferenceActivity {
|
||||
private Preference mIntentDemo;
|
||||
private Preference mCryptoProvider;
|
||||
|
||||
/**
|
||||
* Called when the activity is first created.
|
||||
*/
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// load preferences from xml
|
||||
addPreferencesFromResource(R.xml.base_preference);
|
||||
|
||||
// find preferences
|
||||
mIntentDemo = (Preference) findPreference("intent_demo");
|
||||
mCryptoProvider = (Preference) findPreference("openpgp_provider_demo");
|
||||
|
||||
mIntentDemo.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
startActivity(new Intent(BaseActivity.this, IntentActivity.class));
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
mCryptoProvider.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
startActivity(new Intent(BaseActivity.this, OpenPgpProviderActivity.class));
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* 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.sufficientlysecure.keychain.demo;
|
||||
|
||||
public final class Constants {
|
||||
public static final String TAG = "Keychain";
|
||||
}
|
@ -1,583 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* 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.sufficientlysecure.keychain.demo;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.sufficientlysecure.keychain.api.OpenKeychainIntents;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
public class IntentActivity extends PreferenceActivity {
|
||||
private Preference mEncrypt;
|
||||
private Preference mEncryptUri;
|
||||
private Preference mDecrypt;
|
||||
private Preference mImportKey;
|
||||
private Preference mImportKeyFromKeyserver;
|
||||
private Preference mImportKeyFromQrCode;
|
||||
private Preference mOpenpgp4fpr;
|
||||
|
||||
private static final int REQUEST_CODE_SELECT_PHOTO = 100;
|
||||
|
||||
/**
|
||||
* Called when the activity is first created.
|
||||
*/
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// load preferences from xml
|
||||
addPreferencesFromResource(R.xml.intent_preference);
|
||||
|
||||
// find preferences
|
||||
mEncrypt = (Preference) findPreference("ENCRYPT");
|
||||
mEncryptUri = (Preference) findPreference("ENCRYPT_URI");
|
||||
mDecrypt = (Preference) findPreference("DECRYPT");
|
||||
mImportKey = (Preference) findPreference("IMPORT_KEY");
|
||||
mImportKeyFromKeyserver = (Preference) findPreference("IMPORT_KEY_FROM_KEYSERVER");
|
||||
mImportKeyFromQrCode = (Preference) findPreference("IMPORT_KEY_FROM_QR_CODE");
|
||||
mOpenpgp4fpr = (Preference) findPreference("openpgp4fpr");
|
||||
|
||||
|
||||
mEncrypt.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
try {
|
||||
Intent intent = new Intent(OpenKeychainIntents.ENCRYPT);
|
||||
intent.putExtra(OpenKeychainIntents.ENCRYPT_EXTRA_TEXT, "Hello world!");
|
||||
startActivity(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
mEncryptUri.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
|
||||
photoPickerIntent.setType("image/*");
|
||||
startActivityForResult(photoPickerIntent, REQUEST_CODE_SELECT_PHOTO);
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
mDecrypt.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
try {
|
||||
Intent intent = new Intent(OpenKeychainIntents.DECRYPT);
|
||||
intent.putExtra(OpenKeychainIntents.DECRYPT_EXTRA_TEXT, TEST_SIGNED_MESSAGE);
|
||||
startActivity(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
mImportKey.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
try {
|
||||
Intent intent = new Intent(OpenKeychainIntents.IMPORT_KEY);
|
||||
byte[] pubkey = null;
|
||||
try {
|
||||
pubkey = TEST_PUBKEY.getBytes("UTF-8");
|
||||
intent.putExtra(OpenKeychainIntents.IMPORT_KEY_EXTRA_KEY_BYTES, pubkey);
|
||||
startActivity(intent);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(Constants.TAG, "UnsupportedEncodingException", e);
|
||||
}
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
mImportKeyFromKeyserver.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
try {
|
||||
Intent intent = new Intent(OpenKeychainIntents.IMPORT_KEY_FROM_KEYSERVER);
|
||||
intent.putExtra(OpenKeychainIntents.IMPORT_KEY_FROM_KEYSERVER_QUERY, "Richard Stallman");
|
||||
startActivity(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
mImportKeyFromQrCode.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
try {
|
||||
Intent intent = new Intent(OpenKeychainIntents.IMPORT_KEY_FROM_QR_CODE);
|
||||
startActivity(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
mOpenpgp4fpr.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse("openpgp4fpr:73EE2314F65FA92EC2390D3A718C070100012282"));
|
||||
startActivity(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent) {
|
||||
super.onActivityResult(requestCode, resultCode, imageReturnedIntent);
|
||||
|
||||
switch (requestCode) {
|
||||
case REQUEST_CODE_SELECT_PHOTO:
|
||||
if (resultCode == RESULT_OK) {
|
||||
Uri selectedImage = imageReturnedIntent.getData();
|
||||
|
||||
String[] filePathColumn = {MediaStore.Images.Media.DATA};
|
||||
|
||||
Cursor cursor = getContentResolver().query(
|
||||
selectedImage, filePathColumn, null, null, null);
|
||||
cursor.moveToFirst();
|
||||
|
||||
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
|
||||
String filePath = cursor.getString(columnIndex);
|
||||
cursor.close();
|
||||
|
||||
// TODO: after fixing DECRYPT, we could use Uri selectedImage directly
|
||||
Log.d(Constants.TAG, "filePath: " + filePath);
|
||||
|
||||
try {
|
||||
Intent intent = new Intent(OpenKeychainIntents.ENCRYPT);
|
||||
Uri dataUri = Uri.parse("file://" + filePath);
|
||||
Log.d(Constants.TAG, "Uri: " + dataUri);
|
||||
intent.setData(dataUri);
|
||||
startActivity(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final String TEST_SIGNED_MESSAGE = "-----BEGIN PGP SIGNED MESSAGE-----\n" +
|
||||
"Hash: SHA1\n" +
|
||||
"\n" +
|
||||
"Hello world!\n" +
|
||||
"-----BEGIN PGP SIGNATURE-----\n" +
|
||||
"Version: GnuPG v1.4.12 (GNU/Linux)\n" +
|
||||
"Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/\n" +
|
||||
"\n" +
|
||||
"iQEcBAEBAgAGBQJS/7vTAAoJEHGMBwEAASKCkGYH/2jBLzamVyqd61jrjMQM0jUv\n" +
|
||||
"MkDcPUxPrYH3wZOO0HcgdBQEo66GZEC2ATmo8izJUMk35Q5jas99k0ac9pXhPUPE\n" +
|
||||
"5qDXdQS10S07R6J0SeDYFWDSyrSiDTCZpFkVu3JGP/3S0SkMYXPzfYlh8Ciuxu7i\n" +
|
||||
"FR5dmIiz3VQaBgTBSCBFEomNFM5ypynBJqKIzIty8v0NbV72Rtg6Xg76YqWQ/6MC\n" +
|
||||
"/MlT3y3++HhfpEmLf5WLEXljbuZ4SfCybgYXG9gBzhJu3+gmBoSicdYTZDHSxBBR\n" +
|
||||
"BwI+ueLbhgRz+gU+WJFE7xNw35xKtBp1C4PR0iKI8rZCSHLjsRVzor7iwDaR51M=\n" +
|
||||
"=3Ydc\n" +
|
||||
"-----END PGP SIGNATURE-----";
|
||||
|
||||
private static final String TEST_PUBKEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||
"Version: GnuPG v1.4.12 (GNU/Linux)\n" +
|
||||
"\n" +
|
||||
"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\n" +
|
||||
"k3wUxv46o0y15asU\n" +
|
||||
"=Bbew\n" +
|
||||
"-----END PGP PUBLIC KEY BLOCK-----\n";
|
||||
|
||||
}
|
@ -1,379 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* 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.sufficientlysecure.keychain.demo;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.openintents.openpgp.OpenPgpError;
|
||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||
import org.openintents.openpgp.util.OpenPgpApi;
|
||||
import org.openintents.openpgp.util.OpenPgpServiceConnection;
|
||||
import org.openintents.openpgp.util.OpenPgpUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class OpenPgpProviderActivity extends Activity {
|
||||
private EditText mMessage;
|
||||
private EditText mCiphertext;
|
||||
private EditText mEncryptUserIds;
|
||||
private Button mSign;
|
||||
private Button mEncrypt;
|
||||
private Button mSignAndEncrypt;
|
||||
private Button mDecryptAndVerify;
|
||||
private EditText mAccount;
|
||||
private EditText mGetKeyEdit;
|
||||
private EditText mGetKeyIdsEdit;
|
||||
private Button mGetKey;
|
||||
private Button mGetKeyIds;
|
||||
|
||||
private OpenPgpServiceConnection mServiceConnection;
|
||||
|
||||
public static final int REQUEST_CODE_SIGN = 9910;
|
||||
public static final int REQUEST_CODE_ENCRYPT = 9911;
|
||||
public static final int REQUEST_CODE_SIGN_AND_ENCRYPT = 9912;
|
||||
public static final int REQUEST_CODE_DECRYPT_AND_VERIFY = 9913;
|
||||
public static final int REQUEST_CODE_GET_KEY = 9914;
|
||||
public static final int REQUEST_CODE_GET_KEY_IDS = 9915;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.openpgp_provider);
|
||||
|
||||
mMessage = (EditText) findViewById(R.id.crypto_provider_demo_message);
|
||||
mCiphertext = (EditText) findViewById(R.id.crypto_provider_demo_ciphertext);
|
||||
mEncryptUserIds = (EditText) findViewById(R.id.crypto_provider_demo_encrypt_user_id);
|
||||
mSign = (Button) findViewById(R.id.crypto_provider_demo_sign);
|
||||
mEncrypt = (Button) findViewById(R.id.crypto_provider_demo_encrypt);
|
||||
mSignAndEncrypt = (Button) findViewById(R.id.crypto_provider_demo_sign_and_encrypt);
|
||||
mDecryptAndVerify = (Button) findViewById(R.id.crypto_provider_demo_decrypt_and_verify);
|
||||
mAccount = (EditText) findViewById(R.id.crypto_provider_demo_account);
|
||||
mGetKeyEdit = (EditText) findViewById(R.id.crypto_provider_demo_get_key_edit);
|
||||
mGetKeyIdsEdit = (EditText) findViewById(R.id.crypto_provider_demo_get_key_ids_edit);
|
||||
mGetKey = (Button) findViewById(R.id.crypto_provider_demo_get_key);
|
||||
mGetKeyIds = (Button) findViewById(R.id.crypto_provider_demo_get_key_ids);
|
||||
|
||||
mSign.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
sign(new Intent());
|
||||
}
|
||||
});
|
||||
mEncrypt.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
encrypt(new Intent());
|
||||
}
|
||||
});
|
||||
mSignAndEncrypt.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
signAndEncrypt(new Intent());
|
||||
}
|
||||
});
|
||||
mDecryptAndVerify.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
decryptAndVerify(new Intent());
|
||||
}
|
||||
});
|
||||
mGetKey.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
getKey(new Intent());
|
||||
}
|
||||
});
|
||||
mGetKeyIds.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
getKeyIds(new Intent());
|
||||
}
|
||||
});
|
||||
|
||||
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
String providerPackageName = settings.getString("openpgp_provider_list", "");
|
||||
if (TextUtils.isEmpty(providerPackageName)) {
|
||||
Toast.makeText(this, "No OpenPGP Provider selected!", Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
} else {
|
||||
// bind to service
|
||||
mServiceConnection = new OpenPgpServiceConnection(
|
||||
OpenPgpProviderActivity.this, providerPackageName);
|
||||
mServiceConnection.bindToService();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleError(final OpenPgpError error) {
|
||||
runOnUiThread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(OpenPgpProviderActivity.this,
|
||||
"onError id:" + error.getErrorId() + "\n\n" + error.getMessage(),
|
||||
Toast.LENGTH_LONG).show();
|
||||
Log.e(Constants.TAG, "onError getErrorId:" + error.getErrorId());
|
||||
Log.e(Constants.TAG, "onError getMessage:" + error.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showToast(final String message) {
|
||||
runOnUiThread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(OpenPgpProviderActivity.this,
|
||||
message,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes input from message or ciphertext EditText and turns it into a ByteArrayInputStream
|
||||
*
|
||||
* @param ciphertext
|
||||
* @return
|
||||
*/
|
||||
private InputStream getInputstream(boolean ciphertext) {
|
||||
InputStream is = null;
|
||||
try {
|
||||
String inputStr;
|
||||
if (ciphertext) {
|
||||
inputStr = mCiphertext.getText().toString();
|
||||
} else {
|
||||
inputStr = mMessage.getText().toString();
|
||||
}
|
||||
is = new ByteArrayInputStream(inputStr.getBytes("UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(Constants.TAG, "UnsupportedEncodingException", e);
|
||||
}
|
||||
|
||||
return is;
|
||||
}
|
||||
|
||||
private class MyCallback implements OpenPgpApi.IOpenPgpCallback {
|
||||
boolean returnToCiphertextField;
|
||||
ByteArrayOutputStream os;
|
||||
int requestCode;
|
||||
|
||||
private MyCallback(boolean returnToCiphertextField, ByteArrayOutputStream os, int requestCode) {
|
||||
this.returnToCiphertextField = returnToCiphertextField;
|
||||
this.os = os;
|
||||
this.requestCode = requestCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReturn(Intent result) {
|
||||
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
|
||||
case OpenPgpApi.RESULT_CODE_SUCCESS: {
|
||||
showToast("RESULT_CODE_SUCCESS");
|
||||
|
||||
// encrypt/decrypt/sign/verify
|
||||
if (os != null) {
|
||||
try {
|
||||
Log.d(OpenPgpApi.TAG, "result: " + os.toByteArray().length
|
||||
+ " str=" + os.toString("UTF-8"));
|
||||
|
||||
if (returnToCiphertextField) {
|
||||
mCiphertext.setText(os.toString("UTF-8"));
|
||||
} else {
|
||||
mMessage.setText(os.toString("UTF-8"));
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(Constants.TAG, "UnsupportedEncodingException", e);
|
||||
}
|
||||
}
|
||||
|
||||
// verify
|
||||
if (result.hasExtra(OpenPgpApi.RESULT_SIGNATURE)) {
|
||||
OpenPgpSignatureResult sigResult
|
||||
= result.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
|
||||
showToast(sigResult.toString());
|
||||
}
|
||||
|
||||
// get key ids
|
||||
if (result.hasExtra(OpenPgpApi.RESULT_KEY_IDS)) {
|
||||
long[] keyIds = result.getLongArrayExtra(OpenPgpApi.RESULT_KEY_IDS);
|
||||
String out = "keyIds: ";
|
||||
for (int i = 0; i < keyIds.length; i++) {
|
||||
out += OpenPgpUtils.convertKeyIdToHex(keyIds[i]) + ", ";
|
||||
}
|
||||
|
||||
showToast(out);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: {
|
||||
showToast("RESULT_CODE_USER_INTERACTION_REQUIRED");
|
||||
|
||||
PendingIntent pi = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
|
||||
try {
|
||||
OpenPgpProviderActivity.this.startIntentSenderForResult(pi.getIntentSender(),
|
||||
requestCode, null, 0, 0, 0);
|
||||
} catch (IntentSender.SendIntentException e) {
|
||||
Log.e(Constants.TAG, "SendIntentException", e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpenPgpApi.RESULT_CODE_ERROR: {
|
||||
showToast("RESULT_CODE_ERROR");
|
||||
|
||||
OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
|
||||
handleError(error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void sign(Intent data) {
|
||||
data.setAction(OpenPgpApi.ACTION_SIGN);
|
||||
data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||
data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString());
|
||||
|
||||
InputStream is = getInputstream(false);
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
|
||||
OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
|
||||
api.executeApiAsync(data, is, os, new MyCallback(true, os, REQUEST_CODE_SIGN));
|
||||
}
|
||||
|
||||
public void encrypt(Intent data) {
|
||||
data.setAction(OpenPgpApi.ACTION_ENCRYPT);
|
||||
data.putExtra(OpenPgpApi.EXTRA_USER_IDS, mEncryptUserIds.getText().toString().split(","));
|
||||
data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||
data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString());
|
||||
|
||||
InputStream is = getInputstream(false);
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
|
||||
OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
|
||||
api.executeApiAsync(data, is, os, new MyCallback(true, os, REQUEST_CODE_ENCRYPT));
|
||||
}
|
||||
|
||||
public void signAndEncrypt(Intent data) {
|
||||
data.setAction(OpenPgpApi.ACTION_SIGN_AND_ENCRYPT);
|
||||
data.putExtra(OpenPgpApi.EXTRA_USER_IDS, mEncryptUserIds.getText().toString().split(","));
|
||||
data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||
data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString());
|
||||
|
||||
InputStream is = getInputstream(false);
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
|
||||
OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
|
||||
api.executeApiAsync(data, is, os, new MyCallback(true, os, REQUEST_CODE_SIGN_AND_ENCRYPT));
|
||||
}
|
||||
|
||||
public void decryptAndVerify(Intent data) {
|
||||
data.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
|
||||
data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||
data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString());
|
||||
|
||||
InputStream is = getInputstream(true);
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
|
||||
OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
|
||||
api.executeApiAsync(data, is, os, new MyCallback(false, os, REQUEST_CODE_DECRYPT_AND_VERIFY));
|
||||
}
|
||||
|
||||
public void getKey(Intent data) {
|
||||
data.setAction(OpenPgpApi.ACTION_GET_KEY);
|
||||
data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString());
|
||||
data.putExtra(OpenPgpApi.EXTRA_KEY_ID, Long.decode(mGetKeyEdit.getText().toString()));
|
||||
|
||||
OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
|
||||
api.executeApiAsync(data, null, null, new MyCallback(false, null, REQUEST_CODE_GET_KEY));
|
||||
}
|
||||
|
||||
public void getKeyIds(Intent data) {
|
||||
data.setAction(OpenPgpApi.ACTION_GET_KEY_IDS);
|
||||
data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString());
|
||||
data.putExtra(OpenPgpApi.EXTRA_USER_IDS, mGetKeyIdsEdit.getText().toString().split(","));
|
||||
|
||||
OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
|
||||
api.executeApiAsync(data, null, null, new MyCallback(false, null, REQUEST_CODE_GET_KEY_IDS));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
Log.d(Constants.TAG, "onActivityResult resultCode: " + resultCode);
|
||||
|
||||
// try again after user interaction
|
||||
if (resultCode == RESULT_OK) {
|
||||
/*
|
||||
* The data originally given to one of the methods above, is again
|
||||
* returned here to be used when calling the method again after user
|
||||
* interaction. The Intent now also contains results from the user
|
||||
* interaction, for example selected key ids.
|
||||
*/
|
||||
switch (requestCode) {
|
||||
case REQUEST_CODE_SIGN: {
|
||||
sign(data);
|
||||
break;
|
||||
}
|
||||
case REQUEST_CODE_ENCRYPT: {
|
||||
encrypt(data);
|
||||
break;
|
||||
}
|
||||
case REQUEST_CODE_SIGN_AND_ENCRYPT: {
|
||||
signAndEncrypt(data);
|
||||
break;
|
||||
}
|
||||
case REQUEST_CODE_DECRYPT_AND_VERIFY: {
|
||||
decryptAndVerify(data);
|
||||
break;
|
||||
}
|
||||
case REQUEST_CODE_GET_KEY: {
|
||||
getKey(data);
|
||||
break;
|
||||
}
|
||||
case REQUEST_CODE_GET_KEY_IDS: {
|
||||
getKeyIds(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
if (mServiceConnection != null) {
|
||||
mServiceConnection.unbindFromService();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 5.6 KiB |
@ -1,158 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/parent_scroll"
|
||||
android:fillViewport="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:padding="8dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Encrypt UserIds (split with ',')"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
|
||||
<EditText
|
||||
android:id="@+id/crypto_provider_demo_encrypt_user_id"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="dominik@dominikschuermann.de"
|
||||
android:textAppearance="@android:style/TextAppearance.Small" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Message"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/child_scroll1"
|
||||
android:fillViewport="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="120dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/crypto_provider_demo_message"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scrollHorizontally="true"
|
||||
android:scrollbars="vertical"
|
||||
android:text="message"
|
||||
android:hint="cleartext message"
|
||||
android:textAppearance="@android:style/TextAppearance.Small" />
|
||||
</ScrollView>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Ciphertext"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/child_scroll2"
|
||||
android:fillViewport="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="120dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/crypto_provider_demo_ciphertext"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="ciphertext"
|
||||
android:hint="ciphertext"
|
||||
android:textAppearance="@android:style/TextAppearance.Small" />
|
||||
</ScrollView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Button
|
||||
android:id="@+id/crypto_provider_demo_sign"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Sign"
|
||||
android:layout_gravity="center_vertical" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/crypto_provider_demo_encrypt"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Encrypt"
|
||||
android:layout_gravity="center_vertical" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/crypto_provider_demo_sign_and_encrypt"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Sign and Encrypt"
|
||||
android:layout_gravity="center_vertical" />
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/crypto_provider_demo_decrypt_and_verify"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Decrypt and Verify" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Account ID:"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<EditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Alice <alice@example.com>"
|
||||
android:id="@+id/crypto_provider_demo_account" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Get key:"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<EditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="0x718c070100012282"
|
||||
android:id="@+id/crypto_provider_demo_get_key_edit" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/crypto_provider_demo_get_key"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Get key" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Get key ids:"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<EditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="dominik@dominikschuermann.de"
|
||||
android:id="@+id/crypto_provider_demo_get_key_ids_edit" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/crypto_provider_demo_get_key_ids"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Get key ids" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<PreferenceCategory android:title="OpenKeychain Intents">
|
||||
<Preference
|
||||
android:key="intent_demo"
|
||||
android:title="Intent Demo" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="OpenPGP Provider API">
|
||||
<org.openintents.openpgp.util.OpenPgpListPreference
|
||||
android:key="openpgp_provider_list"
|
||||
android:title="Select OpenPGP Provider!" />
|
||||
<Preference
|
||||
android:key="openpgp_provider_demo"
|
||||
android:title="OpenPGP Provider Demo" />
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<PreferenceCategory android:title="Intents (org.sufficientlysecure.keychain.action.)">
|
||||
<Preference
|
||||
android:key="ENCRYPT"
|
||||
android:title="ENCRYPT" />
|
||||
<Preference
|
||||
android:key="ENCRYPT_URI"
|
||||
android:title="ENCRYPT with Uri" />
|
||||
<Preference
|
||||
android:key="DECRYPT"
|
||||
android:title="DECRYPT" />
|
||||
<Preference
|
||||
android:key="IMPORT_KEY"
|
||||
android:title="IMPORT_KEY" />
|
||||
<Preference
|
||||
android:key="IMPORT_KEY_FROM_KEYSERVER"
|
||||
android:title="IMPORT_KEY_FROM_KEYSERVER" />
|
||||
<Preference
|
||||
android:key="IMPORT_KEY_FROM_QR_CODE"
|
||||
android:title="IMPORT_KEY_FROM_QR_CODE" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="Special Intents">
|
||||
<Preference
|
||||
android:key="openpgp4fpr"
|
||||
android:title="VIEW openpgp4fpr:73EE2314F65FA92EC2390D3A718C070100012282" />
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
@ -1,6 +0,0 @@
|
||||
#Sun Feb 09 19:19:25 CET 2014
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-bin.zip
|
164
OpenKeychain-API/gradle/gradlew
vendored
@ -1,164 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
fi
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >&-
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >&-
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
90
OpenKeychain-API/gradle/gradlew.bat
vendored
@ -1,90 +0,0 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
BIN
OpenKeychain-API/gradle/wrapper/gradle-wrapper.jar
vendored
@ -1,6 +0,0 @@
|
||||
#Thu Mar 06 22:23:44 CET 2014
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-bin.zip
|
164
OpenKeychain-API/gradlew
vendored
@ -1,164 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
fi
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >&-
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >&-
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
90
OpenKeychain-API/gradlew.bat
vendored
@ -1,90 +0,0 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
@ -1,29 +0,0 @@
|
||||
#Android specific
|
||||
bin
|
||||
gen
|
||||
obj
|
||||
lint.xml
|
||||
local.properties
|
||||
release.properties
|
||||
ant.properties
|
||||
*.class
|
||||
*.apk
|
||||
|
||||
#Gradle
|
||||
.gradle
|
||||
build
|
||||
gradle.properties
|
||||
|
||||
#Maven
|
||||
target
|
||||
pom.xml.*
|
||||
|
||||
#Eclipse
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
||||
.metadata
|
||||
|
||||
#IntelliJ IDEA
|
||||
.idea
|
||||
*.iml
|
@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.sufficientlysecure.keychain.api"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="9"
|
||||
android:targetSdkVersion="19" />
|
||||
|
||||
<application/>
|
||||
|
||||
</manifest>
|
@ -1,202 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
@ -1,35 +0,0 @@
|
||||
// please leave this here, so this library builds on its own
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:0.10.0'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'android-library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 19
|
||||
buildToolsVersion '19.0.3'
|
||||
|
||||
// NOTE: We are using the old folder structure to also support Eclipse
|
||||
sourceSets {
|
||||
main {
|
||||
manifest.srcFile 'AndroidManifest.xml'
|
||||
java.srcDirs = ['src']
|
||||
resources.srcDirs = ['src']
|
||||
aidl.srcDirs = ['src']
|
||||
renderscript.srcDirs = ['src']
|
||||
res.srcDirs = ['res']
|
||||
assets.srcDirs = ['assets']
|
||||
}
|
||||
}
|
||||
|
||||
// Do not abort build if lint finds errors
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="keychain-api-library" default="help">
|
||||
|
||||
<!-- The local.properties file is created and updated by the 'android' tool.
|
||||
It contains the path to the SDK. It should *NOT* be checked into
|
||||
Version Control Systems. -->
|
||||
<property file="local.properties" />
|
||||
|
||||
<!-- The ant.properties file can be created by you. It is only edited by the
|
||||
'android' tool to add properties to it.
|
||||
This is the place to change some Ant specific build properties.
|
||||
Here are some properties you may want to change/update:
|
||||
|
||||
source.dir
|
||||
The name of the source directory. Default is 'src'.
|
||||
out.dir
|
||||
The name of the output directory. Default is 'bin'.
|
||||
|
||||
For other overridable properties, look at the beginning of the rules
|
||||
files in the SDK, at tools/ant/build.xml
|
||||
|
||||
Properties related to the SDK location or the project target should
|
||||
be updated using the 'android' tool with the 'update' action.
|
||||
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems.
|
||||
|
||||
-->
|
||||
<property file="ant.properties" />
|
||||
|
||||
<!-- if sdk.dir was not set from one of the property file, then
|
||||
get it from the ANDROID_HOME env var.
|
||||
This must be done before we load project.properties since
|
||||
the proguard config can use sdk.dir -->
|
||||
<property environment="env" />
|
||||
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
|
||||
<isset property="env.ANDROID_HOME" />
|
||||
</condition>
|
||||
|
||||
<!-- The project.properties file is created and updated by the 'android'
|
||||
tool, as well as ADT.
|
||||
|
||||
This contains project specific properties such as project target, and library
|
||||
dependencies. Lower level build properties are stored in ant.properties
|
||||
(or in .classpath for Eclipse projects).
|
||||
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems. -->
|
||||
<loadproperties srcFile="project.properties" />
|
||||
|
||||
<!-- quick check on sdk.dir -->
|
||||
<fail
|
||||
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
|
||||
unless="sdk.dir"
|
||||
/>
|
||||
|
||||
<!--
|
||||
Import per project custom build rules if present at the root of the project.
|
||||
This is the place to put custom intermediary targets such as:
|
||||
-pre-build
|
||||
-pre-compile
|
||||
-post-compile (This is typically used for code obfuscation.
|
||||
Compiled code location: ${out.classes.absolute.dir}
|
||||
If this is not done in place, override ${out.dex.input.absolute.dir})
|
||||
-post-package
|
||||
-post-build
|
||||
-pre-clean
|
||||
-->
|
||||
<import file="custom_rules.xml" optional="true" />
|
||||
|
||||
<!-- Import the actual build file.
|
||||
|
||||
To customize existing targets, there are two options:
|
||||
- Customize only one target:
|
||||
- copy/paste the target into this file, *before* the
|
||||
<import> task.
|
||||
- customize it to your needs.
|
||||
- Customize the whole content of build.xml
|
||||
- copy/paste the content of the rules files (minus the top node)
|
||||
into this file, replacing the <import> task.
|
||||
- customize to your needs.
|
||||
|
||||
***********************
|
||||
****** IMPORTANT ******
|
||||
***********************
|
||||
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
|
||||
in order to avoid having your file be overridden by tools such as "android update project"
|
||||
-->
|
||||
<!-- version-tag: 1 -->
|
||||
<import file="${sdk.dir}/tools/ant/build.xml" />
|
||||
|
||||
</project>
|
@ -1,20 +0,0 @@
|
||||
# To enable ProGuard in your project, edit project.properties
|
||||
# to define the proguard.config property as described in that file.
|
||||
#
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in ${sdk.dir}/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the ProGuard
|
||||
# include property in project.properties.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
@ -1,15 +0,0 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system edit
|
||||
# "ant.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
#
|
||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
|
||||
# Project target.
|
||||
target=android-19
|
||||
android.library=true
|
@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* 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.sufficientlysecure.keychain.api;
|
||||
|
||||
public class OpenKeychainIntents {
|
||||
|
||||
public static final String ENCRYPT = "org.sufficientlysecure.keychain.action.ENCRYPT";
|
||||
public static final String ENCRYPT_EXTRA_TEXT = "text"; // String
|
||||
public static final String ENCRYPT_ASCII_ARMOR = "ascii_armor"; // boolean
|
||||
|
||||
public static final String DECRYPT = "org.sufficientlysecure.keychain.action.DECRYPT";
|
||||
public static final String DECRYPT_EXTRA_TEXT = "text"; // String
|
||||
|
||||
public static final String IMPORT_KEY = "org.sufficientlysecure.keychain.action.IMPORT_KEY";
|
||||
public static final String IMPORT_KEY_EXTRA_KEY_BYTES = "key_bytes"; // byte[]
|
||||
|
||||
public static final String IMPORT_KEY_FROM_KEYSERVER = "org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_KEYSERVER";
|
||||
public static final String IMPORT_KEY_FROM_KEYSERVER_QUERY = "query"; // String
|
||||
public static final String IMPORT_KEY_FROM_KEYSERVER_FINGERPRINT = "fingerprint"; // String
|
||||
|
||||
public static final String IMPORT_KEY_FROM_QR_CODE = "org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_QR_CODE";
|
||||
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
#Android specific
|
||||
bin
|
||||
gen
|
||||
obj
|
||||
lint.xml
|
||||
local.properties
|
||||
release.properties
|
||||
ant.properties
|
||||
*.class
|
||||
*.apk
|
||||
|
||||
#Gradle
|
||||
.gradle
|
||||
build
|
||||
gradle.properties
|
||||
|
||||
#Maven
|
||||
target
|
||||
pom.xml.*
|
||||
|
||||
#Eclipse
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
||||
.metadata
|
||||
|
||||
#IntelliJ IDEA
|
||||
.idea
|
||||
*.iml
|
@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.openintents.openpgp"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="9"
|
||||
android:targetSdkVersion="19" />
|
||||
|
||||
<application/>
|
||||
|
||||
</manifest>
|
@ -1,202 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
@ -1,35 +0,0 @@
|
||||
// please leave this here, so this library builds on its own
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:0.10.0'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'android-library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 19
|
||||
buildToolsVersion '19.0.3'
|
||||
|
||||
// NOTE: We are using the old folder structure to also support Eclipse
|
||||
sourceSets {
|
||||
main {
|
||||
manifest.srcFile 'AndroidManifest.xml'
|
||||
java.srcDirs = ['src']
|
||||
resources.srcDirs = ['src']
|
||||
aidl.srcDirs = ['src']
|
||||
renderscript.srcDirs = ['src']
|
||||
res.srcDirs = ['res']
|
||||
assets.srcDirs = ['assets']
|
||||
}
|
||||
}
|
||||
|
||||
// Do not abort build if lint finds errors
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="keychain-api-library" default="help">
|
||||
|
||||
<!-- The local.properties file is created and updated by the 'android' tool.
|
||||
It contains the path to the SDK. It should *NOT* be checked into
|
||||
Version Control Systems. -->
|
||||
<property file="local.properties" />
|
||||
|
||||
<!-- The ant.properties file can be created by you. It is only edited by the
|
||||
'android' tool to add properties to it.
|
||||
This is the place to change some Ant specific build properties.
|
||||
Here are some properties you may want to change/update:
|
||||
|
||||
source.dir
|
||||
The name of the source directory. Default is 'src'.
|
||||
out.dir
|
||||
The name of the output directory. Default is 'bin'.
|
||||
|
||||
For other overridable properties, look at the beginning of the rules
|
||||
files in the SDK, at tools/ant/build.xml
|
||||
|
||||
Properties related to the SDK location or the project target should
|
||||
be updated using the 'android' tool with the 'update' action.
|
||||
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems.
|
||||
|
||||
-->
|
||||
<property file="ant.properties" />
|
||||
|
||||
<!-- if sdk.dir was not set from one of the property file, then
|
||||
get it from the ANDROID_HOME env var.
|
||||
This must be done before we load project.properties since
|
||||
the proguard config can use sdk.dir -->
|
||||
<property environment="env" />
|
||||
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
|
||||
<isset property="env.ANDROID_HOME" />
|
||||
</condition>
|
||||
|
||||
<!-- The project.properties file is created and updated by the 'android'
|
||||
tool, as well as ADT.
|
||||
|
||||
This contains project specific properties such as project target, and library
|
||||
dependencies. Lower level build properties are stored in ant.properties
|
||||
(or in .classpath for Eclipse projects).
|
||||
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems. -->
|
||||
<loadproperties srcFile="project.properties" />
|
||||
|
||||
<!-- quick check on sdk.dir -->
|
||||
<fail
|
||||
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
|
||||
unless="sdk.dir"
|
||||
/>
|
||||
|
||||
<!--
|
||||
Import per project custom build rules if present at the root of the project.
|
||||
This is the place to put custom intermediary targets such as:
|
||||
-pre-build
|
||||
-pre-compile
|
||||
-post-compile (This is typically used for code obfuscation.
|
||||
Compiled code location: ${out.classes.absolute.dir}
|
||||
If this is not done in place, override ${out.dex.input.absolute.dir})
|
||||
-post-package
|
||||
-post-build
|
||||
-pre-clean
|
||||
-->
|
||||
<import file="custom_rules.xml" optional="true" />
|
||||
|
||||
<!-- Import the actual build file.
|
||||
|
||||
To customize existing targets, there are two options:
|
||||
- Customize only one target:
|
||||
- copy/paste the target into this file, *before* the
|
||||
<import> task.
|
||||
- customize it to your needs.
|
||||
- Customize the whole content of build.xml
|
||||
- copy/paste the content of the rules files (minus the top node)
|
||||
into this file, replacing the <import> task.
|
||||
- customize to your needs.
|
||||
|
||||
***********************
|
||||
****** IMPORTANT ******
|
||||
***********************
|
||||
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
|
||||
in order to avoid having your file be overridden by tools such as "android update project"
|
||||
-->
|
||||
<!-- version-tag: 1 -->
|
||||
<import file="${sdk.dir}/tools/ant/build.xml" />
|
||||
|
||||
</project>
|
@ -1,20 +0,0 @@
|
||||
# To enable ProGuard in your project, edit project.properties
|
||||
# to define the proguard.config property as described in that file.
|
||||
#
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in ${sdk.dir}/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the ProGuard
|
||||
# include property in project.properties.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
@ -1,15 +0,0 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system edit
|
||||
# "ant.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
#
|
||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
|
||||
# Project target.
|
||||
target=android-19
|
||||
android.library=true
|
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.3 KiB |
@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="openpgp_list_preference_none">None</string>
|
||||
<string name="openpgp_install_openkeychain_via">Install OpenKeychain via %s</string>
|
||||
|
||||
</resources>
|
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openintents.openpgp;
|
||||
|
||||
interface IOpenPgpService {
|
||||
|
||||
// see OpenPgpApi for documentation
|
||||
Intent execute(in Intent data, in ParcelFileDescriptor input, in ParcelFileDescriptor output);
|
||||
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openintents.openpgp;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* Parcelable versioning has been copied from Dashclock Widget
|
||||
* https://code.google.com/p/dashclock/source/browse/api/src/main/java/com/google/android/apps/dashclock/api/ExtensionData.java
|
||||
*/
|
||||
public class OpenPgpError implements Parcelable {
|
||||
/**
|
||||
* Since there might be a case where new versions of the client using the library getting
|
||||
* old versions of the protocol (and thus old versions of this class), we need a versioning
|
||||
* system for the parcels sent between the clients and the providers.
|
||||
*/
|
||||
public static final int PARCELABLE_VERSION = 1;
|
||||
|
||||
// possible values for errorId
|
||||
public static final int CLIENT_SIDE_ERROR = -1;
|
||||
public static final int GENERIC_ERROR = 0;
|
||||
public static final int INCOMPATIBLE_API_VERSIONS = 1;
|
||||
public static final int NO_OR_WRONG_PASSPHRASE = 2;
|
||||
public static final int NO_USER_IDS = 3;
|
||||
|
||||
int errorId;
|
||||
String message;
|
||||
|
||||
public OpenPgpError() {
|
||||
}
|
||||
|
||||
public OpenPgpError(int errorId, String message) {
|
||||
this.errorId = errorId;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public OpenPgpError(OpenPgpError b) {
|
||||
this.errorId = b.errorId;
|
||||
this.message = b.message;
|
||||
}
|
||||
|
||||
public int getErrorId() {
|
||||
return errorId;
|
||||
}
|
||||
|
||||
public void setErrorId(int errorId) {
|
||||
this.errorId = errorId;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
/**
|
||||
* NOTE: When adding fields in the process of updating this API, make sure to bump
|
||||
* {@link #PARCELABLE_VERSION}.
|
||||
*/
|
||||
dest.writeInt(PARCELABLE_VERSION);
|
||||
// Inject a placeholder that will store the parcel size from this point on
|
||||
// (not including the size itself).
|
||||
int sizePosition = dest.dataPosition();
|
||||
dest.writeInt(0);
|
||||
int startPosition = dest.dataPosition();
|
||||
// version 1
|
||||
dest.writeInt(errorId);
|
||||
dest.writeString(message);
|
||||
// Go back and write the size
|
||||
int parcelableSize = dest.dataPosition() - startPosition;
|
||||
dest.setDataPosition(sizePosition);
|
||||
dest.writeInt(parcelableSize);
|
||||
dest.setDataPosition(startPosition + parcelableSize);
|
||||
}
|
||||
|
||||
public static final Creator<OpenPgpError> CREATOR = new Creator<OpenPgpError>() {
|
||||
public OpenPgpError createFromParcel(final Parcel source) {
|
||||
int parcelableVersion = source.readInt();
|
||||
int parcelableSize = source.readInt();
|
||||
int startPosition = source.dataPosition();
|
||||
|
||||
OpenPgpError error = new OpenPgpError();
|
||||
error.errorId = source.readInt();
|
||||
error.message = source.readString();
|
||||
|
||||
// skip over all fields added in future versions of this parcel
|
||||
source.setDataPosition(startPosition + parcelableSize);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
public OpenPgpError[] newArray(final int size) {
|
||||
return new OpenPgpError[size];
|
||||
}
|
||||
};
|
||||
}
|
@ -1,163 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openintents.openpgp;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import org.openintents.openpgp.util.OpenPgpUtils;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Parcelable versioning has been copied from Dashclock Widget
|
||||
* https://code.google.com/p/dashclock/source/browse/api/src/main/java/com/google/android/apps/dashclock/api/ExtensionData.java
|
||||
*/
|
||||
public class OpenPgpSignatureResult implements Parcelable {
|
||||
/**
|
||||
* Since there might be a case where new versions of the client using the library getting
|
||||
* old versions of the protocol (and thus old versions of this class), we need a versioning
|
||||
* system for the parcels sent between the clients and the providers.
|
||||
*/
|
||||
public static final int PARCELABLE_VERSION = 1;
|
||||
|
||||
// generic error on signature verification
|
||||
public static final int SIGNATURE_ERROR = 0;
|
||||
// successfully verified signature, with certified public key
|
||||
public static final int SIGNATURE_SUCCESS_CERTIFIED = 1;
|
||||
// no public key was found for this signature verification
|
||||
public static final int SIGNATURE_UNKNOWN_PUB_KEY = 2;
|
||||
// successfully verified signature, but with uncertified public key
|
||||
public static final int SIGNATURE_SUCCESS_UNCERTIFIED = 3;
|
||||
|
||||
int status;
|
||||
boolean signatureOnly;
|
||||
String userId;
|
||||
long keyId;
|
||||
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public boolean isSignatureOnly() {
|
||||
return signatureOnly;
|
||||
}
|
||||
|
||||
public void setSignatureOnly(boolean signatureOnly) {
|
||||
this.signatureOnly = signatureOnly;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public long getKeyId() {
|
||||
return keyId;
|
||||
}
|
||||
|
||||
public void setKeyId(long keyId) {
|
||||
this.keyId = keyId;
|
||||
}
|
||||
|
||||
public OpenPgpSignatureResult() {
|
||||
|
||||
}
|
||||
|
||||
public OpenPgpSignatureResult(int signatureStatus, String signatureUserId,
|
||||
boolean signatureOnly, long keyId) {
|
||||
this.status = signatureStatus;
|
||||
this.signatureOnly = signatureOnly;
|
||||
this.userId = signatureUserId;
|
||||
this.keyId = keyId;
|
||||
}
|
||||
|
||||
public OpenPgpSignatureResult(OpenPgpSignatureResult b) {
|
||||
this.status = b.status;
|
||||
this.userId = b.userId;
|
||||
this.signatureOnly = b.signatureOnly;
|
||||
this.keyId = b.keyId;
|
||||
}
|
||||
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
/**
|
||||
* NOTE: When adding fields in the process of updating this API, make sure to bump
|
||||
* {@link #PARCELABLE_VERSION}.
|
||||
*/
|
||||
dest.writeInt(PARCELABLE_VERSION);
|
||||
// Inject a placeholder that will store the parcel size from this point on
|
||||
// (not including the size itself).
|
||||
int sizePosition = dest.dataPosition();
|
||||
dest.writeInt(0);
|
||||
int startPosition = dest.dataPosition();
|
||||
// version 1
|
||||
dest.writeInt(status);
|
||||
dest.writeByte((byte) (signatureOnly ? 1 : 0));
|
||||
dest.writeString(userId);
|
||||
dest.writeLong(keyId);
|
||||
// Go back and write the size
|
||||
int parcelableSize = dest.dataPosition() - startPosition;
|
||||
dest.setDataPosition(sizePosition);
|
||||
dest.writeInt(parcelableSize);
|
||||
dest.setDataPosition(startPosition + parcelableSize);
|
||||
}
|
||||
|
||||
public static final Creator<OpenPgpSignatureResult> CREATOR = new Creator<OpenPgpSignatureResult>() {
|
||||
public OpenPgpSignatureResult createFromParcel(final Parcel source) {
|
||||
int parcelableVersion = source.readInt();
|
||||
int parcelableSize = source.readInt();
|
||||
int startPosition = source.dataPosition();
|
||||
|
||||
OpenPgpSignatureResult vr = new OpenPgpSignatureResult();
|
||||
vr.status = source.readInt();
|
||||
vr.signatureOnly = source.readByte() == 1;
|
||||
vr.userId = source.readString();
|
||||
vr.keyId = source.readLong();
|
||||
|
||||
// skip over all fields added in future versions of this parcel
|
||||
source.setDataPosition(startPosition + parcelableSize);
|
||||
|
||||
return vr;
|
||||
}
|
||||
|
||||
public OpenPgpSignatureResult[] newArray(final int size) {
|
||||
return new OpenPgpSignatureResult[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String out = new String();
|
||||
out += "\nstatus: " + status;
|
||||
out += "\nuserId: " + userId;
|
||||
out += "\nsignatureOnly: " + signatureOnly;
|
||||
out += "\nkeyId: " + OpenPgpUtils.convertKeyIdToHex(keyId);
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
@ -1,270 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openintents.openpgp.util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.Log;
|
||||
|
||||
import org.openintents.openpgp.IOpenPgpService;
|
||||
import org.openintents.openpgp.OpenPgpError;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class OpenPgpApi {
|
||||
|
||||
public static final String TAG = "OpenPgp API";
|
||||
|
||||
public static final int API_VERSION = 3;
|
||||
public static final String SERVICE_INTENT = "org.openintents.openpgp.IOpenPgpService";
|
||||
|
||||
/**
|
||||
* General extras
|
||||
* --------------
|
||||
*
|
||||
* required extras:
|
||||
* int EXTRA_API_VERSION (always required)
|
||||
*
|
||||
* returned extras:
|
||||
* int RESULT_CODE (RESULT_CODE_ERROR, RESULT_CODE_SUCCESS or RESULT_CODE_USER_INTERACTION_REQUIRED)
|
||||
* OpenPgpError RESULT_ERROR (if RESULT_CODE == RESULT_CODE_ERROR)
|
||||
* PendingIntent RESULT_INTENT (if RESULT_CODE == RESULT_CODE_USER_INTERACTION_REQUIRED)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sign only
|
||||
* <p/>
|
||||
* optional extras:
|
||||
* boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for ouput)
|
||||
* String EXTRA_PASSPHRASE (key passphrase)
|
||||
*/
|
||||
public static final String ACTION_SIGN = "org.openintents.openpgp.action.SIGN";
|
||||
|
||||
/**
|
||||
* Encrypt
|
||||
* <p/>
|
||||
* required extras:
|
||||
* String[] EXTRA_USER_IDS (=emails of recipients, if more than one key has a user_id, a PendingIntent is returned via RESULT_INTENT)
|
||||
* or
|
||||
* long[] EXTRA_KEY_IDS
|
||||
* <p/>
|
||||
* optional extras:
|
||||
* boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for ouput)
|
||||
* String EXTRA_PASSPHRASE (key passphrase)
|
||||
*/
|
||||
public static final String ACTION_ENCRYPT = "org.openintents.openpgp.action.ENCRYPT";
|
||||
|
||||
/**
|
||||
* Sign and encrypt
|
||||
* <p/>
|
||||
* required extras:
|
||||
* String[] EXTRA_USER_IDS (=emails of recipients, if more than one key has a user_id, a PendingIntent is returned via RESULT_INTENT)
|
||||
* or
|
||||
* long[] EXTRA_KEY_IDS
|
||||
* <p/>
|
||||
* optional extras:
|
||||
* boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for ouput)
|
||||
* String EXTRA_PASSPHRASE (key passphrase)
|
||||
*/
|
||||
public static final String ACTION_SIGN_AND_ENCRYPT = "org.openintents.openpgp.action.SIGN_AND_ENCRYPT";
|
||||
|
||||
/**
|
||||
* Decrypts and verifies given input stream. This methods handles encrypted-only, signed-and-encrypted,
|
||||
* and also signed-only input.
|
||||
* <p/>
|
||||
* If OpenPgpSignatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY
|
||||
* in addition a PendingIntent is returned via RESULT_INTENT to download missing keys.
|
||||
* <p/>
|
||||
* optional extras:
|
||||
* boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for ouput)
|
||||
* <p/>
|
||||
* returned extras:
|
||||
* OpenPgpSignatureResult RESULT_SIGNATURE
|
||||
*/
|
||||
public static final String ACTION_DECRYPT_VERIFY = "org.openintents.openpgp.action.DECRYPT_VERIFY";
|
||||
|
||||
/**
|
||||
* Get key ids based on given user ids (=emails)
|
||||
* <p/>
|
||||
* required extras:
|
||||
* String[] EXTRA_USER_IDS
|
||||
* <p/>
|
||||
* returned extras:
|
||||
* long[] RESULT_KEY_IDS
|
||||
*/
|
||||
public static final String ACTION_GET_KEY_IDS = "org.openintents.openpgp.action.GET_KEY_IDS";
|
||||
|
||||
/**
|
||||
* This action returns RESULT_CODE_SUCCESS if the OpenPGP Provider already has the key
|
||||
* corresponding to the given key id in its database.
|
||||
* <p/>
|
||||
* It returns RESULT_CODE_USER_INTERACTION_REQUIRED if the Provider does not have the key.
|
||||
* The PendingIntent from RESULT_INTENT can be used to retrieve those from a keyserver.
|
||||
* <p/>
|
||||
* required extras:
|
||||
* long EXTRA_KEY_ID
|
||||
*/
|
||||
public static final String ACTION_GET_KEY = "org.openintents.openpgp.action.GET_KEY";
|
||||
|
||||
/* Intent extras */
|
||||
public static final String EXTRA_API_VERSION = "api_version";
|
||||
|
||||
public static final String EXTRA_ACCOUNT_NAME = "account_name";
|
||||
|
||||
// SIGN, ENCRYPT, SIGN_AND_ENCRYPT, DECRYPT_VERIFY
|
||||
// request ASCII Armor for output
|
||||
// OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
|
||||
public static final String EXTRA_REQUEST_ASCII_ARMOR = "ascii_armor";
|
||||
|
||||
// ENCRYPT, SIGN_AND_ENCRYPT
|
||||
public static final String EXTRA_USER_IDS = "user_ids";
|
||||
public static final String EXTRA_KEY_IDS = "key_ids";
|
||||
// optional extras:
|
||||
public static final String EXTRA_PASSPHRASE = "passphrase";
|
||||
|
||||
// GET_KEY
|
||||
public static final String EXTRA_KEY_ID = "key_id";
|
||||
public static final String RESULT_KEY_IDS = "key_ids";
|
||||
|
||||
/* Service Intent returns */
|
||||
public static final String RESULT_CODE = "result_code";
|
||||
|
||||
// get actual error object from RESULT_ERROR
|
||||
public static final int RESULT_CODE_ERROR = 0;
|
||||
// success!
|
||||
public static final int RESULT_CODE_SUCCESS = 1;
|
||||
// get PendingIntent from RESULT_INTENT, start PendingIntent with startIntentSenderForResult,
|
||||
// and execute service method again in onActivityResult
|
||||
public static final int RESULT_CODE_USER_INTERACTION_REQUIRED = 2;
|
||||
|
||||
public static final String RESULT_ERROR = "error";
|
||||
public static final String RESULT_INTENT = "intent";
|
||||
|
||||
// DECRYPT_VERIFY
|
||||
public static final String RESULT_SIGNATURE = "signature";
|
||||
|
||||
IOpenPgpService mService;
|
||||
Context mContext;
|
||||
|
||||
public OpenPgpApi(Context context, IOpenPgpService service) {
|
||||
this.mContext = context;
|
||||
this.mService = service;
|
||||
}
|
||||
|
||||
public interface IOpenPgpCallback {
|
||||
void onReturn(final Intent result);
|
||||
}
|
||||
|
||||
private class OpenPgpAsyncTask extends AsyncTask<Void, Integer, Intent> {
|
||||
Intent data;
|
||||
InputStream is;
|
||||
OutputStream os;
|
||||
IOpenPgpCallback callback;
|
||||
|
||||
private OpenPgpAsyncTask(Intent data, InputStream is, OutputStream os, IOpenPgpCallback callback) {
|
||||
this.data = data;
|
||||
this.is = is;
|
||||
this.os = os;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Intent doInBackground(Void... unused) {
|
||||
return executeApi(data, is, os);
|
||||
}
|
||||
|
||||
protected void onPostExecute(Intent result) {
|
||||
callback.onReturn(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public void executeApiAsync(Intent data, InputStream is, OutputStream os, IOpenPgpCallback callback) {
|
||||
OpenPgpAsyncTask task = new OpenPgpAsyncTask(data, is, os, callback);
|
||||
|
||||
// don't serialize async tasks!
|
||||
// http://commonsware.com/blog/2012/04/20/asynctask-threading-regression-confirmed.html
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
|
||||
} else {
|
||||
task.execute((Void[]) null);
|
||||
}
|
||||
}
|
||||
|
||||
public Intent executeApi(Intent data, InputStream is, OutputStream os) {
|
||||
try {
|
||||
data.putExtra(EXTRA_API_VERSION, OpenPgpApi.API_VERSION);
|
||||
|
||||
Intent result;
|
||||
|
||||
// pipe the input and output
|
||||
ParcelFileDescriptor input = null;
|
||||
if (is != null) {
|
||||
input = ParcelFileDescriptorUtil.pipeFrom(is,
|
||||
new ParcelFileDescriptorUtil.IThreadListener() {
|
||||
|
||||
@Override
|
||||
public void onThreadFinished(Thread thread) {
|
||||
//Log.d(OpenPgpApi.TAG, "Copy to service finished");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
ParcelFileDescriptor output = null;
|
||||
if (os != null) {
|
||||
output = ParcelFileDescriptorUtil.pipeTo(os,
|
||||
new ParcelFileDescriptorUtil.IThreadListener() {
|
||||
|
||||
@Override
|
||||
public void onThreadFinished(Thread thread) {
|
||||
//Log.d(OpenPgpApi.TAG, "Service finished writing!");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// blocks until result is ready
|
||||
result = mService.execute(data, input, output);
|
||||
// close() is required to halt the TransferThread
|
||||
if (output != null) {
|
||||
output.close();
|
||||
}
|
||||
// TODO: close input?
|
||||
|
||||
// set class loader to current context to allow unparcelling
|
||||
// of OpenPgpError and OpenPgpSignatureResult
|
||||
// http://stackoverflow.com/a/3806769
|
||||
result.setExtrasClassLoader(mContext.getClassLoader());
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
Log.e(OpenPgpApi.TAG, "Exception in executeApi call", e);
|
||||
Intent result = new Intent();
|
||||
result.putExtra(RESULT_CODE, RESULT_CODE_ERROR);
|
||||
result.putExtra(RESULT_ERROR,
|
||||
new OpenPgpError(OpenPgpError.CLIENT_SIDE_ERROR, e.getMessage()));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,257 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openintents.openpgp.util;
|
||||
|
||||
import android.app.AlertDialog.Builder;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.preference.DialogPreference;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.TextView;
|
||||
import org.openintents.openpgp.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Does not extend ListPreference, but is very similar to it!
|
||||
* http://grepcode.com/file_/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/preference/ListPreference.java/?v=source
|
||||
*/
|
||||
public class OpenPgpListPreference extends DialogPreference {
|
||||
private static final String OPENKEYCHAIN_PACKAGE = "org.sufficientlysecure.keychain";
|
||||
private static final String MARKET_INTENT_URI_BASE = "market://details?id=%s";
|
||||
private static final Intent MARKET_INTENT = new Intent(Intent.ACTION_VIEW, Uri.parse(
|
||||
String.format(MARKET_INTENT_URI_BASE, OPENKEYCHAIN_PACKAGE)));
|
||||
|
||||
private ArrayList<OpenPgpProviderEntry> mLegacyList = new ArrayList<OpenPgpProviderEntry>();
|
||||
private ArrayList<OpenPgpProviderEntry> mList = new ArrayList<OpenPgpProviderEntry>();
|
||||
|
||||
private String mSelectedPackage;
|
||||
|
||||
public OpenPgpListPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public OpenPgpListPreference(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public method to add new entries for legacy applications
|
||||
*
|
||||
* @param packageName
|
||||
* @param simpleName
|
||||
* @param icon
|
||||
*/
|
||||
public void addLegacyProvider(int position, String packageName, String simpleName, Drawable icon) {
|
||||
mLegacyList.add(position, new OpenPgpProviderEntry(packageName, simpleName, icon));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPrepareDialogBuilder(Builder builder) {
|
||||
mList.clear();
|
||||
|
||||
// add "none"-entry
|
||||
mList.add(0, new OpenPgpProviderEntry("",
|
||||
getContext().getString(R.string.openpgp_list_preference_none),
|
||||
getContext().getResources().getDrawable(R.drawable.ic_action_cancel_launchersize)));
|
||||
|
||||
// add all additional (legacy) providers
|
||||
mList.addAll(mLegacyList);
|
||||
|
||||
// search for OpenPGP providers...
|
||||
ArrayList<OpenPgpProviderEntry> providerList = new ArrayList<OpenPgpProviderEntry>();
|
||||
Intent intent = new Intent(OpenPgpApi.SERVICE_INTENT);
|
||||
List<ResolveInfo> resInfo = getContext().getPackageManager().queryIntentServices(intent, 0);
|
||||
if (!resInfo.isEmpty()) {
|
||||
for (ResolveInfo resolveInfo : resInfo) {
|
||||
if (resolveInfo.serviceInfo == null)
|
||||
continue;
|
||||
|
||||
String packageName = resolveInfo.serviceInfo.packageName;
|
||||
String simpleName = String.valueOf(resolveInfo.serviceInfo.loadLabel(getContext()
|
||||
.getPackageManager()));
|
||||
Drawable icon = resolveInfo.serviceInfo.loadIcon(getContext().getPackageManager());
|
||||
|
||||
providerList.add(new OpenPgpProviderEntry(packageName, simpleName, icon));
|
||||
}
|
||||
}
|
||||
|
||||
if (providerList.isEmpty()) {
|
||||
// add install links if provider list is empty
|
||||
resInfo = getContext().getPackageManager().queryIntentActivities
|
||||
(MARKET_INTENT, 0);
|
||||
for (ResolveInfo resolveInfo : resInfo) {
|
||||
Intent marketIntent = new Intent(MARKET_INTENT);
|
||||
marketIntent.setPackage(resolveInfo.activityInfo.packageName);
|
||||
Drawable icon = resolveInfo.activityInfo.loadIcon(getContext().getPackageManager());
|
||||
String marketName = String.valueOf(resolveInfo.activityInfo.applicationInfo
|
||||
.loadLabel(getContext().getPackageManager()));
|
||||
String simpleName = String.format(getContext().getString(R.string
|
||||
.openpgp_install_openkeychain_via), marketName);
|
||||
mList.add(new OpenPgpProviderEntry(OPENKEYCHAIN_PACKAGE, simpleName,
|
||||
icon, marketIntent));
|
||||
}
|
||||
} else {
|
||||
// add provider
|
||||
mList.addAll(providerList);
|
||||
}
|
||||
|
||||
// Init ArrayAdapter with OpenPGP Providers
|
||||
ListAdapter adapter = new ArrayAdapter<OpenPgpProviderEntry>(getContext(),
|
||||
android.R.layout.select_dialog_singlechoice, android.R.id.text1, mList) {
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
// User super class to create the View
|
||||
View v = super.getView(position, convertView, parent);
|
||||
TextView tv = (TextView) v.findViewById(android.R.id.text1);
|
||||
|
||||
// Put the image on the TextView
|
||||
tv.setCompoundDrawablesWithIntrinsicBounds(mList.get(position).icon, null,
|
||||
null, null);
|
||||
|
||||
// Add margin between image and text (support various screen densities)
|
||||
int dp10 = (int) (10 * getContext().getResources().getDisplayMetrics().density + 0.5f);
|
||||
tv.setCompoundDrawablePadding(dp10);
|
||||
|
||||
return v;
|
||||
}
|
||||
};
|
||||
|
||||
builder.setSingleChoiceItems(adapter, getIndexOfProviderList(getValue()),
|
||||
new DialogInterface.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
OpenPgpProviderEntry entry = mList.get(which);
|
||||
|
||||
if (entry.intent != null) {
|
||||
/*
|
||||
* Intents are called as activity
|
||||
*
|
||||
* Current approach is to assume the user installed the app.
|
||||
* If he does not, the selected package is not valid.
|
||||
*
|
||||
* However applications should always consider this could happen,
|
||||
* as the user might remove the currently used OpenPGP app.
|
||||
*/
|
||||
getContext().startActivity(entry.intent);
|
||||
}
|
||||
|
||||
mSelectedPackage = entry.packageName;
|
||||
|
||||
/*
|
||||
* Clicking on an item simulates the positive button click, and dismisses
|
||||
* the dialog.
|
||||
*/
|
||||
OpenPgpListPreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* The typical interaction for list-based dialogs is to have click-on-an-item dismiss the
|
||||
* dialog instead of the user having to press 'Ok'.
|
||||
*/
|
||||
builder.setPositiveButton(null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDialogClosed(boolean positiveResult) {
|
||||
super.onDialogClosed(positiveResult);
|
||||
|
||||
if (positiveResult && (mSelectedPackage != null)) {
|
||||
if (callChangeListener(mSelectedPackage)) {
|
||||
setValue(mSelectedPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getIndexOfProviderList(String packageName) {
|
||||
for (OpenPgpProviderEntry app : mList) {
|
||||
if (app.packageName.equals(packageName)) {
|
||||
return mList.indexOf(app);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void setValue(String packageName) {
|
||||
mSelectedPackage = packageName;
|
||||
persistString(packageName);
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return mSelectedPackage;
|
||||
}
|
||||
|
||||
public String getEntry() {
|
||||
return getEntryByValue(mSelectedPackage);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object onGetDefaultValue(TypedArray a, int index) {
|
||||
return a.getString(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
|
||||
setValue(restoreValue ? getPersistedString(mSelectedPackage) : (String) defaultValue);
|
||||
}
|
||||
|
||||
public String getEntryByValue(String packageName) {
|
||||
for (OpenPgpProviderEntry app : mList) {
|
||||
if (app.packageName.equals(packageName)) {
|
||||
return app.simpleName;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class OpenPgpProviderEntry {
|
||||
private String packageName;
|
||||
private String simpleName;
|
||||
private Drawable icon;
|
||||
private Intent intent;
|
||||
|
||||
public OpenPgpProviderEntry(String packageName, String simpleName, Drawable icon) {
|
||||
this.packageName = packageName;
|
||||
this.simpleName = simpleName;
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public OpenPgpProviderEntry(String packageName, String simpleName, Drawable icon, Intent intent) {
|
||||
this(packageName, simpleName, icon);
|
||||
this.intent = intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return simpleName;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openintents.openpgp.util;
|
||||
|
||||
import org.openintents.openpgp.IOpenPgpService;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
|
||||
public class OpenPgpServiceConnection {
|
||||
|
||||
// interface to create callbacks for onServiceConnected
|
||||
public interface OnBound {
|
||||
public void onBound(IOpenPgpService service);
|
||||
}
|
||||
|
||||
private Context mApplicationContext;
|
||||
|
||||
private IOpenPgpService mService;
|
||||
private String mProviderPackageName;
|
||||
|
||||
private OnBound mOnBoundListener;
|
||||
|
||||
/**
|
||||
* Create new OpenPgpServiceConnection
|
||||
*
|
||||
* @param context
|
||||
* @param providerPackageName specify package name of OpenPGP provider,
|
||||
* e.g., "org.sufficientlysecure.keychain"
|
||||
*/
|
||||
public OpenPgpServiceConnection(Context context, String providerPackageName) {
|
||||
this.mApplicationContext = context.getApplicationContext();
|
||||
this.mProviderPackageName = providerPackageName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new OpenPgpServiceConnection
|
||||
*
|
||||
* @param context
|
||||
* @param providerPackageName specify package name of OpenPGP provider,
|
||||
* e.g., "org.sufficientlysecure.keychain"
|
||||
* @param onBoundListener callback, executed when connection to service has been established
|
||||
*/
|
||||
public OpenPgpServiceConnection(Context context, String providerPackageName,
|
||||
OnBound onBoundListener) {
|
||||
this.mApplicationContext = context.getApplicationContext();
|
||||
this.mProviderPackageName = providerPackageName;
|
||||
this.mOnBoundListener = onBoundListener;
|
||||
}
|
||||
|
||||
public IOpenPgpService getService() {
|
||||
return mService;
|
||||
}
|
||||
|
||||
public boolean isBound() {
|
||||
return (mService != null);
|
||||
}
|
||||
|
||||
private ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
mService = IOpenPgpService.Stub.asInterface(service);
|
||||
if (mOnBoundListener != null) {
|
||||
mOnBoundListener.onBound(mService);
|
||||
}
|
||||
}
|
||||
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
mService = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* If not already bound, bind to service!
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean bindToService() {
|
||||
// if not already bound...
|
||||
if (mService == null) {
|
||||
try {
|
||||
Intent serviceIntent = new Intent();
|
||||
serviceIntent.setAction(IOpenPgpService.class.getName());
|
||||
// NOTE: setPackage is very important to restrict the intent to this provider only!
|
||||
serviceIntent.setPackage(mProviderPackageName);
|
||||
mApplicationContext.bindService(serviceIntent, mServiceConnection,
|
||||
Context.BIND_AUTO_CREATE);
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void unbindFromService() {
|
||||
mApplicationContext.unbindService(mServiceConnection);
|
||||
}
|
||||
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openintents.openpgp.util;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ResolveInfo;
|
||||
|
||||
public class OpenPgpUtils {
|
||||
|
||||
public static final Pattern PGP_MESSAGE = Pattern.compile(
|
||||
".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*",
|
||||
Pattern.DOTALL);
|
||||
|
||||
public static final Pattern PGP_SIGNED_MESSAGE = Pattern.compile(
|
||||
".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
|
||||
Pattern.DOTALL);
|
||||
|
||||
public static final int PARSE_RESULT_NO_PGP = -1;
|
||||
public static final int PARSE_RESULT_MESSAGE = 0;
|
||||
public static final int PARSE_RESULT_SIGNED_MESSAGE = 1;
|
||||
|
||||
public static int parseMessage(String message) {
|
||||
Matcher matcherSigned = PGP_SIGNED_MESSAGE.matcher(message);
|
||||
Matcher matcherMessage = PGP_MESSAGE.matcher(message);
|
||||
|
||||
if (matcherMessage.matches()) {
|
||||
return PARSE_RESULT_MESSAGE;
|
||||
} else if (matcherSigned.matches()) {
|
||||
return PARSE_RESULT_SIGNED_MESSAGE;
|
||||
} else {
|
||||
return PARSE_RESULT_NO_PGP;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isAvailable(Context context) {
|
||||
Intent intent = new Intent(OpenPgpApi.SERVICE_INTENT);
|
||||
List<ResolveInfo> resInfo = context.getPackageManager().queryIntentServices(intent, 0);
|
||||
if (!resInfo.isEmpty()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static String convertKeyIdToHex(long keyId) {
|
||||
return "0x" + convertKeyIdToHex32bit(keyId >> 32) + convertKeyIdToHex32bit(keyId);
|
||||
}
|
||||
|
||||
private static String convertKeyIdToHex32bit(long keyId) {
|
||||
String hexString = Long.toHexString(keyId & 0xffffffffL).toLowerCase(Locale.US);
|
||||
while (hexString.length() < 8) {
|
||||
hexString = "0" + hexString;
|
||||
}
|
||||
return hexString;
|
||||
}
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* 2013 Flow (http://stackoverflow.com/questions/18212152/transfer-inputstream-to-another-service-across-process-boundaries-with-parcelf)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openintents.openpgp.util;
|
||||
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class ParcelFileDescriptorUtil {
|
||||
|
||||
public interface IThreadListener {
|
||||
void onThreadFinished(final Thread thread);
|
||||
}
|
||||
|
||||
public static ParcelFileDescriptor pipeFrom(InputStream inputStream, IThreadListener listener)
|
||||
throws IOException {
|
||||
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
|
||||
ParcelFileDescriptor readSide = pipe[0];
|
||||
ParcelFileDescriptor writeSide = pipe[1];
|
||||
|
||||
// start the transfer thread
|
||||
new TransferThread(inputStream, new ParcelFileDescriptor.AutoCloseOutputStream(writeSide),
|
||||
listener)
|
||||
.start();
|
||||
|
||||
return readSide;
|
||||
}
|
||||
|
||||
public static ParcelFileDescriptor pipeTo(OutputStream outputStream, IThreadListener listener)
|
||||
throws IOException {
|
||||
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
|
||||
ParcelFileDescriptor readSide = pipe[0];
|
||||
ParcelFileDescriptor writeSide = pipe[1];
|
||||
|
||||
// start the transfer thread
|
||||
new TransferThread(new ParcelFileDescriptor.AutoCloseInputStream(readSide), outputStream,
|
||||
listener)
|
||||
.start();
|
||||
|
||||
return writeSide;
|
||||
}
|
||||
|
||||
static class TransferThread extends Thread {
|
||||
final InputStream mIn;
|
||||
final OutputStream mOut;
|
||||
final IThreadListener mListener;
|
||||
|
||||
TransferThread(InputStream in, OutputStream out, IThreadListener listener) {
|
||||
super("ParcelFileDescriptor Transfer Thread");
|
||||
mIn = in;
|
||||
mOut = out;
|
||||
mListener = listener;
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
byte[] buf = new byte[1024];
|
||||
int len;
|
||||
|
||||
try {
|
||||
while ((len = mIn.read(buf)) > 0) {
|
||||
mOut.write(buf, 0, len);
|
||||
}
|
||||
mOut.flush(); // just to be safe
|
||||
} catch (IOException e) {
|
||||
//Log.e(OpenPgpApi.TAG, "TransferThread" + getId() + ": writing failed", e);
|
||||
} finally {
|
||||
try {
|
||||
mIn.close();
|
||||
} catch (IOException e) {
|
||||
//Log.e(OpenPgpApi.TAG, "TransferThread" + getId(), e);
|
||||
}
|
||||
try {
|
||||
mOut.close();
|
||||
} catch (IOException e) {
|
||||
//Log.e(OpenPgpApi.TAG, "TransferThread" + getId(), e);
|
||||
}
|
||||
}
|
||||
if (mListener != null) {
|
||||
//Log.d(OpenPgpApi.TAG, "TransferThread " + getId() + " finished!");
|
||||
mListener.onThreadFinished(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
include ':example-app'
|
||||
include ':libraries:openpgp-api-library'
|
||||
include ':libraries:openkeychain-api-library'
|
@ -10,8 +10,8 @@ sourceSets {
|
||||
dependencies {
|
||||
compile 'com.android.support:support-v4:19.1.0'
|
||||
compile 'com.android.support:appcompat-v7:19.1.0'
|
||||
compile project(':OpenKeychain-API:libraries:openpgp-api-library')
|
||||
compile project(':OpenKeychain-API:libraries:openkeychain-api-library')
|
||||
compile project(':extern:openpgp-api-lib')
|
||||
compile project(':extern:openkeychain-api-lib')
|
||||
compile project(':extern:html-textview')
|
||||
compile project(':extern:StickyListHeaders:library')
|
||||
compile project(':extern:AndroidBootstrap:AndroidBootstrap')
|
||||
@ -29,8 +29,8 @@ dependencies {
|
||||
testLocalCompile 'com.google.android:android:4.1.1.4'
|
||||
testLocalCompile 'com.android.support:support-v4:19.1.0'
|
||||
testLocalCompile 'com.android.support:appcompat-v7:19.1.0'
|
||||
testLocalCompile project(':OpenKeychain-API:libraries:openpgp-api-library')
|
||||
testLocalCompile project(':OpenKeychain-API:libraries:openkeychain-api-library')
|
||||
testLocalCompile project(':extern:openpgp-api-lib')
|
||||
testLocalCompile project(':extern:openkeychain-api-lib')
|
||||
testLocalCompile project(':extern:html-textview')
|
||||
testLocalCompile project(':extern:StickyListHeaders:library')
|
||||
testLocalCompile project(':extern:AndroidBootstrap:AndroidBootstrap')
|
||||
|
@ -3,8 +3,8 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.sufficientlysecure.keychain"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="26100"
|
||||
android:versionName="2.6.1">
|
||||
android:versionCode="27000"
|
||||
android:versionName="2.7">
|
||||
|
||||
<!--
|
||||
General remarks
|
||||
@ -232,7 +232,12 @@
|
||||
<activity
|
||||
android:name=".ui.UploadKeyActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||
android:label="@string/title_send_key" />
|
||||
android:label="@string/title_send_key"
|
||||
android:parentActivityName=".ui.ViewKeyActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".ui.ViewKeyActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.PreferencesActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||
@ -254,7 +259,12 @@
|
||||
<activity
|
||||
android:name=".ui.CertifyKeyActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||
android:label="@string/title_certify_key" />
|
||||
android:label="@string/title_certify_key"
|
||||
android:parentActivityName=".ui.ViewKeyActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".ui.ViewKeyActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.ImportKeysActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||
|
@ -18,6 +18,9 @@
|
||||
package org.sufficientlysecure.keychain;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Environment;
|
||||
|
||||
import org.spongycastle.jce.provider.BouncyCastleProvider;
|
||||
@ -70,5 +73,22 @@ public class KeychainApplication extends Application {
|
||||
// that the directory doesn't exist at this point
|
||||
}
|
||||
}
|
||||
|
||||
brandGlowEffect(getApplicationContext(),
|
||||
getApplicationContext().getResources().getColor(R.color.emphasis));
|
||||
}
|
||||
|
||||
static void brandGlowEffect(Context context, int brandColor) {
|
||||
// terrible hack to brand the edge overscroll glow effect
|
||||
// https://gist.github.com/menny/7878762#file-brandgloweffect_full-java
|
||||
|
||||
//glow
|
||||
int glowDrawableId = context.getResources().getIdentifier("overscroll_glow", "drawable", "android");
|
||||
Drawable androidGlow = context.getResources().getDrawable(glowDrawableId);
|
||||
androidGlow.setColorFilter(brandColor, PorterDuff.Mode.SRC_IN);
|
||||
//edge
|
||||
int edgeDrawableId = context.getResources().getIdentifier("overscroll_edge", "drawable", "android");
|
||||
Drawable androidEdge = context.getResources().getDrawable(edgeDrawableId);
|
||||
androidEdge.setColorFilter(brandColor, PorterDuff.Mode.SRC_IN);
|
||||
}
|
||||
}
|
||||
|
@ -32,25 +32,6 @@ import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
public class ActionBarHelper {
|
||||
|
||||
/**
|
||||
* Set actionbar without home button if called from another app
|
||||
*
|
||||
* @param activity
|
||||
*/
|
||||
public static void setBackButton(ActionBarActivity activity) {
|
||||
final ActionBar actionBar = activity.getSupportActionBar();
|
||||
Log.d(Constants.TAG, "calling package (only set when using startActivityForResult)="
|
||||
+ activity.getCallingPackage());
|
||||
if (activity.getCallingPackage() != null
|
||||
&& activity.getCallingPackage().equals(Constants.PACKAGE_NAME)) {
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
actionBar.setHomeButtonEnabled(true);
|
||||
} else {
|
||||
actionBar.setDisplayHomeAsUpEnabled(false);
|
||||
actionBar.setHomeButtonEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets custom view on ActionBar for Done/Cancel activities
|
||||
*
|
||||
|
@ -112,16 +112,18 @@ public class FileHelper {
|
||||
|
||||
if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||
String[] projection = {"_data"};
|
||||
Cursor cursor = null;
|
||||
|
||||
Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
|
||||
try {
|
||||
cursor = context.getContentResolver().query(uri, projection, null, null, null);
|
||||
int columnIndex = cursor.getColumnIndexOrThrow("_data");
|
||||
if (cursor.moveToFirst()) {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
int columnIndex = cursor.getColumnIndexOrThrow("_data");
|
||||
return cursor.getString(columnIndex);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Eat it
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||
return uri.getPath();
|
||||
|
@ -16,7 +16,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.util;
|
||||
package org.sufficientlysecure.keychain.keyimport;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
@ -32,9 +32,8 @@ import org.apache.http.util.EntityUtils;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
@ -167,21 +166,6 @@ public class HkpKeyServer extends KeyServer {
|
||||
mPort = port;
|
||||
}
|
||||
|
||||
private static String readAll(InputStream in, String encoding) throws IOException {
|
||||
ByteArrayOutputStream raw = new ByteArrayOutputStream();
|
||||
|
||||
byte buffer[] = new byte[1 << 16];
|
||||
int n = 0;
|
||||
while ((n = in.read(buffer)) != -1) {
|
||||
raw.write(buffer, 0, n);
|
||||
}
|
||||
|
||||
if (encoding == null) {
|
||||
encoding = "utf8";
|
||||
}
|
||||
return raw.toString(encoding);
|
||||
}
|
||||
|
||||
private String query(String request) throws QueryException, HttpError {
|
||||
InetAddress ips[];
|
||||
try {
|
@ -15,12 +15,11 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ui.adapter;
|
||||
package org.sufficientlysecure.keychain.keyimport;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import org.spongycastle.bcpg.SignatureSubpacketTags;
|
||||
import org.spongycastle.openpgp.PGPKeyRing;
|
||||
@ -203,7 +202,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
||||
* Constructor for later querying from keyserver
|
||||
*/
|
||||
public ImportKeysListEntry() {
|
||||
// keys from keyserver are always public keys
|
||||
// keys from keyserver are always public keys; from keybase too
|
||||
secretKey = false;
|
||||
// do not select by default
|
||||
mSelected = false;
|
@ -16,10 +16,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.util;
|
||||
|
||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
|
||||
package org.sufficientlysecure.keychain.keyimport;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class KeyServer {
|
||||
@ -49,4 +50,19 @@ public abstract class KeyServer {
|
||||
abstract String get(String keyIdHex) throws QueryException;
|
||||
|
||||
abstract void add(String armoredKey) throws AddKeyException;
|
||||
|
||||
public static String readAll(InputStream in, String encoding) throws IOException {
|
||||
ByteArrayOutputStream raw = new ByteArrayOutputStream();
|
||||
|
||||
byte buffer[] = new byte[1 << 16];
|
||||
int n = 0;
|
||||
while ((n = in.read(buffer)) != -1) {
|
||||
raw.write(buffer, 0, n);
|
||||
}
|
||||
|
||||
if (encoding == null) {
|
||||
encoding = "utf8";
|
||||
}
|
||||
return raw.toString(encoding);
|
||||
}
|
||||
}
|
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Tim Bray <tbray@textuality.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.keyimport;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.util.JWalk;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.TimeZone;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
public class KeybaseKeyServer extends KeyServer {
|
||||
|
||||
private WeakHashMap<String, String> mKeyCache = new WeakHashMap<String, String>();
|
||||
|
||||
@Override
|
||||
public ArrayList<ImportKeysListEntry> search(String query) throws QueryException, TooManyResponses,
|
||||
InsufficientQuery {
|
||||
ArrayList<ImportKeysListEntry> results = new ArrayList<ImportKeysListEntry>();
|
||||
|
||||
JSONObject fromQuery = getFromKeybase("_/api/1.0/user/autocomplete.json?q=", query);
|
||||
try {
|
||||
|
||||
JSONArray matches = JWalk.getArray(fromQuery, "completions");
|
||||
for (int i = 0; i < matches.length(); i++) {
|
||||
JSONObject match = matches.getJSONObject(i);
|
||||
|
||||
// only list them if they have a key
|
||||
if (JWalk.optObject(match, "components", "key_fingerprint") != null) {
|
||||
results.add(makeEntry(match));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new QueryException("Unexpected structure in keybase search result: " + e.getMessage());
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private JSONObject getUser(String keybaseID) throws QueryException {
|
||||
try {
|
||||
return getFromKeybase("_/api/1.0/user/lookup.json?username=", keybaseID);
|
||||
} catch (Exception e) {
|
||||
String detail = "";
|
||||
if (keybaseID != null) {
|
||||
detail = ". Query was for user '" + keybaseID + "'";
|
||||
}
|
||||
throw new QueryException(e.getMessage() + detail);
|
||||
}
|
||||
}
|
||||
|
||||
private ImportKeysListEntry makeEntry(JSONObject match) throws QueryException, JSONException {
|
||||
|
||||
String keybaseID = JWalk.getString(match, "components", "username", "val");
|
||||
String key_fingerprint = JWalk.getString(match, "components", "key_fingerprint", "val");
|
||||
key_fingerprint = key_fingerprint.replace(" ", "").toUpperCase();
|
||||
match = getUser(keybaseID);
|
||||
|
||||
final ImportKeysListEntry entry = new ImportKeysListEntry();
|
||||
|
||||
// TODO: Fix; have suggested keybase provide this value to avoid search-time crypto calls
|
||||
entry.setBitStrength(4096);
|
||||
entry.setAlgorithm("RSA");
|
||||
entry.setKeyIdHex("0x" + key_fingerprint);
|
||||
entry.setRevoked(false);
|
||||
|
||||
// ctime
|
||||
final long creationDate = JWalk.getLong(match, "them", "public_keys", "primary", "ctime");
|
||||
final GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
||||
tmpGreg.setTimeInMillis(creationDate * 1000);
|
||||
entry.setDate(tmpGreg.getTime());
|
||||
|
||||
// key bits
|
||||
// we have to fetch the user object to construct the search-result list, so we might as
|
||||
// well (weakly) remember the key, in case they try to import it
|
||||
mKeyCache.put(keybaseID, JWalk.getString(match,"them", "public_keys", "primary", "bundle"));
|
||||
|
||||
// String displayName = JWalk.getString(match, "them", "profile", "full_name");
|
||||
ArrayList<String> userIds = new ArrayList<String>();
|
||||
String name = "keybase.io/" + keybaseID + " <" + keybaseID + "@keybase.io>";
|
||||
userIds.add(name);
|
||||
userIds.add(keybaseID);
|
||||
entry.setUserIds(userIds);
|
||||
entry.setPrimaryUserId(name);
|
||||
return entry;
|
||||
}
|
||||
|
||||
private JSONObject getFromKeybase(String path, String query) throws QueryException {
|
||||
try {
|
||||
String url = "https://keybase.io/" + path + URLEncoder.encode(query, "utf8");
|
||||
Log.d(Constants.TAG, "keybase query: " + url);
|
||||
|
||||
URL realUrl = new URL(url);
|
||||
HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
|
||||
conn.setConnectTimeout(5000); // TODO: Reasonable values for keybase
|
||||
conn.setReadTimeout(25000);
|
||||
conn.connect();
|
||||
int response = conn.getResponseCode();
|
||||
if (response >= 200 && response < 300) {
|
||||
String text = readAll(conn.getInputStream(), conn.getContentEncoding());
|
||||
try {
|
||||
JSONObject json = new JSONObject(text);
|
||||
if (JWalk.getInt(json, "status", "code") != 0) {
|
||||
throw new QueryException("Keybase autocomplete search failed");
|
||||
}
|
||||
return json;
|
||||
} catch (JSONException e) {
|
||||
throw new QueryException("Keybase.io query returned broken JSON");
|
||||
}
|
||||
} else {
|
||||
String message = readAll(conn.getErrorStream(), conn.getContentEncoding());
|
||||
throw new QueryException("Keybase.io query error (status=" + response +
|
||||
"): " + message);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new QueryException("Keybase.io query error");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(String id) throws QueryException {
|
||||
String key = mKeyCache.get(id);
|
||||
if (key == null) {
|
||||
try {
|
||||
JSONObject user = getUser(id);
|
||||
key = JWalk.getString(user, "them", "public_keys", "primary", "bundle");
|
||||
} catch (Exception e) {
|
||||
throw new QueryException(e.getMessage());
|
||||
}
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(String armoredKey) throws AddKeyException {
|
||||
throw new AddKeyException();
|
||||
}
|
||||
}
|
@ -36,11 +36,10 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
|
||||
import org.sufficientlysecure.keychain.util.HkpKeyServer;
|
||||
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
|
||||
import org.sufficientlysecure.keychain.keyimport.HkpKeyServer;
|
||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||
import org.sufficientlysecure.keychain.util.KeyServer.AddKeyException;
|
||||
import org.sufficientlysecure.keychain.util.KeychainServiceListener;
|
||||
import org.sufficientlysecure.keychain.keyimport.KeyServer.AddKeyException;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@ -51,6 +50,11 @@ import java.util.List;
|
||||
|
||||
public class PgpImportExport {
|
||||
|
||||
// TODO: is this really used?
|
||||
public interface KeychainServiceListener {
|
||||
boolean hasServiceStopped();
|
||||
}
|
||||
|
||||
private Context mContext;
|
||||
private Progressable mProgressable;
|
||||
|
||||
|
@ -181,8 +181,6 @@ public class PgpSignEncrypt {
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: test this option!
|
||||
*
|
||||
* @param encryptToSigner
|
||||
* @return
|
||||
*/
|
||||
|
@ -255,53 +255,60 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
}
|
||||
}.getReadableDatabase();
|
||||
|
||||
Cursor c = null;
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
// we insert in two steps: first, all public keys that have secret keys
|
||||
c = db.rawQuery("SELECT key_ring_data FROM key_rings WHERE type = 1 OR EXISTS ("
|
||||
cursor = db.rawQuery("SELECT key_ring_data FROM key_rings WHERE type = 1 OR EXISTS ("
|
||||
+ " SELECT 1 FROM key_rings d2 WHERE key_rings.master_key_id = d2.master_key_id"
|
||||
+ " AND d2.type = 1) ORDER BY type ASC", null);
|
||||
Log.d(Constants.TAG, "Importing " + c.getCount() + " secret keyrings from apg.db...");
|
||||
for (int i = 0; i < c.getCount(); i++) {
|
||||
c.moveToPosition(i);
|
||||
byte[] data = c.getBlob(0);
|
||||
PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data);
|
||||
ProviderHelper providerHelper = new ProviderHelper(context);
|
||||
if (ring instanceof PGPPublicKeyRing)
|
||||
providerHelper.saveKeyRing((PGPPublicKeyRing) ring);
|
||||
else if (ring instanceof PGPSecretKeyRing)
|
||||
providerHelper.saveKeyRing((PGPSecretKeyRing) ring);
|
||||
else {
|
||||
Log.e(Constants.TAG, "Unknown blob data type!");
|
||||
Log.d(Constants.TAG, "Importing " + cursor.getCount() + " secret keyrings from apg.db...");
|
||||
if (cursor != null) {
|
||||
for (int i = 0; i < cursor.getCount(); i++) {
|
||||
cursor.moveToPosition(i);
|
||||
byte[] data = cursor.getBlob(0);
|
||||
PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data);
|
||||
ProviderHelper providerHelper = new ProviderHelper(context);
|
||||
if (ring instanceof PGPPublicKeyRing)
|
||||
providerHelper.saveKeyRing((PGPPublicKeyRing) ring);
|
||||
else if (ring instanceof PGPSecretKeyRing)
|
||||
providerHelper.saveKeyRing((PGPSecretKeyRing) ring);
|
||||
else {
|
||||
Log.e(Constants.TAG, "Unknown blob data type!");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
// afterwards, insert all keys, starting with public keys that have secret keys, then
|
||||
// secret keys, then all others. this order is necessary to ensure all certifications
|
||||
// are recognized properly.
|
||||
c = db.rawQuery("SELECT key_ring_data FROM key_rings ORDER BY (type = 0 AND EXISTS ("
|
||||
cursor = db.rawQuery("SELECT key_ring_data FROM key_rings ORDER BY (type = 0 AND EXISTS ("
|
||||
+ " SELECT 1 FROM key_rings d2 WHERE key_rings.master_key_id = d2.master_key_id AND"
|
||||
+ " d2.type = 1)) DESC, type DESC", null);
|
||||
// import from old database
|
||||
Log.d(Constants.TAG, "Importing " + c.getCount() + " keyrings from apg.db...");
|
||||
for (int i = 0; i < c.getCount(); i++) {
|
||||
c.moveToPosition(i);
|
||||
byte[] data = c.getBlob(0);
|
||||
PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data);
|
||||
ProviderHelper providerHelper = new ProviderHelper(context);
|
||||
if (ring instanceof PGPPublicKeyRing) {
|
||||
providerHelper.saveKeyRing((PGPPublicKeyRing) ring);
|
||||
} else if (ring instanceof PGPSecretKeyRing) {
|
||||
providerHelper.saveKeyRing((PGPSecretKeyRing) ring);
|
||||
} else {
|
||||
Log.e(Constants.TAG, "Unknown blob data type!");
|
||||
Log.d(Constants.TAG, "Importing " + cursor.getCount() + " keyrings from apg.db...");
|
||||
if (cursor != null) {
|
||||
for (int i = 0; i < cursor.getCount(); i++) {
|
||||
cursor.moveToPosition(i);
|
||||
byte[] data = cursor.getBlob(0);
|
||||
PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data);
|
||||
ProviderHelper providerHelper = new ProviderHelper(context);
|
||||
if (ring instanceof PGPPublicKeyRing) {
|
||||
providerHelper.saveKeyRing((PGPPublicKeyRing) ring);
|
||||
} else if (ring instanceof PGPSecretKeyRing) {
|
||||
providerHelper.saveKeyRing((PGPSecretKeyRing) ring);
|
||||
} else {
|
||||
Log.e(Constants.TAG, "Unknown blob data type!");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(Constants.TAG, "Error importing apg.db!", e);
|
||||
} finally {
|
||||
if (c != null) {
|
||||
c.close();
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
if (db != null) {
|
||||
db.close();
|
||||
|
@ -567,20 +567,21 @@ public class KeychainProvider extends ContentProvider {
|
||||
}
|
||||
|
||||
SQLiteDatabase db = getDb().getReadableDatabase();
|
||||
Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, having, orderBy);
|
||||
|
||||
// Tell the cursor what uri to watch, so it knows when its source data changes
|
||||
c.setNotificationUri(getContext().getContentResolver(), uri);
|
||||
Cursor cursor = qb.query(db, projection, selection, selectionArgs, groupBy, having, orderBy);
|
||||
if (cursor != null) {
|
||||
// Tell the cursor what uri to watch, so it knows when its source data changes
|
||||
cursor.setNotificationUri(getContext().getContentResolver(), uri);
|
||||
}
|
||||
|
||||
if (Constants.DEBUG) {
|
||||
Log.d(Constants.TAG,
|
||||
"Query: "
|
||||
+ qb.buildQuery(projection, selection, selectionArgs, null, null,
|
||||
orderBy, null));
|
||||
Log.d(Constants.TAG, "Cursor: " + DatabaseUtils.dumpCursorToString(c));
|
||||
Log.d(Constants.TAG, "Cursor: " + DatabaseUtils.dumpCursorToString(cursor));
|
||||
}
|
||||
|
||||
return c;
|
||||
return cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,6 +26,7 @@ import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.net.Uri;
|
||||
import android.os.RemoteException;
|
||||
import android.support.v4.util.LongSparseArray;
|
||||
|
||||
import org.spongycastle.bcpg.ArmoredOutputStream;
|
||||
import org.spongycastle.bcpg.S2K;
|
||||
@ -103,36 +104,38 @@ public class ProviderHelper {
|
||||
throws NotFoundException {
|
||||
Cursor cursor = mContentResolver.query(uri, proj, null, null, null);
|
||||
|
||||
HashMap<String, Object> result = new HashMap<String, Object>(proj.length);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
int pos = 0;
|
||||
for (String p : proj) {
|
||||
switch (types[pos]) {
|
||||
case FIELD_TYPE_NULL:
|
||||
result.put(p, cursor.isNull(pos));
|
||||
break;
|
||||
case FIELD_TYPE_INTEGER:
|
||||
result.put(p, cursor.getLong(pos));
|
||||
break;
|
||||
case FIELD_TYPE_FLOAT:
|
||||
result.put(p, cursor.getFloat(pos));
|
||||
break;
|
||||
case FIELD_TYPE_STRING:
|
||||
result.put(p, cursor.getString(pos));
|
||||
break;
|
||||
case FIELD_TYPE_BLOB:
|
||||
result.put(p, cursor.getBlob(pos));
|
||||
break;
|
||||
try {
|
||||
HashMap<String, Object> result = new HashMap<String, Object>(proj.length);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
int pos = 0;
|
||||
for (String p : proj) {
|
||||
switch (types[pos]) {
|
||||
case FIELD_TYPE_NULL:
|
||||
result.put(p, cursor.isNull(pos));
|
||||
break;
|
||||
case FIELD_TYPE_INTEGER:
|
||||
result.put(p, cursor.getLong(pos));
|
||||
break;
|
||||
case FIELD_TYPE_FLOAT:
|
||||
result.put(p, cursor.getFloat(pos));
|
||||
break;
|
||||
case FIELD_TYPE_STRING:
|
||||
result.put(p, cursor.getString(pos));
|
||||
break;
|
||||
case FIELD_TYPE_BLOB:
|
||||
result.put(p, cursor.getBlob(pos));
|
||||
break;
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Object getUnifiedData(long masterKeyId, String column, int type)
|
||||
@ -172,22 +175,24 @@ public class ProviderHelper {
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Map<Long, PGPKeyRing> getPGPKeyRings(Uri queryUri) {
|
||||
public LongSparseArray<PGPKeyRing> getPGPKeyRings(Uri queryUri) {
|
||||
Cursor cursor = mContentResolver.query(queryUri,
|
||||
new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA},
|
||||
null, null, null);
|
||||
|
||||
Map<Long, PGPKeyRing> result = new HashMap<Long, PGPKeyRing>(cursor.getCount());
|
||||
if (cursor != null && cursor.moveToFirst()) do {
|
||||
long masterKeyId = cursor.getLong(0);
|
||||
byte[] data = cursor.getBlob(1);
|
||||
if (data != null) {
|
||||
result.put(masterKeyId, PgpConversionHelper.BytesToPGPKeyRing(data));
|
||||
LongSparseArray<PGPKeyRing> result = new LongSparseArray<PGPKeyRing>(cursor.getCount());
|
||||
try {
|
||||
if (cursor != null && cursor.moveToFirst()) do {
|
||||
long masterKeyId = cursor.getLong(0);
|
||||
byte[] data = cursor.getBlob(1);
|
||||
if (data != null) {
|
||||
result.put(masterKeyId, PgpConversionHelper.BytesToPGPKeyRing(data));
|
||||
}
|
||||
} while (cursor.moveToNext());
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
} while (cursor.moveToNext());
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -255,11 +260,11 @@ public class ProviderHelper {
|
||||
|
||||
@Deprecated
|
||||
public PGPKeyRing getPGPKeyRing(Uri queryUri) throws NotFoundException {
|
||||
Map<Long, PGPKeyRing> result = getPGPKeyRings(queryUri);
|
||||
if (result.isEmpty()) {
|
||||
LongSparseArray<PGPKeyRing> result = getPGPKeyRings(queryUri);
|
||||
if (result.size() == 0) {
|
||||
throw new NotFoundException("PGPKeyRing object not found!");
|
||||
} else {
|
||||
return result.values().iterator().next();
|
||||
return result.valueAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -312,7 +317,7 @@ public class ProviderHelper {
|
||||
}
|
||||
|
||||
// get a list of owned secret keys, for verification filtering
|
||||
Map<Long, PGPKeyRing> allKeyRings = getPGPKeyRings(KeyRingData.buildSecretKeyRingUri());
|
||||
LongSparseArray<PGPKeyRing> allKeyRings = getPGPKeyRings(KeyRingData.buildSecretKeyRingUri());
|
||||
// special case: available secret keys verify themselves!
|
||||
if (secretRing != null)
|
||||
allKeyRings.put(secretRing.getSecretKey().getKeyID(), secretRing);
|
||||
@ -350,7 +355,7 @@ public class ProviderHelper {
|
||||
}
|
||||
}
|
||||
// verify signatures from known private keys
|
||||
if (allKeyRings.containsKey(certId)) {
|
||||
if (allKeyRings.indexOfKey(certId) >= 0) {
|
||||
// mark them as verified
|
||||
cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME),
|
||||
@ -634,27 +639,29 @@ public class ProviderHelper {
|
||||
}, inMasterKeyList, null, null);
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
int masterIdCol = cursor.getColumnIndex(KeyRingData.MASTER_KEY_ID);
|
||||
int dataCol = cursor.getColumnIndex(KeyRingData.KEY_RING_DATA);
|
||||
if (cursor.moveToFirst()) {
|
||||
do {
|
||||
Log.d(Constants.TAG, "masterKeyId: " + cursor.getLong(masterIdCol));
|
||||
try {
|
||||
if (cursor != null) {
|
||||
int masterIdCol = cursor.getColumnIndex(KeyRingData.MASTER_KEY_ID);
|
||||
int dataCol = cursor.getColumnIndex(KeyRingData.KEY_RING_DATA);
|
||||
if (cursor.moveToFirst()) {
|
||||
do {
|
||||
Log.d(Constants.TAG, "masterKeyId: " + cursor.getLong(masterIdCol));
|
||||
|
||||
byte[] data = cursor.getBlob(dataCol);
|
||||
byte[] data = cursor.getBlob(dataCol);
|
||||
|
||||
// get actual keyring data blob and write it to ByteArrayOutputStream
|
||||
try {
|
||||
output.add(getKeyRingAsArmoredString(data));
|
||||
} catch (IOException e) {
|
||||
Log.e(Constants.TAG, "IOException", e);
|
||||
}
|
||||
} while (cursor.moveToNext());
|
||||
// get actual keyring data blob and write it to ByteArrayOutputStream
|
||||
try {
|
||||
output.add(getKeyRingAsArmoredString(data));
|
||||
} catch (IOException e) {
|
||||
Log.e(Constants.TAG, "IOException", e);
|
||||
}
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
if (output.size() > 0) {
|
||||
@ -668,17 +675,19 @@ public class ProviderHelper {
|
||||
Cursor cursor = mContentResolver.query(ApiApps.CONTENT_URI, null, null, null, null);
|
||||
|
||||
ArrayList<String> packageNames = new ArrayList<String>();
|
||||
if (cursor != null) {
|
||||
int packageNameCol = cursor.getColumnIndex(ApiApps.PACKAGE_NAME);
|
||||
if (cursor.moveToFirst()) {
|
||||
do {
|
||||
packageNames.add(cursor.getString(packageNameCol));
|
||||
} while (cursor.moveToNext());
|
||||
try {
|
||||
if (cursor != null) {
|
||||
int packageNameCol = cursor.getColumnIndex(ApiApps.PACKAGE_NAME);
|
||||
if (cursor.moveToFirst()) {
|
||||
do {
|
||||
packageNames.add(cursor.getString(packageNameCol));
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return packageNames;
|
||||
@ -726,13 +735,19 @@ public class ProviderHelper {
|
||||
public AppSettings getApiAppSettings(Uri uri) {
|
||||
AppSettings settings = null;
|
||||
|
||||
Cursor cur = mContentResolver.query(uri, null, null, null, null);
|
||||
if (cur != null && cur.moveToFirst()) {
|
||||
settings = new AppSettings();
|
||||
settings.setPackageName(cur.getString(
|
||||
cur.getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME)));
|
||||
settings.setPackageSignature(cur.getBlob(
|
||||
cur.getColumnIndex(KeychainContract.ApiApps.PACKAGE_SIGNATURE)));
|
||||
Cursor cursor = mContentResolver.query(uri, null, null, null, null);
|
||||
try {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
settings = new AppSettings();
|
||||
settings.setPackageName(cursor.getString(
|
||||
cursor.getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME)));
|
||||
settings.setPackageSignature(cursor.getBlob(
|
||||
cursor.getColumnIndex(KeychainContract.ApiApps.PACKAGE_SIGNATURE)));
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
return settings;
|
||||
@ -741,20 +756,26 @@ public class ProviderHelper {
|
||||
public AccountSettings getApiAccountSettings(Uri accountUri) {
|
||||
AccountSettings settings = null;
|
||||
|
||||
Cursor cur = mContentResolver.query(accountUri, null, null, null, null);
|
||||
if (cur != null && cur.moveToFirst()) {
|
||||
settings = new AccountSettings();
|
||||
Cursor cursor = mContentResolver.query(accountUri, null, null, null, null);
|
||||
try {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
settings = new AccountSettings();
|
||||
|
||||
settings.setAccountName(cur.getString(
|
||||
cur.getColumnIndex(KeychainContract.ApiAccounts.ACCOUNT_NAME)));
|
||||
settings.setKeyId(cur.getLong(
|
||||
cur.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID)));
|
||||
settings.setCompression(cur.getInt(
|
||||
cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.COMPRESSION)));
|
||||
settings.setHashAlgorithm(cur.getInt(
|
||||
cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.HASH_ALORITHM)));
|
||||
settings.setEncryptionAlgorithm(cur.getInt(
|
||||
cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM)));
|
||||
settings.setAccountName(cursor.getString(
|
||||
cursor.getColumnIndex(KeychainContract.ApiAccounts.ACCOUNT_NAME)));
|
||||
settings.setKeyId(cursor.getLong(
|
||||
cursor.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID)));
|
||||
settings.setCompression(cursor.getInt(
|
||||
cursor.getColumnIndexOrThrow(KeychainContract.ApiAccounts.COMPRESSION)));
|
||||
settings.setHashAlgorithm(cursor.getInt(
|
||||
cursor.getColumnIndexOrThrow(KeychainContract.ApiAccounts.HASH_ALORITHM)));
|
||||
settings.setEncryptionAlgorithm(cursor.getInt(
|
||||
cursor.getColumnIndexOrThrow(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM)));
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
return settings;
|
||||
@ -764,10 +785,16 @@ public class ProviderHelper {
|
||||
Set<Long> keyIds = new HashSet<Long>();
|
||||
|
||||
Cursor cursor = mContentResolver.query(uri, null, null, null, null);
|
||||
if (cursor != null) {
|
||||
int keyIdColumn = cursor.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID);
|
||||
while (cursor.moveToNext()) {
|
||||
keyIds.add(cursor.getLong(keyIdColumn));
|
||||
try {
|
||||
if (cursor != null) {
|
||||
int keyIdColumn = cursor.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID);
|
||||
while (cursor.moveToNext()) {
|
||||
keyIds.add(cursor.getLong(keyIdColumn));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -780,18 +807,18 @@ public class ProviderHelper {
|
||||
String[] projection = new String[]{ApiApps.PACKAGE_SIGNATURE};
|
||||
|
||||
Cursor cursor = mContentResolver.query(queryUri, projection, null, null, null);
|
||||
try {
|
||||
byte[] signature = null;
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
int signatureCol = 0;
|
||||
|
||||
byte[] signature = null;
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
int signatureCol = 0;
|
||||
|
||||
signature = cursor.getBlob(signatureCol);
|
||||
signature = cursor.getBlob(signatureCol);
|
||||
}
|
||||
return signature;
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return signature;
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity;
|
||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||
import org.sufficientlysecure.keychain.ui.ImportKeysActivity;
|
||||
import org.sufficientlysecure.keychain.ui.ViewKeyActivity;
|
||||
import org.sufficientlysecure.keychain.util.InputData;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
@ -69,19 +70,25 @@ public class OpenPgpService extends RemoteService {
|
||||
|
||||
for (String email : encryptionUserIds) {
|
||||
Uri uri = KeyRings.buildUnifiedKeyRingsFindByEmailUri(email);
|
||||
Cursor cur = getContentResolver().query(uri, null, null, null, null);
|
||||
if (cur.moveToFirst()) {
|
||||
long id = cur.getLong(cur.getColumnIndex(KeyRings.MASTER_KEY_ID));
|
||||
keyIds.add(id);
|
||||
} else {
|
||||
missingUserIdsCheck = true;
|
||||
missingUserIds.add(email);
|
||||
Log.d(Constants.TAG, "user id missing");
|
||||
}
|
||||
if (cur.moveToNext()) {
|
||||
duplicateUserIdsCheck = true;
|
||||
duplicateUserIds.add(email);
|
||||
Log.d(Constants.TAG, "more than one user id with the same email");
|
||||
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
|
||||
try {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
long id = cursor.getLong(cursor.getColumnIndex(KeyRings.MASTER_KEY_ID));
|
||||
keyIds.add(id);
|
||||
} else {
|
||||
missingUserIdsCheck = true;
|
||||
missingUserIds.add(email);
|
||||
Log.d(Constants.TAG, "user id missing");
|
||||
}
|
||||
if (cursor != null && cursor.moveToNext()) {
|
||||
duplicateUserIdsCheck = true;
|
||||
duplicateUserIds.add(email);
|
||||
Log.d(Constants.TAG, "more than one user id with the same email");
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -417,7 +424,15 @@ public class OpenPgpService extends RemoteService {
|
||||
Intent result = new Intent();
|
||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||
|
||||
// TODO: also return PendingIntent that opens the key view activity
|
||||
// also return PendingIntent that opens the key view activity
|
||||
Intent intent = new Intent(getBaseContext(), ViewKeyActivity.class);
|
||||
intent.setData(KeyRings.buildGenericKeyRingUri(Long.toString(masterKeyId)));
|
||||
|
||||
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
|
||||
intent,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
|
||||
|
||||
return result;
|
||||
} catch (ProviderHelper.NotFoundException e) {
|
||||
|
@ -40,6 +40,9 @@ public class AppSettingsActivity extends ActionBarActivity {
|
||||
private AppSettingsFragment mSettingsFragment;
|
||||
private AccountsListFragment mAccountsListFragment;
|
||||
|
||||
// model
|
||||
AppSettings mAppSettings;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@ -80,22 +83,39 @@ public class AppSettingsActivity extends ActionBarActivity {
|
||||
case R.id.menu_api_settings_revoke:
|
||||
revokeAccess();
|
||||
return true;
|
||||
case R.id.menu_api_settings_start:
|
||||
startApp();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void startApp() {
|
||||
Intent i;
|
||||
PackageManager manager = getPackageManager();
|
||||
try {
|
||||
i = manager.getLaunchIntentForPackage(mAppSettings.getPackageName());
|
||||
if (i == null)
|
||||
throw new PackageManager.NameNotFoundException();
|
||||
i.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
startActivity(i);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(Constants.TAG, "startApp", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadData(Bundle savedInstanceState, Uri appUri) {
|
||||
AppSettings settings = new ProviderHelper(this).getApiAppSettings(appUri);
|
||||
mSettingsFragment.setAppSettings(settings);
|
||||
mAppSettings = new ProviderHelper(this).getApiAppSettings(appUri);
|
||||
mSettingsFragment.setAppSettings(mAppSettings);
|
||||
|
||||
String appName;
|
||||
PackageManager pm = getPackageManager();
|
||||
try {
|
||||
ApplicationInfo ai = pm.getApplicationInfo(settings.getPackageName(), 0);
|
||||
ApplicationInfo ai = pm.getApplicationInfo(mAppSettings.getPackageName(), 0);
|
||||
appName = (String) pm.getApplicationLabel(ai);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// fallback
|
||||
appName = settings.getPackageName();
|
||||
appName = mAppSettings.getPackageName();
|
||||
}
|
||||
setTitle(appName);
|
||||
|
||||
|
@ -250,7 +250,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
||||
|
||||
// set text on view
|
||||
HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_select_pub_keys_text);
|
||||
textView.setHtmlFromString(text);
|
||||
textView.setHtmlFromString(text, true);
|
||||
|
||||
/* Load select pub keys fragment */
|
||||
// Check that the activity is using the layout version with
|
||||
@ -292,7 +292,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
||||
|
||||
// set text on view
|
||||
HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_app_error_message_text);
|
||||
textView.setHtmlFromString(text);
|
||||
textView.setHtmlFromString(text, true);
|
||||
} else {
|
||||
Log.e(Constants.TAG, "Action does not exist!");
|
||||
setResult(RESULT_CANCELED);
|
||||
|
@ -50,10 +50,10 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
|
||||
import org.sufficientlysecure.keychain.util.HkpKeyServer;
|
||||
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
|
||||
import org.sufficientlysecure.keychain.keyimport.HkpKeyServer;
|
||||
import org.sufficientlysecure.keychain.util.InputData;
|
||||
import org.sufficientlysecure.keychain.util.KeychainServiceListener;
|
||||
import org.sufficientlysecure.keychain.keyimport.KeybaseKeyServer;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||
|
||||
@ -75,7 +75,7 @@ import java.util.List;
|
||||
* after doing them.
|
||||
*/
|
||||
public class KeychainIntentService extends IntentService
|
||||
implements Progressable, KeychainServiceListener {
|
||||
implements Progressable, PgpImportExport.KeychainServiceListener {
|
||||
|
||||
/* extras that can be given by intent */
|
||||
public static final String EXTRA_MESSENGER = "messenger";
|
||||
@ -99,6 +99,7 @@ public class KeychainIntentService extends IntentService
|
||||
|
||||
public static final String ACTION_UPLOAD_KEYRING = Constants.INTENT_PREFIX + "UPLOAD_KEYRING";
|
||||
public static final String ACTION_DOWNLOAD_AND_IMPORT_KEYS = Constants.INTENT_PREFIX + "QUERY_KEYRING";
|
||||
public static final String ACTION_IMPORT_KEYBASE_KEYS = Constants.INTENT_PREFIX + "DOWNLOAD_KEYBASE";
|
||||
|
||||
public static final String ACTION_CERTIFY_KEYRING = Constants.INTENT_PREFIX + "SIGN_KEYRING";
|
||||
|
||||
@ -319,6 +320,7 @@ public class KeychainIntentService extends IntentService
|
||||
.setEncryptionMasterKeyIds(encryptionKeyIds)
|
||||
.setSymmetricPassphrase(symmetricPassphrase)
|
||||
.setSignatureMasterKeyId(signatureKeyId)
|
||||
.setEncryptToSigner(true)
|
||||
.setSignatureHashAlgorithm(
|
||||
Preferences.getPreferences(this).getDefaultHashAlgorithm())
|
||||
.setSignaturePassphrase(
|
||||
@ -681,8 +683,7 @@ public class KeychainIntentService extends IntentService
|
||||
new String[]{KeyRings.MASTER_KEY_ID, KeyRings.HAS_ANY_SECRET},
|
||||
selection, null, null);
|
||||
try {
|
||||
cursor.moveToFirst();
|
||||
do {
|
||||
if (cursor != null && cursor.moveToFirst()) do {
|
||||
// export public either way
|
||||
publicMasterKeyIds.add(cursor.getLong(0));
|
||||
// add secret if available (and requested)
|
||||
@ -690,7 +691,9 @@ public class KeychainIntentService extends IntentService
|
||||
secretMasterKeyIds.add(cursor.getLong(0));
|
||||
} while (cursor.moveToNext());
|
||||
} finally {
|
||||
cursor.close();
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
PgpImportExport pgpImportExport = new PgpImportExport(this, this, this);
|
||||
@ -729,6 +732,55 @@ public class KeychainIntentService extends IntentService
|
||||
} catch (Exception e) {
|
||||
sendErrorToHandler(e);
|
||||
}
|
||||
} else if (ACTION_IMPORT_KEYBASE_KEYS.equals(action)) {
|
||||
ArrayList<ImportKeysListEntry> entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST);
|
||||
|
||||
try {
|
||||
KeybaseKeyServer server = new KeybaseKeyServer();
|
||||
for (ImportKeysListEntry entry : entries) {
|
||||
// the keybase handle is in userId(1)
|
||||
String keybaseID = entry.getUserIds().get(1);
|
||||
byte[] downloadedKeyBytes = server.get(keybaseID).getBytes();
|
||||
|
||||
// create PGPKeyRing object based on downloaded armored key
|
||||
PGPKeyRing downloadedKey = null;
|
||||
BufferedInputStream bufferedInput =
|
||||
new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes));
|
||||
if (bufferedInput.available() > 0) {
|
||||
InputStream in = PGPUtil.getDecoderStream(bufferedInput);
|
||||
PGPObjectFactory objectFactory = new PGPObjectFactory(in);
|
||||
|
||||
// get first object in block
|
||||
Object obj;
|
||||
if ((obj = objectFactory.nextObject()) != null) {
|
||||
|
||||
if (obj instanceof PGPKeyRing) {
|
||||
downloadedKey = (PGPKeyRing) obj;
|
||||
} else {
|
||||
throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// save key bytes in entry object for doing the
|
||||
// actual import afterwards
|
||||
entry.setBytes(downloadedKey.getEncoded());
|
||||
}
|
||||
|
||||
Intent importIntent = new Intent(this, KeychainIntentService.class);
|
||||
importIntent.setAction(ACTION_IMPORT_KEYRING);
|
||||
Bundle importData = new Bundle();
|
||||
importData.putParcelableArrayList(IMPORT_KEY_LIST, entries);
|
||||
importIntent.putExtra(EXTRA_DATA, importData);
|
||||
importIntent.putExtra(EXTRA_MESSENGER, mMessenger);
|
||||
|
||||
// now import it with this service
|
||||
onHandleIntent(importIntent);
|
||||
|
||||
// result is handled in ACTION_IMPORT_KEYRING
|
||||
} catch (Exception e) {
|
||||
sendErrorToHandler(e);
|
||||
}
|
||||
} else if (ACTION_DOWNLOAD_AND_IMPORT_KEYS.equals(action)) {
|
||||
try {
|
||||
ArrayList<ImportKeysListEntry> entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST);
|
||||
|
@ -24,7 +24,8 @@ import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.devspark.appmsg.AppMsg;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
|
||||
@ -99,9 +100,9 @@ public class KeychainIntentServiceHandler extends Handler {
|
||||
|
||||
// show error from service
|
||||
if (data.containsKey(DATA_ERROR)) {
|
||||
Toast.makeText(mActivity,
|
||||
AppMsg.makeText(mActivity,
|
||||
mActivity.getString(R.string.error_message, data.getString(DATA_ERROR)),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
AppMsg.STYLE_ALERT).show();
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -26,10 +26,11 @@ import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.app.NavUtils;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
@ -40,7 +41,6 @@ import android.widget.ListView;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.beardedhen.androidbootstrap.BootstrapButton;
|
||||
import com.devspark.appmsg.AppMsg;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
@ -64,7 +64,7 @@ import java.util.ArrayList;
|
||||
*/
|
||||
public class CertifyKeyActivity extends ActionBarActivity implements
|
||||
SelectSecretKeyLayoutFragment.SelectSecretKeyCallback, LoaderManager.LoaderCallbacks<Cursor> {
|
||||
private BootstrapButton mSignButton;
|
||||
private View mSignButton;
|
||||
private CheckBox mUploadKeyCheckbox;
|
||||
private Spinner mSelectKeyserverSpinner;
|
||||
|
||||
@ -86,20 +86,16 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
||||
|
||||
setContentView(R.layout.certify_key_activity);
|
||||
|
||||
final ActionBar actionBar = getSupportActionBar();
|
||||
actionBar.setDisplayShowTitleEnabled(true);
|
||||
actionBar.setDisplayHomeAsUpEnabled(false);
|
||||
actionBar.setHomeButtonEnabled(false);
|
||||
|
||||
mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getSupportFragmentManager()
|
||||
.findFragmentById(R.id.sign_key_select_key_fragment);
|
||||
mSelectKeyFragment.setCallback(this);
|
||||
mSelectKeyFragment.setFilterCertify(true);
|
||||
|
||||
mSelectKeyserverSpinner = (Spinner) findViewById(R.id.sign_key_keyserver);
|
||||
mSelectKeyserverSpinner = (Spinner) findViewById(R.id.upload_key_keyserver);
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
|
||||
android.R.layout.simple_spinner_item, Preferences.getPreferences(this)
|
||||
.getKeyServers());
|
||||
.getKeyServers()
|
||||
);
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
mSelectKeyserverSpinner.setAdapter(adapter);
|
||||
|
||||
@ -122,14 +118,14 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
||||
}
|
||||
});
|
||||
|
||||
mSignButton = (BootstrapButton) findViewById(R.id.sign_key_sign_button);
|
||||
mSignButton = findViewById(R.id.sign_key_sign_button);
|
||||
mSignButton.setOnClickListener(new OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mPubKeyId != 0) {
|
||||
if (mMasterKeyId == 0) {
|
||||
mSelectKeyFragment.setError(getString(R.string.select_key_to_sign));
|
||||
mSelectKeyFragment.setError(getString(R.string.select_key_to_certify));
|
||||
} else {
|
||||
initiateSigning();
|
||||
}
|
||||
@ -145,7 +141,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
||||
}
|
||||
Log.e(Constants.TAG, "uri: " + mDataUri);
|
||||
|
||||
mUserIds = (ListView) findViewById(R.id.user_ids);
|
||||
mUserIds = (ListView) findViewById(R.id.view_key_user_ids);
|
||||
|
||||
mUserIdsAdapter = new ViewKeyUserIdsAdapter(this, null, 0, true);
|
||||
mUserIds.setAdapter(mUserIdsAdapter);
|
||||
@ -201,7 +197,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
||||
|
||||
byte[] fingerprintBlob = data.getBlob(INDEX_FINGERPRINT);
|
||||
String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob);
|
||||
((TextView) findViewById(R.id.fingerprint))
|
||||
((TextView) findViewById(R.id.view_key_fingerprint))
|
||||
.setText(PgpKeyHelper.colorizeFingerprint(fingerprint));
|
||||
}
|
||||
break;
|
||||
@ -318,7 +314,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
||||
// fill values for this action
|
||||
Bundle data = new Bundle();
|
||||
|
||||
Spinner keyServer = (Spinner) findViewById(R.id.sign_key_keyserver);
|
||||
Spinner keyServer = (Spinner) findViewById(R.id.upload_key_keyserver);
|
||||
String server = (String) keyServer.getSelectedItem();
|
||||
data.putString(KeychainIntentService.UPLOAD_KEY_SERVER, server);
|
||||
|
||||
@ -359,4 +355,17 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
||||
public void onKeySelected(long secretKeyId) {
|
||||
mMasterKeyId = secretKeyId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home: {
|
||||
Intent viewIntent = NavUtils.getParentActivityIntent(this);
|
||||
viewIntent.setData(KeyRings.buildGenericKeyRingUri(mDataUri));
|
||||
NavUtils.navigateUpTo(this, viewIntent);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ import android.widget.Toast;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
|
||||
import org.sufficientlysecure.keychain.helper.FileHelper;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
|
||||
@ -68,9 +67,6 @@ public class DecryptActivity extends DrawerActivity {
|
||||
|
||||
setContentView(R.layout.decrypt_activity);
|
||||
|
||||
// set actionbar without home button if called from another app
|
||||
ActionBarHelper.setBackButton(this);
|
||||
|
||||
initView();
|
||||
|
||||
setupDrawerNavigation(savedInstanceState);
|
||||
|
@ -54,7 +54,7 @@ public class DecryptFileFragment extends DecryptFragment {
|
||||
private EditText mFilename;
|
||||
private CheckBox mDeleteAfter;
|
||||
private BootstrapButton mBrowse;
|
||||
private BootstrapButton mDecryptButton;
|
||||
private View mDecryptButton;
|
||||
|
||||
private String mInputFilename = null;
|
||||
private String mOutputFilename = null;
|
||||
@ -71,7 +71,7 @@ public class DecryptFileFragment extends DecryptFragment {
|
||||
mFilename = (EditText) view.findViewById(R.id.decrypt_file_filename);
|
||||
mBrowse = (BootstrapButton) view.findViewById(R.id.decrypt_file_browse);
|
||||
mDeleteAfter = (CheckBox) view.findViewById(R.id.decrypt_file_delete_after_decryption);
|
||||
mDecryptButton = (BootstrapButton) view.findViewById(R.id.decrypt_file_action_decrypt);
|
||||
mDecryptButton = view.findViewById(R.id.decrypt_file_action_decrypt);
|
||||
mBrowse.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
FileHelper.openFile(DecryptFileFragment.this, mFilename.getText().toString(), "*/*",
|
||||
|
@ -192,7 +192,7 @@ public class DecryptFragment extends Fragment {
|
||||
mLookupKey.setVisibility(View.GONE);
|
||||
|
||||
// successful decryption-only
|
||||
mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_blue));
|
||||
mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_purple));
|
||||
mResultText.setText(R.string.decrypt_result_decrypted);
|
||||
}
|
||||
}
|
||||
|
@ -47,8 +47,8 @@ public class DecryptMessageFragment extends DecryptFragment {
|
||||
|
||||
// view
|
||||
private EditText mMessage;
|
||||
private BootstrapButton mDecryptButton;
|
||||
private BootstrapButton mDecryptFromCLipboardButton;
|
||||
private View mDecryptButton;
|
||||
private View mDecryptFromCLipboardButton;
|
||||
|
||||
// model
|
||||
private String mCiphertext;
|
||||
@ -61,8 +61,8 @@ public class DecryptMessageFragment extends DecryptFragment {
|
||||
View view = inflater.inflate(R.layout.decrypt_message_fragment, container, false);
|
||||
|
||||
mMessage = (EditText) view.findViewById(R.id.message);
|
||||
mDecryptButton = (BootstrapButton) view.findViewById(R.id.action_decrypt);
|
||||
mDecryptFromCLipboardButton = (BootstrapButton) view.findViewById(R.id.action_decrypt_from_clipboard);
|
||||
mDecryptButton = view.findViewById(R.id.action_decrypt);
|
||||
mDecryptFromCLipboardButton = view.findViewById(R.id.action_decrypt_from_clipboard);
|
||||
mDecryptButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@ -108,11 +108,11 @@ public class DecryptMessageFragment extends DecryptFragment {
|
||||
mCiphertext = matcher.group(1);
|
||||
decryptStart(null);
|
||||
} else {
|
||||
AppMsg.makeText(getActivity(), R.string.error_invalid_data, AppMsg.STYLE_INFO)
|
||||
AppMsg.makeText(getActivity(), R.string.error_invalid_data, AppMsg.STYLE_ALERT)
|
||||
.show();
|
||||
}
|
||||
} else {
|
||||
AppMsg.makeText(getActivity(), R.string.error_invalid_data, AppMsg.STYLE_INFO)
|
||||
AppMsg.makeText(getActivity(), R.string.error_invalid_data, AppMsg.STYLE_ALERT)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.widget.Editor;
|
||||
@ -502,7 +503,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
||||
int curID = 0;
|
||||
for (String userID : userIDs) {
|
||||
if (userID.equals("") && (!userID.equals(originalIDs.get(curID)) || newIDs.get(curID))) {
|
||||
AlertDialog.Builder alert = new AlertDialog.Builder(
|
||||
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(
|
||||
EditKeyActivity.this);
|
||||
|
||||
alert.setIcon(R.drawable.ic_dialog_alert_holo_light);
|
||||
@ -525,7 +526,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
||||
}
|
||||
);
|
||||
alert.setCancelable(false);
|
||||
alert.create().show();
|
||||
alert.show();
|
||||
return;
|
||||
}
|
||||
curID++;
|
||||
@ -614,7 +615,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
||||
|
||||
private void cancelClicked() {
|
||||
if (needsSaving()) { //ask if we want to save
|
||||
AlertDialog.Builder alert = new AlertDialog.Builder(
|
||||
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(
|
||||
EditKeyActivity.this);
|
||||
|
||||
alert.setIcon(R.drawable.ic_dialog_alert_holo_light);
|
||||
@ -637,7 +638,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
||||
}
|
||||
});
|
||||
alert.setCancelable(false);
|
||||
alert.create().show();
|
||||
alert.show();
|
||||
} else {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
|
@ -27,7 +27,6 @@ import android.widget.Toast;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
|
||||
import org.sufficientlysecure.keychain.helper.FileHelper;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
@ -145,26 +144,28 @@ public class EncryptActivity extends DrawerActivity implements
|
||||
|
||||
setContentView(R.layout.encrypt_activity);
|
||||
|
||||
// set actionbar without home button if called from another app
|
||||
ActionBarHelper.setBackButton(this);
|
||||
|
||||
initView();
|
||||
|
||||
setupDrawerNavigation(savedInstanceState);
|
||||
// if called with an intent action, do not init drawer navigation
|
||||
if (ACTION_ENCRYPT.equals(getIntent().getAction())) {
|
||||
// TODO: back button to key?
|
||||
} else {
|
||||
setupDrawerNavigation(savedInstanceState);
|
||||
}
|
||||
|
||||
// Handle intent actions
|
||||
handleActions(getIntent());
|
||||
|
||||
mTabsAdapterMode.addTab(EncryptAsymmetricFragment.class,
|
||||
mAsymmetricFragmentBundle, getString(R.string.label_asymmetric));
|
||||
mAsymmetricFragmentBundle, getString(R.string.label_asymmetric));
|
||||
mTabsAdapterMode.addTab(EncryptSymmetricFragment.class,
|
||||
mSymmetricFragmentBundle, getString(R.string.label_symmetric));
|
||||
mSymmetricFragmentBundle, getString(R.string.label_symmetric));
|
||||
mViewPagerMode.setCurrentItem(mSwitchToMode);
|
||||
|
||||
mTabsAdapterContent.addTab(EncryptMessageFragment.class,
|
||||
mMessageFragmentBundle, getString(R.string.label_message));
|
||||
mMessageFragmentBundle, getString(R.string.label_message));
|
||||
mTabsAdapterContent.addTab(EncryptFileFragment.class,
|
||||
mFileFragmentBundle, getString(R.string.label_file));
|
||||
mFileFragmentBundle, getString(R.string.label_file));
|
||||
mViewPagerContent.setCurrentItem(mSwitchToContent);
|
||||
}
|
||||
|
||||
@ -217,9 +218,9 @@ public class EncryptActivity extends DrawerActivity implements
|
||||
|
||||
// preselect keys given by intent
|
||||
mAsymmetricFragmentBundle.putLongArray(EncryptAsymmetricFragment.ARG_ENCRYPTION_KEY_IDS,
|
||||
encryptionKeyIds);
|
||||
encryptionKeyIds);
|
||||
mAsymmetricFragmentBundle.putLong(EncryptAsymmetricFragment.ARG_SIGNATURE_KEY_ID,
|
||||
signatureKeyId);
|
||||
signatureKeyId);
|
||||
mSwitchToMode = PAGER_MODE_ASYMMETRIC;
|
||||
|
||||
/**
|
||||
@ -241,9 +242,10 @@ public class EncryptActivity extends DrawerActivity implements
|
||||
} else {
|
||||
Log.e(Constants.TAG,
|
||||
"Direct binary data without actual file in filesystem is not supported " +
|
||||
"by Intents. Please use the Remote Service API!");
|
||||
Toast.makeText(this, R.string.error_only_files_are_supported, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
"by Intents. Please use the Remote Service API!"
|
||||
);
|
||||
Toast.makeText(this, R.string.error_only_files_are_supported,
|
||||
Toast.LENGTH_LONG).show();
|
||||
// end activity
|
||||
finish();
|
||||
}
|
||||
|
@ -220,9 +220,6 @@ public class EncryptAsymmetricFragment extends Fragment {
|
||||
private void selectPublicKeys() {
|
||||
Intent intent = new Intent(getActivity(), SelectPublicKeyActivity.class);
|
||||
Vector<Long> keyIds = new Vector<Long>();
|
||||
if (mSecretKeyId != 0) {
|
||||
keyIds.add(mSecretKeyId);
|
||||
}
|
||||
if (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) {
|
||||
for (int i = 0; i < mEncryptionKeyIds.length; ++i) {
|
||||
keyIds.add(mEncryptionKeyIds[i]);
|
||||
|
@ -67,7 +67,7 @@ public class EncryptFileFragment extends Fragment {
|
||||
private CheckBox mDeleteAfter = null;
|
||||
private CheckBox mShareAfter = null;
|
||||
private BootstrapButton mBrowse = null;
|
||||
private BootstrapButton mEncryptFile;
|
||||
private View mEncryptFile;
|
||||
|
||||
private FileDialogFragment mFileDialog;
|
||||
|
||||
@ -92,7 +92,7 @@ public class EncryptFileFragment extends Fragment {
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.encrypt_file_fragment, container, false);
|
||||
|
||||
mEncryptFile = (BootstrapButton) view.findViewById(R.id.action_encrypt_file);
|
||||
mEncryptFile = view.findViewById(R.id.action_encrypt_file);
|
||||
mEncryptFile.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
@ -28,9 +28,8 @@ import android.support.v4.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.beardedhen.androidbootstrap.BootstrapButton;
|
||||
import com.devspark.appmsg.AppMsg;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
@ -46,9 +45,9 @@ import org.sufficientlysecure.keychain.util.Log;
|
||||
public class EncryptMessageFragment extends Fragment {
|
||||
public static final String ARG_TEXT = "text";
|
||||
|
||||
private EditText mMessage = null;
|
||||
private BootstrapButton mEncryptShare;
|
||||
private BootstrapButton mEncryptClipboard;
|
||||
private TextView mMessage = null;
|
||||
private View mEncryptShare;
|
||||
private View mEncryptClipboard;
|
||||
|
||||
private EncryptActivityInterface mEncryptInterface;
|
||||
|
||||
@ -70,9 +69,9 @@ public class EncryptMessageFragment extends Fragment {
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.encrypt_message_fragment, container, false);
|
||||
|
||||
mMessage = (EditText) view.findViewById(R.id.message);
|
||||
mEncryptClipboard = (BootstrapButton) view.findViewById(R.id.action_encrypt_clipboard);
|
||||
mEncryptShare = (BootstrapButton) view.findViewById(R.id.action_encrypt_share);
|
||||
mMessage = (TextView) view.findViewById(R.id.message);
|
||||
mEncryptClipboard = view.findViewById(R.id.action_encrypt_clipboard);
|
||||
mEncryptShare = view.findViewById(R.id.action_encrypt_share);
|
||||
mEncryptClipboard.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
@ -45,7 +45,7 @@ public class HelpAboutFragment extends Fragment {
|
||||
HtmlTextView aboutTextView = (HtmlTextView) view.findViewById(R.id.help_about_text);
|
||||
|
||||
// load html from raw resource (Parsing handled by HtmlTextView library)
|
||||
aboutTextView.setHtmlFromRawResource(getActivity(), R.raw.help_about);
|
||||
aboutTextView.setHtmlFromRawResource(getActivity(), R.raw.help_about, true);
|
||||
|
||||
// no flickering when clicking textview for Android < 4
|
||||
aboutTextView.setTextColor(getResources().getColor(android.R.color.black));
|
||||
|
@ -24,7 +24,9 @@ import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.TabsAdapter;
|
||||
import org.sufficientlysecure.keychain.util.SlidingTabLayout;
|
||||
|
||||
public class HelpActivity extends ActionBarActivity {
|
||||
public static final String EXTRA_SELECTED_TAB = "selected_tab";
|
||||
@ -37,25 +39,27 @@ public class HelpActivity extends ActionBarActivity {
|
||||
public static final int TAB_ABOUT = 5;
|
||||
|
||||
ViewPager mViewPager;
|
||||
TabsAdapter mTabsAdapter;
|
||||
private PagerTabStripAdapter mTabsAdapter;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.help_activity);
|
||||
|
||||
mViewPager = (ViewPager) findViewById(R.id.pager);
|
||||
|
||||
final ActionBar actionBar = getSupportActionBar();
|
||||
actionBar.setDisplayShowTitleEnabled(true);
|
||||
actionBar.setDisplayHomeAsUpEnabled(false);
|
||||
actionBar.setHomeButtonEnabled(false);
|
||||
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
|
||||
|
||||
mTabsAdapter = new TabsAdapter(this, mViewPager);
|
||||
setContentView(R.layout.help_activity);
|
||||
|
||||
int selectedTab = 0;
|
||||
mViewPager = (ViewPager) findViewById(R.id.pager);
|
||||
SlidingTabLayout slidingTabLayout =
|
||||
(SlidingTabLayout) findViewById(R.id.sliding_tab_layout);
|
||||
|
||||
mTabsAdapter = new PagerTabStripAdapter(this);
|
||||
mViewPager.setAdapter(mTabsAdapter);
|
||||
|
||||
int selectedTab = TAB_START;
|
||||
Intent intent = getIntent();
|
||||
if (intent.getExtras() != null && intent.getExtras().containsKey(EXTRA_SELECTED_TAB)) {
|
||||
selectedTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB);
|
||||
@ -63,30 +67,36 @@ public class HelpActivity extends ActionBarActivity {
|
||||
|
||||
Bundle startBundle = new Bundle();
|
||||
startBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_start);
|
||||
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_start)),
|
||||
HelpHtmlFragment.class, startBundle, (selectedTab == TAB_START));
|
||||
mTabsAdapter.addTab(HelpHtmlFragment.class, startBundle,
|
||||
getString(R.string.help_tab_start));
|
||||
|
||||
Bundle faqBundle = new Bundle();
|
||||
faqBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_faq);
|
||||
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_faq)),
|
||||
HelpHtmlFragment.class, faqBundle, (selectedTab == TAB_FAQ));
|
||||
mTabsAdapter.addTab(HelpHtmlFragment.class, faqBundle,
|
||||
getString(R.string.help_tab_faq));
|
||||
|
||||
Bundle wotBundle = new Bundle();
|
||||
wotBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_wot);
|
||||
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_wot)),
|
||||
HelpHtmlFragment.class, wotBundle, (selectedTab == TAB_WOT));
|
||||
mTabsAdapter.addTab(HelpHtmlFragment.class, wotBundle,
|
||||
getString(R.string.help_tab_wot));
|
||||
|
||||
Bundle nfcBundle = new Bundle();
|
||||
nfcBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_nfc_beam);
|
||||
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_nfc_beam)),
|
||||
HelpHtmlFragment.class, nfcBundle, (selectedTab == TAB_NFC));
|
||||
mTabsAdapter.addTab(HelpHtmlFragment.class, nfcBundle,
|
||||
getString(R.string.help_tab_nfc_beam));
|
||||
|
||||
Bundle changelogBundle = new Bundle();
|
||||
changelogBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_changelog);
|
||||
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_changelog)),
|
||||
HelpHtmlFragment.class, changelogBundle, (selectedTab == TAB_CHANGELOG));
|
||||
mTabsAdapter.addTab(HelpHtmlFragment.class, changelogBundle,
|
||||
getString(R.string.help_tab_changelog));
|
||||
|
||||
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_about)),
|
||||
HelpAboutFragment.class, null, (selectedTab == TAB_ABOUT));
|
||||
mTabsAdapter.addTab(HelpAboutFragment.class, null,
|
||||
getString(R.string.help_tab_about));
|
||||
|
||||
// NOTE: must be after adding the tabs!
|
||||
slidingTabLayout.setViewPager(mViewPager);
|
||||
|
||||
// switch to tab selected by extra
|
||||
mViewPager.setCurrentItem(selectedTab);
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ public class HelpHtmlFragment extends Fragment {
|
||||
scroller.addView(text);
|
||||
|
||||
// load html from raw resource (Parsing handled by HtmlTextView library)
|
||||
text.setHtmlFromRawResource(getActivity(), mHtmlFile);
|
||||
text.setHtmlFromRawResource(getActivity(), mHtmlFile, true);
|
||||
|
||||
// no flickering when clicking textview for Android < 4
|
||||
text.setTextColor(getResources().getColor(android.R.color.black));
|
||||
|
@ -38,16 +38,14 @@ import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
||||
import com.beardedhen.androidbootstrap.BootstrapButton;
|
||||
import com.devspark.appmsg.AppMsg;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
|
||||
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.BadImportKeyDialogFragment;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
@ -62,6 +60,8 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
|
||||
+ "IMPORT_KEY_FROM_KEYSERVER";
|
||||
public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN = Constants.INTENT_PREFIX
|
||||
+ "IMPORT_KEY_FROM_KEY_SERVER_AND_RETURN";
|
||||
public static final String ACTION_IMPORT_KEY_FROM_KEYBASE = Constants.INTENT_PREFIX
|
||||
+ "IMPORT_KEY_FROM_KEYBASE";
|
||||
|
||||
// Actions for internal use only:
|
||||
public static final String ACTION_IMPORT_KEY_FROM_FILE = Constants.INTENT_PREFIX
|
||||
@ -85,20 +85,22 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
|
||||
private ImportKeysListFragment mListFragment;
|
||||
private String[] mNavigationStrings;
|
||||
private Fragment mCurrentFragment;
|
||||
private BootstrapButton mImportButton;
|
||||
private View mImportButton;
|
||||
|
||||
private static final Class[] NAVIGATION_CLASSES = new Class[]{
|
||||
ImportKeysServerFragment.class,
|
||||
ImportKeysFileFragment.class,
|
||||
ImportKeysQrCodeFragment.class,
|
||||
ImportKeysClipboardFragment.class,
|
||||
ImportKeysNFCFragment.class
|
||||
ImportKeysNFCFragment.class,
|
||||
ImportKeysKeybaseFragment.class
|
||||
};
|
||||
private static final int NAV_SERVER = 0;
|
||||
private static final int NAV_FILE = 1;
|
||||
private static final int NAV_QR_CODE = 2;
|
||||
private static final int NAV_CLIPBOARD = 3;
|
||||
private static final int NAV_NFC = 4;
|
||||
private static final int NAV_KEYBASE = 5;
|
||||
|
||||
private int mCurrentNavPosition = -1;
|
||||
|
||||
@ -108,7 +110,7 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
|
||||
|
||||
setContentView(R.layout.import_keys_activity);
|
||||
|
||||
mImportButton = (BootstrapButton) findViewById(R.id.import_import);
|
||||
mImportButton = findViewById(R.id.import_import);
|
||||
mImportButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@ -121,7 +123,6 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
|
||||
if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) {
|
||||
setTitle(R.string.nav_import);
|
||||
} else {
|
||||
ActionBarHelper.setBackButton(this);
|
||||
getSupportActionBar().setDisplayShowTitleEnabled(false);
|
||||
|
||||
// set drop down navigation
|
||||
@ -236,6 +237,12 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
|
||||
// NOTE: this only displays the appropriate fragment, no actions are taken
|
||||
loadNavFragment(NAV_NFC, null);
|
||||
|
||||
// no immediate actions!
|
||||
startListFragment(savedInstanceState, null, null, null);
|
||||
} else if (ACTION_IMPORT_KEY_FROM_KEYBASE.equals(action)) {
|
||||
// NOTE: this only displays the appropriate fragment, no actions are taken
|
||||
loadNavFragment(NAV_KEYBASE, null);
|
||||
|
||||
// no immediate actions!
|
||||
startListFragment(savedInstanceState, null, null, null);
|
||||
} else {
|
||||
@ -340,8 +347,8 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
|
||||
startListFragment(savedInstanceState, null, null, query);
|
||||
}
|
||||
|
||||
public void loadCallback(byte[] importData, Uri dataUri, String serverQuery, String keyServer) {
|
||||
mListFragment.loadNew(importData, dataUri, serverQuery, keyServer);
|
||||
public void loadCallback(byte[] importData, Uri dataUri, String serverQuery, String keyServer, String keybaseQuery) {
|
||||
mListFragment.loadNew(importData, dataUri, serverQuery, keyServer, keybaseQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -449,6 +456,31 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
|
||||
|
||||
// start service with intent
|
||||
startService(intent);
|
||||
} else if (mListFragment.getKeybaseQuery() != null) {
|
||||
// Send all information needed to service to query keys in other thread
|
||||
Intent intent = new Intent(this, KeychainIntentService.class);
|
||||
|
||||
intent.setAction(KeychainIntentService.ACTION_IMPORT_KEYBASE_KEYS);
|
||||
|
||||
// fill values for this action
|
||||
Bundle data = new Bundle();
|
||||
|
||||
// get selected key entries
|
||||
ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedData();
|
||||
data.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST, selectedEntries);
|
||||
|
||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(saveHandler);
|
||||
intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
|
||||
|
||||
// show progress dialog
|
||||
saveHandler.showProgressDialog(this);
|
||||
|
||||
// start service with intent
|
||||
startService(intent);
|
||||
|
||||
} else {
|
||||
AppMsg.makeText(this, R.string.error_nothing_import, AppMsg.STYLE_ALERT).show();
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ public class ImportKeysClipboardFragment extends Fragment {
|
||||
return;
|
||||
}
|
||||
}
|
||||
mImportActivity.loadCallback(sendText.getBytes(), null, null, null);
|
||||
mImportActivity.loadCallback(sendText.getBytes(), null, null, null, null);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -85,7 +85,7 @@ public class ImportKeysFileFragment extends Fragment {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
|
||||
// load data
|
||||
mImportActivity.loadCallback(null, data.getData(), null, null);
|
||||
mImportActivity.loadCallback(null, data.getData(), null, null, null);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.beardedhen.androidbootstrap.BootstrapButton;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
/**
|
||||
* Import public keys from the Keybase.io directory. First cut: just raw search.
|
||||
* TODO: make a pick list of the people you’re following on keybase
|
||||
*/
|
||||
public class ImportKeysKeybaseFragment extends Fragment {
|
||||
|
||||
private ImportKeysActivity mImportActivity;
|
||||
private BootstrapButton mSearchButton;
|
||||
private EditText mQueryEditText;
|
||||
|
||||
public static final String ARG_QUERY = "query";
|
||||
|
||||
/**
|
||||
* Creates new instance of this fragment
|
||||
*/
|
||||
public static ImportKeysKeybaseFragment newInstance() {
|
||||
ImportKeysKeybaseFragment frag = new ImportKeysKeybaseFragment();
|
||||
|
||||
Bundle args = new Bundle();
|
||||
frag.setArguments(args);
|
||||
|
||||
return frag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflate the layout for this fragment
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.import_keys_keybase_fragment, container, false);
|
||||
|
||||
mQueryEditText = (EditText) view.findViewById(R.id.import_keybase_query);
|
||||
|
||||
mSearchButton = (BootstrapButton) view.findViewById(R.id.import_keybase_search);
|
||||
mSearchButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
String query = mQueryEditText.getText().toString();
|
||||
search(query);
|
||||
|
||||
// close keyboard after pressing search
|
||||
InputMethodManager imm =
|
||||
(InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(mQueryEditText.getWindowToken(), 0);
|
||||
}
|
||||
});
|
||||
|
||||
mQueryEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
|
||||
String query = mQueryEditText.getText().toString();
|
||||
search(query);
|
||||
|
||||
// Don't return true to let the keyboard close itself after pressing search
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
mImportActivity = (ImportKeysActivity) getActivity();
|
||||
|
||||
// set displayed values
|
||||
if (getArguments() != null) {
|
||||
if (getArguments().containsKey(ARG_QUERY)) {
|
||||
String query = getArguments().getString(ARG_QUERY);
|
||||
mQueryEditText.setText(query, TextView.BufferType.EDITABLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void search(String query) {
|
||||
mImportActivity.loadCallback(null, null, null, null, query);
|
||||
}
|
||||
}
|
@ -33,11 +33,12 @@ import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.helper.Preferences;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.AsyncTaskResultWrapper;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
|
||||
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListKeybaseLoader;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListLoader;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListServerLoader;
|
||||
import org.sufficientlysecure.keychain.util.InputData;
|
||||
import org.sufficientlysecure.keychain.util.KeyServer;
|
||||
import org.sufficientlysecure.keychain.keyimport.KeyServer;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@ -60,9 +61,11 @@ public class ImportKeysListFragment extends ListFragment implements
|
||||
private Uri mDataUri;
|
||||
private String mServerQuery;
|
||||
private String mKeyServer;
|
||||
private String mKeybaseQuery;
|
||||
|
||||
private static final int LOADER_ID_BYTES = 0;
|
||||
private static final int LOADER_ID_SERVER_QUERY = 1;
|
||||
private static final int LOADER_ID_KEYBASE = 2;
|
||||
|
||||
public byte[] getKeyBytes() {
|
||||
return mKeyBytes;
|
||||
@ -76,6 +79,10 @@ public class ImportKeysListFragment extends ListFragment implements
|
||||
return mServerQuery;
|
||||
}
|
||||
|
||||
public String getKeybaseQuery() {
|
||||
return mKeybaseQuery;
|
||||
}
|
||||
|
||||
public String getKeyServer() {
|
||||
return mKeyServer;
|
||||
}
|
||||
@ -148,6 +155,16 @@ public class ImportKeysListFragment extends ListFragment implements
|
||||
// give arguments to onCreateLoader()
|
||||
getLoaderManager().initLoader(LOADER_ID_SERVER_QUERY, null, this);
|
||||
}
|
||||
|
||||
if (mKeybaseQuery != null) {
|
||||
// Start out with a progress indicator.
|
||||
setListShown(false);
|
||||
|
||||
// Prepare the loader. Either re-connect with an existing one,
|
||||
// or start a new one.
|
||||
// give arguments to onCreateLoader()
|
||||
getLoaderManager().initLoader(LOADER_ID_KEYBASE, null, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -157,16 +174,18 @@ public class ImportKeysListFragment extends ListFragment implements
|
||||
// Select checkbox!
|
||||
// Update underlying data and notify adapter of change. The adapter will
|
||||
// update the view automatically.
|
||||
|
||||
ImportKeysListEntry entry = mAdapter.getItem(position);
|
||||
entry.setSelected(!entry.isSelected());
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void loadNew(byte[] keyBytes, Uri dataUri, String serverQuery, String keyServer) {
|
||||
public void loadNew(byte[] keyBytes, Uri dataUri, String serverQuery, String keyServer, String keybaseQuery) {
|
||||
mKeyBytes = keyBytes;
|
||||
mDataUri = dataUri;
|
||||
mServerQuery = serverQuery;
|
||||
mKeyServer = keyServer;
|
||||
mKeybaseQuery = keybaseQuery;
|
||||
|
||||
if (mKeyBytes != null || mDataUri != null) {
|
||||
// Start out with a progress indicator.
|
||||
@ -181,6 +200,13 @@ public class ImportKeysListFragment extends ListFragment implements
|
||||
|
||||
getLoaderManager().restartLoader(LOADER_ID_SERVER_QUERY, null, this);
|
||||
}
|
||||
|
||||
if (mKeybaseQuery != null) {
|
||||
// Start out with a progress indicator.
|
||||
setListShown(false);
|
||||
|
||||
getLoaderManager().restartLoader(LOADER_ID_KEYBASE, null, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -194,6 +220,9 @@ public class ImportKeysListFragment extends ListFragment implements
|
||||
case LOADER_ID_SERVER_QUERY: {
|
||||
return new ImportKeysListServerLoader(getActivity(), mServerQuery, mKeyServer);
|
||||
}
|
||||
case LOADER_ID_KEYBASE: {
|
||||
return new ImportKeysListKeybaseLoader(getActivity(), mKeybaseQuery);
|
||||
}
|
||||
|
||||
default:
|
||||
return null;
|
||||
@ -248,7 +277,7 @@ public class ImportKeysListFragment extends ListFragment implements
|
||||
if (error == null) {
|
||||
AppMsg.makeText(
|
||||
getActivity(), getResources().getQuantityString(R.plurals.keys_found,
|
||||
mAdapter.getCount(), mAdapter.getCount()),
|
||||
mAdapter.getCount(), mAdapter.getCount()),
|
||||
AppMsg.STYLE_INFO
|
||||
).show();
|
||||
} else if (error instanceof KeyServer.InsufficientQuery) {
|
||||
@ -263,6 +292,19 @@ public class ImportKeysListFragment extends ListFragment implements
|
||||
}
|
||||
break;
|
||||
|
||||
case LOADER_ID_KEYBASE:
|
||||
|
||||
if (error == null) {
|
||||
AppMsg.makeText(
|
||||
getActivity(), getResources().getQuantityString(R.plurals.keys_found,
|
||||
mAdapter.getCount(), mAdapter.getCount()),
|
||||
AppMsg.STYLE_INFO
|
||||
).show();
|
||||
} else if (error instanceof KeyServer.QueryException) {
|
||||
AppMsg.makeText(getActivity(), R.string.error_keyserver_query,
|
||||
AppMsg.STYLE_ALERT).show();
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -279,6 +321,10 @@ public class ImportKeysListFragment extends ListFragment implements
|
||||
// Clear the data in the adapter.
|
||||
mAdapter.clear();
|
||||
break;
|
||||
case LOADER_ID_KEYBASE:
|
||||
// Clear the data in the adapter.
|
||||
mAdapter.clear();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ public class ImportKeysQrCodeFragment extends Fragment {
|
||||
|
||||
// is this a full key encoded as qr code?
|
||||
if (scannedContent.startsWith("-----BEGIN PGP")) {
|
||||
mImportActivity.loadCallback(scannedContent.getBytes(), null, null, null);
|
||||
mImportActivity.loadCallback(scannedContent.getBytes(), null, null, null, null);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -197,7 +197,7 @@ public class ImportKeysQrCodeFragment extends Fragment {
|
||||
for (String in : mScannedContent) {
|
||||
result += in;
|
||||
}
|
||||
mImportActivity.loadCallback(result.getBytes(), null, null, null);
|
||||
mImportActivity.loadCallback(result.getBytes(), null, null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,7 +151,7 @@ public class ImportKeysServerFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void search(String query, String keyServer) {
|
||||
mImportActivity.loadCallback(null, null, query, keyServer);
|
||||
mImportActivity.loadCallback(null, null, query, keyServer, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -29,7 +29,6 @@ import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
@ -45,11 +44,10 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.AbsListView.MultiChoiceModeListener;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
@ -77,18 +75,13 @@ import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
|
||||
* Public key list with sticky list headers. It does _not_ extend ListFragment because it uses
|
||||
* StickyListHeaders library which does not extend upon ListView.
|
||||
*/
|
||||
public class KeyListFragment extends Fragment
|
||||
public class KeyListFragment extends LoaderFragment
|
||||
implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener,
|
||||
LoaderManager.LoaderCallbacks<Cursor> {
|
||||
|
||||
private KeyListAdapter mAdapter;
|
||||
private StickyListHeadersListView mStickyList;
|
||||
|
||||
// rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
|
||||
boolean mListShown;
|
||||
View mProgressContainer;
|
||||
View mListContainer;
|
||||
|
||||
private String mCurQuery;
|
||||
private SearchView mSearchView;
|
||||
// empty list layout
|
||||
@ -100,14 +93,15 @@ public class KeyListFragment extends Fragment
|
||||
* Load custom layout with StickyListView from library
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View root = inflater.inflate(R.layout.key_list_fragment, container, false);
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
|
||||
View root = super.onCreateView(inflater, superContainer, savedInstanceState);
|
||||
View view = inflater.inflate(R.layout.key_list_fragment, getContainer());
|
||||
|
||||
mStickyList = (StickyListHeadersListView) root.findViewById(R.id.key_list_list);
|
||||
mStickyList = (StickyListHeadersListView) view.findViewById(R.id.key_list_list);
|
||||
mStickyList.setOnItemClickListener(this);
|
||||
|
||||
// empty view
|
||||
mButtonEmptyCreate = (BootstrapButton) root.findViewById(R.id.key_list_empty_button_create);
|
||||
mButtonEmptyCreate = (BootstrapButton) view.findViewById(R.id.key_list_empty_button_create);
|
||||
mButtonEmptyCreate.setOnClickListener(new OnClickListener() {
|
||||
|
||||
@Override
|
||||
@ -119,7 +113,7 @@ public class KeyListFragment extends Fragment
|
||||
startActivityForResult(intent, 0);
|
||||
}
|
||||
});
|
||||
mButtonEmptyImport = (BootstrapButton) root.findViewById(R.id.key_list_empty_button_import);
|
||||
mButtonEmptyImport = (BootstrapButton) view.findViewById(R.id.key_list_empty_button_import);
|
||||
mButtonEmptyImport.setOnClickListener(new OnClickListener() {
|
||||
|
||||
@Override
|
||||
@ -130,11 +124,6 @@ public class KeyListFragment extends Fragment
|
||||
}
|
||||
});
|
||||
|
||||
// rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
|
||||
mListContainer = root.findViewById(R.id.key_list_list_container);
|
||||
mProgressContainer = root.findViewById(R.id.key_list_progress_container);
|
||||
mListShown = true;
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@ -233,9 +222,8 @@ public class KeyListFragment extends Fragment
|
||||
// We have a menu item to show in action bar.
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
// NOTE: Not supported by StickyListHeader, but reimplemented here
|
||||
// Start out with a progress indicator.
|
||||
setListShown(false);
|
||||
setContentShown(false);
|
||||
|
||||
// Create an empty adapter we will use to display the loaded data.
|
||||
mAdapter = new KeyListAdapter(getActivity(), null, 0);
|
||||
@ -297,12 +285,11 @@ public class KeyListFragment extends Fragment
|
||||
// this view is made visible if no data is available
|
||||
mStickyList.setEmptyView(getActivity().findViewById(R.id.key_list_empty));
|
||||
|
||||
// NOTE: Not supported by StickyListHeader, but reimplemented here
|
||||
// The list should now be shown.
|
||||
if (isResumed()) {
|
||||
setListShown(true);
|
||||
setContentShown(true);
|
||||
} else {
|
||||
setListShownNoAnimation(true);
|
||||
setContentShownNoAnimation(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -380,6 +367,9 @@ public class KeyListFragment extends Fragment
|
||||
// Execute this when searching
|
||||
mSearchView.setOnQueryTextListener(this);
|
||||
|
||||
View searchPlate = mSearchView.findViewById(android.support.v7.appcompat.R.id.search_plate);
|
||||
searchPlate.setBackgroundResource(R.drawable.keychaintheme_searchview_holo_light);
|
||||
|
||||
// Erase search result without focus
|
||||
MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() {
|
||||
@Override
|
||||
@ -414,43 +404,6 @@ public class KeyListFragment extends Fragment
|
||||
return true;
|
||||
}
|
||||
|
||||
// rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
|
||||
public void setListShown(boolean shown, boolean animate) {
|
||||
if (mListShown == shown) {
|
||||
return;
|
||||
}
|
||||
mListShown = shown;
|
||||
if (shown) {
|
||||
if (animate) {
|
||||
mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
|
||||
getActivity(), android.R.anim.fade_out));
|
||||
mListContainer.startAnimation(AnimationUtils.loadAnimation(
|
||||
getActivity(), android.R.anim.fade_in));
|
||||
}
|
||||
mProgressContainer.setVisibility(View.GONE);
|
||||
mListContainer.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
if (animate) {
|
||||
mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
|
||||
getActivity(), android.R.anim.fade_in));
|
||||
mListContainer.startAnimation(AnimationUtils.loadAnimation(
|
||||
getActivity(), android.R.anim.fade_out));
|
||||
}
|
||||
mProgressContainer.setVisibility(View.VISIBLE);
|
||||
mListContainer.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
// rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
|
||||
public void setListShown(boolean shown) {
|
||||
setListShown(shown, true);
|
||||
}
|
||||
|
||||
// rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
|
||||
public void setListShownNoAnimation(boolean shown) {
|
||||
setListShown(shown, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements StickyListHeadersAdapter from library
|
||||
*/
|
||||
@ -475,7 +428,7 @@ public class KeyListFragment extends Fragment
|
||||
TextView mMainUserIdRest;
|
||||
View mStatusDivider;
|
||||
FrameLayout mStatusLayout;
|
||||
Button mButton;
|
||||
ImageButton mButton;
|
||||
TextView mRevoked;
|
||||
ImageView mVerified;
|
||||
}
|
||||
@ -488,7 +441,7 @@ public class KeyListFragment extends Fragment
|
||||
holder.mMainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
|
||||
holder.mStatusDivider = (View) view.findViewById(R.id.status_divider);
|
||||
holder.mStatusLayout = (FrameLayout) view.findViewById(R.id.status_layout);
|
||||
holder.mButton = (Button) view.findViewById(R.id.edit);
|
||||
holder.mButton = (ImageButton) view.findViewById(R.id.edit);
|
||||
holder.mRevoked = (TextView) view.findViewById(R.id.revoked);
|
||||
holder.mVerified = (ImageView) view.findViewById(R.id.verified);
|
||||
view.setTag(holder);
|
||||
|