added initial support for HKP key servers, allowing searching and key import

Update issue 9
Can search a key server now, touch a result to import the key. Still needs better error handling and some Intents to import keys based on key ID. Also still need key server preferences.
This commit is contained in:
Thialfihar 2010-08-17 01:02:39 +00:00
parent b3a63beffc
commit 6e9146c91a
18 changed files with 624 additions and 19 deletions

View File

@ -177,6 +177,11 @@
android:label="@string/title_mailInbox" android:label="@string/title_mailInbox"
android:configChanges="keyboardHidden|orientation|keyboard"/> android:configChanges="keyboardHidden|orientation|keyboard"/>
<activity
android:name=".KeyServerQueryActivity"
android:label="@string/title_keyServerQuery"
android:configChanges="keyboardHidden|orientation|keyboard"/>
<activity <activity
android:name=".PreferencesActivity" android:name=".PreferencesActivity"
android:label="@string/title_preferences" android:label="@string/title_preferences"
@ -201,5 +206,6 @@
<uses-permission android:name="com.google.android.gm.permission.READ_GMAIL" /> <uses-permission android:name="com.google.android.gm.permission.READ_GMAIL" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.fsck.k9.permission.READ_ATTACHMENT" /> <uses-permission android:name="com.fsck.k9.permission.READ_ATTACHMENT" />
<uses-permission android:name="android.permission.INTERNET"/>
</manifest> </manifest>

View File

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

View File

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:singleLine="true"
android:paddingLeft="3dip"
android:paddingRight="?android:attr/scrollbarSize"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="vertical">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="vertical"
android:paddingLeft="5dip"
android:paddingRight="5dip"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1">
<TextView
android:id="@+id/mainUserId"
android:text="Main User ID"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/mainUserIdRest"
android:text="&lt;user@somewhere.com&gt;"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:minWidth="90dip"
android:paddingLeft="3dip"
android:gravity="right"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/keyId"
android:text="BBBBBBBB"
android:textAppearance="?android:attr/textAppearanceSmall"
android:typeface="monospace"
android:layout_width="wrap_content"
android:layout_height="fill_parent"/>
<TextView
android:id="@+id/algorithm"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/status"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#e00"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/list"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="30dip">
</LinearLayout>
</LinearLayout>

View File

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

View File

@ -40,6 +40,7 @@
<string name="title_exportKeys">Export Keys</string> <string name="title_exportKeys">Export Keys</string>
<string name="title_keyNotFound">Key Not Found</string> <string name="title_keyNotFound">Key Not Found</string>
<string name="title_help">Getting Started</string> <string name="title_help">Getting Started</string>
<string name="title_keyServerQuery">Query Key Server</string>
<!-- section_lowerCase: capitalized words, no punctuation --> <!-- section_lowerCase: capitalized words, no punctuation -->
<string name="section_userIds">User IDs</string> <string name="section_userIds">User IDs</string>
@ -70,6 +71,7 @@
<string name="btn_clearFilter">Clear Filter</string> <string name="btn_clearFilter">Clear Filter</string>
<string name="btn_changePassPhrase">Change Pass Phrase</string> <string name="btn_changePassPhrase">Change Pass Phrase</string>
<string name="btn_setPassPhrase">Set Pass Phrase</string> <string name="btn_setPassPhrase">Set Pass Phrase</string>
<string name="btn_search">Search</string>
<!-- menu_lowerCase: capitalized words, no punctuation --> <!-- menu_lowerCase: capitalized words, no punctuation -->
<string name="menu_about">About</string> <string name="menu_about">About</string>
@ -86,6 +88,7 @@
<string name="menu_editKey">Edit Key</string> <string name="menu_editKey">Edit Key</string>
<string name="menu_search">Search</string> <string name="menu_search">Search</string>
<string name="menu_help">Help</string> <string name="menu_help">Help</string>
<string name="menu_keyServer">Key Server</string>
<!-- label_lowerCase: capitalized words, no punctuation --> <!-- label_lowerCase: capitalized words, no punctuation -->
<string name="label_sign">Sign</string> <string name="label_sign">Sign</string>
@ -255,6 +258,7 @@
<string name="progress_decompressingData">decompressing data...</string> <string name="progress_decompressingData">decompressing data...</string>
<string name="progress_verifyingIntegrity">verifying integrity...</string> <string name="progress_verifyingIntegrity">verifying integrity...</string>
<string name="progress_deletingSecurely">deleting \'%s\' securely...</string> <string name="progress_deletingSecurely">deleting \'%s\' securely...</string>
<string name="progress_querying">querying...</string>
<!-- permission strings --> <!-- permission strings -->
<string name="permission_read_key_details_label">Read key details from APG.</string> <string name="permission_read_key_details_label">Read key details from APG.</string>

View File

@ -191,6 +191,10 @@ public class Apg {
Pattern.compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*", Pattern.compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
Pattern.DOTALL); Pattern.DOTALL);
public static Pattern PGP_PUBLIC_KEY =
Pattern.compile(".*?(-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----).*",
Pattern.DOTALL);
private static HashMap<Long, CachedPassPhrase> mPassPhraseCache = private static HashMap<Long, CachedPassPhrase> mPassPhraseCache =
new HashMap<Long, CachedPassPhrase>(); new HashMap<Long, CachedPassPhrase>();
private static String mEditPassPhrase = null; private static String mEditPassPhrase = null;
@ -1006,6 +1010,25 @@ public class Apg {
return algorithmStr + ", " + keySize + "bit"; return algorithmStr + ", " + keySize + "bit";
} }
public static String getFingerPrint(long keyId) {
String fingerPrint = Long.toHexString(keyId & 0xffffffffL).toUpperCase();
while (fingerPrint.length() < 8) {
fingerPrint = "0" + fingerPrint;
}
return fingerPrint;
}
public static String keyToHex(long keyId) {
return getFingerPrint(keyId >> 32) + getFingerPrint(keyId);
}
public static long keyFromHex(String data) {
int len = data.length();
String s2 = data.substring(len - 8);
String s1 = data.substring(0, len - 8);
return (Long.parseLong(s1, 16) << 32) | Long.parseLong(s2, 16);
}
public static void deleteKey(int keyRingId) { public static void deleteKey(int keyRingId) {
mDatabase.deleteKeyRing(keyRingId); mDatabase.deleteKeyRing(keyRingId);
} }

View File

@ -157,6 +157,13 @@ public class BaseActivity extends Activity
return mProgressDialog; return mProgressDialog;
} }
case Id.dialog.querying: {
mProgressDialog.setMessage(this.getString(R.string.progress_querying));
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mProgressDialog.setCancelable(false);
return mProgressDialog;
}
default: { default: {
break; break;
} }
@ -362,6 +369,7 @@ public class BaseActivity extends Activity
case Id.message.import_done: // intentionally no break case Id.message.import_done: // intentionally no break
case Id.message.export_done: // intentionally no break case Id.message.export_done: // intentionally no break
case Id.message.query_done: // intentionally no break
case Id.message.done: { case Id.message.done: {
mProgressDialog = null; mProgressDialog = null;
doneCallback(msg); doneCallback(msg);

View File

@ -584,7 +584,7 @@ public class DecryptActivity extends BaseActivity {
if (data.getBoolean(Apg.EXTRA_SIGNATURE)) { if (data.getBoolean(Apg.EXTRA_SIGNATURE)) {
String userId = data.getString(Apg.EXTRA_SIGNATURE_USER_ID); String userId = data.getString(Apg.EXTRA_SIGNATURE_USER_ID);
mSignatureKeyId = data.getLong(Apg.EXTRA_SIGNATURE_KEY_ID); mSignatureKeyId = data.getLong(Apg.EXTRA_SIGNATURE_KEY_ID);
mUserIdRest.setText("id: " + Long.toHexString(mSignatureKeyId & 0xffffffffL)); mUserIdRest.setText("id: " + Apg.getFingerPrint(mSignatureKeyId));
if (userId == null) { if (userId == null) {
userId = getResources().getString(R.string.unknownUserId); userId = getResources().getString(R.string.unknownUserId);
} }

View File

@ -39,7 +39,6 @@ import android.os.Bundle;
import android.os.Message; import android.os.Message;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.widget.Button; import android.widget.Button;

View File

@ -0,0 +1,119 @@
package org.thialfihar.android.apg;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.text.Html;
public class HkpKeyServer extends KeyServer {
private String mHost;
private short mPort = 11371;
// example:
// pub 2048R/<a href="/pks/lookup?op=get&search=0x887DF4BE9F5C9090">9F5C9090</a> 2009-08-17 <a href="/pks/lookup?op=vindex&search=0x887DF4BE9F5C9090">Jörg Runge &lt;joerg@joergrunge.de&gt;</a>
public static Pattern PUB_KEY_LINE =
Pattern.compile("pub +([0-9]+)([a-z]+)/.*?0x([0-9a-z]+).*? +([0-9-]+) +(.+)[\n\r]+((?: +.+[\n\r]+)*)",
Pattern.CASE_INSENSITIVE);
public static Pattern USER_ID_LINE =
Pattern.compile("^ +(.+)$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);
public HkpKeyServer(String host) {
mHost = host;
}
public HkpKeyServer(String host, short port) {
mHost = host;
mPort = port;
}
@Override
List<KeyInfo> search(String query)
throws MalformedURLException, IOException {
Vector<KeyInfo> results = new Vector<KeyInfo>();
String url = "http://" + mHost + ":" + mPort + "/pks/lookup?op=index&search=" +
URLEncoder.encode(query, "utf8");
URL realUrl = new URL(url);
URLConnection conn = realUrl.openConnection();
InputStream is = conn.getInputStream();
ByteArrayOutputStream raw = new ByteArrayOutputStream();
byte buffer[] = new byte[1 << 16];
int n = 0;
while ((n = is.read(buffer)) != -1) {
raw.write(buffer, 0, n);
}
String encoding = conn.getContentEncoding();
if (encoding == null) {
encoding = "utf8";
}
String data = raw.toString(encoding);
Matcher matcher = PUB_KEY_LINE.matcher(data);
while (matcher.find()) {
KeyInfo info = new KeyInfo();
info.size = Integer.parseInt(matcher.group(1));
info.algorithm = matcher.group(2);
info.keyId = Apg.keyFromHex(matcher.group(3));
info.fingerPrint = Apg.getFingerPrint(info.keyId);
String chunks[] = matcher.group(4).split("-");
info.date = new GregorianCalendar(Integer.parseInt(chunks[0]),
Integer.parseInt(chunks[1]),
Integer.parseInt(chunks[2])).getTime();
info.userIds = new Vector<String>();
if (matcher.group(5).startsWith("*** KEY")) {
info.revoked = matcher.group(5);
} else {
String tmp = matcher.group(5).replaceAll("<.*?>", "");
tmp = Html.fromHtml(tmp).toString();
info.userIds.add(tmp);
}
if (matcher.group(6).length() > 0) {
Matcher matcher2 = USER_ID_LINE.matcher(matcher.group(6));
while (matcher2.find()) {
String tmp = matcher2.group(1).replaceAll("<.*?>", "");
tmp = Html.fromHtml(tmp).toString();
info.userIds.add(tmp);
}
}
results.add(info);
}
return results;
}
@Override
String get(long keyId)
throws MalformedURLException, IOException {
String url = "http://" + mHost + ":" + mPort +
"/pks/lookup?op=get&search=0x" + Apg.keyToHex(keyId);
URL realUrl = new URL(url);
URLConnection conn = realUrl.openConnection();
InputStream is = conn.getInputStream();
ByteArrayOutputStream raw = new ByteArrayOutputStream();
byte buffer[] = new byte[1 << 16];
int n = 0;
while ((n = is.read(buffer)) != -1) {
raw.write(buffer, 0, n);
}
String data = raw.toString();
Matcher matcher = Apg.PGP_PUBLIC_KEY.matcher(data);
if (matcher.find()) {
return matcher.group(1);
}
return null;
}
}

View File

@ -35,6 +35,7 @@ public final class Id {
public static final int preferences = 0x21070008; public static final int preferences = 0x21070008;
public static final int search = 0x21070009; public static final int search = 0x21070009;
public static final int help = 0x21070010; public static final int help = 0x21070010;
public static final int key_server = 0x21070011;
} }
} }
@ -48,6 +49,7 @@ public final class Id {
public static final int create_key = 0x21070007; public static final int create_key = 0x21070007;
public static final int edit_key = 0x21070008; public static final int edit_key = 0x21070008;
public static final int delete_done = 0x21070009; public static final int delete_done = 0x21070009;
public static final int query_done = 0x21070010;
} }
public static final class request { public static final class request {
@ -78,6 +80,7 @@ public final class Id {
public static final int delete_file = 0x21070012; public static final int delete_file = 0x21070012;
public static final int deleting = 0x21070013; public static final int deleting = 0x21070013;
public static final int help = 0x21070014; public static final int help = 0x21070014;
public static final int querying = 0x21070015;
} }
public static final class task { public static final class task {
@ -156,4 +159,9 @@ public final class Id {
public static final int encrypted_data = 1; public static final int encrypted_data = 1;
public static final int keys = 2; public static final int keys = 2;
} }
public static final class query {
public static final int search = 0x21070001;
public static final int get = 0x21070002;
}
} }

View File

@ -710,10 +710,7 @@ public class KeyListActivity extends BaseActivity {
} }
TextView keyId = (TextView) view.findViewById(R.id.keyId); TextView keyId = (TextView) view.findViewById(R.id.keyId);
String keyIdStr = Long.toHexString(child.keyId & 0xffffffffL); String keyIdStr = Apg.getFingerPrint(child.keyId);
while (keyIdStr.length() < 8) {
keyIdStr = "0" + keyIdStr;
}
keyId.setText(keyIdStr); keyId.setText(keyIdStr);
TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails); TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
String algorithmStr = Apg.getAlgorithmInfo(child.algorithm, child.keySize); String algorithmStr = Apg.getAlgorithmInfo(child.algorithm, child.keySize);

View File

@ -0,0 +1,23 @@
package org.thialfihar.android.apg;
import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.util.Date;
import java.util.List;
import java.util.Vector;
public abstract class KeyServer {
static public class KeyInfo implements Serializable {
private static final long serialVersionUID = -7797972113284992662L;
Vector<String> userIds;
String revoked;
Date date;
String fingerPrint;
long keyId;
int size;
String algorithm;
}
abstract List<KeyInfo> search(String query) throws MalformedURLException, IOException;
abstract String get(long keyId) throws MalformedURLException, IOException;
}

View File

@ -0,0 +1,246 @@
package org.thialfihar.android.apg;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.List;
import java.util.Vector;
import org.thialfihar.android.apg.KeyServer.KeyInfo;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class KeyServerQueryActivity extends BaseActivity {
private ListView mList;
private EditText mQuery;
private Button mSearch;
private KeyInfoListAdapter mAdapter;
private int mQueryType;
private String mQueryString;
private long mQueryId;
private volatile List<KeyInfo> mSearchResult;
private volatile String mKeyData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.key_server_query_layout);
mQuery = (EditText) findViewById(R.id.query);
mSearch = (Button) findViewById(R.id.btn_search);
mList = (ListView) findViewById(R.id.list);
mAdapter = new KeyInfoListAdapter(this);
mList.setAdapter(mAdapter);
mList.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapter, View view, int position, long keyId) {
get(keyId);
}
});
mSearch.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String query = mQuery.getText().toString();
search(query);
}
});
mQuery.setText("cartman");
}
private void search(String query) {
showDialog(Id.dialog.querying);
mQueryType = Id.query.search;
mQueryString = query;
startThread();
}
private void get(long keyId) {
showDialog(Id.dialog.querying);
mQueryType = Id.query.get;
mQueryId = keyId;
startThread();
}
@Override
public void run() {
String error = null;
Bundle data = new Bundle();
Message msg = new Message();
try {
HkpKeyServer server = new HkpKeyServer("198.128.3.63");//"pool.sks-keyservers.net");
if (mQueryType == Id.query.search) {
mSearchResult = server.search(mQueryString);
} else if (mQueryType == Id.query.get) {
mKeyData = server.get(mQueryId);
}
} catch (MalformedURLException e) {
error = "" + e;
} catch (IOException e) {
error = "" + e;
}
data.putInt(Apg.EXTRA_STATUS, Id.message.done);
if (error != null) {
data.putString(Apg.EXTRA_ERROR, error);
}
msg.setData(data);
sendMessage(msg);
}
@Override
public void doneCallback(Message msg) {
super.doneCallback(msg);
removeDialog(Id.dialog.querying);
Bundle data = msg.getData();
String error = data.getString(Apg.EXTRA_ERROR);
if (error != null) {
Toast.makeText(this, getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();
return;
}
if (mQueryType == Id.query.search) {
if (mSearchResult != null) {
mAdapter.setKeys(mSearchResult);
}
} else if (mQueryType == Id.query.get) {
if (mKeyData != null) {
Intent intent = new Intent(this, PublicKeyListActivity.class);
intent.setAction(Apg.Intent.IMPORT);
intent.putExtra(Apg.EXTRA_TEXT, mKeyData);
startActivity(intent);
}
}
}
public class KeyInfoListAdapter extends BaseAdapter {
protected LayoutInflater mInflater;
protected Activity mActivity;
protected List<KeyInfo> mKeys;
public KeyInfoListAdapter(Activity activity) {
mActivity = activity;
mInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mKeys = new Vector<KeyInfo>();
}
public void setKeys(List<KeyInfo> keys) {
mKeys = keys;
notifyDataSetChanged();
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public int getCount() {
return mKeys.size();
}
@Override
public Object getItem(int position) {
return mKeys.get(position);
}
@Override
public long getItemId(int position) {
return mKeys.get(position).keyId;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
KeyInfo keyInfo = mKeys.get(position);
View view = mInflater.inflate(R.layout.key_server_query_result_item, null);
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
mainUserId.setText(R.string.unknownUserId);
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
mainUserIdRest.setText("");
TextView keyId = (TextView) view.findViewById(R.id.keyId);
keyId.setText(R.string.noKey);
TextView algorithm = (TextView) view.findViewById(R.id.algorithm);
algorithm.setText("");
TextView status = (TextView) view.findViewById(R.id.status);
status.setText("");
String userId = keyInfo.userIds.get(0);
if (userId != null) {
String chunks[] = userId.split(" <", 2);
userId = chunks[0];
if (chunks.length > 1) {
mainUserIdRest.setText("<" + chunks[1]);
}
mainUserId.setText(userId);
}
keyId.setText(Apg.getFingerPrint(keyInfo.keyId));
if (mainUserIdRest.getText().length() == 0) {
mainUserIdRest.setVisibility(View.GONE);
}
algorithm.setText("" + keyInfo.size + "/" + keyInfo.algorithm);
if (keyInfo.revoked != null) {
status.setText("revoked");
} else {
status.setVisibility(View.GONE);
}
LinearLayout ll = (LinearLayout) view.findViewById(R.id.list);
if (keyInfo.userIds.size() == 1) {
ll.setVisibility(View.GONE);
} else {
boolean first = true;
boolean second = true;
for (String uid : keyInfo.userIds) {
if (first) {
first = false;
continue;
}
if (!second) {
View sep = new View(mActivity);
sep.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, 1));
sep.setBackgroundResource(android.R.drawable.divider_horizontal_dark);
ll.addView(sep);
}
TextView uidView = (TextView) mInflater.inflate(R.layout.key_server_query_result_user_id, null);
uidView.setText(uid);
ll.addView(uidView);
second = false;
}
}
return view;
}
}
}

View File

@ -292,12 +292,14 @@ public class MainActivity extends BaseActivity {
menu.add(0, Id.menu.option.manage_secret_keys, 1, R.string.menu_manageSecretKeys) menu.add(0, Id.menu.option.manage_secret_keys, 1, R.string.menu_manageSecretKeys)
.setIcon(android.R.drawable.ic_menu_manage); .setIcon(android.R.drawable.ic_menu_manage);
menu.add(1, Id.menu.option.create, 2, R.string.menu_addAccount) menu.add(1, Id.menu.option.create, 2, R.string.menu_addAccount)
.setIcon(android.R.drawable.ic_menu_add); .setIcon(android.R.drawable.ic_menu_add);
menu.add(2, Id.menu.option.preferences, 3, R.string.menu_preferences) menu.add(2, Id.menu.option.preferences, 3, R.string.menu_preferences)
.setIcon(android.R.drawable.ic_menu_preferences); .setIcon(android.R.drawable.ic_menu_preferences);
menu.add(2, Id.menu.option.about, 4, R.string.menu_about) menu.add(2, Id.menu.option.key_server, 4, R.string.menu_keyServer)
.setIcon(android.R.drawable.ic_menu_search);
menu.add(3, Id.menu.option.about, 5, R.string.menu_about)
.setIcon(android.R.drawable.ic_menu_info_details); .setIcon(android.R.drawable.ic_menu_info_details);
menu.add(22, Id.menu.option.help, 4, R.string.menu_help) menu.add(3, Id.menu.option.help, 6, R.string.menu_help)
.setIcon(android.R.drawable.ic_menu_help); .setIcon(android.R.drawable.ic_menu_help);
return true; return true;
} }
@ -325,6 +327,11 @@ public class MainActivity extends BaseActivity {
return true; return true;
} }
case Id.menu.option.key_server: {
startActivity(new Intent(this, KeyServerQueryActivity.class));
return true;
}
default: { default: {
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }

View File

@ -187,7 +187,7 @@ public class SelectPublicKeyListAdapter extends BaseAdapter {
} }
long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID
keyId.setText("" + Long.toHexString(masterKeyId & 0xffffffffL)); keyId.setText(Apg.getFingerPrint(masterKeyId));
if (mainUserIdRest.getText().length() == 0) { if (mainUserIdRest.getText().length() == 0) {
mainUserIdRest.setVisibility(View.GONE); mainUserIdRest.setVisibility(View.GONE);

View File

@ -146,7 +146,7 @@ public class SelectSecretKeyListAdapter extends BaseAdapter {
} }
long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID
keyId.setText("" + Long.toHexString(masterKeyId & 0xffffffffL)); keyId.setText(Apg.getFingerPrint(masterKeyId));
if (mainUserIdRest.getText().length() == 0) { if (mainUserIdRest.getText().length() == 0) {
mainUserIdRest.setVisibility(View.GONE); mainUserIdRest.setVisibility(View.GONE);

View File

@ -141,14 +141,8 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
} }
mAlgorithm.setText(Apg.getAlgorithmInfo(key)); mAlgorithm.setText(Apg.getAlgorithmInfo(key));
String keyId1Str = Long.toHexString((key.getKeyID() >> 32) & 0xffffffffL); String keyId1Str = Apg.getFingerPrint(key.getKeyID());
while (keyId1Str.length() < 8) { String keyId2Str = Apg.getFingerPrint(key.getKeyID() >> 32);
keyId1Str = "0" + keyId1Str;
}
String keyId2Str = Long.toHexString(key.getKeyID() & 0xffffffffL);
while (keyId2Str.length() < 8) {
keyId2Str = "0" + keyId2Str;
}
mKeyId.setText(keyId1Str + " " + keyId2Str); mKeyId.setText(keyId1Str + " " + keyId2Str);
Vector<Choice> choices = new Vector<Choice>(); Vector<Choice> choices = new Vector<Choice>();