added search feature for key management and select Activities

This commit is contained in:
Thialfihar 2010-06-05 21:47:16 +00:00
parent a089dbbb73
commit 84d00abea1
20 changed files with 486 additions and 75 deletions

View File

@ -38,12 +38,34 @@
<activity <activity
android:name=".PublicKeyListActivity" android:name=".PublicKeyListActivity"
android:label="@string/title_managePublicKeys" android:label="@string/title_managePublicKeys"
android:configChanges="keyboardHidden|orientation|keyboard" /> android:configChanges="keyboardHidden|orientation|keyboard"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable_public_keys"/>
</activity>
<activity <activity
android:name=".SecretKeyListActivity" android:name=".SecretKeyListActivity"
android:label="@string/title_manageSecretKeys" android:label="@string/title_manageSecretKeys"
android:configChanges="keyboardHidden|orientation|keyboard"/> android:configChanges="keyboardHidden|orientation|keyboard"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable_secret_keys"/>
</activity>
<activity <activity
android:name=".EditKeyActivity" android:name=".EditKeyActivity"
@ -53,7 +75,8 @@
<activity <activity
android:name=".SelectPublicKeyListActivity" android:name=".SelectPublicKeyListActivity"
android:label="@string/title_selectRecipients" android:label="@string/title_selectRecipients"
android:configChanges="keyboardHidden|orientation|keyboard"> android:configChanges="keyboardHidden|orientation|keyboard"
android:launchMode="singleTop">
<intent-filter> <intent-filter>
<action android:name="org.thialfihar.android.apg.intent.SELECT_PUBLIC_KEYS" /> <action android:name="org.thialfihar.android.apg.intent.SELECT_PUBLIC_KEYS" />
@ -61,12 +84,21 @@
<data android:mimeType="text/*"/> <data android:mimeType="text/*"/>
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable_public_keys"/>
</activity> </activity>
<activity <activity
android:name=".SelectSecretKeyListActivity" android:name=".SelectSecretKeyListActivity"
android:label="@string/title_selectSignature" android:label="@string/title_selectSignature"
android:configChanges="keyboardHidden|orientation|keyboard"> android:configChanges="keyboardHidden|orientation|keyboard"
android:launchMode="singleTop">
<intent-filter> <intent-filter>
<action android:name="org.thialfihar.android.apg.intent.SELECT_SECRET_KEY" /> <action android:name="org.thialfihar.android.apg.intent.SELECT_SECRET_KEY" />
@ -74,6 +106,14 @@
<data android:mimeType="text/*"/> <data android:mimeType="text/*"/>
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable_secret_keys"/>
</activity> </activity>
<activity <activity

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingBottom="5dip"
android:orientation="horizontal">
<TextView
android:id="@+id/filterInfo"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAppearance="?android:attr/textAppearanceMedium"
android:ellipsize="end"/>
<Button
android:id="@+id/btn_clear"
android:text="@string/btn_clearFilter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>

View File

@ -14,15 +14,18 @@
limitations under the License. limitations under the License.
--> -->
<ScrollView <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:fillViewport="true"> android:orientation="vertical">
<include android:id="@+id/layout_filter" layout="@layout/filter_info"/>
<ExpandableListView <ExpandableListView
android:id="@+id/list" android:id="@+id/list"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent"/> android:layout_height="fill_parent"
android:isScrollContainer="true"/>
</ScrollView> </LinearLayout>

View File

@ -21,6 +21,8 @@
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:fillViewport="true"> android:fillViewport="true">
<include android:id="@+id/layout_filter" layout="@layout/filter_info"/>
<ListView <ListView
android:id="@+id/list" android:id="@+id/list"
android:choiceMode="multipleChoice" android:choiceMode="multipleChoice"

View File

@ -21,6 +21,8 @@
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:fillViewport="true"> android:fillViewport="true">
<include android:id="@+id/layout_filter" layout="@layout/filter_info"/>
<ListView <ListView
android:id="@+id/list" android:id="@+id/list"
android:layout_width="fill_parent" android:layout_width="fill_parent"

View File

@ -62,6 +62,7 @@
<string name="btn_doNotSave">Cancel</string> <string name="btn_doNotSave">Cancel</string>
<string name="btn_delete">Delete</string> <string name="btn_delete">Delete</string>
<string name="btn_noDate">None</string> <string name="btn_noDate">None</string>
<string name="btn_clearFilter">Clear Filter</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>
@ -239,4 +240,14 @@
<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>
<!-- action strings -->
<string name="action_encrypt">Encrypt</string>
<string name="action_decrypt">Decrypt</string>
<string name="action_importPublic">Import Public Keys</string>
<string name="action_importSecret">Import Secret Keys</string>
<string name="hint_publicKeys">Search Public Keys</string>
<string name="hint_secretKeys">Search Secret Keys</string>
<string name="filterInfo">Filter: \"%s\"</string>
</resources> </resources>

View File

@ -62,6 +62,7 @@
<string name="btn_doNotSave">Cancel</string> <string name="btn_doNotSave">Cancel</string>
<string name="btn_delete">Delete</string> <string name="btn_delete">Delete</string>
<string name="btn_noDate">None</string> <string name="btn_noDate">None</string>
<string name="btn_clearFilter">Clear Filter</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>
@ -239,4 +240,14 @@
<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>
<!-- action strings -->
<string name="action_encrypt">Encrypt</string>
<string name="action_decrypt">Decrypt</string>
<string name="action_importPublic">Import Public Keys</string>
<string name="action_importSecret">Import Secret Keys</string>
<string name="hint_publicKeys">Search Public Keys</string>
<string name="hint_secretKeys">Search Secret Keys</string>
<string name="filterInfo">Filter: \"%s\"</string>
</resources> </resources>

View File

@ -62,6 +62,7 @@
<string name="btn_doNotSave">Cancel</string> <string name="btn_doNotSave">Cancel</string>
<string name="btn_delete">Delete</string> <string name="btn_delete">Delete</string>
<string name="btn_noDate">None</string> <string name="btn_noDate">None</string>
<string name="btn_clearFilter">Clear Filter</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>
@ -239,4 +240,14 @@
<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>
<!-- action strings -->
<string name="action_encrypt">Encrypt</string>
<string name="action_decrypt">Decrypt</string>
<string name="action_importPublic">Import Public Keys</string>
<string name="action_importSecret">Import Secret Keys</string>
<string name="hint_publicKeys">Search Public Keys</string>
<string name="hint_secretKeys">Search Secret Keys</string>
<string name="filterInfo">Filter: \"%s\"</string>
</resources> </resources>

View File

@ -62,6 +62,7 @@
<string name="btn_doNotSave">Prekliči</string> <string name="btn_doNotSave">Prekliči</string>
<string name="btn_delete">Izbriši</string> <string name="btn_delete">Izbriši</string>
<string name="btn_noDate">Brez</string> <string name="btn_noDate">Brez</string>
<string name="btn_clearFilter">Clear Filter</string>
<!-- menu_lowerCase: capitalized words, no punctuation --> <!-- menu_lowerCase: capitalized words, no punctuation -->
<string name="menu_about">O programu</string> <string name="menu_about">O programu</string>
@ -239,4 +240,14 @@
<string name="progress_decompressingData">raztezam podatke...</string> <string name="progress_decompressingData">raztezam podatke...</string>
<string name="progress_verifyingIntegrity">overovljam integriteto...</string> <string name="progress_verifyingIntegrity">overovljam integriteto...</string>
<!-- action strings -->
<string name="action_encrypt">Encrypt</string>
<string name="action_decrypt">Decrypt</string>
<string name="action_importPublic">Import Public Keys</string>
<string name="action_importSecret">Import Secret Keys</string>
<string name="hint_publicKeys">Search Public Keys</string>
<string name="hint_secretKeys">Search Secret Keys</string>
<string name="filterInfo">Filter: \"%s\"</string>
</resources> </resources>

View File

@ -62,6 +62,7 @@
<string name="btn_doNotSave">Cancel</string> <string name="btn_doNotSave">Cancel</string>
<string name="btn_delete">Delete</string> <string name="btn_delete">Delete</string>
<string name="btn_noDate">None</string> <string name="btn_noDate">None</string>
<string name="btn_clearFilter">Clear Filter</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>
@ -246,9 +247,12 @@
<!-- action strings --> <!-- action strings -->
<string name="action_encrypt">Encrypt</string> <string name="action_encrypt">Encrypt</string>
<string name="action_decrypt">Decrypt</string> <string name="action_decrypt">Decrypt</string>
<string name="action_import_public">Import Public Keys</string> <string name="action_importPublic">Import Public Keys</string>
<string name="action_import_secret">Import Secret Keys</string> <string name="action_importSecret">Import Secret Keys</string>
<string name="hint_publicKeys">Search Public Keys</string>
<string name="hint_secretKeys">Search Secret Keys</string>
<string name="filterInfo">Filter: \"%s\"</string>
</resources> </resources>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<searchable
xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/app_name"
android:hint="@string/hint_publicKeys">
</searchable>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<searchable
xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/app_name"
android:hint="@string/hint_secretKeys">
</searchable>

View File

@ -79,6 +79,7 @@ import org.bouncycastle2.openpgp.PGPSignatureList;
import org.bouncycastle2.openpgp.PGPSignatureSubpacketGenerator; import org.bouncycastle2.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle2.openpgp.PGPSignatureSubpacketVector; import org.bouncycastle2.openpgp.PGPSignatureSubpacketVector;
import org.bouncycastle2.openpgp.PGPUtil; import org.bouncycastle2.openpgp.PGPUtil;
import org.thialfihar.android.apg.provider.DataProvider;
import org.thialfihar.android.apg.provider.Database; import org.thialfihar.android.apg.provider.Database;
import org.thialfihar.android.apg.provider.KeyRings; import org.thialfihar.android.apg.provider.KeyRings;
import org.thialfihar.android.apg.provider.Keys; import org.thialfihar.android.apg.provider.Keys;
@ -92,6 +93,7 @@ import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -130,6 +132,18 @@ public class Apg {
public static final String EXTRA_MAX = "max"; public static final String EXTRA_MAX = "max";
public static final String EXTRA_ACCOUNT = "account"; public static final String EXTRA_ACCOUNT = "account";
public static final String AUTHORITY = DataProvider.AUTHORITY;
public static final Uri CONTENT_URI_SECRET_KEY_RINGS =
Uri.parse("content://" + AUTHORITY + "/key_rings/secret/");
public static final Uri CONTENT_URI_SECRET_KEY_RING_BY_KEY_ID =
Uri.parse("content://" + AUTHORITY + "/key_rings/secret/key_id/");
public static final Uri CONTENT_URI_PUBLIC_KEY_RINGS =
Uri.parse("content://" + AUTHORITY + "/key_rings/public/");
public static final Uri CONTENT_URI_PUBLIC_KEY_RING_BY_KEY_ID =
Uri.parse("content://" + AUTHORITY + "/key_rings/public/key_id/");
public static String VERSION = "1.0.1"; public static String VERSION = "1.0.1";
public static String FULL_VERSION = "APG v" + VERSION; public static String FULL_VERSION = "APG v" + VERSION;

View File

@ -66,8 +66,8 @@ public class GeneralActivity extends BaseActivity {
Vector<Choice> choices = new Vector<Choice>(); Vector<Choice> choices = new Vector<Choice>();
if (containsKeys) { if (containsKeys) {
choices.add(new Choice(Id.choice.action.import_public, getString(R.string.action_import_public))); choices.add(new Choice(Id.choice.action.import_public, getString(R.string.action_importPublic)));
choices.add(new Choice(Id.choice.action.import_secret, getString(R.string.action_import_secret))); choices.add(new Choice(Id.choice.action.import_secret, getString(R.string.action_importSecret)));
} }
if (isEncrypted) { if (isEncrypted) {

View File

@ -29,11 +29,13 @@ import org.thialfihar.android.apg.provider.UserIds;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
import android.app.SearchManager;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Message; import android.os.Message;
@ -41,7 +43,9 @@ import android.view.LayoutInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.BaseExpandableListAdapter; import android.widget.BaseExpandableListAdapter;
import android.widget.Button;
import android.widget.ExpandableListView; import android.widget.ExpandableListView;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
@ -51,6 +55,9 @@ import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
public class KeyListActivity extends BaseActivity { public class KeyListActivity extends BaseActivity {
protected ExpandableListView mList; protected ExpandableListView mList;
protected KeyListAdapter mListAdapter; protected KeyListAdapter mListAdapter;
protected View mFilterLayout;
protected Button mClearFilterButton;
protected TextView mFilterInfo;
protected int mSelectedItem = -1; protected int mSelectedItem = -1;
protected int mTask = 0; protected int mTask = 0;
@ -66,9 +73,49 @@ public class KeyListActivity extends BaseActivity {
setContentView(R.layout.key_list); setContentView(R.layout.key_list);
mList = (ExpandableListView) findViewById(R.id.list); mList = (ExpandableListView) findViewById(R.id.list);
mListAdapter = new KeyListAdapter(this);
mList.setAdapter(mListAdapter);
registerForContextMenu(mList); registerForContextMenu(mList);
mFilterLayout = (View) findViewById(R.id.layout_filter);
mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo);
mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear);
mClearFilterButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
handleIntent(new Intent());
}
});
handleIntent(getIntent());
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
handleIntent(intent);
}
private void handleIntent(Intent intent) {
String searchString = null;
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
searchString = intent.getStringExtra(SearchManager.QUERY);
if (searchString != null && searchString.trim().length() == 0) {
searchString = null;
}
}
if (searchString == null) {
mFilterLayout.setVisibility(View.GONE);
} else {
mFilterLayout.setVisibility(View.VISIBLE);
mFilterInfo.setText(getString(R.string.filterInfo, searchString));
}
if (mListAdapter != null) {
mListAdapter.cleanup();
}
mListAdapter = new KeyListAdapter(this, searchString);
mList.setAdapter(mListAdapter);
} }
@Override @Override
@ -371,6 +418,7 @@ public class KeyListActivity extends BaseActivity {
private Vector<Vector<KeyChild>> mChildren; private Vector<Vector<KeyChild>> mChildren;
private SQLiteDatabase mDatabase; private SQLiteDatabase mDatabase;
private Cursor mCursor; private Cursor mCursor;
private String mSearchString;
private class KeyChild { private class KeyChild {
public static final int KEY = 0; public static final int KEY = 0;
@ -401,11 +449,13 @@ public class KeyListActivity extends BaseActivity {
} }
} }
public KeyListAdapter(Context context) { public KeyListAdapter(Context context, String searchString) {
mSearchString = searchString;
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mDatabase = Apg.getDatabase().db(); mDatabase = Apg.getDatabase().db();
mCursor = mDatabase.query( SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " + qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
"(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " + Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" + Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
@ -413,7 +463,32 @@ public class KeyListActivity extends BaseActivity {
" INNER JOIN " + UserIds.TABLE_NAME + " ON " + " INNER JOIN " + UserIds.TABLE_NAME + " ON " +
"(" + Keys.TABLE_NAME + "." + Keys._ID + " = " + "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " + UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ", UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0')");
if (searchString != null && searchString.trim().length() > 0) {
String[] chunks = searchString.trim().split(" +");
qb.appendWhere("EXISTS (SELECT tmp." + UserIds._ID + " FROM " +
UserIds.TABLE_NAME + " AS tmp WHERE " +
"tmp." + UserIds.KEY_ID + " = " +
Keys.TABLE_NAME + "." + Keys._ID);
for (int i = 0; i < chunks.length; ++i) {
qb.appendWhere(" AND tmp." + UserIds.USER_ID + " LIKE ");
qb.appendWhereEscapeString("%" + chunks[i] + "%");
}
qb.appendWhere(")");
}
String query = qb.buildQuery(new String[] {
KeyRings.TABLE_NAME + "." + KeyRings._ID, // 0
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1
UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 2
},
KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
new String[] { "" + (mKeyType == Id.type.public_key ?
Id.database.type_public : Id.database.type_secret) },
null, null, UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC", null);
mCursor = qb.query(mDatabase,
new String[] { new String[] {
KeyRings.TABLE_NAME + "." + KeyRings._ID, // 0 KeyRings.TABLE_NAME + "." + KeyRings._ID, // 0
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1 KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1
@ -424,10 +499,33 @@ public class KeyListActivity extends BaseActivity {
Id.database.type_public : Id.database.type_secret) }, Id.database.type_public : Id.database.type_secret) },
null, null, UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC"); null, null, UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC");
// content provider way for reference, might have to go back to it sometime:
/*Uri contentUri = null;
if (mKeyType == Id.type.secret_key) {
contentUri = Apg.CONTENT_URI_SECRET_KEY_RINGS;
} else {
contentUri = Apg.CONTENT_URI_PUBLIC_KEY_RINGS;
}
mCursor = getContentResolver().query(
contentUri,
new String[] {
DataProvider._ID, // 0
DataProvider.MASTER_KEY_ID, // 1
DataProvider.USER_ID, // 2
},
null, null, null);*/
startManagingCursor(mCursor); startManagingCursor(mCursor);
rebuild(false); rebuild(false);
} }
public void cleanup() {
if (mCursor != null) {
stopManagingCursor(mCursor);
mCursor.close();
}
}
public void rebuild(boolean requery) { public void rebuild(boolean requery) {
if (requery) { if (requery) {
mCursor.requery(); mCursor.requery();

View File

@ -18,50 +18,32 @@ package org.thialfihar.android.apg;
import java.util.Vector; import java.util.Vector;
import android.app.SearchManager;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
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;
import android.widget.ListView; import android.widget.ListView;
import android.widget.TextView;
public class SelectPublicKeyListActivity extends BaseActivity { public class SelectPublicKeyListActivity extends BaseActivity {
protected Intent mIntent;
protected ListView mList; protected ListView mList;
protected SelectPublicKeyListAdapter mListAdapter;
protected View mFilterLayout;
protected Button mClearFilterButton;
protected TextView mFilterInfo;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.select_public_key); setContentView(R.layout.select_public_key);
// fill things
mIntent = getIntent();
long selectedKeyIds[] = null;
if (mIntent.getExtras() != null) {
selectedKeyIds = mIntent.getExtras().getLongArray(Apg.EXTRA_SELECTION);
}
mList = (ListView) findViewById(R.id.list); mList = (ListView) findViewById(R.id.list);
// needed in Android 1.5, where the XML attribute gets ignored // needed in Android 1.5, where the XML attribute gets ignored
mList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); mList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
SelectPublicKeyListAdapter adapter = new SelectPublicKeyListAdapter(this, mList);
mList.setAdapter(adapter);
if (selectedKeyIds != null) {
for (int i = 0; i < adapter.getCount(); ++i) {
long keyId = adapter.getItemId(i);
for (int j = 0; j < selectedKeyIds.length; ++j) {
if (keyId == selectedKeyIds[j]) {
mList.setItemChecked(i, true);
break;
}
}
}
}
Button okButton = (Button) findViewById(R.id.btn_ok); Button okButton = (Button) findViewById(R.id.btn_ok);
okButton.setOnClickListener(new OnClickListener() { okButton.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@ -70,13 +52,85 @@ public class SelectPublicKeyListActivity extends BaseActivity {
}); });
Button cancelButton = (Button) findViewById(R.id.btn_cancel); Button cancelButton = (Button) findViewById(R.id.btn_cancel);
cancelButton.setOnClickListener(new OnClickListener() { cancelButton.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
cancelClicked(); cancelClicked();
} }
}); });
mFilterLayout = (View) findViewById(R.id.layout_filter);
mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo);
mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear);
mClearFilterButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
handleIntent(new Intent());
}
});
handleIntent(getIntent());
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
handleIntent(intent);
}
private void handleIntent(Intent intent) {
String searchString = null;
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
searchString = intent.getStringExtra(SearchManager.QUERY);
if (searchString != null && searchString.trim().length() == 0) {
searchString = null;
}
}
long selectedKeyIds[] = null;
if (getIntent().getExtras() != null) {
selectedKeyIds = getIntent().getExtras().getLongArray(Apg.EXTRA_SELECTION);
}
if (selectedKeyIds == null) {
Vector<Long> vector = new Vector<Long>();
for (int i = 0; i < mList.getCount(); ++i) {
if (mList.isItemChecked(i)) {
vector.add(mList.getItemIdAtPosition(i));
}
}
selectedKeyIds = new long[vector.size()];
for (int i = 0; i < vector.size(); ++i) {
selectedKeyIds[i] = vector.get(i);
}
}
if (searchString == null) {
mFilterLayout.setVisibility(View.GONE);
} else {
mFilterLayout.setVisibility(View.VISIBLE);
mFilterInfo.setText(getString(R.string.filterInfo, searchString));
}
if (mListAdapter != null) {
mListAdapter.cleanup();
}
mListAdapter = new SelectPublicKeyListAdapter(this, mList, searchString, selectedKeyIds);
mList.setAdapter(mListAdapter);
if (selectedKeyIds != null) {
for (int i = 0; i < mListAdapter.getCount(); ++i) {
long keyId = mListAdapter.getItemId(i);
for (int j = 0; j < selectedKeyIds.length; ++j) {
if (keyId == selectedKeyIds[j]) {
mList.setItemChecked(i, true);
break;
}
}
}
}
} }
private void cancelClicked() { private void cancelClicked() {

View File

@ -26,6 +26,7 @@ import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -39,14 +40,20 @@ public class SelectPublicKeyListAdapter extends BaseAdapter {
protected ListView mParent; protected ListView mParent;
protected SQLiteDatabase mDatabase; protected SQLiteDatabase mDatabase;
protected Cursor mCursor; protected Cursor mCursor;
protected String mSearchString;
protected Activity mActivity;
public SelectPublicKeyListAdapter(Activity activity, ListView parent) { public SelectPublicKeyListAdapter(Activity activity, ListView parent,
String searchString, long selectedKeyIds[]) {
mSearchString = searchString;
mActivity = activity;
mParent = parent; mParent = parent;
mDatabase = Apg.getDatabase().db(); mDatabase = Apg.getDatabase().db();
mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
long now = new Date().getTime() / 1000; long now = new Date().getTime() / 1000;
mCursor = mDatabase.query( SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " + qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
"(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " + Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" + Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
@ -54,7 +61,36 @@ public class SelectPublicKeyListAdapter extends BaseAdapter {
" INNER JOIN " + UserIds.TABLE_NAME + " ON " + " INNER JOIN " + UserIds.TABLE_NAME + " ON " +
"(" + Keys.TABLE_NAME + "." + Keys._ID + " = " + "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " + UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ", UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
if (searchString != null && searchString.trim().length() > 0) {
String[] chunks = searchString.trim().split(" +");
qb.appendWhere("(EXISTS (SELECT tmp." + UserIds._ID + " FROM " +
UserIds.TABLE_NAME + " AS tmp WHERE " +
"tmp." + UserIds.KEY_ID + " = " +
Keys.TABLE_NAME + "." + Keys._ID);
for (int i = 0; i < chunks.length; ++i) {
qb.appendWhere(" AND tmp." + UserIds.USER_ID + " LIKE ");
qb.appendWhereEscapeString("%" + chunks[i] + "%");
}
qb.appendWhere("))");
if (selectedKeyIds != null && selectedKeyIds.length > 0) {
qb.appendWhere(" OR ");
qb.appendWhere("(" + KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID +
" IN (");
for (int i = 0; i < selectedKeyIds.length; ++i) {
if (i != 0) {
qb.appendWhere(", ");
}
qb.appendWhereEscapeString("" + selectedKeyIds[i]);
}
qb.appendWhere("))");
}
}
mCursor = qb.query(mDatabase,
new String[] { new String[] {
KeyRings.TABLE_NAME + "." + KeyRings._ID, // 0 KeyRings.TABLE_NAME + "." + KeyRings._ID, // 0
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1 KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1
@ -80,6 +116,13 @@ public class SelectPublicKeyListAdapter extends BaseAdapter {
activity.startManagingCursor(mCursor); activity.startManagingCursor(mCursor);
} }
public void cleanup() {
if (mCursor != null) {
mActivity.stopManagingCursor(mCursor);
mCursor.close();
}
}
@Override @Override
public boolean isEnabled(int position) { public boolean isEnabled(int position) {
mCursor.moveToPosition(position); mCursor.moveToPosition(position);

View File

@ -16,16 +16,23 @@
package org.thialfihar.android.apg; package org.thialfihar.android.apg;
import android.app.SearchManager;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView; import android.widget.ListView;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemClickListener;
public class SelectSecretKeyListActivity extends BaseActivity { public class SelectSecretKeyListActivity extends BaseActivity {
protected ListView mList; protected ListView mList;
protected SelectSecretKeyListAdapter mListAdapter; protected SelectSecretKeyListAdapter mListAdapter;
protected View mFilterLayout;
protected Button mClearFilterButton;
protected TextView mFilterInfo;
protected long mSelectedKeyId = 0; protected long mSelectedKeyId = 0;
@ -36,8 +43,6 @@ public class SelectSecretKeyListActivity extends BaseActivity {
setContentView(R.layout.select_secret_key); setContentView(R.layout.select_secret_key);
mList = (ListView) findViewById(R.id.list); mList = (ListView) findViewById(R.id.list);
mListAdapter = new SelectSecretKeyListAdapter(this, mList);
mList.setAdapter(mListAdapter);
mList.setOnItemClickListener(new OnItemClickListener() { mList.setOnItemClickListener(new OnItemClickListener() {
@Override @Override
@ -48,5 +53,48 @@ public class SelectSecretKeyListActivity extends BaseActivity {
finish(); finish();
} }
}); });
mFilterLayout = (View) findViewById(R.id.layout_filter);
mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo);
mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear);
mClearFilterButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
handleIntent(new Intent());
}
});
handleIntent(getIntent());
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
handleIntent(intent);
}
private void handleIntent(Intent intent) {
String searchString = null;
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
searchString = intent.getStringExtra(SearchManager.QUERY);
if (searchString != null && searchString.trim().length() == 0) {
searchString = null;
}
}
if (searchString == null) {
mFilterLayout.setVisibility(View.GONE);
} else {
mFilterLayout.setVisibility(View.VISIBLE);
mFilterInfo.setText(getString(R.string.filterInfo, searchString));
}
if (mListAdapter != null) {
mListAdapter.cleanup();
}
mListAdapter = new SelectSecretKeyListAdapter(this, mList, searchString);
mList.setAdapter(mListAdapter);
} }
} }

View File

@ -10,6 +10,7 @@ import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -22,14 +23,19 @@ public class SelectSecretKeyListAdapter extends BaseAdapter {
protected ListView mParent; protected ListView mParent;
protected SQLiteDatabase mDatabase; protected SQLiteDatabase mDatabase;
protected Cursor mCursor; protected Cursor mCursor;
protected String mSearchString;
protected Activity mActivity;
public SelectSecretKeyListAdapter(Activity activity, ListView parent) { public SelectSecretKeyListAdapter(Activity activity, ListView parent, String searchString) {
mSearchString = searchString;
mActivity = activity;
mParent = parent; mParent = parent;
mDatabase = Apg.getDatabase().db(); mDatabase = Apg.getDatabase().db();
mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
long now = new Date().getTime() / 1000; long now = new Date().getTime() / 1000;
mCursor = mDatabase.query( SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " + qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
"(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " + Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" + Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
@ -37,7 +43,22 @@ public class SelectSecretKeyListAdapter extends BaseAdapter {
" INNER JOIN " + UserIds.TABLE_NAME + " ON " + " INNER JOIN " + UserIds.TABLE_NAME + " ON " +
"(" + Keys.TABLE_NAME + "." + Keys._ID + " = " + "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " + UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ", UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
if (searchString != null && searchString.trim().length() > 0) {
String[] chunks = searchString.trim().split(" +");
qb.appendWhere("EXISTS (SELECT tmp." + UserIds._ID + " FROM " +
UserIds.TABLE_NAME + " AS tmp WHERE " +
"tmp." + UserIds.KEY_ID + " = " +
Keys.TABLE_NAME + "." + Keys._ID);
for (int i = 0; i < chunks.length; ++i) {
qb.appendWhere(" AND tmp." + UserIds.USER_ID + " LIKE ");
qb.appendWhereEscapeString("%" + chunks[i] + "%");
}
qb.appendWhere(")");
}
mCursor = qb.query(mDatabase,
new String[] { new String[] {
KeyRings.TABLE_NAME + "." + KeyRings._ID, // 0 KeyRings.TABLE_NAME + "." + KeyRings._ID, // 0
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1 KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1
@ -63,6 +84,13 @@ public class SelectSecretKeyListAdapter extends BaseAdapter {
activity.startManagingCursor(mCursor); activity.startManagingCursor(mCursor);
} }
public void cleanup() {
if (mCursor != null) {
mActivity.stopManagingCursor(mCursor);
mCursor.close();
}
}
@Override @Override
public boolean isEnabled(int position) { public boolean isEnabled(int position) {
mCursor.moveToPosition(position); mCursor.moveToPosition(position);

View File

@ -72,6 +72,7 @@ public class DataProvider extends ContentProvider {
private static final String USER_ID_CONTENT_ITEM_TYPE = private static final String USER_ID_CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/vnd.thialfihar.apg.user_id"; "vnd.android.cursor.item/vnd.thialfihar.apg.user_id";
public static final String _ID = "_id";
public static final String MASTER_KEY_ID = "master_key_id"; public static final String MASTER_KEY_ID = "master_key_id";
public static final String KEY_ID = "key_id"; public static final String KEY_ID = "key_id";
public static final String USER_ID = "user_id"; public static final String USER_ID = "user_id";
@ -117,6 +118,7 @@ public class DataProvider extends ContentProvider {
// TODO: implement the others, then use them for the lists // TODO: implement the others, then use them for the lists
SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
HashMap<String, String> projectionMap = new HashMap<String, String>(); HashMap<String, String> projectionMap = new HashMap<String, String>();
int match = mUriMatcher.match(uri); int match = mUriMatcher.match(uri);
int type; int type;
switch (match) { switch (match) {
@ -148,6 +150,15 @@ public class DataProvider extends ContentProvider {
qb.appendWhere(KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = " + type); qb.appendWhere(KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = " + type);
switch (match) { switch (match) {
case PUBLIC_KEY_RING_ID:
case SECRET_KEY_RING_ID: {
qb.appendWhere(" AND " +
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(2));
// break omitted intentionally
}
case PUBLIC_KEY_RINGS: case PUBLIC_KEY_RINGS:
case SECRET_KEY_RINGS: { case SECRET_KEY_RINGS: {
qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " + qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
@ -160,34 +171,17 @@ public class DataProvider extends ContentProvider {
UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " + UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') "); UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
projectionMap.put(_ID,
KeyRings.TABLE_NAME + "." + KeyRings._ID);
projectionMap.put(MASTER_KEY_ID, projectionMap.put(MASTER_KEY_ID,
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID); KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID);
projectionMap.put(USER_ID, projectionMap.put(USER_ID,
UserIds.TABLE_NAME + "." + UserIds.USER_ID); UserIds.TABLE_NAME + "." + UserIds.USER_ID);
break; if (TextUtils.isEmpty(sortOrder)) {
sortOrder = UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC";
} }
case PUBLIC_KEY_RING_ID:
case SECRET_KEY_RING_ID: {
qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
"(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
") " +
" INNER JOIN " + UserIds.TABLE_NAME + " ON " +
"(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
projectionMap.put(MASTER_KEY_ID,
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID);
projectionMap.put(USER_ID,
UserIds.TABLE_NAME + "." + UserIds.USER_ID);
qb.appendWhere(" AND " +
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(2));
break; break;
} }
@ -207,6 +201,8 @@ public class DataProvider extends ContentProvider {
UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " + UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') "); UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
projectionMap.put(_ID,
KeyRings.TABLE_NAME + "." + KeyRings._ID);
projectionMap.put(MASTER_KEY_ID, projectionMap.put(MASTER_KEY_ID,
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID); KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID);
projectionMap.put(USER_ID, projectionMap.put(USER_ID,