diff options
Diffstat (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui')
63 files changed, 2823 insertions, 2339 deletions
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java index aab6b81d9..5dc06c16d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java @@ -17,47 +17,47 @@ package org.sufficientlysecure.keychain.ui; -import java.util.Iterator; - -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.spongycastle.openpgp.PGPSignature; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.Preferences; -import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.service.PassphraseCacheService; -import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; -import org.sufficientlysecure.keychain.util.Log; - import android.app.ProgressDialog; import android.content.Intent; +import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Messenger; +import android.support.v4.app.LoaderManager; +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.View; import android.view.View.OnClickListener; -import android.widget.ArrayAdapter; -import android.widget.CheckBox; -import android.widget.CompoundButton; +import android.widget.*; import android.widget.CompoundButton.OnCheckedChangeListener; -import android.widget.Spinner; -import android.widget.Toast; - import com.beardedhen.androidbootstrap.BootstrapButton; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.OtherHelper; +import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +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.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.ui.adapter.ViewKeyUserIdsAdapter; +import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; +import org.sufficientlysecure.keychain.util.Log; + +import java.util.ArrayList; /** * Signs the specified public key with the specified secret master key */ public class CertifyKeyActivity extends ActionBarActivity implements - SelectSecretKeyLayoutFragment.SelectSecretKeyCallback { + SelectSecretKeyLayoutFragment.SelectSecretKeyCallback, LoaderManager.LoaderCallbacks<Cursor> { private BootstrapButton mSignButton; private CheckBox mUploadKeyCheckbox; private Spinner mSelectKeyserverSpinner; @@ -68,6 +68,12 @@ public class CertifyKeyActivity extends ActionBarActivity implements private long mPubKeyId = 0; private long mMasterKeyId = 0; + private ListView mUserIds; + private ViewKeyUserIdsAdapter mUserIdsAdapter; + + private static final int LOADER_ID_KEYRING = 0; + private static final int LOADER_ID_USER_IDS = 1; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -87,7 +93,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements mSelectKeyserverSpinner = (Spinner) findViewById(R.id.sign_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); @@ -131,9 +137,18 @@ public class CertifyKeyActivity extends ActionBarActivity implements finish(); return; } + Log.e(Constants.TAG, "uri: " + mDataUri); PGPPublicKeyRing signKey = (PGPPublicKeyRing) ProviderHelper.getPGPKeyRing(this, mDataUri); + mUserIds = (ListView) findViewById(R.id.user_ids); + + mUserIdsAdapter = new ViewKeyUserIdsAdapter(this, null, 0, true); + mUserIds.setAdapter(mUserIdsAdapter); + + getSupportLoaderManager().initLoader(LOADER_ID_KEYRING, null, this); + getSupportLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this); + if (signKey != null) { mPubKeyId = PgpKeyHelper.getMasterKey(signKey).getKeyID(); } @@ -144,6 +159,78 @@ public class CertifyKeyActivity extends ActionBarActivity implements } } + static final String[] KEYRING_PROJECTION = + new String[] { + KeychainContract.KeyRings._ID, + KeychainContract.KeyRings.MASTER_KEY_ID, + KeychainContract.Keys.FINGERPRINT, + KeychainContract.UserIds.USER_ID + }; + static final int INDEX_MASTER_KEY_ID = 1; + static final int INDEX_FINGERPRINT = 2; + static final int INDEX_USER_ID = 3; + + static final String[] USER_IDS_PROJECTION = + new String[]{ + KeychainContract.UserIds._ID, + KeychainContract.UserIds.USER_ID, + KeychainContract.UserIds.RANK + }; + static final String USER_IDS_SORT_ORDER = + KeychainContract.UserIds.RANK + " ASC"; + + @Override + public Loader<Cursor> onCreateLoader(int id, Bundle args) { + switch(id) { + case LOADER_ID_KEYRING: + return new CursorLoader(this, mDataUri, KEYRING_PROJECTION, null, null, null); + case LOADER_ID_USER_IDS: { + Uri baseUri = KeychainContract.UserIds.buildUserIdsUri(mDataUri); + return new CursorLoader(this, baseUri, USER_IDS_PROJECTION, null, null, USER_IDS_SORT_ORDER); + } + } + return null; + } + + @Override + public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + switch(loader.getId()) { + case LOADER_ID_KEYRING: + // the first key here is our master key + if (data.moveToFirst()) { + // TODO: put findViewById in onCreate! + + long keyId = data.getLong(INDEX_MASTER_KEY_ID); + String keyIdStr = PgpKeyHelper.convertKeyIdToHexShort(keyId); + ((TextView) findViewById(R.id.key_id)).setText(keyIdStr); + + String mainUserId = data.getString(INDEX_USER_ID); + ((TextView) findViewById(R.id.main_user_id)).setText(mainUserId); + + byte[] fingerprintBlob = data.getBlob(INDEX_FINGERPRINT); + if (fingerprintBlob == null) { + // FALLBACK for old database entries + fingerprintBlob = ProviderHelper.getFingerprint(this, mDataUri); + } + String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob); + ((TextView) findViewById(R.id.fingerprint)).setText(PgpKeyHelper.colorizeFingerprint(fingerprint)); + } + break; + case LOADER_ID_USER_IDS: + mUserIdsAdapter.swapCursor(data); + break; + } + } + + @Override + public void onLoaderReset(Loader<Cursor> loader) { + switch(loader.getId()) { + case LOADER_ID_USER_IDS: + mUserIdsAdapter.swapCursor(null); + break; + } + } + private void showPassphraseDialog(final long secretKeyId) { // Message is received after passphrase is cached Handler returnHandler = new Handler() { @@ -179,6 +266,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements // if we have already signed this key, dont bother doing it again boolean alreadySigned = false; + /* todo: reconsider this at a later point when certs are in the db @SuppressWarnings("unchecked") Iterator<PGPSignature> itr = pubring.getPublicKey(mPubKeyId).getSignatures(); while (itr.hasNext()) { @@ -188,6 +276,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements break; } } + */ if (!alreadySigned) { /* @@ -215,6 +304,15 @@ public class CertifyKeyActivity extends ActionBarActivity implements * kicks off the actual signing process on a background thread */ private void startSigning() { + + // Bail out if there is not at least one user id selected + ArrayList<String> userIds = mUserIdsAdapter.getSelectedUserIds(); + if(userIds.isEmpty()) { + Toast.makeText(CertifyKeyActivity.this, "No User IDs to sign selected!", + Toast.LENGTH_SHORT).show(); + return; + } + // Send all information needed to service to sign key in other thread Intent intent = new Intent(this, KeychainIntentService.class); @@ -225,14 +323,15 @@ public class CertifyKeyActivity extends ActionBarActivity implements data.putLong(KeychainIntentService.CERTIFY_KEY_MASTER_KEY_ID, mMasterKeyId); data.putLong(KeychainIntentService.CERTIFY_KEY_PUB_KEY_ID, mPubKeyId); + data.putStringArrayList(KeychainIntentService.CERTIFY_KEY_UIDS, userIds); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - // Message is received after signing is done in ApgService + // Message is received after signing is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, - R.string.progress_signing, ProgressDialog.STYLE_SPINNER) { + getString(R.string.progress_signing), ProgressDialog.STYLE_SPINNER) { public void handleMessage(Message message) { - // handle messages by standard ApgHandler first + // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { @@ -249,7 +348,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements finish(); } } - }; + } }; // Create a new Messenger for the communication back @@ -281,11 +380,11 @@ public class CertifyKeyActivity extends ActionBarActivity implements intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - // Message is received after uploading is done in ApgService + // Message is received after uploading is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, - R.string.progress_exporting, ProgressDialog.STYLE_HORIZONTAL) { + getString(R.string.progress_exporting), ProgressDialog.STYLE_HORIZONTAL) { public void handleMessage(Message message) { - // handle messages by standard ApgHandler first + // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { @@ -295,7 +394,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements setResult(RESULT_OK); finish(); } - }; + } }; // Create a new Messenger for the communication back diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java index 42288ca37..9b3b00c19 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java @@ -17,14 +17,20 @@ package org.sufficientlysecure.keychain.ui; -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.util.regex.Matcher; - +import android.annotation.SuppressLint; +import android.app.ProgressDialog; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.animation.AnimationUtils; +import android.widget.*; +import com.beardedhen.androidbootstrap.BootstrapButton; +import com.devspark.appmsg.AppMsg; import org.openintents.openpgp.OpenPgpSignatureResult; import org.spongycastle.openpgp.PGPPublicKeyRing; import org.sufficientlysecure.keychain.Constants; @@ -33,10 +39,10 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; import org.sufficientlysecure.keychain.helper.ActionBarHelper; import org.sufficientlysecure.keychain.helper.FileHelper; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult; import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; import org.sufficientlysecure.keychain.pgp.exception.NoAsymmetricEncryptionException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.ProviderHelper; @@ -48,27 +54,8 @@ import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; import org.sufficientlysecure.keychain.util.Log; -import android.annotation.SuppressLint; -import android.app.ProgressDialog; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.animation.AnimationUtils; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.RelativeLayout; -import android.widget.TextView; -import android.widget.Toast; -import android.widget.ViewFlipper; - -import com.beardedhen.androidbootstrap.BootstrapButton; -import com.devspark.appmsg.AppMsg; +import java.io.*; +import java.util.regex.Matcher; @SuppressLint("NewApi") public class DecryptActivity extends DrawerActivity { @@ -364,7 +351,7 @@ public class DecryptActivity extends DrawerActivity { } } else { Log.e(Constants.TAG, - "Direct binary data without actual file in filesystem is not supported. Please use the Remote Service API!"); + "Direct binary data without actual file in filesystem is not supported. Please use the Remote Service API!"); Toast.makeText(this, R.string.error_only_files_are_supported, Toast.LENGTH_LONG) .show(); // end activity @@ -383,7 +370,7 @@ public class DecryptActivity extends DrawerActivity { if (filename.endsWith(".asc") || filename.endsWith(".gpg") || filename.endsWith(".pgp")) { filename = filename.substring(0, filename.length() - 4); } - mOutputFilename = Constants.path.APP_DIR + "/" + filename; + mOutputFilename = Constants.Path.APP_DIR + "/" + filename; } private void updateSource() { @@ -456,8 +443,7 @@ public class DecryptActivity extends DrawerActivity { getDecryptionKeyFromInputStream(); // if we need a symmetric passphrase or a passphrase to use a secret key ask for it - if (mSecretKeyId == Id.key.symmetric - || PassphraseCacheService.getCachedPassphrase(this, mSecretKeyId) == null) { + if (mAssumeSymmetricEncryption || PassphraseCacheService.getCachedPassphrase(this, mSecretKeyId) == null) { showPassphraseDialog(); } else { if (mDecryptTarget == Id.target.file) { @@ -507,6 +493,7 @@ public class DecryptActivity extends DrawerActivity { * TODO: Rework function, remove global variables */ private void getDecryptionKeyFromInputStream() { + mAssumeSymmetricEncryption = false; InputStream inStream = null; if (mContentUri != null) { try { @@ -546,7 +533,6 @@ public class DecryptActivity extends DrawerActivity { if (mSecretKeyId == Id.key.none) { throw new PgpGeneralException(getString(R.string.error_no_secret_key_found)); } - mAssumeSymmetricEncryption = false; } catch (NoAsymmetricEncryptionException e) { if (inStream.markSupported()) { inStream.reset(); @@ -559,6 +545,7 @@ public class DecryptActivity extends DrawerActivity { mAssumeSymmetricEncryption = true; } } catch (Exception e) { + Log.e(Constants.TAG, "error while reading decryption key from input stream", e); AppMsg.makeText(this, getString(R.string.error_message, e.getMessage()), AppMsg.STYLE_ALERT).show(); } @@ -644,11 +631,11 @@ public class DecryptActivity extends DrawerActivity { intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - // Message is received after encrypting is done in ApgService + // Message is received after encrypting is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, - R.string.progress_decrypting, ProgressDialog.STYLE_HORIZONTAL) { + getString(R.string.progress_decrypting), ProgressDialog.STYLE_HORIZONTAL) { public void handleMessage(Message message) { - // handle messages by standard ApgHandler first + // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { @@ -744,8 +731,6 @@ public class DecryptActivity extends DrawerActivity { } } } - - ; }; // Create a new Messenger for the communication back diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java index 08ca262c3..f01e67449 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java @@ -17,30 +17,21 @@ package org.sufficientlysecure.keychain.ui; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity; -import org.sufficientlysecure.keychain.util.Log; - import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; +import android.graphics.Color; import android.os.Bundle; import android.support.v4.app.ActionBarDrawerToggle; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarActivity; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.ListView; -import android.widget.TextView; - +import android.view.*; +import android.widget.*; import com.beardedhen.androidbootstrap.FontAwesomeText; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; public class DrawerActivity extends ActionBarActivity { private DrawerLayout mDrawerLayout; @@ -49,10 +40,8 @@ public class DrawerActivity extends ActionBarActivity { private CharSequence mDrawerTitle; private CharSequence mTitle; + private boolean mIsDrawerLocked = false; - private static Class[] mItemsClass = new Class[] { KeyListPublicActivity.class, - EncryptActivity.class, DecryptActivity.class, ImportKeysActivity.class, - KeyListSecretActivity.class, RegisteredAppsListActivity.class }; private Class mSelectedItem; private static final int MENU_ID_PREFERENCE = 222; @@ -62,18 +51,29 @@ public class DrawerActivity extends ActionBarActivity { mDrawerTitle = getString(R.string.app_name); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerList = (ListView) findViewById(R.id.left_drawer); + ViewGroup viewGroup = (ViewGroup) findViewById(R.id.content_frame); + int leftMarginLoaded = ((ViewGroup.MarginLayoutParams) viewGroup.getLayoutParams()).leftMargin; + int leftMarginInTablets = (int) getResources().getDimension(R.dimen.drawer_size); + int errorInMarginAllowed = 5; + + // if the left margin of the loaded layout is close to the + // one used in tablets then set drawer as open and locked + if( Math.abs(leftMarginLoaded - leftMarginInTablets) < errorInMarginAllowed) { + mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN, mDrawerList); + mDrawerLayout.setScrimColor(Color.TRANSPARENT); + mIsDrawerLocked = true; + } else { + // set a custom shadow that overlays the main content when the drawer opens + mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); + mIsDrawerLocked = false; + } - // set a custom shadow that overlays the main content when the drawer - // opens - mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); - - NavItem mItemIconTexts[] = new NavItem[] { + NavItem mItemIconTexts[] = new NavItem[]{ new NavItem("fa-user", getString(R.string.nav_contacts)), new NavItem("fa-lock", getString(R.string.nav_encrypt)), new NavItem("fa-unlock", getString(R.string.nav_decrypt)), new NavItem("fa-download", getString(R.string.nav_import)), - new NavItem("fa-key", getString(R.string.nav_secret_keys)), - new NavItem("fa-android", getString(R.string.nav_apps)) }; + new NavItem("fa-android", getString(R.string.nav_apps))}; mDrawerList.setAdapter(new NavigationDrawerAdapter(this, R.layout.drawer_list_item, mItemIconTexts)); @@ -81,32 +81,24 @@ public class DrawerActivity extends ActionBarActivity { mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); // enable ActionBar app icon to behave as action to toggle nav drawer - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setHomeButtonEnabled(true); + // if the drawer is not locked + if ( !mIsDrawerLocked ) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeButtonEnabled(true); + } // ActionBarDrawerToggle ties together the the proper interactions // between the sliding drawer and the action bar app icon mDrawerToggle = new ActionBarDrawerToggle(this, /* host Activity */ - mDrawerLayout, /* DrawerLayout object */ - R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */ - R.string.drawer_open, /* "open drawer" description for accessibility */ - R.string.drawer_close /* "close drawer" description for accessibility */ + mDrawerLayout, /* DrawerLayout object */ + R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */ + R.string.drawer_open, /* "open drawer" description for accessibility */ + R.string.drawer_close /* "close drawer" description for accessibility */ ) { public void onDrawerClosed(View view) { getSupportActionBar().setTitle(mTitle); - // creates call to onPrepareOptionsMenu() - supportInvalidateOptionsMenu(); - - // call intent activity if selected - if(mSelectedItem != null) { - finish(); - overridePendingTransition(0, 0); - Intent intent = new Intent(DrawerActivity.this, mSelectedItem); - startActivity(intent); - // disable animation of activity start - overridePendingTransition(0, 0); - } + callIntentForDrawerItem(mSelectedItem); } public void onDrawerOpened(View drawerView) { @@ -116,13 +108,40 @@ public class DrawerActivity extends ActionBarActivity { supportInvalidateOptionsMenu(); } }; - mDrawerLayout.setDrawerListener(mDrawerToggle); + if ( !mIsDrawerLocked ) { + mDrawerLayout.setDrawerListener(mDrawerToggle); + } else { + // If the drawer is locked open make it un-focusable + // so that it doesn't consume all the Back button presses + mDrawerLayout.setFocusableInTouchMode(false); + } // if (savedInstanceState == null) { // selectItem(0); // } } + /** + * Uses startActivity to call the Intent of the given class + * @param drawerItem the class of the drawer item you want to load. Based on Constants.DrawerItems.* + */ + public void callIntentForDrawerItem(Class drawerItem) { + // creates call to onPrepareOptionsMenu() + supportInvalidateOptionsMenu(); + + // call intent activity if selected + if (drawerItem != null) { + finish(); + overridePendingTransition(0, 0); + + Intent intent = new Intent(this, drawerItem); + startActivity(intent); + + // disable animation of activity start + overridePendingTransition(0, 0); + } + } + @Override public boolean onCreateOptionsMenu(Menu menu) { menu.add(42, MENU_ID_PREFERENCE, 100, R.string.menu_preferences); @@ -150,18 +169,18 @@ public class DrawerActivity extends ActionBarActivity { } switch (item.getItemId()) { - case MENU_ID_PREFERENCE: { - Intent intent = new Intent(this, PreferencesActivity.class); - startActivity(intent); - return true; - } - case MENU_ID_HELP: { - Intent intent = new Intent(this, HelpActivity.class); - startActivity(intent); - return true; - } - default: - return super.onOptionsItemSelected(item); + case MENU_ID_PREFERENCE: { + Intent intent = new Intent(this, PreferencesActivity.class); + startActivity(intent); + return true; + } + case MENU_ID_HELP: { + Intent intent = new Intent(this, HelpActivity.class); + startActivity(intent); + return true; + } + default: + return super.onOptionsItemSelected(item); } // Handle action buttons @@ -193,10 +212,18 @@ public class DrawerActivity extends ActionBarActivity { private void selectItem(int position) { // update selected item and title, then close the drawer mDrawerList.setItemChecked(position, true); - // setTitle(mDrawerTitles[position]); - mDrawerLayout.closeDrawer(mDrawerList); // set selected class - mSelectedItem = mItemsClass[position]; + mSelectedItem = Constants.DrawerItems.ARRAY[position]; + + // setTitle(mDrawerTitles[position]); + // If drawer isn't locked just close the drawer and + // it will move to the selected item by itself (via drawer toggle listener) + if ( !mIsDrawerLocked ) { + mDrawerLayout.closeDrawer(mDrawerList); + // else move to the selected item yourself + } else { + callIntentForDrawerItem(mSelectedItem); + } } /** @@ -229,15 +256,15 @@ public class DrawerActivity extends ActionBarActivity { } private class NavigationDrawerAdapter extends ArrayAdapter<NavItem> { - Context context; - int layoutResourceId; - NavItem data[] = null; + Context mContext; + int mLayoutResourceId; + NavItem mData[] = null; public NavigationDrawerAdapter(Context context, int layoutResourceId, NavItem[] data) { super(context, layoutResourceId, data); - this.layoutResourceId = layoutResourceId; - this.context = context; - this.data = data; + this.mLayoutResourceId = layoutResourceId; + this.mContext = context; + this.mData = data; } @Override @@ -246,21 +273,21 @@ public class DrawerActivity extends ActionBarActivity { NavItemHolder holder = null; if (row == null) { - LayoutInflater inflater = ((Activity) context).getLayoutInflater(); - row = inflater.inflate(layoutResourceId, parent, false); + LayoutInflater inflater = ((Activity) mContext).getLayoutInflater(); + row = inflater.inflate(mLayoutResourceId, parent, false); holder = new NavItemHolder(); - holder.img = (FontAwesomeText) row.findViewById(R.id.drawer_item_icon); - holder.txtTitle = (TextView) row.findViewById(R.id.drawer_item_text); + holder.mImg = (FontAwesomeText) row.findViewById(R.id.drawer_item_icon); + holder.mTxtTitle = (TextView) row.findViewById(R.id.drawer_item_text); row.setTag(holder); } else { holder = (NavItemHolder) row.getTag(); } - NavItem item = data[position]; - holder.txtTitle.setText(item.title); - holder.img.setIcon(item.icon); + NavItem item = mData[position]; + holder.mTxtTitle.setText(item.title); + holder.mImg.setIcon(item.icon); return row; } @@ -268,8 +295,8 @@ public class DrawerActivity extends ActionBarActivity { } static class NavItemHolder { - FontAwesomeText img; - TextView txtTitle; + FontAwesomeText mImg; + TextView mTxtTitle; } -}
\ No newline at end of file +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 0726897c3..b7fffc7ff 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -23,6 +23,25 @@ import java.util.List; import java.util.Vector; import org.spongycastle.bcpg.sig.KeyFlags; +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import android.support.v7.app.ActionBarActivity; +import android.view.*; +import android.view.View.OnClickListener; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.LinearLayout; +import android.widget.Toast; +import com.beardedhen.androidbootstrap.BootstrapButton; import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKeyRing; import org.sufficientlysecure.keychain.Constants; @@ -32,6 +51,7 @@ import org.sufficientlysecure.keychain.helper.ExportHelper; import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; 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.service.KeychainIntentServiceHandler; @@ -49,31 +69,12 @@ import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; import android.app.AlertDialog; -import android.app.Activity; -import android.app.ProgressDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; -import android.support.v7.app.ActionBarActivity; import android.support.v4.app.ActivityCompat; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.view.View.OnClickListener; import android.view.ViewGroup; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.CompoundButton.OnCheckedChangeListener; -import android.widget.LinearLayout; -import android.widget.Toast; - -import com.beardedhen.androidbootstrap.BootstrapButton; public class EditKeyActivity extends ActionBarActivity implements EditorListener { @@ -113,7 +114,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener Vector<String> mUserIds; Vector<PGPSecretKey> mKeys; Vector<Integer> mKeysUsages; - boolean masterCanSign = true; + boolean mMasterCanSign = true; ExportHelper mExportHelper; @@ -169,7 +170,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener /** * Handle intent action to create new key - * + * * @param intent */ private void handleActionCreateKey(Intent intent) { @@ -211,9 +212,10 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener serviceIntent.putExtra(KeychainIntentService.EXTRA_DATA, data); - // Message is received after generating is done in ApgService + // Message is received after generating is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( - this, R.string.progress_generating, ProgressDialog.STYLE_SPINNER, true, + this, getResources().getQuantityString(R.plurals.progress_generating, 1), + ProgressDialog.STYLE_HORIZONTAL, true, new DialogInterface.OnCancelListener() { @Override @@ -227,7 +229,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener @Override public void handleMessage(Message message) { - // handle messages by standard ApgHandler first + // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { @@ -272,7 +274,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener /** * Handle intent action to edit existing key - * + * * @param intent */ private void handleActionEditKey(Intent intent) { @@ -283,12 +285,10 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener } else { Log.d(Constants.TAG, "uri: " + mDataUri); - long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment()); - // get master key id using row id - long masterKeyId = ProviderHelper.getSecretMasterKeyId(this, keyRingRowId); + long masterKeyId = ProviderHelper.getMasterKeyId(this, mDataUri); - masterCanSign = ProviderHelper.getSecretMasterKeyCanCertify(this, keyRingRowId); + mMasterCanSign = ProviderHelper.getMasterKeyCanCertify(this, mDataUri); finallyEdit(masterKeyId); } } @@ -350,11 +350,16 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener if (needsSaving()) { Toast.makeText(this, R.string.error_save_first, Toast.LENGTH_LONG).show(); } else { - mExportHelper.showExportKeysDialog(mDataUri, Id.type.secret_key, Constants.path.APP_DIR - + "/secexport.asc"); + long masterKeyId = ProviderHelper.getMasterKeyId(this, mDataUri); + long[] ids = new long[]{masterKeyId}; + mExportHelper.showExportKeysDialog(ids, Id.type.secret_key, Constants.Path.APP_DIR_FILE_SEC, + null); + return true; } return true; - case R.id.menu_key_edit_delete: { + case R.id.menu_key_edit_delete: + long rowId= ProviderHelper.getRowId(this,mDataUri); + Uri convertUri = KeychainContract.KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)); // Message is received after key is deleted Handler returnHandler = new Handler() { @Override @@ -363,12 +368,10 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener setResult(RESULT_CANCELED); finish(); } - } - }; - - mExportHelper.deleteKey(mDataUri, Id.type.secret_key, returnHandler); + }}; + mExportHelper.deleteKey(convertUri, returnHandler); return true; - } + case R.id.menu_key_edit_save: saveClicked(); return true; @@ -407,8 +410,8 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener } mCurrentPassphrase = ""; - buildLayout(false); + mIsPassPhraseSet = PassphraseCacheService.hasPassphrase(this, masterKeyId); if (!mIsPassPhraseSet) { // check "no passphrase" checkbox and remove button @@ -466,20 +469,23 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener // find views mChangePassphrase = (BootstrapButton) findViewById(R.id.edit_key_btn_change_passphrase); mNoPassphrase = (CheckBox) findViewById(R.id.edit_key_no_passphrase); - // Build layout based on given userIds and keys + LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); LinearLayout container = (LinearLayout) findViewById(R.id.edit_key_container); + if(mIsPassPhraseSet){ + mChangePassphrase.setText(getString(R.string.btn_change_passphrase)); + } mUserIdsView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false); mUserIdsView.setType(Id.type.user_id); - mUserIdsView.setCanEdit(masterCanSign); + mUserIdsView.setCanEdit(mMasterCanSign); mUserIdsView.setUserIds(mUserIds); mUserIdsView.setEditorListener(this); container.addView(mUserIdsView); mKeysView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false); mKeysView.setType(Id.type.key); - mKeysView.setCanEdit(masterCanSign); + mKeysView.setCanEdit(mMasterCanSign); mKeysView.setKeys(mKeys, mKeysUsages, newKeys); mKeysView.setEditorListener(this); container.addView(mKeysView); @@ -602,17 +608,16 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener // fill values for this action Bundle data = new Bundle(); - - data.putBoolean(KeychainIntentService.SAVE_KEYRING_CAN_SIGN, masterCanSign); + data.putBoolean(KeychainIntentService.SAVE_KEYRING_CAN_SIGN, mMasterCanSign); data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, saveParams); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - // Message is received after saving is done in ApgService + // Message is received after saving is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, - R.string.progress_saving, ProgressDialog.STYLE_HORIZONTAL) { + getString(R.string.progress_saving), ProgressDialog.STYLE_HORIZONTAL) { public void handleMessage(Message message) { - // handle messages by standard ApgHandler first + // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { @@ -640,8 +645,9 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener // start service with intent startService(intent); } catch (PgpGeneralException e) { + Log.e(Constants.TAG, getString(R.string.error_message, e.getMessage())); Toast.makeText(this, getString(R.string.error_message, e.getMessage()), - Toast.LENGTH_SHORT).show(); + Toast.LENGTH_SHORT).show(); } } @@ -679,7 +685,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener /** * Returns user ids from the SectionView - * + * * @param userIdsView * @return */ @@ -692,11 +698,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener for (int i = 0; i < userIdEditors.getChildCount(); ++i) { UserIdEditor editor = (UserIdEditor) userIdEditors.getChildAt(i); String userId; - try { - userId = editor.getValue(); - } catch (UserIdEditor.InvalidEmailException e) { - throw new PgpGeneralException(e.getMessage()); - } + userId = editor.getValue(); if (editor.isMainUserId()) { userIds.add(0, userId); @@ -719,7 +721,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener /** * Returns keys from the SectionView - * + * * @param keysView * @return */ @@ -742,7 +744,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener /** * Returns usage selections of keys from the SectionView - * + * * @param keysView * @return */ diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java index faac9b157..4f18f69d7 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -17,9 +17,22 @@ package org.sufficientlysecure.keychain.ui; -import java.io.File; -import java.util.Vector; - +import android.app.ProgressDialog; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.*; +import com.beardedhen.androidbootstrap.BootstrapButton; +import com.beardedhen.androidbootstrap.FontAwesomeText; +import com.devspark.appmsg.AppMsg; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPSecretKey; @@ -43,27 +56,8 @@ import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; import org.sufficientlysecure.keychain.util.Choice; import org.sufficientlysecure.keychain.util.Log; -import android.app.ProgressDialog; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.animation.AnimationUtils; -import android.widget.ArrayAdapter; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.Spinner; -import android.widget.TextView; -import android.widget.Toast; -import android.widget.ViewFlipper; - -import com.beardedhen.androidbootstrap.BootstrapButton; -import com.devspark.appmsg.AppMsg; +import java.io.File; +import java.util.Vector; public class EncryptActivity extends DrawerActivity { @@ -108,6 +102,7 @@ public class EncryptActivity extends DrawerActivity { private EditText mFilename = null; private CheckBox mDeleteAfter = null; + private CheckBox mShareAfter = null; private BootstrapButton mBrowse = null; private String mInputFilename = null; @@ -602,11 +597,11 @@ public class EncryptActivity extends DrawerActivity { intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - // Message is received after encrypting is done in ApgService + // Message is received after encrypting is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, - R.string.progress_encrypting, ProgressDialog.STYLE_HORIZONTAL) { + getString(R.string.progress_encrypting), ProgressDialog.STYLE_HORIZONTAL) { public void handleMessage(Message message) { - // handle messages by standard ApgHandler first + // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { @@ -650,6 +645,15 @@ public class EncryptActivity extends DrawerActivity { .newInstance(mInputFilename); deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog"); } + + if (mShareAfter.isChecked()) { + // Share encrypted file + Intent sendFileIntent = new Intent(Intent.ACTION_SEND); + sendFileIntent.setType("*/*"); + sendFileIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(mOutputFilename)); + startActivity(Intent.createChooser(sendFileIntent, + getString(R.string.title_send_file))); + } break; default: @@ -659,8 +663,6 @@ public class EncryptActivity extends DrawerActivity { } } } - - ; }; // Create a new Messenger for the communication back @@ -785,6 +787,11 @@ public class EncryptActivity extends DrawerActivity { } }); + + + + + mFileCompression = (Spinner) findViewById(R.id.fileCompression); Choice[] choices = new Choice[]{ new Choice(Id.choice.compression.none, getString(R.string.choice_none) + " (" @@ -794,7 +801,7 @@ public class EncryptActivity extends DrawerActivity { new Choice(Id.choice.compression.zlib, "ZLIB (" + getString(R.string.compression_fast) + ")"), new Choice(Id.choice.compression.bzip2, "BZIP2 (" - + getString(R.string.compression_very_slow) + ")"),}; + + getString(R.string.compression_very_slow) + ")"), }; ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(this, android.R.layout.simple_spinner_item, choices); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); @@ -809,6 +816,7 @@ public class EncryptActivity extends DrawerActivity { } mDeleteAfter = (CheckBox) findViewById(R.id.deleteAfterEncryption); + mShareAfter = (CheckBox) findViewById(R.id.shareAfterEncryption); mAsciiArmor = (CheckBox) findViewById(R.id.asciiArmour); mAsciiArmor.setChecked(Preferences.getPreferences(this).getDefaultAsciiArmour()); @@ -947,8 +955,8 @@ public class EncryptActivity extends DrawerActivity { case Id.request.secret_keys: { if (resultCode == RESULT_OK) { - Bundle bundle = data.getExtras(); - mSecretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID); + Uri uri_master_key = data.getData(); + mSecretKeyId = Long.valueOf(uri_master_key.getLastPathSegment()); } else { mSecretKeyId = Id.key.none; } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java index 12d689274..a484b57de 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java @@ -17,11 +17,6 @@ package org.sufficientlysecure.keychain.ui; -import org.sufficientlysecure.htmltextview.HtmlTextView; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.util.Log; - import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -31,6 +26,10 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import org.sufficientlysecure.htmltextview.HtmlTextView; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.util.Log; public class HelpAboutFragment extends Fragment { @@ -55,7 +54,7 @@ public class HelpAboutFragment extends Fragment { /** * Get the current package version. - * + * * @return The current version. */ private String getVersion() { @@ -73,4 +72,4 @@ public class HelpAboutFragment extends Fragment { return result; } -}
\ No newline at end of file +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java index 9ccd7e088..32f37a0a5 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2012-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 @@ -17,24 +17,16 @@ package org.sufficientlysecure.keychain.ui; -import java.util.ArrayList; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.ui.adapter.TabsAdapter; - -import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentStatePagerAdapter; -import android.support.v4.app.FragmentTransaction; import android.support.v4.view.ViewPager; import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarActivity; -import android.widget.TextView; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.adapter.TabsAdapter; public class HelpActivity extends ActionBarActivity { - public static final String EXTRA_SELECTED_TAB = "selectedTab"; + public static final String EXTRA_SELECTED_TAB = "selected_tab"; ViewPager mViewPager; TabsAdapter mTabsAdapter; @@ -64,19 +56,24 @@ 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 == 0 ? true : false)); + HelpHtmlFragment.class, startBundle, (selectedTab == 0)); + + 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 == 1)); 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 == 1 ? true : false)); + HelpHtmlFragment.class, nfcBundle, (selectedTab == 2)); 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 == 2 ? true : false)); + HelpHtmlFragment.class, changelogBundle, (selectedTab == 3)); mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_about)), - HelpAboutFragment.class, null, (selectedTab == 3 ? true : false)); + HelpAboutFragment.class, null, (selectedTab == 4)); } -}
\ No newline at end of file +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpHtmlFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpHtmlFragment.java index 3afb5bbe0..6b3c51b08 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpHtmlFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpHtmlFragment.java @@ -17,8 +17,6 @@ package org.sufficientlysecure.keychain.ui; -import org.sufficientlysecure.htmltextview.HtmlTextView; - import android.app.Activity; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -27,11 +25,12 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ScrollView; +import org.sufficientlysecure.htmltextview.HtmlTextView; public class HelpHtmlFragment extends Fragment { private Activity mActivity; - private int htmlFile; + private int mHtmlFile; public static final String ARG_HTML_FILE = "htmlFile"; @@ -52,8 +51,8 @@ public class HelpHtmlFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mActivity = getActivity(); - - htmlFile = getArguments().getInt(ARG_HTML_FILE); + + mHtmlFile = getArguments().getInt(ARG_HTML_FILE); ScrollView scroller = new ScrollView(mActivity); HtmlTextView text = new HtmlTextView(mActivity); @@ -66,11 +65,11 @@ public class HelpHtmlFragment extends Fragment { scroller.addView(text); // load html from raw resource (Parsing handled by HtmlTextView library) - text.setHtmlFromRawResource(getActivity(), htmlFile); + text.setHtmlFromRawResource(getActivity(), mHtmlFile); // no flickering when clicking textview for Android < 4 text.setTextColor(getResources().getColor(android.R.color.black)); return scroller; } -}
\ No newline at end of file +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 5ac421a44..834509f53 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -34,10 +34,8 @@ import android.support.v7.app.ActionBar; 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.pgp.PgpKeyHelper; @@ -161,7 +159,7 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa } else if (extras.containsKey(EXTRA_KEY_ID)) { long keyId = intent.getLongExtra(EXTRA_KEY_ID, 0); if (keyId != 0) { - query = "0x" + PgpKeyHelper.convertKeyToHex(keyId); + query = PgpKeyHelper.convertKeyIdToHex(keyId); } } else if (extras.containsKey(EXTRA_FINGERPRINT)) { String fingerprint = intent.getStringExtra(EXTRA_FINGERPRINT); @@ -169,7 +167,8 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa query = "0x" + fingerprint; } } else { - Log.e(Constants.TAG, "IMPORT_KEY_FROM_KEYSERVER action needs to contain the 'query', 'key_id', or 'fingerprint' extra!"); + Log.e(Constants.TAG, + "IMPORT_KEY_FROM_KEYSERVER action needs to contain the 'query', 'key_id', or 'fingerprint' extra!"); return; } @@ -338,7 +337,7 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa // } else { // status.putString( // EXTRA_ERROR, - // "Scanned fingerprint does NOT match the fingerprint of the received key. You shouldnt trust this key."); + // "Scanned fingerprint does NOT match the fingerprint of the received key. You shouldnt trust this key."); // } // } // } catch (QueryException e) { @@ -360,51 +359,54 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa // } - // Message is received after importing is done in ApgService - KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, - R.string.progress_importing, ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { - // get returned data bundle - Bundle returnData = message.getData(); - - int added = returnData.getInt(KeychainIntentService.RESULT_IMPORT_ADDED); - int updated = returnData - .getInt(KeychainIntentService.RESULT_IMPORT_UPDATED); - int bad = returnData.getInt(KeychainIntentService.RESULT_IMPORT_BAD); - String toastMessage; - if (added > 0 && updated > 0) { - String addedStr = getResources().getQuantityString( - R.plurals.keys_added_and_updated_1, added, added); - String updatedStr = getResources().getQuantityString( - R.plurals.keys_added_and_updated_2, updated, updated); - toastMessage = addedStr + updatedStr; - } else if (added > 0) { - toastMessage = getResources().getQuantityString(R.plurals.keys_added, - added, added); - } else if (updated > 0) { - toastMessage = getResources().getQuantityString(R.plurals.keys_updated, - updated, updated); - } else { - toastMessage = getString(R.string.no_keys_added_or_updated); - } - AppMsg.makeText(ImportKeysActivity.this, toastMessage, AppMsg.STYLE_INFO) - .show(); - if (bad > 0) { - BadImportKeyDialogFragment badImportKeyDialogFragment = BadImportKeyDialogFragment.newInstance(bad); - badImportKeyDialogFragment.show(getSupportFragmentManager(), "badKeyDialog"); - } - } - } - }; - /** * Import keys with mImportData */ public void importKeys() { + // Message is received after importing is done in KeychainIntentService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( + this, + getString(R.string.progress_importing), + ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + // get returned data bundle + Bundle returnData = message.getData(); + + int added = returnData.getInt(KeychainIntentService.RESULT_IMPORT_ADDED); + int updated = returnData + .getInt(KeychainIntentService.RESULT_IMPORT_UPDATED); + int bad = returnData.getInt(KeychainIntentService.RESULT_IMPORT_BAD); + String toastMessage; + if (added > 0 && updated > 0) { + String addedStr = getResources().getQuantityString( + R.plurals.keys_added_and_updated_1, added, added); + String updatedStr = getResources().getQuantityString( + R.plurals.keys_added_and_updated_2, updated, updated); + toastMessage = addedStr + updatedStr; + } else if (added > 0) { + toastMessage = getResources().getQuantityString(R.plurals.keys_added, + added, added); + } else if (updated > 0) { + toastMessage = getResources().getQuantityString(R.plurals.keys_updated, + updated, updated); + } else { + toastMessage = getString(R.string.no_keys_added_or_updated); + } + AppMsg.makeText(ImportKeysActivity.this, toastMessage, AppMsg.STYLE_INFO) + .show(); + if (bad > 0) { + BadImportKeyDialogFragment badImportKeyDialogFragment = + BadImportKeyDialogFragment.newInstance(bad); + badImportKeyDialogFragment.show(getSupportFragmentManager(), "badKeyDialog"); + } + } + } + }; + if (mListFragment.getKeyBytes() != null || mListFragment.getDataUri() != null) { Log.d(Constants.TAG, "importKeys started"); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java index 211fc1f44..3f0b4a46e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java @@ -17,17 +17,15 @@ package org.sufficientlysecure.keychain.ui; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; - import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; - import com.beardedhen.androidbootstrap.BootstrapButton; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; public class ImportKeysClipboardFragment extends Fragment { @@ -60,8 +58,9 @@ public class ImportKeysClipboardFragment extends Fragment { public void onClick(View v) { CharSequence clipboardText = ClipboardReflection.getClipboardText(getActivity()); String sendText = ""; - if (clipboardText != null) + if (clipboardText != null) { sendText = clipboardText.toString(); + } mImportActivity.loadCallback(sendText.getBytes(), null, null, null); } }); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java index 60b33c8a2..31d5f3fd0 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java @@ -17,11 +17,6 @@ package org.sufficientlysecure.keychain.ui; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.FileHelper; - import android.app.Activity; import android.content.Intent; import android.os.Bundle; @@ -29,8 +24,11 @@ import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import com.beardedhen.androidbootstrap.BootstrapButton; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.FileHelper; public class ImportKeysFileFragment extends Fragment { private ImportKeysActivity mImportActivity; @@ -62,7 +60,7 @@ public class ImportKeysFileFragment extends Fragment { // open .asc or .gpg files // setting it to text/plain prevents Cynaogenmod's file manager from selecting asc // or gpg types! - FileHelper.openFile(ImportKeysFileFragment.this, Constants.path.APP_DIR + "/", + FileHelper.openFile(ImportKeysFileFragment.this, Constants.Path.APP_DIR + "/", "*/*", Id.request.filename); } }); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java index 1118f0264..9e8506193 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java @@ -17,25 +17,6 @@ package org.sufficientlysecure.keychain.ui; -import java.io.ByteArrayInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - -import org.sufficientlysecure.keychain.Constants; -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.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.util.Log; - import android.app.Activity; import android.net.Uri; import android.os.Bundle; @@ -44,8 +25,21 @@ import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; import android.view.View; import android.widget.ListView; - import com.devspark.appmsg.AppMsg; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.ui.adapter.*; +import org.sufficientlysecure.keychain.util.InputData; +import org.sufficientlysecure.keychain.util.KeyServer; +import org.sufficientlysecure.keychain.util.Log; + +import java.io.ByteArrayInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; public class ImportKeysListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> { @@ -184,7 +178,8 @@ public class ImportKeysListFragment extends ListFragment implements } @Override - public Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> onCreateLoader(int id, Bundle args) { + public Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> + onCreateLoader(int id, Bundle args) { switch (id) { case LOADER_ID_BYTES: { InputData inputData = getInputData(mKeyBytes, mDataUri); @@ -219,27 +214,44 @@ public class ImportKeysListFragment extends ListFragment implements } else { setListShownNoAnimation(true); } + + Exception error = data.getError(); + switch (loader.getId()) { case LOADER_ID_BYTES: + + if (error == null) { + // No error + } else if (error instanceof ImportKeysListLoader.FileHasNoContent) { + AppMsg.makeText(getActivity(), R.string.error_import_file_no_content, + AppMsg.STYLE_ALERT).show(); + } else if (error instanceof ImportKeysListLoader.NonPgpPart) { + AppMsg.makeText(getActivity(), + ((ImportKeysListLoader.NonPgpPart) error).getCount() + " " + getResources(). + getQuantityString(R.plurals.error_import_non_pgp_part, + ((ImportKeysListLoader.NonPgpPart) error).getCount()), + new AppMsg.Style(AppMsg.LENGTH_LONG, R.color.confirm)).show(); + } else { + AppMsg.makeText(getActivity(), R.string.error_generic_report_bug, + new AppMsg.Style(AppMsg.LENGTH_LONG, R.color.alert)).show(); + } break; case LOADER_ID_SERVER_QUERY: - Exception error = data.getError(); - - if(error == null){ + 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.InsufficientQuery){ + } else if (error instanceof KeyServer.InsufficientQuery) { AppMsg.makeText(getActivity(), R.string.error_keyserver_insufficient_query, AppMsg.STYLE_ALERT).show(); - }else if(error instanceof KeyServer.QueryException){ + } else if (error instanceof KeyServer.QueryException) { AppMsg.makeText(getActivity(), R.string.error_keyserver_query, AppMsg.STYLE_ALERT).show(); - }else if(error instanceof KeyServer.TooManyResponses){ + } else if (error instanceof KeyServer.TooManyResponses) { AppMsg.makeText(getActivity(), R.string.error_keyserver_too_many_responses, AppMsg.STYLE_ALERT).show(); } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java index 83af8cf48..110647284 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java @@ -17,8 +17,6 @@ package org.sufficientlysecure.keychain.ui; -import org.sufficientlysecure.keychain.R; - import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -26,8 +24,8 @@ import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; - import com.beardedhen.androidbootstrap.BootstrapButton; +import org.sufficientlysecure.keychain.R; public class ImportKeysNFCFragment extends Fragment { @@ -59,7 +57,7 @@ public class ImportKeysNFCFragment extends Fragment { public void onClick(View v) { // show nfc help Intent intent = new Intent(getActivity(), HelpActivity.class); - intent.putExtra(HelpActivity.EXTRA_SELECTED_TAB, 1); + intent.putExtra(HelpActivity.EXTRA_SELECTED_TAB, 2); startActivityForResult(intent, 0); } }); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java index ee91b2434..10c0752b1 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java @@ -17,14 +17,6 @@ package org.sufficientlysecure.keychain.ui; -import java.util.ArrayList; -import java.util.Locale; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4; -import org.sufficientlysecure.keychain.util.Log; - import android.content.Intent; import android.net.Uri; import android.os.Bundle; @@ -36,9 +28,15 @@ import android.view.ViewGroup; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; - import com.beardedhen.androidbootstrap.BootstrapButton; import com.google.zxing.integration.android.IntentResult; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4; +import org.sufficientlysecure.keychain.util.Log; + +import java.util.ArrayList; +import java.util.Locale; public class ImportKeysQrCodeFragment extends Fragment { @@ -94,45 +92,45 @@ public class ImportKeysQrCodeFragment extends Fragment { @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode & 0xFFFF) { - case IntentIntegratorSupportV4.REQUEST_CODE: { - IntentResult scanResult = IntentIntegratorSupportV4.parseActivityResult(requestCode, - resultCode, data); - if (scanResult != null && scanResult.getFormatName() != null) { - String scannedContent = scanResult.getContents(); - - Log.d(Constants.TAG, "scannedContent: " + scannedContent); - - // look if it's fingerprint only - if (scannedContent.toLowerCase(Locale.ENGLISH).startsWith(Constants.FINGERPRINT_SCHEME)) { - importFingerprint(Uri.parse(scanResult.getContents())); - return; + case IntentIntegratorSupportV4.REQUEST_CODE: { + IntentResult scanResult = IntentIntegratorSupportV4.parseActivityResult(requestCode, + resultCode, data); + if (scanResult != null && scanResult.getFormatName() != null) { + String scannedContent = scanResult.getContents(); + + Log.d(Constants.TAG, "scannedContent: " + scannedContent); + + // look if it's fingerprint only + if (scannedContent.toLowerCase(Locale.ENGLISH).startsWith(Constants.FINGERPRINT_SCHEME)) { + importFingerprint(Uri.parse(scanResult.getContents())); + return; + } + + // look if it is the whole key + String[] parts = scannedContent.split(","); + if (parts.length == 3) { + importParts(parts); + return; + } + + // is this a full key encoded as qr code? + if (scannedContent.startsWith("-----BEGIN PGP")) { + mImportActivity.loadCallback(scannedContent.getBytes(), null, null, null); + return; + } + + // fail... + Toast.makeText(getActivity(), R.string.import_qr_code_wrong, Toast.LENGTH_LONG) + .show(); } - // look if it is the whole key - String[] parts = scannedContent.split(","); - if (parts.length == 3) { - importParts(parts); - return; - } - - // is this a full key encoded as qr code? - if (scannedContent.startsWith("-----BEGIN PGP")) { - mImportActivity.loadCallback(scannedContent.getBytes(), null, null, null); - return; - } - - // fail... - Toast.makeText(getActivity(), R.string.import_qr_code_wrong, Toast.LENGTH_LONG) - .show(); + break; } - break; - } - - default: - super.onActivityResult(requestCode, resultCode, data); + default: + super.onActivityResult(requestCode, resultCode, data); - break; + break; } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java index d77015aa7..0b2fff64e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java @@ -17,11 +17,6 @@ package org.sufficientlysecure.keychain.ui; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.Preferences; -import org.sufficientlysecure.keychain.util.Log; - import android.content.Context; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -36,8 +31,11 @@ import android.widget.ArrayAdapter; import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; - import com.beardedhen.androidbootstrap.BootstrapButton; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.util.Log; public class ImportKeysServerFragment extends Fragment { public static final String ARG_QUERY = "query"; @@ -96,7 +94,8 @@ public class ImportKeysServerFragment extends Fragment { search(query, keyServer); // close keyboard after pressing search - InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + InputMethodManager imm = + (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(mQueryEditText.getWindowToken(), 0); } }); @@ -110,7 +109,6 @@ public class ImportKeysServerFragment extends Fragment { search(query, keyServer); // Don't return true to let the keyboard close itself after pressing search - // http://stackoverflow.com/questions/2342620/how-to-hide-keyboard-after-typing-in-edittext-in-android return false; } return false; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java index e58ebe819..06df6f12d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2012-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 @@ -17,17 +17,16 @@ package org.sufficientlysecure.keychain.ui; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.ExportHelper; - import android.content.Intent; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ExportHelper; -public class KeyListSecretActivity extends DrawerActivity { +public class KeyListActivity extends DrawerActivity { ExportHelper mExportHelper; @@ -37,7 +36,7 @@ public class KeyListSecretActivity extends DrawerActivity { mExportHelper = new ExportHelper(this); - setContentView(R.layout.key_list_secret_activity); + setContentView(R.layout.key_list_activity); // now setup navigation drawer in DrawerActivity... setupDrawerNavigation(savedInstanceState); @@ -46,34 +45,35 @@ public class KeyListSecretActivity extends DrawerActivity { @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); - getMenuInflater().inflate(R.menu.key_list_secret, menu); + getMenuInflater().inflate(R.menu.key_list, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case R.id.menu_key_list_secret_create: - createKey(); - - return true; - case R.id.menu_key_list_secret_create_expert: - createKeyExpert(); - - return true; - case R.id.menu_key_list_secret_export: - mExportHelper.showExportKeysDialog(null, Id.type.secret_key, Constants.path.APP_DIR - + "/secexport.asc"); - - return true; - case R.id.menu_key_list_secret_import: - Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class); - intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE); - startActivityForResult(intentImportFromFile, 0); - - return true; - default: - return super.onOptionsItemSelected(item); + case R.id.menu_key_list_import: + callIntentForDrawerItem(Constants.DrawerItems.IMPORT_KEYS); + + return true; + case R.id.menu_key_list_export: + mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.Path.APP_DIR_FILE_PUB, null); + + return true; + case R.id.menu_key_list_create: + createKey(); + + return true; + case R.id.menu_key_list_create_expert: + createKeyExpert(); + + return true; + case R.id.menu_key_list_secret_export: + mExportHelper.showExportKeysDialog(null, Id.type.secret_key, Constants.Path.APP_DIR_FILE_SEC, null); + + return true; + default: + return super.onOptionsItemSelected(item); } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java new file mode 100644 index 000000000..957c822d2 --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -0,0 +1,686 @@ +/* + * Copyright (C) 2013-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.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Color; +import android.net.Uri; +import android.os.*; +import android.support.v4.app.Fragment; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v4.view.MenuItemCompat; +import android.support.v7.app.ActionBarActivity; +import android.support.v7.widget.SearchView; +import android.text.TextUtils; +import android.view.*; +import android.view.View.OnClickListener; +import android.view.animation.AnimationUtils; +import android.widget.*; +import android.widget.AbsListView.MultiChoiceModeListener; +import com.beardedhen.androidbootstrap.BootstrapButton; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ExportHelper; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyTypes; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.provider.KeychainDatabase; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.ui.adapter.HighlightQueryCursorAdapter; +import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; +import org.sufficientlysecure.keychain.util.Log; +import se.emilsjolander.stickylistheaders.ApiLevelTooLowException; +import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; +import se.emilsjolander.stickylistheaders.StickyListHeadersListView; + +import java.util.ArrayList; +import java.util.HashMap; + +/** + * 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 + 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 + private BootstrapButton mButtonEmptyCreate; + private BootstrapButton mButtonEmptyImport; + + + /** + * 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); + + mStickyList = (StickyListHeadersListView) root.findViewById(R.id.key_list_list); + mStickyList.setOnItemClickListener(this); + + + // empty view + mButtonEmptyCreate = (BootstrapButton) root.findViewById(R.id.key_list_empty_button_create); + mButtonEmptyCreate.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + Intent intent = new Intent(getActivity(), EditKeyActivity.class); + intent.setAction(EditKeyActivity.ACTION_CREATE_KEY); + intent.putExtra(EditKeyActivity.EXTRA_GENERATE_DEFAULT_KEYS, true); + intent.putExtra(EditKeyActivity.EXTRA_USER_IDS, ""); // show user id view + startActivityForResult(intent, 0); + } + }); + mButtonEmptyImport = (BootstrapButton) root.findViewById(R.id.key_list_empty_button_import); + mButtonEmptyImport.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + Intent intent = new Intent(getActivity(), ImportKeysActivity.class); + intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE); + startActivityForResult(intent, 0); + } + }); + + // 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; + } + + /** + * Define Adapter and Loader on create of Activity + */ + @SuppressLint("NewApi") + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mStickyList.setOnItemClickListener(this); + mStickyList.setAreHeadersSticky(true); + mStickyList.setDrawingListUnderStickyHeader(false); + mStickyList.setFastScrollEnabled(true); + try { + mStickyList.setFastScrollAlwaysVisible(true); + } catch (ApiLevelTooLowException e) { + } + + // this view is made visible if no data is available + mStickyList.setEmptyView(getActivity().findViewById(R.id.key_list_empty)); + + /* + * ActionBarSherlock does not support MultiChoiceModeListener. Thus multi-selection is only + * available for Android >= 3.0 + */ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + mStickyList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); + mStickyList.getWrappedList().setMultiChoiceModeListener(new MultiChoiceModeListener() { + + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + android.view.MenuInflater inflater = getActivity().getMenuInflater(); + inflater.inflate(R.menu.key_list_multi, menu); + return true; + } + + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + return false; + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + + // get IDs for checked positions as long array + long[] ids; + + switch (item.getItemId()) { + case R.id.menu_key_list_multi_encrypt: { + ids = mAdapter.getCurrentSelectedMasterKeyIds(); + encrypt(mode, ids); + break; + } + case R.id.menu_key_list_multi_delete: { + ids = mStickyList.getWrappedList().getCheckedItemIds(); + showDeleteKeyDialog(mode, ids); + break; + } + case R.id.menu_key_list_multi_export: { + ids = mStickyList.getWrappedList().getCheckedItemIds(); + long[] masterKeyIds = new long[2*ids.length]; + ArrayList<Long> allPubRowIds = + ProviderHelper.getPublicKeyRingsRowIds(getActivity()); + for (int i = 0; i < ids.length; i++) { + if (allPubRowIds.contains(ids[i])) { + masterKeyIds[i] = ProviderHelper.getPublicMasterKeyId(getActivity(), ids[i]); + } else { + masterKeyIds[i] = ProviderHelper.getSecretMasterKeyId(getActivity(), ids[i]); + } + } + ExportHelper mExportHelper = new ExportHelper((ActionBarActivity) getActivity()); + mExportHelper + .showExportKeysDialog(masterKeyIds, Id.type.public_key, + Constants.Path.APP_DIR_FILE_PUB, + getString(R.string.also_export_secret_keys)); + break; + } + case R.id.menu_key_list_multi_select_all: { + // select all + for (int i = 0; i < mStickyList.getCount(); i++) { + mStickyList.setItemChecked(i, true); + } + break; + } + } + return true; + } + + @Override + public void onDestroyActionMode(ActionMode mode) { + mAdapter.clearSelection(); + } + + @Override + public void onItemCheckedStateChanged(ActionMode mode, int position, long id, + boolean checked) { + if (checked) { + mAdapter.setNewSelection(position, checked); + } else { + mAdapter.removeSelection(position); + } + int count = mStickyList.getCheckedItemCount(); + String keysSelected = getResources().getQuantityString( + R.plurals.key_list_selected_keys, count, count); + mode.setTitle(keysSelected); + } + + }); + } + + // 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); + + // Create an empty adapter we will use to display the loaded data. + mAdapter = new KeyListAdapter(getActivity(), null, Id.type.public_key); + mStickyList.setAdapter(mAdapter); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + getLoaderManager().initLoader(0, null, this); + } + + // These are the rows that we will retrieve. + static final String[] PROJECTION = new String[]{ + KeychainContract.KeyRings._ID, + KeychainContract.KeyRings.TYPE, + KeychainContract.KeyRings.MASTER_KEY_ID, + KeychainContract.UserIds.USER_ID, + KeychainContract.Keys.IS_REVOKED + }; + + static final int INDEX_TYPE = 1; + static final int INDEX_MASTER_KEY_ID = 2; + static final int INDEX_USER_ID = 3; + static final int INDEX_IS_REVOKED = 4; + + static final String SORT_ORDER = + // show secret before public key + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings.TYPE + " DESC, " + // sort by user id otherwise + + UserIds.USER_ID + " ASC"; + + @Override + public Loader<Cursor> onCreateLoader(int id, Bundle args) { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + Uri baseUri = KeyRings.buildUnifiedKeyRingsUri(); + String where = null; + String whereArgs[] = null; + if (mCurQuery != null) { + where = KeychainContract.UserIds.USER_ID + " LIKE ?"; + whereArgs = new String[]{"%" + mCurQuery + "%"}; + } + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + return new CursorLoader(getActivity(), baseUri, PROJECTION, where, whereArgs, SORT_ORDER); + } + + @Override + public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.setSearchQuery(mCurQuery); + mAdapter.swapCursor(data); + + mStickyList.setAdapter(mAdapter); + + // NOTE: Not supported by StickyListHeader, but reimplemented here + // The list should now be shown. + if (isResumed()) { + setListShown(true); + } else { + setListShownNoAnimation(true); + } + } + + @Override + public void onLoaderReset(Loader<Cursor> loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + mAdapter.swapCursor(null); + } + + /** + * On click on item, start key view activity + */ + @Override + public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { + Intent viewIntent = null; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + viewIntent = new Intent(getActivity(), ViewKeyActivity.class); + } else { + viewIntent = new Intent(getActivity(), ViewKeyActivityJB.class); + } + viewIntent.setData( + KeychainContract + .KeyRings.buildPublicKeyRingsByMasterKeyIdUri( + Long.toString(mAdapter.getMasterKeyId(position)))); + startActivity(viewIntent); + } + + @TargetApi(11) + protected void encrypt(ActionMode mode, long[] keyRingMasterKeyIds) { + Intent intent = new Intent(getActivity(), EncryptActivity.class); + intent.setAction(EncryptActivity.ACTION_ENCRYPT); + intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, keyRingMasterKeyIds); + // used instead of startActivity set actionbar based on callingPackage + startActivityForResult(intent, 0); + + mode.finish(); + } + + /** + * Show dialog to delete key + * + * @param keyRingRowIds + */ + @TargetApi(11) + // TODO: this method needs an overhaul to handle both public and secret keys gracefully! + public void showDeleteKeyDialog(final ActionMode mode, long[] keyRingRowIds) { + // Message is received after key is deleted + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) { + mode.finish(); + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, + keyRingRowIds); + + deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog"); + } + + + @Override + public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { + // Get the searchview + MenuItem searchItem = menu.findItem(R.id.menu_key_list_search); + mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem); + + // Execute this when searching + mSearchView.setOnQueryTextListener(this); + + // Erase search result without focus + MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() { + @Override + public boolean onMenuItemActionExpand(MenuItem item) { + return true; + } + + @Override + public boolean onMenuItemActionCollapse(MenuItem item) { + mCurQuery = null; + mSearchView.setQuery("", true); + getLoaderManager().restartLoader(0, null, KeyListFragment.this); + return true; + } + }); + + super.onCreateOptionsMenu(menu, inflater); + } + + @Override + public boolean onQueryTextSubmit(String s) { + return true; + } + + @Override + public boolean onQueryTextChange(String s) { + // Called when the action bar search text has changed. Update + // the search filter, and restart the loader to do a new query + // with this filter. + mCurQuery = !TextUtils.isEmpty(s) ? s : null; + getLoaderManager().restartLoader(0, null, this); + 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 + */ + private class KeyListAdapter extends HighlightQueryCursorAdapter implements StickyListHeadersAdapter { + private LayoutInflater mInflater; + + private HashMap<Integer, Boolean> mSelection = new HashMap<Integer, Boolean>(); + + public KeyListAdapter(Context context, Cursor c, int flags) { + super(context, c, flags); + + mInflater = LayoutInflater.from(context); + } + + @Override + public Cursor swapCursor(Cursor newCursor) { + return super.swapCursor(newCursor); + } + + /** + * Bind cursor data to the item list view + * <p/> + * NOTE: CursorAdapter already implements the ViewHolder pattern in its getView() method. Thus + * no ViewHolder is required here. + */ + @Override + public void bindView(View view, Context context, Cursor cursor) { + + { // set name and stuff, common to both key types + TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId); + TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); + + String userId = cursor.getString(INDEX_USER_ID); + String[] userIdSplit = PgpKeyHelper.splitUserId(userId); + if (userIdSplit[0] != null) { + mainUserId.setText(highlightSearchQuery(userIdSplit[0])); + } else { + mainUserId.setText(R.string.user_id_no_name); + } + if (userIdSplit[1] != null) { + mainUserIdRest.setText(highlightSearchQuery(userIdSplit[1])); + mainUserIdRest.setVisibility(View.VISIBLE); + } else { + mainUserIdRest.setVisibility(View.GONE); + } + } + + { // set edit button and revoked info, specific by key type + View statusDivider = (View) view.findViewById(R.id.status_divider); + FrameLayout statusLayout = (FrameLayout) view.findViewById(R.id.status_layout); + Button button = (Button) view.findViewById(R.id.edit); + TextView revoked = (TextView) view.findViewById(R.id.revoked); + + if (cursor.getInt(KeyListFragment.INDEX_TYPE) == KeyTypes.SECRET) { + // this is a secret key - show the edit button + statusDivider.setVisibility(View.VISIBLE); + statusLayout.setVisibility(View.VISIBLE); + revoked.setVisibility(View.GONE); + button.setVisibility(View.VISIBLE); + + final long id = cursor.getLong(INDEX_MASTER_KEY_ID); + button.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + Intent editIntent = new Intent(getActivity(), EditKeyActivity.class); + editIntent.setData( + KeychainContract.KeyRings + .buildSecretKeyRingsByMasterKeyIdUri(Long.toString(id))); + editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY); + startActivityForResult(editIntent, 0); + } + }); + } else { + // this is a public key - hide the edit button, show if it's revoked + statusDivider.setVisibility(View.GONE); + button.setVisibility(View.GONE); + + boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; + statusLayout.setVisibility(isRevoked ? View.VISIBLE : View.GONE); + revoked.setVisibility(isRevoked ? View.VISIBLE : View.GONE); + } + } + + } + + public long getMasterKeyId(int id) { + if (!mCursor.moveToPosition(id)) { + throw new IllegalStateException("couldn't move cursor to position " + id); + } + + return mCursor.getLong(INDEX_MASTER_KEY_ID); + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return mInflater.inflate(R.layout.key_list_item, parent, false); + } + + /** + * Creates a new header view and binds the section headers to it. It uses the ViewHolder + * pattern. Most functionality is similar to getView() from Android's CursorAdapter. + * <p/> + * NOTE: The variables mDataValid and mCursor are available due to the super class + * CursorAdapter. + */ + @Override + public View getHeaderView(int position, View convertView, ViewGroup parent) { + HeaderViewHolder holder; + if (convertView == null) { + holder = new HeaderViewHolder(); + convertView = mInflater.inflate(R.layout.key_list_header, parent, false); + holder.mText = (TextView) convertView.findViewById(R.id.stickylist_header_text); + holder.mCount = (TextView) convertView.findViewById(R.id.contacts_num); + convertView.setTag(holder); + } else { + holder = (HeaderViewHolder) convertView.getTag(); + } + + if (!mDataValid) { + // no data available at this point + Log.d(Constants.TAG, "getHeaderView: No data available at this point!"); + return convertView; + } + + if (!mCursor.moveToPosition(position)) { + throw new IllegalStateException("couldn't move cursor to position " + position); + } + + if (mCursor.getInt(KeyListFragment.INDEX_TYPE) == KeyTypes.SECRET) { + { // set contact count + int num = mCursor.getCount(); + String contactsTotal = getResources().getQuantityString(R.plurals.n_contacts, num, num); + holder.mCount.setText(contactsTotal); + holder.mCount.setVisibility(View.VISIBLE); + } + + holder.mText.setText(convertView.getResources().getString(R.string.my_keys)); + return convertView; + } + + // set header text as first char in user id + String userId = mCursor.getString(KeyListFragment.INDEX_USER_ID); + String headerText = convertView.getResources().getString(R.string.user_id_no_name); + if (userId != null && userId.length() > 0) { + headerText = "" + + mCursor.getString(KeyListFragment.INDEX_USER_ID).subSequence(0, 1).charAt(0); + } + holder.mText.setText(headerText); + holder.mCount.setVisibility(View.GONE); + return convertView; + } + + /** + * Header IDs should be static, position=1 should always return the same Id that is. + */ + @Override + public long getHeaderId(int position) { + if (!mDataValid) { + // no data available at this point + Log.d(Constants.TAG, "getHeaderView: No data available at this point!"); + return -1; + } + + if (!mCursor.moveToPosition(position)) { + throw new IllegalStateException("couldn't move cursor to position " + position); + } + + // early breakout: all secret keys are assigned id 0 + if (mCursor.getInt(KeyListFragment.INDEX_TYPE) == KeyTypes.SECRET) { + return 1L; + } + // otherwise, return the first character of the name as ID + String userId = mCursor.getString(KeyListFragment.INDEX_USER_ID); + if (userId != null && userId.length() > 0) { + return userId.charAt(0); + } else { + return Long.MAX_VALUE; + } + } + + class HeaderViewHolder { + TextView mText; + TextView mCount; + } + + /** + * -------------------------- MULTI-SELECTION METHODS -------------- + */ + public void setNewSelection(int position, boolean value) { + mSelection.put(position, value); + notifyDataSetChanged(); + } + + public long[] getCurrentSelectedMasterKeyIds() { + long[] ids = new long[mSelection.size()]; + int i = 0; + // get master key ids + for (int pos : mSelection.keySet()) { + ids[i++] = mAdapter.getMasterKeyId(pos); + } + return ids; + } + + public void removeSelection(int position) { + mSelection.remove(position); + notifyDataSetChanged(); + } + + public void clearSelection() { + mSelection.clear(); + notifyDataSetChanged(); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + // let the adapter handle setting up the row views + View v = super.getView(position, convertView, parent); + + /** + * Change color for multi-selection + */ + if (mSelection.get(position) != null) { + // selected position color + v.setBackgroundColor(parent.getResources().getColor(R.color.emphasis)); + } else { + // default color + v.setBackgroundColor(Color.TRANSPARENT); + } + + return v; + } + + } + +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java deleted file mode 100644 index 4521786f7..000000000 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2012-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 org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.ExportHelper; - -import android.content.Intent; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; - -public class KeyListPublicActivity extends DrawerActivity { - - ExportHelper mExportHelper; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mExportHelper = new ExportHelper(this); - - setContentView(R.layout.key_list_public_activity); - - // now setup navigation drawer in DrawerActivity... - setupDrawerNavigation(savedInstanceState); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - getMenuInflater().inflate(R.menu.key_list_public, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_key_list_public_import: - Intent intentImport = new Intent(this, ImportKeysActivity.class); - startActivityForResult(intentImport, 0); - - return true; - case R.id.menu_key_list_public_export: - mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.path.APP_DIR - + "/pubexport.asc"); - - return true; - default: - return super.onOptionsItemSelected(item); - } - } - -} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java deleted file mode 100644 index f2cb8a265..000000000 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Copyright (C) 2013 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 java.util.ArrayList; -import java.util.Set; - -import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.ui.adapter.KeyListPublicAdapter; -import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; - -import se.emilsjolander.stickylistheaders.ApiLevelTooLowException; -import se.emilsjolander.stickylistheaders.StickyListHeadersListView; - -import android.annotation.SuppressLint; -import android.annotation.TargetApi; -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.os.Build; -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; -import android.support.v4.view.MenuItemCompat; -import android.support.v7.widget.SearchView; -import android.text.TextUtils; -import android.view.ActionMode; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.AbsListView.MultiChoiceModeListener; -import android.widget.AdapterView; -import android.widget.ListView; -import android.widget.Toast; - -import com.beardedhen.androidbootstrap.BootstrapButton; - -/** - * 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 KeyListPublicFragment extends Fragment implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener, - LoaderManager.LoaderCallbacks<Cursor> { - - private KeyListPublicAdapter mAdapter; - private StickyListHeadersListView mStickyList; - private String mCurQuery; - private SearchView mSearchView; - // empty list layout - private BootstrapButton mButtonEmptyCreate; - private BootstrapButton mButtonEmptyImport; - - - /** - * Load custom layout with StickyListView from library - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.key_list_public_fragment, container, false); - setHasOptionsMenu(true); - mButtonEmptyCreate = (BootstrapButton) view.findViewById(R.id.key_list_empty_button_create); - mButtonEmptyCreate.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - Intent intent = new Intent(getActivity(), EditKeyActivity.class); - intent.setAction(EditKeyActivity.ACTION_CREATE_KEY); - intent.putExtra(EditKeyActivity.EXTRA_GENERATE_DEFAULT_KEYS, true); - intent.putExtra(EditKeyActivity.EXTRA_USER_IDS, ""); // show user id view - startActivityForResult(intent, 0); - } - }); - - mButtonEmptyImport = (BootstrapButton) view.findViewById(R.id.key_list_empty_button_import); - mButtonEmptyImport.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - Intent intent = new Intent(getActivity(), ImportKeysActivity.class); - intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE); - startActivityForResult(intent, 0); - } - }); - - return view; - } - - /** - * Define Adapter and Loader on create of Activity - */ - @SuppressLint("NewApi") - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mStickyList = (StickyListHeadersListView) getActivity().findViewById(R.id.list); - - mStickyList.setOnItemClickListener(this); - mStickyList.setAreHeadersSticky(true); - mStickyList.setDrawingListUnderStickyHeader(false); - mStickyList.setFastScrollEnabled(true); - try { - mStickyList.setFastScrollAlwaysVisible(true); - } catch (ApiLevelTooLowException e) { - } - - // this view is made visible if no data is available - mStickyList.setEmptyView(getActivity().findViewById(R.id.empty)); - - /* - * ActionBarSherlock does not support MultiChoiceModeListener. Thus multi-selection is only - * available for Android >= 3.0 - */ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - mStickyList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); - mStickyList.getWrappedList().setMultiChoiceModeListener(new MultiChoiceModeListener() { - - @Override - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - android.view.MenuInflater inflater = getActivity().getMenuInflater(); - inflater.inflate(R.menu.key_list_public_multi, menu); - return true; - } - - @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - return false; - } - - @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - Set<Integer> positions = mAdapter.getCurrentCheckedPosition(); - - // get IDs for checked positions as long array - long[] ids = new long[positions.size()]; - int i = 0; - for (int pos : positions) { - ids[i] = mAdapter.getItemId(pos); - i++; - } - - switch (item.getItemId()) { - case R.id.menu_key_list_public_multi_encrypt: { - encrypt(mode, ids); - break; - } - case R.id.menu_key_list_public_multi_delete: { - showDeleteKeyDialog(mode, ids); - break; - } - case R.id.menu_key_list_public_multi_select_all: { - //Select all - int localCount = mStickyList.getCount(); - for(int k = 0; k < localCount; k++) { - mStickyList.setItemChecked(k, true); - } - break; - } - } - return true; - } - - @Override - public void onDestroyActionMode(ActionMode mode) { - mAdapter.clearSelection(); - } - - @Override - public void onItemCheckedStateChanged(ActionMode mode, int position, long id, - boolean checked) { - if (checked) { - mAdapter.setNewSelection(position, checked); - } else { - mAdapter.removeSelection(position); - } - int count = mAdapter.getCurrentCheckedPosition().size(); - String keysSelected = getResources().getQuantityString( - R.plurals.key_list_selected_keys, count, count); - mode.setTitle(keysSelected); - } - - }); - } - - // NOTE: Not supported by StickyListHeader, thus no indicator is shown while loading - // Start out with a progress indicator. - // setListShown(false); - - // Create an empty adapter we will use to display the loaded data. - mAdapter = new KeyListPublicAdapter(getActivity(), null, Id.type.public_key, USER_ID_INDEX); - mStickyList.setAdapter(mAdapter); - - // Prepare the loader. Either re-connect with an existing one, - // or start a new one. - getLoaderManager().initLoader(0, null, this); - } - - // These are the rows that we will retrieve. - static final String[] PROJECTION = new String[]{ - KeychainContract.KeyRings._ID, - KeychainContract.KeyRings.MASTER_KEY_ID, - KeychainContract.UserIds.USER_ID, - KeychainContract.Keys.IS_REVOKED - }; - - static final int USER_ID_INDEX = 2; - - static final String SORT_ORDER = UserIds.USER_ID + " ASC"; - - @Override - public Loader<Cursor> onCreateLoader(int id, Bundle args) { - // This is called when a new Loader needs to be created. This - // sample only has one Loader, so we don't care about the ID. - Uri baseUri = KeyRings.buildPublicKeyRingsUri(); - String where = null; - String whereArgs[] = null; - if(mCurQuery != null){ - where = KeychainContract.UserIds.USER_ID + " LIKE ?"; - whereArgs = new String[]{mCurQuery+"%"}; - } - // Now create and return a CursorLoader that will take care of - // creating a Cursor for the data being displayed. - return new CursorLoader(getActivity(), baseUri, PROJECTION, where, whereArgs, SORT_ORDER); - } - - @Override - public void onLoadFinished(Loader<Cursor> loader, Cursor data) { - // Swap the new cursor in. (The framework will take care of closing the - // old cursor once we return.) - mAdapter.swapCursor(data); - - mStickyList.setAdapter(mAdapter); - - // NOTE: Not supported by StickyListHeader, thus no indicator is shown while loading - // The list should now be shown. - // if (isResumed()) { - // setListShown(true); - // } else { - // setListShownNoAnimation(true); - // } - } - - @Override - public void onLoaderReset(Loader<Cursor> loader) { - // This is called when the last Cursor provided to onLoadFinished() - // above is about to be closed. We need to make sure we are no - // longer using it. - mAdapter.swapCursor(null); - } - - /** - * On click on item, start key view activity - */ - @Override - public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { - Intent viewIntent = null; - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { - viewIntent = new Intent(getActivity(), ViewKeyActivity.class); - } else { - viewIntent = new Intent(getActivity(), ViewKeyActivityJB.class); - } - viewIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsUri(Long.toString(id))); - startActivity(viewIntent); - } - - @TargetApi(11) - public void encrypt(ActionMode mode, long[] keyRingRowIds) { - // get master key ids from row ids - long[] keyRingIds = new long[keyRingRowIds.length]; - for (int i = 0; i < keyRingRowIds.length; i++) { - keyRingIds[i] = ProviderHelper.getPublicMasterKeyId(getActivity(), keyRingRowIds[i]); - } - - Intent intent = new Intent(getActivity(), EncryptActivity.class); - intent.setAction(EncryptActivity.ACTION_ENCRYPT); - intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, keyRingIds); - // used instead of startActivity set actionbar based on callingPackage - startActivityForResult(intent, 0); - - mode.finish(); - } - - /** - * Show dialog to delete key - * - * @param keyRingRowIds - */ - @TargetApi(11) - public void showDeleteKeyDialog(final ActionMode mode, long[] keyRingRowIds) { - // Message is received after key is deleted - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) { - Bundle returnData = message.getData(); - if (returnData != null - && returnData.containsKey(DeleteKeyDialogFragment.MESSAGE_NOT_DELETED)) { - ArrayList<String> notDeleted = - returnData.getStringArrayList(DeleteKeyDialogFragment.MESSAGE_NOT_DELETED); - String notDeletedMsg = ""; - for (String userId : notDeleted) { - notDeletedMsg += userId + "\n"; - } - Toast.makeText(getActivity(), getString(R.string.error_can_not_delete_contacts, notDeletedMsg) - + getResources().getQuantityString(R.plurals.error_can_not_delete_info, notDeleted.size()), - Toast.LENGTH_LONG).show(); - - mode.finish(); - } - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, - keyRingRowIds, Id.type.public_key); - - deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog"); - } - - - @Override - public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { - - // Get the searchview - MenuItem searchItem = menu.findItem(R.id.menu_key_list_public_search); - mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem); - - // Execute this when searching - mSearchView.setOnQueryTextListener(this); - - super.onCreateOptionsMenu(menu, inflater); - - } - - @Override - public boolean onQueryTextSubmit(String s) { - return true; - } - - @Override - public boolean onQueryTextChange(String s) { - // Called when the action bar search text has changed. Update - // the search filter, and restart the loader to do a new query - // with this filter. - String newQuery = !TextUtils.isEmpty(s) ? s : null; - mCurQuery = newQuery; - getLoaderManager().restartLoader(0, null, this); - return true; - } -} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java deleted file mode 100644 index eaac6d8b1..000000000 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (C) 2013 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 java.util.ArrayList; -import java.util.Set; - -import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; -import org.sufficientlysecure.keychain.ui.adapter.KeyListSecretAdapter; -import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; - -import android.annotation.SuppressLint; -import android.annotation.TargetApi; -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; -import android.support.v4.app.ListFragment; -import android.support.v4.app.LoaderManager; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; -import android.view.ActionMode; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ListView; -import android.widget.AbsListView.MultiChoiceModeListener; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.Toast; - -public class KeyListSecretFragment extends ListFragment implements - LoaderManager.LoaderCallbacks<Cursor>, OnItemClickListener { - - private KeyListSecretActivity mKeyListSecretActivity; - private KeyListSecretAdapter mAdapter; - - /** - * Define Adapter and Loader on create of Activity - */ - @SuppressLint("NewApi") - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mKeyListSecretActivity = (KeyListSecretActivity) getActivity(); - - getListView().setOnItemClickListener(this); - - // Give some text to display if there is no data. In a real - // application this would come from a resource. - setEmptyText(getString(R.string.list_empty)); - - /* - * ActionBarSherlock does not support MultiChoiceModeListener. Thus multi-selection is only - * available for Android >= 3.0 - */ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); - getListView().setMultiChoiceModeListener(new MultiChoiceModeListener() { - - @Override - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - android.view.MenuInflater inflater = getActivity().getMenuInflater(); - inflater.inflate(R.menu.key_list_secret_multi, menu); - return true; - } - - @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - return false; - } - - @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - Set<Integer> positions = mAdapter.getCurrentCheckedPosition(); - - // get IDs for checked positions as long array - long[] ids = new long[positions.size()]; - int i = 0; - for (int pos : positions) { - ids[i] = mAdapter.getItemId(pos); - i++; - } - - switch (item.getItemId()) { - case R.id.menu_key_list_public_multi_delete: { - showDeleteKeyDialog(mode, ids); - break; - } - case R.id.menu_key_list_public_multi_select_all: { - //Select all - int localCount = getListView().getCount(); - for(int k = 0; k < localCount; k++) { - getListView().setItemChecked(k, true); - } - break; - } - } - return true; - } - - @Override - public void onDestroyActionMode(ActionMode mode) { - mAdapter.clearSelection(); - } - - @Override - public void onItemCheckedStateChanged(ActionMode mode, int position, long id, - boolean checked) { - if (checked) { - mAdapter.setNewSelection(position, checked); - } else { - mAdapter.removeSelection(position); - } - - int count = getListView().getCheckedItemCount(); - String keysSelected = getResources().getQuantityString( - R.plurals.key_list_selected_keys, count, count); - mode.setTitle(keysSelected); - } - - }); - } - - // We have a menu item to show in action bar. - setHasOptionsMenu(true); - - // Start out with a progress indicator. - setListShown(false); - - // Create an empty adapter we will use to display the loaded data. - mAdapter = new KeyListSecretAdapter(mKeyListSecretActivity, null, 0); - setListAdapter(mAdapter); - - // Prepare the loader. Either re-connect with an existing one, - // or start a new one. - getLoaderManager().initLoader(0, null, this); - } - - // These are the rows that we will retrieve. - static final String[] PROJECTION = new String[]{KeyRings._ID, KeyRings.MASTER_KEY_ID, - UserIds.USER_ID}; - static final String SORT_ORDER = UserIds.USER_ID + " COLLATE LOCALIZED ASC"; - - public Loader<Cursor> onCreateLoader(int id, Bundle args) { - // This is called when a new Loader needs to be created. This - // sample only has one Loader, so we don't care about the ID. - // First, pick the base URI to use depending on whether we are - // currently filtering. - Uri baseUri = KeyRings.buildSecretKeyRingsUri(); - - // Now create and return a CursorLoader that will take care of - // creating a Cursor for the data being displayed. - return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, SORT_ORDER); - } - - public void onLoadFinished(Loader<Cursor> loader, Cursor data) { - // Swap the new cursor in. (The framework will take care of closing the - // old cursor once we return.) - mAdapter.swapCursor(data); - - // The list should now be shown. - if (isResumed()) { - setListShown(true); - } else { - setListShownNoAnimation(true); - } - } - - public void onLoaderReset(Loader<Cursor> loader) { - // This is called when the last Cursor provided to onLoadFinished() - // above is about to be closed. We need to make sure we are no - // longer using it. - mAdapter.swapCursor(null); - } - - /** - * On click on item, start key view activity - */ - @Override - public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { - Intent editIntent = new Intent(mKeyListSecretActivity, EditKeyActivity.class); - editIntent.setData(KeychainContract.KeyRings.buildSecretKeyRingsUri(Long.toString(id))); - editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY); - startActivityForResult(editIntent, 0); - } - - /** - * Show dialog to delete key - * - * @param keyRingRowIds - */ - @TargetApi(11) - public void showDeleteKeyDialog(final ActionMode mode, long[] keyRingRowIds) { - // Message is received after key is deleted - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) { - mode.finish(); - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, - keyRingRowIds, Id.type.secret_key); - - deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog"); - } - -} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java index af20b499b..04179cb80 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java @@ -16,6 +16,11 @@ package org.sufficientlysecure.keychain.ui; +import android.annotation.SuppressLint; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.preference.*; import org.spongycastle.bcpg.HashAlgorithmTags; import org.spongycastle.openpgp.PGPEncryptedData; import org.sufficientlysecure.keychain.Constants; @@ -24,25 +29,13 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.ui.widget.IntegerListPreference; -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.Intent; -import android.os.Build; -import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.Preference; -import android.preference.PreferenceActivity; -import android.preference.PreferenceFragment; -import android.preference.PreferenceScreen; -import android.support.v7.app.ActionBarActivity; - import java.util.List; @SuppressLint("NewApi") public class PreferencesActivity extends PreferenceActivity { - public final static String ACTION_PREFS_GEN = "org.sufficientlysecure.keychain.ui.PREFS_GEN"; - public final static String ACTION_PREFS_ADV = "org.sufficientlysecure.keychain.ui.PREFS_ADV"; + public static final String ACTION_PREFS_GEN = "org.sufficientlysecure.keychain.ui.PREFS_GEN"; + public static final String ACTION_PREFS_ADV = "org.sufficientlysecure.keychain.ui.PREFS_ADV"; private PreferenceScreen mKeyServerPreference = null; private static Preferences mPreferences; @@ -57,16 +50,15 @@ public class PreferencesActivity extends PreferenceActivity { // actionBar.setDisplayHomeAsUpEnabled(false); // actionBar.setHomeButtonEnabled(false); - //addPreferencesFromResource(R.xml.preferences); String action = getIntent().getAction(); if (action != null && action.equals(ACTION_PREFS_GEN)) { addPreferencesFromResource(R.xml.gen_preferences); initializePassPassPhraceCacheTtl( - (IntegerListPreference) findPreference(Constants.pref.PASS_PHRASE_CACHE_TTL)); + (IntegerListPreference) findPreference(Constants.Pref.PASS_PHRASE_CACHE_TTL)); - mKeyServerPreference = (PreferenceScreen) findPreference(Constants.pref.KEY_SERVERS); + mKeyServerPreference = (PreferenceScreen) findPreference(Constants.Pref.KEY_SERVERS); String servers[] = mPreferences.getKeyServers(); mKeyServerPreference.setSummary(getResources().getQuantityString(R.plurals.n_key_servers, servers.length, servers.length)); @@ -86,11 +78,11 @@ public class PreferencesActivity extends PreferenceActivity { addPreferencesFromResource(R.xml.adv_preferences); initializeEncryptionAlgorithm( - (IntegerListPreference) findPreference(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM)); + (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_ENCRYPTION_ALGORITHM)); - int[] valueIds = new int[] { Id.choice.compression.none, Id.choice.compression.zip, + int[] valueIds = new int[]{Id.choice.compression.none, Id.choice.compression.zip, Id.choice.compression.zlib, Id.choice.compression.bzip2, }; - String[] entries = new String[] { + String[] entries = new String[]{ getString(R.string.choice_none) + " (" + getString(R.string.compression_fast) + ")", "ZIP (" + getString(R.string.compression_fast) + ")", "ZLIB (" + getString(R.string.compression_fast) + ")", @@ -101,20 +93,22 @@ public class PreferencesActivity extends PreferenceActivity { } initializeHashAlgorithm( - (IntegerListPreference) findPreference(Constants.pref.DEFAULT_HASH_ALGORITHM), - valueIds, entries, values); + (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_HASH_ALGORITHM), + valueIds, entries, values); initializeMessageCompression( - (IntegerListPreference) findPreference(Constants.pref.DEFAULT_MESSAGE_COMPRESSION), - valueIds, entries, values); + (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_MESSAGE_COMPRESSION), + valueIds, entries, values); initializeFileCompression( - (IntegerListPreference) findPreference(Constants.pref.DEFAULT_FILE_COMPRESSION), - entries, values); + (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_FILE_COMPRESSION), + entries, values); - initializeAsciiArmour((CheckBoxPreference) findPreference(Constants.pref.DEFAULT_ASCII_ARMOUR)); + initializeAsciiArmour( + (CheckBoxPreference) findPreference(Constants.Pref.DEFAULT_ASCII_ARMOUR)); - initializeForceV3Signatures((CheckBoxPreference) findPreference(Constants.pref.FORCE_V3_SIGNATURES)); + initializeForceV3Signatures( + (CheckBoxPreference) findPreference(Constants.Pref.FORCE_V3_SIGNATURES)); } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { // Load the legacy preferences headers @@ -151,7 +145,9 @@ public class PreferencesActivity extends PreferenceActivity { loadHeadersFromResource(R.xml.preference_headers, target); } - /** This fragment shows the general preferences in android 3.0+ */ + /** + * This fragment shows the general preferences in android 3.0+ + */ public static class GeneralPrefsFragment extends PreferenceFragment { private PreferenceScreen mKeyServerPreference = null; @@ -164,9 +160,9 @@ public class PreferencesActivity extends PreferenceActivity { addPreferencesFromResource(R.xml.gen_preferences); initializePassPassPhraceCacheTtl( - (IntegerListPreference) findPreference(Constants.pref.PASS_PHRASE_CACHE_TTL)); + (IntegerListPreference) findPreference(Constants.Pref.PASS_PHRASE_CACHE_TTL)); - mKeyServerPreference = (PreferenceScreen) findPreference(Constants.pref.KEY_SERVERS); + mKeyServerPreference = (PreferenceScreen) findPreference(Constants.Pref.KEY_SERVERS); String servers[] = mPreferences.getKeyServers(); mKeyServerPreference.setSummary(getResources().getQuantityString(R.plurals.n_key_servers, servers.length, servers.length)); @@ -206,7 +202,9 @@ public class PreferencesActivity extends PreferenceActivity { } } - /** This fragment shows the advanced preferences in android 3.0+ */ + /** + * This fragment shows the advanced preferences in android 3.0+ + */ public static class AdvancedPrefsFragment extends PreferenceFragment { @Override @@ -217,11 +215,11 @@ public class PreferencesActivity extends PreferenceActivity { addPreferencesFromResource(R.xml.adv_preferences); initializeEncryptionAlgorithm( - (IntegerListPreference) findPreference(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM)); + (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_ENCRYPTION_ALGORITHM)); - int[] valueIds = new int[] { Id.choice.compression.none, Id.choice.compression.zip, + int[] valueIds = new int[]{Id.choice.compression.none, Id.choice.compression.zip, Id.choice.compression.zlib, Id.choice.compression.bzip2, }; - String[] entries = new String[] { + String[] entries = new String[]{ getString(R.string.choice_none) + " (" + getString(R.string.compression_fast) + ")", "ZIP (" + getString(R.string.compression_fast) + ")", "ZLIB (" + getString(R.string.compression_fast) + ")", @@ -232,24 +230,26 @@ public class PreferencesActivity extends PreferenceActivity { } initializeHashAlgorithm( - (IntegerListPreference) findPreference(Constants.pref.DEFAULT_HASH_ALGORITHM), - valueIds, entries, values); + (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_HASH_ALGORITHM), + valueIds, entries, values); initializeMessageCompression( - (IntegerListPreference) findPreference(Constants.pref.DEFAULT_MESSAGE_COMPRESSION), - valueIds, entries, values); + (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_MESSAGE_COMPRESSION), + valueIds, entries, values); initializeFileCompression( - (IntegerListPreference) findPreference(Constants.pref.DEFAULT_FILE_COMPRESSION), + (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_FILE_COMPRESSION), entries, values); - initializeAsciiArmour((CheckBoxPreference) findPreference(Constants.pref.DEFAULT_ASCII_ARMOUR)); + initializeAsciiArmour( + (CheckBoxPreference) findPreference(Constants.Pref.DEFAULT_ASCII_ARMOUR)); - initializeForceV3Signatures((CheckBoxPreference) findPreference(Constants.pref.FORCE_V3_SIGNATURES)); + initializeForceV3Signatures( + (CheckBoxPreference) findPreference(Constants.Pref.FORCE_V3_SIGNATURES)); } } - protected boolean isValidFragment (String fragmentName) { + protected boolean isValidFragment(String fragmentName) { return AdvancedPrefsFragment.class.getName().equals(fragmentName) || GeneralPrefsFragment.class.getName().equals(fragmentName) || super.isValidFragment(fragmentName); @@ -270,11 +270,11 @@ public class PreferencesActivity extends PreferenceActivity { } private static void initializeEncryptionAlgorithm(final IntegerListPreference mEncryptionAlgorithm) { - int valueIds[] = { PGPEncryptedData.AES_128, PGPEncryptedData.AES_192, + int valueIds[] = {PGPEncryptedData.AES_128, PGPEncryptedData.AES_192, PGPEncryptedData.AES_256, PGPEncryptedData.BLOWFISH, PGPEncryptedData.TWOFISH, PGPEncryptedData.CAST5, PGPEncryptedData.DES, PGPEncryptedData.TRIPLE_DES, PGPEncryptedData.IDEA, }; - String entries[] = { "AES-128", "AES-192", "AES-256", "Blowfish", "Twofish", "CAST5", + String entries[] = {"AES-128", "AES-192", "AES-256", "Blowfish", "Twofish", "CAST5", "DES", "Triple DES", "IDEA", }; String values[] = new String[valueIds.length]; for (int i = 0; i < values.length; ++i) { @@ -298,10 +298,10 @@ public class PreferencesActivity extends PreferenceActivity { private static void initializeHashAlgorithm (final IntegerListPreference mHashAlgorithm, int[] valueIds, String[] entries, String[] values) { - valueIds = new int[] { HashAlgorithmTags.MD5, HashAlgorithmTags.RIPEMD160, + valueIds = new int[]{HashAlgorithmTags.MD5, HashAlgorithmTags.RIPEMD160, HashAlgorithmTags.SHA1, HashAlgorithmTags.SHA224, HashAlgorithmTags.SHA256, HashAlgorithmTags.SHA384, HashAlgorithmTags.SHA512, }; - entries = new String[] { "MD5", "RIPEMD-160", "SHA-1", "SHA-224", "SHA-256", "SHA-384", + entries = new String[]{"MD5", "RIPEMD-160", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512", }; values = new String[valueIds.length]; for (int i = 0; i < values.length; ++i) { @@ -321,8 +321,9 @@ public class PreferencesActivity extends PreferenceActivity { }); } - private static void initializeMessageCompression - (final IntegerListPreference mMessageCompression, int[] valueIds, String[] entries, String[] values) { + private static void initializeMessageCompression( + final IntegerListPreference mMessageCompression, + int[] valueIds, String[] entries, String[] values) { mMessageCompression.setEntries(entries); mMessageCompression.setEntryValues(values); mMessageCompression.setValue("" + mPreferences.getDefaultMessageCompression()); @@ -377,4 +378,4 @@ public class PreferencesActivity extends PreferenceActivity { } }); } -}
\ No newline at end of file +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java index b39b93f52..719378274 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java @@ -16,14 +16,6 @@ package org.sufficientlysecure.keychain.ui; -import java.util.Vector; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.ActionBarHelper; -import org.sufficientlysecure.keychain.ui.widget.Editor; -import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener; -import org.sufficientlysecure.keychain.ui.widget.KeyServerEditor; - import android.content.Context; import android.content.Intent; import android.os.Bundle; @@ -33,6 +25,13 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.TextView; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ActionBarHelper; +import org.sufficientlysecure.keychain.ui.widget.Editor; +import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener; +import org.sufficientlysecure.keychain.ui.widget.KeyServerEditor; + +import java.util.Vector; public class PreferencesKeyServerActivity extends ActionBarActivity implements OnClickListener, EditorListener { @@ -50,20 +49,21 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O super.onCreate(savedInstanceState); // Inflate a "Done"/"Cancel" custom action bar view - ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_okay, + ActionBarHelper.setTwoButtonView(getSupportActionBar(), R.string.btn_okay, R.drawable.ic_action_done, new View.OnClickListener() { @Override public void onClick(View v) { // ok okClicked(); } - }, R.string.btn_do_not_save, new View.OnClickListener() { + }, R.string.btn_do_not_save, R.drawable.ic_action_cancel, new View.OnClickListener() { @Override public void onClick(View v) { // cancel cancelClicked(); } - }); + } + ); setContentView(R.layout.key_server_preference); @@ -81,11 +81,11 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O Intent intent = getIntent(); String servers[] = intent.getStringArrayExtra(EXTRA_KEY_SERVERS); if (servers != null) { - for (int i = 0; i < servers.length; ++i) { + for (String serv : servers) { KeyServerEditor view = (KeyServerEditor) mInflater.inflate( R.layout.key_server_editor, mEditors, false); view.setEditorListener(this); - view.setValue(servers[i]); + view.setValue(serv); mEditors.addView(view); } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java index 86ae0073f..874703704 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java @@ -17,14 +17,13 @@ package org.sufficientlysecure.keychain.ui; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.ActionBarHelper; - import android.content.Intent; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.view.View; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ActionBarHelper; public class SelectPublicKeyActivity extends ActionBarActivity { @@ -39,27 +38,28 @@ public class SelectPublicKeyActivity extends ActionBarActivity { SelectPublicKeyFragment mSelectFragment; - long selectedMasterKeyIds[]; + long mSelectedMasterKeyIds[]; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Inflate a "Done"/"Cancel" custom action bar view - ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_okay, + ActionBarHelper.setTwoButtonView(getSupportActionBar(), R.string.btn_okay, R.drawable.ic_action_done, new View.OnClickListener() { @Override public void onClick(View v) { // ok okClicked(); } - }, R.string.btn_do_not_save, new View.OnClickListener() { + }, R.string.btn_do_not_save, R.drawable.ic_action_cancel, new View.OnClickListener() { @Override public void onClick(View v) { // cancel cancelClicked(); } - }); + } + ); setContentView(R.layout.select_public_key_activity); @@ -79,7 +79,7 @@ public class SelectPublicKeyActivity extends ActionBarActivity { } // Create an instance of the fragment - mSelectFragment = SelectPublicKeyFragment.newInstance(selectedMasterKeyIds); + mSelectFragment = SelectPublicKeyFragment.newInstance(mSelectedMasterKeyIds); // Add the fragment to the 'fragment_container' FrameLayout getSupportFragmentManager().beginTransaction() @@ -124,7 +124,7 @@ public class SelectPublicKeyActivity extends ActionBarActivity { // } // preselected master keys - selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS); + mSelectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS); } private void cancelClicked() { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java index a43c84cf4..6ab9f1c6e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java @@ -17,20 +17,7 @@ package org.sufficientlysecure.keychain.ui; -import java.util.Date; -import java.util.Vector; - -import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.compatibility.ListFragmentWorkaround; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; -import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; -import org.sufficientlysecure.keychain.provider.KeychainDatabase; -import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; -import org.sufficientlysecure.keychain.ui.adapter.SelectKeyCursorAdapter; - -import android.app.Activity; +import android.content.Context; import android.database.Cursor; import android.database.DatabaseUtils; import android.net.Uri; @@ -41,20 +28,40 @@ import android.support.v4.content.Loader; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; -import android.widget.EditText; -import android.widget.ListView; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.*; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.compatibility.ListFragmentWorkaround; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.provider.KeychainDatabase; +import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; +import org.sufficientlysecure.keychain.ui.adapter.SelectKeyCursorAdapter; + +import java.util.Date; +import java.util.Vector; public class SelectPublicKeyFragment extends ListFragmentWorkaround implements TextWatcher, LoaderManager.LoaderCallbacks<Cursor> { public static final String ARG_PRESELECTED_KEY_IDS = "preselected_key_ids"; - private Activity mActivity; private SelectKeyCursorAdapter mAdapter; - private ListView mListView; private EditText mSearchView; private long mSelectedMasterKeyIds[]; private String mCurQuery; + // copied from ListFragment + static final int INTERNAL_EMPTY_ID = 0x00ff0001; + static final int INTERNAL_PROGRESS_CONTAINER_ID = 0x00ff0002; + static final int INTERNAL_LIST_CONTAINER_ID = 0x00ff0003; + // added for search view + static final int SEARCH_ID = 0x00ff0004; + /** * Creates new instance of this fragment */ @@ -72,27 +79,101 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mSearchView = (EditText)getActivity().findViewById(R.id.select_public_key_search); - mSearchView.addTextChangedListener(this); mSelectedMasterKeyIds = getArguments().getLongArray(ARG_PRESELECTED_KEY_IDS); } /** + * Copied from ListFragment and added EditText for search on top of list. + * We do not use a custom layout here, because this breaks the progress bar functionality + * of ListFragment. + * + * @param inflater + * @param container + * @param savedInstanceState + * @return + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final Context context = getActivity(); + + FrameLayout root = new FrameLayout(context); + + // ------------------------------------------------------------------ + + LinearLayout pframe = new LinearLayout(context); + pframe.setId(INTERNAL_PROGRESS_CONTAINER_ID); + pframe.setOrientation(LinearLayout.VERTICAL); + pframe.setVisibility(View.GONE); + pframe.setGravity(Gravity.CENTER); + + ProgressBar progress = new ProgressBar(context, null, + android.R.attr.progressBarStyleLarge); + pframe.addView(progress, new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + + root.addView(pframe, new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); + + // ------------------------------------------------------------------ + + FrameLayout lframe = new FrameLayout(context); + lframe.setId(INTERNAL_LIST_CONTAINER_ID); + + TextView tv = new TextView(getActivity()); + tv.setId(INTERNAL_EMPTY_ID); + tv.setGravity(Gravity.CENTER); + lframe.addView(tv, new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); + + // Added for search view: linearLayout, mSearchView + LinearLayout linearLayout = new LinearLayout(context); + linearLayout.setOrientation(LinearLayout.VERTICAL); + + mSearchView = new EditText(context); + mSearchView.setId(SEARCH_ID); + mSearchView.setHint(R.string.menu_search); + mSearchView.setCompoundDrawablesWithIntrinsicBounds( + getResources().getDrawable(R.drawable.ic_action_search), null, null, null); + + linearLayout.addView(mSearchView, new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + + ListView lv = new ListView(getActivity()); + lv.setId(android.R.id.list); + lv.setDrawSelectorOnTop(false); + linearLayout.addView(lv, new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); + + lframe.addView(linearLayout, new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); + + root.addView(lframe, new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); + + // ------------------------------------------------------------------ + + root.setLayoutParams(new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); + + return root; + } + + /** * Define Adapter and Loader on create of Activity */ @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - mActivity = getActivity(); - mListView = getListView(); + getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); - mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); // Give some text to display if there is no data. In a real // application this would come from a resource. setEmptyText(getString(R.string.list_empty)); - mAdapter = new SelectKeyCursorAdapter(mActivity, null, 0, mListView, Id.type.public_key); + mSearchView.addTextChangedListener(this); + + mAdapter = new SelectKeyCursorAdapter(getActivity(), null, 0, getListView(), Id.type.public_key); setListAdapter(mAdapter); @@ -106,16 +187,16 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T /** * Selects items based on master key ids in list view - * + * * @param masterKeyIds */ private void preselectMasterKeyIds(long[] masterKeyIds) { if (masterKeyIds != null) { - for (int i = 0; i < mListView.getCount(); ++i) { + for (int i = 0; i < getListView().getCount(); ++i) { long keyId = mAdapter.getMasterKeyId(i); - for (int j = 0; j < masterKeyIds.length; ++j) { - if (keyId == masterKeyIds[j]) { - mListView.setItemChecked(i, true); + for (long masterKeyId : masterKeyIds) { + if (keyId == masterKeyId) { + getListView().setItemChecked(i, true); break; } } @@ -125,15 +206,15 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T /** * Returns all selected master key ids - * + * * @return */ public long[] getSelectedMasterKeyIds() { // mListView.getCheckedItemIds() would give the row ids of the KeyRings not the master key // ids! Vector<Long> vector = new Vector<Long>(); - for (int i = 0; i < mListView.getCount(); ++i) { - if (mListView.isItemChecked(i)) { + for (int i = 0; i < getListView().getCount(); ++i) { + if (getListView().isItemChecked(i)) { vector.add(mAdapter.getMasterKeyId(i)); } } @@ -149,13 +230,13 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T /** * Returns all selected user ids - * + * * @return */ public String[] getSelectedUserIds() { Vector<String> userIds = new Vector<String>(); - for (int i = 0; i < mListView.getCount(); ++i) { - if (mListView.isItemChecked(i)) { + for (int i = 0; i < getListView().getCount(); ++i) { + if (getListView().isItemChecked(i)) { userIds.add((String) mAdapter.getUserId(i)); } } @@ -173,7 +254,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T // These are the rows that we will retrieve. long now = new Date().getTime() / 1000; - String[] projection = new String[] { + String[] projection = new String[]{ KeyRings._ID, KeyRings.MASTER_KEY_ID, UserIds.USER_ID, @@ -204,22 +285,6 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T inMasterKeyList += ")"; } - // 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 (inIdList != null) { - // qb.appendWhere(" OR (" + inIdList + ")"); - // } - // } - String orderBy = UserIds.USER_ID + " ASC"; if (inMasterKeyList != null) { // sort by selected master keys @@ -227,9 +292,9 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T } String where = null; String whereArgs[] = null; - if(mCurQuery != null){ + if (mCurQuery != null) { where = UserIds.USER_ID + " LIKE ?"; - whereArgs = new String[]{mCurQuery+"%"}; + whereArgs = new String[]{"%" + mCurQuery + "%"}; } // Now create and return a CursorLoader that will take care of @@ -241,6 +306,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) + mAdapter.setSearchQuery(mCurQuery); mAdapter.swapCursor(data); // The list should now be shown. @@ -274,8 +340,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T @Override public void afterTextChanged(Editable editable) { - String newQuery = !TextUtils.isEmpty(editable.toString()) ? editable.toString() : null; - mCurQuery = newQuery; + mCurQuery = !TextUtils.isEmpty(editable.toString()) ? editable.toString() : null; getLoaderManager().restartLoader(0, null, this); } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java index cd3b2cf78..0ff88d97c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java @@ -1,43 +1,37 @@ /* - * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de> - * Copyright (C) 2010 Thialfihar <thi@thialfihar.org> + * 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 + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * 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. + * 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 org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; - import android.content.Intent; +import android.net.Uri; import android.os.Bundle; import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarActivity; -import android.view.Menu; -public class SelectSecretKeyActivity extends ActionBarActivity { +import org.sufficientlysecure.keychain.R; - // Actions for internal use only: - public static final String ACTION_SELECT_SECRET_KEY = Constants.INTENT_PREFIX - + "SELECT_SECRET_KEYRING"; +public class SelectSecretKeyActivity extends ActionBarActivity { public static final String EXTRA_FILTER_CERTIFY = "filter_certify"; public static final String RESULT_EXTRA_MASTER_KEY_ID = "master_key_id"; - public static final String RESULT_EXTRA_USER_ID = "user_id"; - private boolean mFilterCertify = false; + private boolean mFilterCertify; private SelectSecretKeyFragment mSelectFragment; @Override @@ -51,23 +45,8 @@ public class SelectSecretKeyActivity extends ActionBarActivity { actionBar.setDisplayHomeAsUpEnabled(false); actionBar.setHomeButtonEnabled(false); - setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); - - // TODO: reimplement! - // mFilterLayout = findViewById(R.id.layout_filter); - // mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo); - // mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear); - // - // mClearFilterButton.setOnClickListener(new OnClickListener() { - // public void onClick(View v) { - // handleIntent(new Intent()); - // } - // }); - mFilterCertify = getIntent().getBooleanExtra(EXTRA_FILTER_CERTIFY, false); - handleIntent(getIntent()); - // Check that the activity is using the layout version with // the fragment_container FrameLayout if (findViewById(R.id.select_secret_key_fragment_container) != null) { @@ -90,49 +69,15 @@ public class SelectSecretKeyActivity extends ActionBarActivity { /** * This is executed by SelectSecretKeyFragment after clicking on an item - * - * @param masterKeyId - * @param userId + * + * @param selectedUri */ - public void afterListSelection(long masterKeyId, String userId) { + public void afterListSelection(Uri selectedUri) { Intent data = new Intent(); - data.putExtra(RESULT_EXTRA_MASTER_KEY_ID, masterKeyId); - data.putExtra(RESULT_EXTRA_USER_ID, (String) userId); + data.setData(selectedUri); + setResult(RESULT_OK, data); finish(); } - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - handleIntent(intent); - } - - private void handleIntent(Intent intent) { - // TODO: reimplement! - - // 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)); - // } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // TODO: reimplement! - // menu.add(0, Id.menu.option.search, 0, R.string.menu_search).setIcon( - // android.R.drawable.ic_menu_search); - return true; - } - } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java index 8ee2d381e..2efa7d33a 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de> * Copyright (C) 2010 Thialfihar <thi@thialfihar.org> * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,17 +17,6 @@ package org.sufficientlysecure.keychain.ui; -import java.util.Date; - -import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; -import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; -import org.sufficientlysecure.keychain.provider.KeychainDatabase; -import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; -import org.sufficientlysecure.keychain.ui.adapter.SelectKeyCursorAdapter; - import android.database.Cursor; import android.net.Uri; import android.os.Bundle; @@ -40,15 +29,26 @@ import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ListView; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.provider.KeychainDatabase; +import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; +import org.sufficientlysecure.keychain.ui.adapter.SelectKeyCursorAdapter; + +import java.util.Date; + public class SelectSecretKeyFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> { private SelectSecretKeyActivity mActivity; private SelectKeyCursorAdapter mAdapter; private ListView mListView; - + private boolean mFilterCertify; - + private static final String ARG_FILTER_CERTIFY = "filter_certify"; /** @@ -56,10 +56,9 @@ public class SelectSecretKeyFragment extends ListFragment implements */ public static SelectSecretKeyFragment newInstance(boolean filterCertify) { SelectSecretKeyFragment frag = new SelectSecretKeyFragment(); - Bundle args = new Bundle(); + Bundle args = new Bundle(); args.putBoolean(ARG_FILTER_CERTIFY, filterCertify); - frag.setArguments(args); return frag; @@ -86,10 +85,10 @@ public class SelectSecretKeyFragment extends ListFragment implements @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { long masterKeyId = mAdapter.getMasterKeyId(position); - String userId = mAdapter.getUserId(position); + Uri result = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(String.valueOf(masterKeyId)); // return data to activity, which results in finishing it - mActivity.afterListSelection(masterKeyId, userId); + mActivity.afterListSelection(result); } }); @@ -122,7 +121,7 @@ public class SelectSecretKeyFragment extends ListFragment implements // These are the rows that we will retrieve. long now = new Date().getTime() / 1000; - String[] projection = new String[] { + String[] projection = new String[]{ KeyRings._ID, KeyRings.MASTER_KEY_ID, UserIds.USER_ID, @@ -142,7 +141,7 @@ public class SelectSecretKeyFragment extends ListFragment implements + Keys.IS_REVOKED + " = '0' AND valid_keys." + Keys.CAN_SIGN + " = '1' AND valid_keys." + Keys.CREATION + " <= '" + now + "' AND " + "(valid_keys." + Keys.EXPIRY + " IS NULL OR valid_keys." + Keys.EXPIRY - + " >= '" + now + "')) AS " + SelectKeyCursorAdapter.PROJECTION_ROW_VALID, }; + + " >= '" + now + "')) AS " + SelectKeyCursorAdapter.PROJECTION_ROW_VALID,}; String orderBy = UserIds.USER_ID + " ASC"; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java index 6bcb84f46..cbc0f4c5c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java @@ -17,17 +17,15 @@ package org.sufficientlysecure.keychain.ui; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.provider.ProviderHelper; - import android.app.Activity; import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; import android.os.Bundle; import android.support.v4.app.Fragment; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; @@ -36,17 +34,34 @@ import android.widget.TextView; import com.beardedhen.androidbootstrap.BootstrapButton; -public class SelectSecretKeyLayoutFragment extends Fragment { +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract; + +public class SelectSecretKeyLayoutFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> { private TextView mKeyUserId; private TextView mKeyUserIdRest; + private TextView mKeyMasterKeyIdHex; + private TextView mNoKeySelected; private BootstrapButton mSelectKeyButton; private Boolean mFilterCertify; + private Uri mReceivedUri = null; + private SelectSecretKeyCallback mCallback; private static final int REQUEST_CODE_SELECT_KEY = 8882; + private static final int LOADER_ID = 0; + + //The Projection we will retrieve, Master Key ID is for convenience sake, + //to avoid having to pass the Key Around + final String[] PROJECTION = new String[]{KeychainContract.UserIds.USER_ID + , KeychainContract.KeyRings.MASTER_KEY_ID}; + final int INDEX_USER_ID = 0; + final int INDEX_MASTER_KEY_ID = 1; + public interface SelectSecretKeyCallback { void onKeySelected(long secretKeyId); } @@ -59,34 +74,30 @@ public class SelectSecretKeyLayoutFragment extends Fragment { mFilterCertify = filterCertify; } - public void selectKey(long secretKeyId) { - if (secretKeyId == Id.key.none) { - mKeyUserId.setText(R.string.api_settings_no_key); - mKeyUserIdRest.setText(""); - } else { - String uid = getResources().getString(R.string.user_id_no_name); - String uidExtra = ""; - PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId( - getActivity(), secretKeyId); - if (keyRing != null) { - PGPSecretKey key = PgpKeyHelper.getMasterKey(keyRing); - if (key != null) { - String userId = PgpKeyHelper.getMainUserIdSafe(getActivity(), key); - String chunks[] = userId.split(" <", 2); - uid = chunks[0]; - if (chunks.length > 1) { - uidExtra = "<" + chunks[1]; - } - } - } - mKeyUserId.setText(uid); - mKeyUserIdRest.setText(uidExtra); - } + public void setNoKeySelected() { + mNoKeySelected.setVisibility(View.VISIBLE); + mKeyUserId.setVisibility(View.GONE); + mKeyUserIdRest.setVisibility(View.GONE); + mKeyMasterKeyIdHex.setVisibility(View.GONE); + } + + public void setSelectedKeyData(String userName, String email, String masterKeyHex) { + + mNoKeySelected.setVisibility(View.GONE); + + mKeyUserId.setText(userName); + mKeyUserIdRest.setText(email); + mKeyMasterKeyIdHex.setText(masterKeyHex); + + mKeyUserId.setVisibility(View.VISIBLE); + mKeyUserIdRest.setVisibility(View.VISIBLE); + mKeyMasterKeyIdHex.setVisibility(View.VISIBLE); + } public void setError(String error) { - mKeyUserId.requestFocus(); - mKeyUserId.setError(error); + mNoKeySelected.requestFocus(); + mNoKeySelected.setError(error); } /** @@ -96,8 +107,10 @@ public class SelectSecretKeyLayoutFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.select_secret_key_layout_fragment, container, false); + mNoKeySelected = (TextView) view.findViewById(R.id.no_key_selected); mKeyUserId = (TextView) view.findViewById(R.id.select_secret_key_user_id); mKeyUserIdRest = (TextView) view.findViewById(R.id.select_secret_key_user_id_rest); + mKeyMasterKeyIdHex = (TextView) view.findViewById(R.id.select_secret_key_master_key_hex); mSelectKeyButton = (BootstrapButton) view .findViewById(R.id.select_secret_key_select_key_button); mFilterCertify = false; @@ -111,6 +124,13 @@ public class SelectSecretKeyLayoutFragment extends Fragment { return view; } + //For AppSettingsFragment + public void selectKey(long masterKeyId) { + Uri buildUri = KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri(String.valueOf(masterKeyId)); + mReceivedUri = buildUri; + getActivity().getSupportLoaderManager().restartLoader(LOADER_ID, null, this); + } + private void startSelectKeyActivity() { Intent intent = new Intent(getActivity(), SelectSecretKeyActivity.class); intent.putExtra(SelectSecretKeyActivity.EXTRA_FILTER_CERTIFY, mFilterCertify); @@ -118,29 +138,74 @@ public class SelectSecretKeyLayoutFragment extends Fragment { } @Override + public Loader<Cursor> onCreateLoader(int id, Bundle args) { + //We don't care about the Loader id + return new CursorLoader(getActivity(), mReceivedUri, PROJECTION, null, null, null); + } + + @Override + public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + if (data.moveToFirst()) { + String userName, email, masterKeyHex; + String userID = data.getString(INDEX_USER_ID); + long masterKeyID = data.getLong(INDEX_MASTER_KEY_ID); + + String splitUserID[] = PgpKeyHelper.splitUserId(userID); + + if (splitUserID[0] != null) { + userName = splitUserID[0]; + } else { + userName = getActivity().getResources().getString(R.string.user_id_no_name); + } + + if (splitUserID[1] != null) { + email = splitUserID[1]; + } else { + email = getActivity().getResources().getString(R.string.error_user_id_no_email); + } + + //TODO Can the cursor return invalid values for the Master Key ? + masterKeyHex = PgpKeyHelper.convertKeyIdToHexShort(masterKeyID); + + //Set the data + setSelectedKeyData(userName, email, masterKeyHex); + + //Give value to the callback + mCallback.onKeySelected(masterKeyID); + } else { + //Set The empty View + setNoKeySelected(); + } + + } + + @Override + public void onLoaderReset(Loader<Cursor> loader) { + return; + } + + // Select Secret Key Activity delivers the intent which was sent by it using interface to Select + // Secret Key Fragment.Intent contains the passed Uri + @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode & 0xFFFF) { - case REQUEST_CODE_SELECT_KEY: { - long secretKeyId; - if (resultCode == Activity.RESULT_OK) { - Bundle bundle = data.getExtras(); - secretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID); + case REQUEST_CODE_SELECT_KEY: { + if (resultCode == Activity.RESULT_OK) { + mReceivedUri = data.getData(); - selectKey(secretKeyId); + //Must be restartLoader() or the data will not be updated on selecting a new key + getActivity().getSupportLoaderManager().restartLoader(LOADER_ID, null, this); - // remove displayed errors - mKeyUserId.setError(null); + mKeyUserId.setError(null); - // give value back to callback - mCallback.onKeySelected(secretKeyId); + } + break; } - break; - } - default: - super.onActivityResult(requestCode, resultCode, data); + default: + super.onActivityResult(requestCode, resultCode, data); - break; + break; } } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java index 550d3047d..0e231e6a8 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java @@ -17,13 +17,6 @@ package org.sufficientlysecure.keychain.ui; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.Preferences; -import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.util.Log; - import android.app.ProgressDialog; import android.content.Intent; import android.net.Uri; @@ -36,8 +29,13 @@ import android.view.View.OnClickListener; import android.widget.ArrayAdapter; import android.widget.Spinner; import android.widget.Toast; - import com.beardedhen.androidbootstrap.BootstrapButton; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.util.Log; /** * Sends the selected public key to a keyserver @@ -59,7 +57,7 @@ public class UploadKeyActivity extends ActionBarActivity { 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); mKeyServerSpinner.setAdapter(adapter); if (adapter.getCount() > 0) { @@ -100,11 +98,11 @@ public class UploadKeyActivity extends ActionBarActivity { intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - // Message is received after uploading is done in ApgService + // Message is received after uploading is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, - R.string.progress_exporting, ProgressDialog.STYLE_HORIZONTAL) { + getString(R.string.progress_exporting), ProgressDialog.STYLE_HORIZONTAL) { public void handleMessage(Message message) { - // handle messages by standard ApgHandler first + // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { @@ -113,7 +111,7 @@ public class UploadKeyActivity extends ActionBarActivity { Toast.LENGTH_SHORT).show(); finish(); } - }; + } }; // Create a new Messenger for the communication back diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 0a452bc8a..3e7e6d7ce 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -29,13 +29,13 @@ import android.support.v7.app.ActionBarActivity; import android.view.Menu; import android.view.MenuItem; import android.widget.Toast; - import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; import org.sufficientlysecure.keychain.helper.ExportHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.ui.adapter.TabsAdapter; import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; @@ -83,17 +83,23 @@ public class ViewKeyActivity extends ActionBarActivity { selectedTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB); } - mDataUri = getIntent().getData(); + { + // normalize mDataUri to a "by row id" query, to ensure it works with any + // given valid /public/ query + long rowId = ProviderHelper.getRowId(this, getIntent().getData()); + // TODO: handle (rowId == 0) with something else than a crash + mDataUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)); + } Bundle mainBundle = new Bundle(); mainBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, mDataUri); mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.key_view_tab_main)), - ViewKeyMainFragment.class, mainBundle, (selectedTab == 0 ? true : false)); + ViewKeyMainFragment.class, mainBundle, (selectedTab == 0)); Bundle certBundle = new Bundle(); certBundle.putParcelable(ViewKeyCertsFragment.ARG_DATA_URI, mDataUri); mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.key_view_tab_certs)), - ViewKeyCertsFragment.class, certBundle, (selectedTab == 1 ? true : false)); + ViewKeyCertsFragment.class, certBundle, (selectedTab == 1)); } @Override @@ -107,7 +113,7 @@ public class ViewKeyActivity extends ActionBarActivity { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: - Intent homeIntent = new Intent(this, KeyListPublicActivity.class); + Intent homeIntent = new Intent(this, KeyListActivity.class); homeIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(homeIntent); return true; @@ -118,8 +124,11 @@ public class ViewKeyActivity extends ActionBarActivity { uploadToKeyserver(mDataUri); return true; case R.id.menu_key_view_export_file: - mExportHelper.showExportKeysDialog(mDataUri, Id.type.public_key, Constants.path.APP_DIR - + "/pubexport.asc"); + long masterKeyId = + ProviderHelper.getPublicMasterKeyId(this, Long.valueOf(mDataUri.getLastPathSegment())); + long[] ids = new long[]{masterKeyId}; + mExportHelper.showExportKeysDialog(ids, Id.type.public_key, + Constants.Path.APP_DIR_FILE_PUB, null); return true; case R.id.menu_key_view_share_default_fingerprint: shareKey(mDataUri, true); @@ -154,7 +163,7 @@ public class ViewKeyActivity extends ActionBarActivity { } private void updateFromKeyserver(Uri dataUri) { - long updateKeyId = ProviderHelper.getMasterKeyId(ViewKeyActivity.this, mDataUri); + long updateKeyId = ProviderHelper.getMasterKeyId(ViewKeyActivity.this, dataUri); if (updateKeyId == 0) { Log.e(Constants.TAG, "this shouldn't happen. KeyId == 0!"); @@ -173,7 +182,7 @@ public class ViewKeyActivity extends ActionBarActivity { String content; if (fingerprintOnly) { byte[] fingerprintBlob = ProviderHelper.getFingerprint(this, dataUri); - String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, false); + String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob); content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint; } else { @@ -228,24 +237,12 @@ public class ViewKeyActivity extends ActionBarActivity { Handler returnHandler = new Handler() { @Override public void handleMessage(Message message) { - if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) { - Bundle returnData = message.getData(); - if (returnData != null - && returnData.containsKey(DeleteKeyDialogFragment.MESSAGE_NOT_DELETED)) { - // we delete only this key, so MESSAGE_NOT_DELETED will solely contain this key - Toast.makeText(ViewKeyActivity.this, - getString(R.string.error_can_not_delete_contact) - + getResources().getQuantityString(R.plurals.error_can_not_delete_info, 1), - Toast.LENGTH_LONG).show(); - } else { - setResult(RESULT_CANCELED); - finish(); - } - } + setResult(RESULT_CANCELED); + finish(); } }; - mExportHelper.deleteKey(dataUri, Id.type.public_key, returnHandler); + mExportHelper.deleteKey(dataUri, returnHandler); } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivityJB.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivityJB.java index 59037d9ff..997ff9c7a 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivityJB.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivityJB.java @@ -18,10 +18,6 @@ package org.sufficientlysecure.keychain.ui; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.provider.ProviderHelper; - import android.annotation.TargetApi; import android.net.Uri; import android.nfc.NdefMessage; @@ -35,6 +31,9 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.widget.Toast; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.ProviderHelper; @TargetApi(Build.VERSION_CODES.JELLY_BEAN) public class ViewKeyActivityJB extends ViewKeyActivity implements CreateNdefMessageCallback, @@ -66,7 +65,7 @@ public class ViewKeyActivityJB extends ViewKeyActivity implements CreateNdefMess // get public keyring as byte array long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri); mSharedKeyringBytes = ProviderHelper.getKeyRingsAsByteArray(this, dataUri, - new long[] { masterKeyId }); + new long[]{masterKeyId}); // Register callback to set NDEF message mNfcAdapter.setNdefPushMessageCallback(this, this); @@ -109,10 +108,10 @@ public class ViewKeyActivityJB extends ViewKeyActivity implements CreateNdefMess @Override public void handleMessage(Message msg) { switch (msg.what) { - case NFC_SENT: - Toast.makeText(getApplicationContext(), R.string.nfc_successfull, Toast.LENGTH_LONG) - .show(); - break; + case NFC_SENT: + Toast.makeText(getApplicationContext(), R.string.nfc_successfull, Toast.LENGTH_LONG) + .show(); + break; } } }; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java index 36d3e6ace..02c04c11d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java @@ -24,9 +24,7 @@ import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import com.beardedhen.androidbootstrap.BootstrapButton; - import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.util.Log; @@ -89,4 +87,4 @@ public class ViewKeyCertsFragment extends Fragment { startActivity(signIntent); } -}
\ No newline at end of file +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index 495764eaf..691be5fa9 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui; import android.content.Intent; import android.database.Cursor; +import android.graphics.Color; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -36,6 +37,7 @@ import com.beardedhen.androidbootstrap.BootstrapButton; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.OtherHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; @@ -46,8 +48,8 @@ import org.sufficientlysecure.keychain.util.Log; import java.util.Date; -public class ViewKeyMainFragment extends Fragment implements - LoaderManager.LoaderCallbacks<Cursor>{ +public class ViewKeyMainFragment extends Fragment implements + LoaderManager.LoaderCallbacks<Cursor> { public static final String ARG_DATA_URI = "uri"; @@ -59,7 +61,10 @@ public class ViewKeyMainFragment extends Fragment implements private TextView mExpiry; private TextView mCreation; private TextView mFingerprint; + private TextView mSecretKey; + private BootstrapButton mActionEdit; private BootstrapButton mActionEncrypt; + private BootstrapButton mActionCertify; private ListView mUserIds; private ListView mKeys; @@ -85,9 +90,12 @@ public class ViewKeyMainFragment extends Fragment implements mCreation = (TextView) view.findViewById(R.id.creation); mExpiry = (TextView) view.findViewById(R.id.expiry); mFingerprint = (TextView) view.findViewById(R.id.fingerprint); + mSecretKey = (TextView) view.findViewById(R.id.secret_key); mUserIds = (ListView) view.findViewById(R.id.user_ids); mKeys = (ListView) view.findViewById(R.id.keys); + mActionEdit = (BootstrapButton) view.findViewById(R.id.action_edit); mActionEncrypt = (BootstrapButton) view.findViewById(R.id.action_encrypt); + mActionCertify = (BootstrapButton) view.findViewById(R.id.action_certify); return view; } @@ -116,6 +124,53 @@ public class ViewKeyMainFragment extends Fragment implements Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString()); + { // label whether secret key is available, and edit button if it is + final long masterKeyId = ProviderHelper.getMasterKeyId(getActivity(), mDataUri); + if (ProviderHelper.hasSecretKeyByMasterKeyId(getActivity(), masterKeyId)) { + // set this attribute. this is a LITTLE unclean, but we have the info available + // right here, so why not. + mSecretKey.setTextColor(getResources().getColor(R.color.emphasis)); + mSecretKey.setText(R.string.secret_key_yes); + + // certify button + // TODO this button MIGHT be useful if the user wants to + // certify a private key with another... + // mActionCertify.setVisibility(View.GONE); + + // edit button + mActionEdit.setVisibility(View.VISIBLE); + mActionEdit.setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + Intent editIntent = new Intent(getActivity(), EditKeyActivity.class); + editIntent.setData( + KeychainContract + .KeyRings.buildSecretKeyRingsByMasterKeyIdUri( + Long.toString(masterKeyId))); + editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY); + startActivityForResult(editIntent, 0); + } + }); + } else { + mSecretKey.setTextColor(Color.BLACK); + mSecretKey.setText(getResources().getString(R.string.secret_key_no)); + + // certify button + mActionCertify.setVisibility(View.VISIBLE); + // edit button + mActionEdit.setVisibility(View.GONE); + } + + // TODO see todo note above, doing this here for now + mActionCertify.setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + certifyKey(KeychainContract.KeyRings.buildPublicKeyRingsByMasterKeyIdUri( + Long.toString(masterKeyId) + )); + } + }); + + } + mActionEncrypt.setOnClickListener(new View.OnClickListener() { @Override @@ -137,21 +192,29 @@ public class ViewKeyMainFragment extends Fragment implements getActivity().getSupportLoaderManager().initLoader(LOADER_ID_KEYS, null, this); } - static final String[] KEYRING_PROJECTION = new String[]{KeychainContract.KeyRings._ID, KeychainContract.KeyRings.MASTER_KEY_ID, - KeychainContract.UserIds.USER_ID}; + static final String[] KEYRING_PROJECTION = + new String[]{KeychainContract.KeyRings._ID, KeychainContract.KeyRings.MASTER_KEY_ID, + KeychainContract.UserIds.USER_ID}; static final int KEYRING_INDEX_ID = 0; static final int KEYRING_INDEX_MASTER_KEY_ID = 1; static final int KEYRING_INDEX_USER_ID = 2; - static final String[] USER_IDS_PROJECTION = new String[]{KeychainContract.UserIds._ID, KeychainContract.UserIds.USER_ID, - KeychainContract.UserIds.RANK,}; - // not the main user id - static final String USER_IDS_SELECTION = KeychainContract.UserIds.RANK + " > 0 "; - static final String USER_IDS_SORT_ORDER = KeychainContract.UserIds.USER_ID + " COLLATE LOCALIZED ASC"; - - static final String[] KEYS_PROJECTION = new String[]{KeychainContract.Keys._ID, KeychainContract.Keys.KEY_ID, - KeychainContract.Keys.IS_MASTER_KEY, KeychainContract.Keys.ALGORITHM, KeychainContract.Keys.KEY_SIZE, KeychainContract.Keys.CAN_CERTIFY, KeychainContract.Keys.CAN_SIGN, - KeychainContract.Keys.CAN_ENCRYPT, KeychainContract.Keys.CREATION, KeychainContract.Keys.EXPIRY, KeychainContract.Keys.FINGERPRINT}; + static final String[] USER_IDS_PROJECTION = + new String[]{ + KeychainContract.UserIds._ID, + KeychainContract.UserIds.USER_ID, + KeychainContract.UserIds.RANK, + }; + static final String USER_IDS_SORT_ORDER = + KeychainContract.UserIds.RANK + " COLLATE LOCALIZED ASC"; + + static final String[] KEYS_PROJECTION = + new String[]{KeychainContract.Keys._ID, KeychainContract.Keys.KEY_ID, + KeychainContract.Keys.IS_MASTER_KEY, KeychainContract.Keys.ALGORITHM, + KeychainContract.Keys.KEY_SIZE, KeychainContract.Keys.CAN_CERTIFY, + KeychainContract.Keys.CAN_SIGN, KeychainContract.Keys.CAN_ENCRYPT, + KeychainContract.Keys.IS_REVOKED, KeychainContract.Keys.CREATION, + KeychainContract.Keys.EXPIRY, KeychainContract.Keys.FINGERPRINT}; static final String KEYS_SORT_ORDER = KeychainContract.Keys.RANK + " ASC"; static final int KEYS_INDEX_ID = 0; static final int KEYS_INDEX_KEY_ID = 1; @@ -161,9 +224,10 @@ public class ViewKeyMainFragment extends Fragment implements static final int KEYS_INDEX_CAN_CERTIFY = 5; static final int KEYS_INDEX_CAN_SIGN = 6; static final int KEYS_INDEX_CAN_ENCRYPT = 7; - static final int KEYS_INDEX_CREATION = 8; - static final int KEYS_INDEX_EXPIRY = 9; - static final int KEYS_INDEX_FINGERPRINT = 10; + static final int KEYS_INDEX_IS_REVOKED = 8; + static final int KEYS_INDEX_CREATION = 9; + static final int KEYS_INDEX_EXPIRY = 10; + static final int KEYS_INDEX_FINGERPRINT = 11; public Loader<Cursor> onCreateLoader(int id, Bundle args) { switch (id) { @@ -179,7 +243,7 @@ public class ViewKeyMainFragment extends Fragment implements // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. - return new CursorLoader(getActivity(), baseUri, USER_IDS_PROJECTION, USER_IDS_SELECTION, null, + return new CursorLoader(getActivity(), baseUri, USER_IDS_PROJECTION, null, null, USER_IDS_SORT_ORDER); } case LOADER_ID_KEYS: { @@ -224,8 +288,7 @@ public class ViewKeyMainFragment extends Fragment implements if (data.moveToFirst()) { // get key id from MASTER_KEY_ID long keyId = data.getLong(KEYS_INDEX_KEY_ID); - - String keyIdStr = "0x" + PgpKeyHelper.convertKeyIdToHex(keyId); + String keyIdStr = PgpKeyHelper.convertKeyIdToHex(keyId); mKeyId.setText(keyIdStr); // get creation date from CREATION @@ -234,8 +297,9 @@ public class ViewKeyMainFragment extends Fragment implements } else { Date creationDate = new Date(data.getLong(KEYS_INDEX_CREATION) * 1000); - mCreation.setText(DateFormat.getDateFormat(getActivity().getApplicationContext()).format( - creationDate)); + mCreation.setText( + DateFormat.getDateFormat(getActivity().getApplicationContext()).format( + creationDate)); } // get expiry date from EXPIRY @@ -244,8 +308,9 @@ public class ViewKeyMainFragment extends Fragment implements } else { Date expiryDate = new Date(data.getLong(KEYS_INDEX_EXPIRY) * 1000); - mExpiry.setText(DateFormat.getDateFormat(getActivity().getApplicationContext()).format( - expiryDate)); + mExpiry.setText( + DateFormat.getDateFormat(getActivity().getApplicationContext()).format( + expiryDate)); } String algorithmStr = PgpKeyHelper.getAlgorithmInfo( @@ -257,10 +322,22 @@ public class ViewKeyMainFragment extends Fragment implements // FALLBACK for old database entries fingerprintBlob = ProviderHelper.getFingerprint(getActivity(), mDataUri); } - String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, true); - fingerprint = fingerprint.replace(" ", "\n"); + String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob); + + mFingerprint.setText(PgpKeyHelper.colorizeFingerprint(fingerprint)); + } - mFingerprint.setText(fingerprint); + // hide encrypt button if no encryption key is available + boolean canEncrypt = false; + data.moveToFirst(); + do { + if (data.getInt(KEYS_INDEX_CAN_ENCRYPT) == 1) { + canEncrypt = true; + break; + } + } while (data.moveToNext()); + if (!canEncrypt) { + mActionEncrypt.setVisibility(View.GONE); } mKeysAdapter.swapCursor(data); @@ -309,5 +386,4 @@ public class ViewKeyMainFragment extends Fragment implements startActivity(signIntent); } - -}
\ No newline at end of file +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java index 2ac19c1d9..5f2aec4fe 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java @@ -22,24 +22,25 @@ package org.sufficientlysecure.keychain.ui.adapter; * You can pass the result and an exception in it if an error occurred. * Concept found at: * https://stackoverflow.com/questions/19593577/how-to-handle-errors-in-custom-asynctaskloader + * * @param <T> - Typ of the result which is wrapped */ -public class AsyncTaskResultWrapper <T>{ +public class AsyncTaskResultWrapper<T> { - private final T result; - private final Exception error; + private final T mResult; + private final Exception mError; - public AsyncTaskResultWrapper(T result, Exception error){ - this.result = result; - this.error = error; + public AsyncTaskResultWrapper(T result, Exception error) { + this.mResult = result; + this.mError = error; } public T getResult() { - return result; + return mResult; } public Exception getError() { - return error; + return mError; } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/HighlightQueryCursorAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/HighlightQueryCursorAdapter.java new file mode 100644 index 000000000..a3ed08a4c --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/HighlightQueryCursorAdapter.java @@ -0,0 +1,65 @@ +/* + * 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.adapter; + +import android.content.Context; +import android.database.Cursor; +import android.support.v4.widget.CursorAdapter; +import android.text.Spannable; +import android.text.style.ForegroundColorSpan; +import org.sufficientlysecure.keychain.R; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public abstract class HighlightQueryCursorAdapter extends CursorAdapter { + + private String mCurQuery; + + public HighlightQueryCursorAdapter(Context context, Cursor c, int flags) { + super(context, c, flags); + mCurQuery = null; + } + + public void setSearchQuery(String searchQuery) { + mCurQuery = searchQuery; + } + + public String getSearchQuery() { + return mCurQuery; + } + + protected Spannable highlightSearchQuery(String text) { + Spannable highlight = Spannable.Factory.getInstance().newSpannable(text); + + if (mCurQuery != null) { + Pattern pattern = Pattern.compile("(?i)" + mCurQuery); + Matcher matcher = pattern.matcher(text); + if (matcher.find()) { + highlight.setSpan( + new ForegroundColorSpan(mContext.getResources().getColor(R.color.emphasis)), + matcher.start(), + matcher.end(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + return highlight; + } else { + return highlight; + } + } +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java index 52186b662..7d3166af9 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java @@ -17,12 +17,6 @@ package org.sufficientlysecure.keychain.ui.adapter; -import java.util.ArrayList; -import java.util.List; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; - import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; @@ -37,11 +31,27 @@ import android.widget.LinearLayout; import android.widget.LinearLayout.LayoutParams; import android.widget.TextView; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.OtherHelper; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; + +import java.util.ArrayList; +import java.util.List; + public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> { protected LayoutInflater mInflater; protected Activity mActivity; - protected List<ImportKeysListEntry> data; + protected List<ImportKeysListEntry> mData; + + static class ViewHolder { + private TextView mainUserId; + private TextView mainUserIdRest; + private TextView keyId; + private TextView fingerprint; + private TextView algorithm; + private TextView status; + } public ImportKeysAdapter(Activity activity) { super(activity, -1); @@ -53,7 +63,7 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> { public void setData(List<ImportKeysListEntry> data) { clear(); if (data != null) { - this.data = data; + this.mData = data; // add data to extended ArrayAdapter if (Build.VERSION.SDK_INT >= 11) { @@ -67,14 +77,15 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> { } public List<ImportKeysListEntry> getData() { - return data; + return mData; } public ArrayList<ImportKeysListEntry> getSelectedData() { ArrayList<ImportKeysListEntry> selectedData = new ArrayList<ImportKeysListEntry>(); - for (ImportKeysListEntry entry : data) { - if (entry.isSelected()) + for (ImportKeysListEntry entry : mData) { + if (entry.isSelected()) { selectedData.add(entry); + } } return selectedData; } @@ -85,17 +96,21 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> { } public View getView(int position, View convertView, ViewGroup parent) { - ImportKeysListEntry entry = data.get(position); - - View view = mInflater.inflate(R.layout.import_keys_list_entry, null); - - TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId); - TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); - TextView keyId = (TextView) view.findViewById(R.id.keyId); - TextView fingerprint = (TextView) view.findViewById(R.id.fingerprint); - TextView algorithm = (TextView) view.findViewById(R.id.algorithm); - TextView status = (TextView) view.findViewById(R.id.status); - + ImportKeysListEntry entry = mData.get(position); + ViewHolder holder; + if (convertView == null) { + holder = new ViewHolder(); + convertView = mInflater.inflate(R.layout.import_keys_list_entry, null); + holder.mainUserId = (TextView) convertView.findViewById(R.id.mainUserId); + holder.mainUserIdRest = (TextView) convertView.findViewById(R.id.mainUserIdRest); + holder.keyId = (TextView) convertView.findViewById(R.id.keyId); + holder.fingerprint = (TextView) convertView.findViewById(R.id.fingerprint); + holder.algorithm = (TextView) convertView.findViewById(R.id.algorithm); + holder.status = (TextView) convertView.findViewById(R.id.status); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } // main user id String userId = entry.userIds.get(0); String[] userIdSplit = PgpKeyHelper.splitUserId(userId); @@ -105,39 +120,40 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> { // show red user id if it is a secret key if (entry.secretKey) { userIdSplit[0] = mActivity.getString(R.string.secret_key) + " " + userIdSplit[0]; - mainUserId.setTextColor(Color.RED); + holder.mainUserId.setTextColor(Color.RED); } - mainUserId.setText(userIdSplit[0]); + holder.mainUserId.setText(userIdSplit[0]); } else { - mainUserId.setText(R.string.user_id_no_name); + holder.mainUserId.setText(R.string.user_id_no_name); } // email if (userIdSplit[1] != null) { - mainUserIdRest.setText(userIdSplit[1]); - mainUserIdRest.setVisibility(View.VISIBLE); + holder.mainUserIdRest.setText(userIdSplit[1]); + holder.mainUserIdRest.setVisibility(View.VISIBLE); } else { - mainUserIdRest.setVisibility(View.GONE); + holder.mainUserIdRest.setVisibility(View.GONE); } - keyId.setText(entry.hexKeyId); + holder.keyId.setText(entry.keyIdHex); - if (entry.fingerPrint != null) { - fingerprint.setText(mActivity.getString(R.string.fingerprint) + " " + entry.fingerPrint); - fingerprint.setVisibility(View.VISIBLE); + if (entry.fingerPrintHex != null) { + holder.fingerprint.setText(PgpKeyHelper.colorizeFingerprint(entry.fingerPrintHex)); + holder.fingerprint.setVisibility(View.VISIBLE); } else { - fingerprint.setVisibility(View.GONE); + holder.fingerprint.setVisibility(View.GONE); } - algorithm.setText("" + entry.bitStrength + "/" + entry.algorithm); + holder.algorithm.setText("" + entry.bitStrength + "/" + entry.algorithm); if (entry.revoked) { - status.setText(R.string.revoked); + holder.status.setText(R.string.revoked); } else { - status.setVisibility(View.GONE); + holder.status.setVisibility(View.GONE); } - LinearLayout ll = (LinearLayout) view.findViewById(R.id.list); + LinearLayout ll = (LinearLayout) convertView.findViewById(R.id.list); + ll.removeAllViews(); if (entry.userIds.size() == 1) { ll.setVisibility(View.GONE); } else { @@ -162,10 +178,10 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> { } } - CheckBox cBox = (CheckBox) view.findViewById(R.id.selected); + CheckBox cBox = (CheckBox) convertView.findViewById(R.id.selected); cBox.setChecked(entry.isSelected()); - return view; + return convertView; } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java index 4a7a9c93a..9b20effc2 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java @@ -19,11 +19,7 @@ package org.sufficientlysecure.keychain.ui.adapter; import android.os.Parcel; import android.os.Parcelable; - -import java.io.IOException; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Date; +import android.util.SparseArray; import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPPublicKey; @@ -33,35 +29,40 @@ import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; + public class ImportKeysListEntry implements Serializable, Parcelable { private static final long serialVersionUID = -7797972103284992662L; - public ArrayList<String> userIds; + public ArrayList<String> userIds; public long keyId; + public String keyIdHex; public boolean revoked; public Date date; // TODO: not displayed - public String fingerPrint; - public String hexKeyId; + public String fingerPrintHex; public int bitStrength; public String algorithm; public boolean secretKey; - private boolean selected; + private boolean mSelected; - private byte[] bytes = new byte[]{}; + private byte[] mBytes = new byte[]{}; public ImportKeysListEntry(ImportKeysListEntry b) { this.userIds = b.userIds; this.keyId = b.keyId; this.revoked = b.revoked; this.date = b.date; - this.fingerPrint = b.fingerPrint; - this.hexKeyId = b.hexKeyId; + this.fingerPrintHex = b.fingerPrintHex; + this.keyIdHex = b.keyIdHex; this.bitStrength = b.bitStrength; this.algorithm = b.algorithm; this.secretKey = b.secretKey; - this.selected = b.selected; - this.bytes = b.bytes; + this.mSelected = b.mSelected; + this.mBytes = b.mBytes; } public int describeContents() { @@ -74,14 +75,14 @@ public class ImportKeysListEntry implements Serializable, Parcelable { dest.writeLong(keyId); dest.writeByte((byte) (revoked ? 1 : 0)); dest.writeSerializable(date); - dest.writeString(fingerPrint); - dest.writeString(hexKeyId); + dest.writeString(fingerPrintHex); + dest.writeString(keyIdHex); dest.writeInt(bitStrength); dest.writeString(algorithm); dest.writeByte((byte) (secretKey ? 1 : 0)); - dest.writeByte((byte) (selected ? 1 : 0)); - dest.writeInt(bytes.length); - dest.writeByteArray(bytes); + dest.writeByte((byte) (mSelected ? 1 : 0)); + dest.writeInt(mBytes.length); + dest.writeByteArray(mBytes); } public static final Creator<ImportKeysListEntry> CREATOR = new Creator<ImportKeysListEntry>() { @@ -92,14 +93,14 @@ public class ImportKeysListEntry implements Serializable, Parcelable { vr.keyId = source.readLong(); vr.revoked = source.readByte() == 1; vr.date = (Date) source.readSerializable(); - vr.fingerPrint = source.readString(); - vr.hexKeyId = source.readString(); + vr.fingerPrintHex = source.readString(); + vr.keyIdHex = source.readString(); vr.bitStrength = source.readInt(); vr.algorithm = source.readString(); vr.secretKey = source.readByte() == 1; - vr.selected = source.readByte() == 1; - vr.bytes = new byte[source.readInt()]; - source.readByteArray(vr.bytes); + vr.mSelected = source.readByte() == 1; + vr.mBytes = new byte[source.readInt()]; + source.readByteArray(vr.mBytes); return vr; } @@ -109,34 +110,105 @@ public class ImportKeysListEntry implements Serializable, Parcelable { } }; - public long getKeyId() { - return keyId; + public String getKeyIdHex() { + return keyIdHex; } public byte[] getBytes() { - return bytes; + return mBytes; } public void setBytes(byte[] bytes) { - this.bytes = bytes; + this.mBytes = bytes; + } + + public boolean isSelected() { + return mSelected; + } + + public void setSelected(boolean selected) { + this.mSelected = selected; + } + + public long getKeyId() { + return keyId; + } + + public void setKeyId(long keyId) { + this.keyId = keyId; + } + + public void setKeyIdHex(String keyIdHex) { + this.keyIdHex = keyIdHex; + } + + public boolean isRevoked() { + return revoked; + } + + public void setRevoked(boolean revoked) { + this.revoked = revoked; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + public String getFingerPrintHex() { + return fingerPrintHex; + } + + public void setFingerPrintHex(String fingerPrintHex) { + this.fingerPrintHex = fingerPrintHex; + } + + public int getBitStrength() { + return bitStrength; + } + + public void setBitStrength(int bitStrength) { + this.bitStrength = bitStrength; + } + + public String getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(String algorithm) { + this.algorithm = algorithm; + } + + public boolean isSecretKey() { + return secretKey; + } + + public void setSecretKey(boolean secretKey) { + this.secretKey = secretKey; + } + + public ArrayList<String> getUserIds() { + return userIds; + } + + public void setUserIds(ArrayList<String> userIds) { + this.userIds = userIds; } /** * Constructor for later querying from keyserver */ public ImportKeysListEntry() { + // keys from keyserver are always public keys secretKey = false; + // do not select by default + mSelected = false; userIds = new ArrayList<String>(); } - public boolean isSelected() { - return selected; - } - - public void setSelected(boolean selected) { - this.selected = selected; - } - /** * Constructor based on key object, used for import from NFC, QR Codes, files */ @@ -144,13 +216,13 @@ public class ImportKeysListEntry implements Serializable, Parcelable { public ImportKeysListEntry(PGPKeyRing pgpKeyRing) { // save actual key object into entry, used to import it later try { - this.bytes = pgpKeyRing.getEncoded(); + this.mBytes = pgpKeyRing.getEncoded(); } catch (IOException e) { Log.e(Constants.TAG, "IOException on pgpKeyRing.getEncoded()", e); } // selected is default - this.selected = true; + this.mSelected = true; if (pgpKeyRing instanceof PGPSecretKeyRing) { secretKey = true; @@ -162,27 +234,39 @@ public class ImportKeysListEntry implements Serializable, Parcelable { for (String userId : new IterableIterator<String>(pgpKeyRing.getPublicKey().getUserIDs())) { userIds.add(userId); } + this.keyId = pgpKeyRing.getPublicKey().getKeyID(); + this.keyIdHex = PgpKeyHelper.convertKeyIdToHex(keyId); this.revoked = pgpKeyRing.getPublicKey().isRevoked(); - this.fingerPrint = PgpKeyHelper.convertFingerprintToHex(pgpKeyRing.getPublicKey() - .getFingerprint(), true); - this.hexKeyId = "0x" + PgpKeyHelper.convertKeyIdToHex(keyId); + this.fingerPrintHex = PgpKeyHelper.convertFingerprintToHex(pgpKeyRing.getPublicKey() + .getFingerprint()); this.bitStrength = pgpKeyRing.getPublicKey().getBitStrength(); - int algorithm = pgpKeyRing.getPublicKey().getAlgorithm(); - if (algorithm == PGPPublicKey.RSA_ENCRYPT || algorithm == PGPPublicKey.RSA_GENERAL - || algorithm == PGPPublicKey.RSA_SIGN) { - this.algorithm = "RSA"; - } else if (algorithm == PGPPublicKey.DSA) { - this.algorithm = "DSA"; - } else if (algorithm == PGPPublicKey.ELGAMAL_ENCRYPT - || algorithm == PGPPublicKey.ELGAMAL_GENERAL) { - this.algorithm = "ElGamal"; - } else if (algorithm == PGPPublicKey.EC || algorithm == PGPPublicKey.ECDSA) { - this.algorithm = "ECC"; - } else { - // TODO: with resources - this.algorithm = "unknown"; - } + final int algorithm = pgpKeyRing.getPublicKey().getAlgorithm(); + this.algorithm = getAlgorithmFromId(algorithm); + } + + /** + * Based on <a href="http://tools.ietf.org/html/rfc2440#section-9.1">OpenPGP Message Format</a> + */ + private final static SparseArray<String> ALGORITHM_IDS = new SparseArray<String>() {{ + put(-1, "unknown"); // TODO: with resources + put(0, "unencrypted"); + put(PGPPublicKey.RSA_GENERAL, "RSA"); + put(PGPPublicKey.RSA_ENCRYPT, "RSA"); + put(PGPPublicKey.RSA_SIGN, "RSA"); + put(PGPPublicKey.ELGAMAL_ENCRYPT, "ElGamal"); + put(PGPPublicKey.ELGAMAL_GENERAL, "ElGamal"); + put(PGPPublicKey.DSA, "DSA"); + put(PGPPublicKey.EC, "ECC"); + put(PGPPublicKey.ECDSA, "ECC"); + put(PGPPublicKey.ECDH, "ECC"); + }}; + + /** + * Based on <a href="http://tools.ietf.org/html/rfc2440#section-9.1">OpenPGP Message Format</a> + */ + public static String getAlgorithmFromId(int algorithmId) { + return (ALGORITHM_IDS.get(algorithmId) != null ? ALGORITHM_IDS.get(algorithmId) : ALGORITHM_IDS.get(-1)); } -}
\ No newline at end of file +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java index 76649b27b..c9983213c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java @@ -17,11 +17,8 @@ package org.sufficientlysecure.keychain.ui.adapter; -import java.io.BufferedInputStream; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - +import android.content.Context; +import android.support.v4.content.AsyncTaskLoader; import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPUtil; @@ -30,16 +27,35 @@ import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.PositionAwareInputStream; -import android.content.Context; -import android.support.v4.content.AsyncTaskLoader; +import java.io.BufferedInputStream; +import java.io.InputStream; +import java.util.ArrayList; + +public class ImportKeysListLoader + extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> { + + public static class FileHasNoContent extends Exception { + + } + + public static class NonPgpPart extends Exception { + private int mCount; + + public NonPgpPart(int count) { + this.mCount = count; + } + + public int getCount() { + return mCount; + } + } -public class ImportKeysListLoader extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> { Context mContext; InputData mInputData; - ArrayList<ImportKeysListEntry> data = new ArrayList<ImportKeysListEntry>(); - AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> entryListWrapper; + ArrayList<ImportKeysListEntry> mData = new ArrayList<ImportKeysListEntry>(); + AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper; public ImportKeysListLoader(Context context, InputData inputData) { super(context); @@ -50,16 +66,16 @@ public class ImportKeysListLoader extends AsyncTaskLoader<AsyncTaskResultWrapper @Override public AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> loadInBackground() { - entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(data, null); + mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, null); if (mInputData == null) { Log.e(Constants.TAG, "Input data is null!"); - return entryListWrapper; + return mEntryListWrapper; } generateListOfKeyrings(mInputData); - return entryListWrapper; + return mEntryListWrapper; } @Override @@ -87,11 +103,15 @@ public class ImportKeysListLoader extends AsyncTaskLoader<AsyncTaskResultWrapper /** * Reads all PGPKeyRing objects from input - * - * @param keyringBytes + * + * @param inputData * @return */ private void generateListOfKeyrings(InputData inputData) { + + boolean isEmpty = true; + int nonPgpCounter = 0; + PositionAwareInputStream progressIn = new PositionAwareInputStream( inputData.getInputStream()); @@ -103,6 +123,7 @@ public class ImportKeysListLoader extends AsyncTaskLoader<AsyncTaskResultWrapper // read all available blocks... (asc files can contain many blocks with BEGIN END) while (bufferedInput.available() > 0) { + isEmpty = false; InputStream in = PGPUtil.getDecoderStream(bufferedInput); PGPObjectFactory objectFactory = new PGPObjectFactory(in); @@ -116,17 +137,31 @@ public class ImportKeysListLoader extends AsyncTaskLoader<AsyncTaskResultWrapper addToData(newKeyring); } else { Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!"); + nonPgpCounter++; } } } } catch (Exception e) { Log.e(Constants.TAG, "Exception on parsing key file!", e); + mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, e); + nonPgpCounter = 0; + } + + if (isEmpty) { + Log.e(Constants.TAG, "File has no content!", new FileHasNoContent()); + mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> + (mData, new FileHasNoContent()); + } + + if (nonPgpCounter > 0) { + mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> + (mData, new NonPgpPart(nonPgpCounter)); } } private void addToData(PGPKeyRing keyring) { ImportKeysListEntry item = new ImportKeysListEntry(keyring); - data.add(item); + mData.add(item); } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java index 3a3b6e58b..a4dd06e40 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java @@ -19,7 +19,6 @@ package org.sufficientlysecure.keychain.ui.adapter; import android.content.Context; import android.support.v4.content.AsyncTaskLoader; - import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.util.HkpKeyServer; import org.sufficientlysecure.keychain.util.KeyServer; @@ -27,14 +26,15 @@ import org.sufficientlysecure.keychain.util.Log; import java.util.ArrayList; -public class ImportKeysListServerLoader extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> { +public class ImportKeysListServerLoader + extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> { Context mContext; String mServerQuery; String mKeyServer; - private ArrayList<ImportKeysListEntry> entryList = new ArrayList<ImportKeysListEntry>(); - private AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> entryListWrapper; + private ArrayList<ImportKeysListEntry> mEntryList = new ArrayList<ImportKeysListEntry>(); + private AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper; public ImportKeysListServerLoader(Context context, String serverQuery, String keyServer) { super(context); @@ -46,16 +46,16 @@ public class ImportKeysListServerLoader extends AsyncTaskLoader<AsyncTaskResultW @Override public AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> loadInBackground() { - entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(entryList, null); + mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, null); if (mServerQuery == null) { Log.e(Constants.TAG, "mServerQuery is null!"); - return entryListWrapper; + return mEntryListWrapper; } queryServer(mServerQuery, mKeyServer); - return entryListWrapper; + return mEntryListWrapper; } @Override @@ -89,18 +89,19 @@ public class ImportKeysListServerLoader extends AsyncTaskLoader<AsyncTaskResultW try { ArrayList<ImportKeysListEntry> searchResult = server.search(query); + mEntryList.clear(); // add result to data - entryList.addAll(searchResult); - entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(entryList, null); + mEntryList.addAll(searchResult); + mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, null); } catch (KeyServer.InsufficientQuery e) { Log.e(Constants.TAG, "InsufficientQuery", e); - entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(entryList, e); + mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e); } catch (KeyServer.QueryException e) { Log.e(Constants.TAG, "QueryException", e); - entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(entryList, e); + mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e); } catch (KeyServer.TooManyResponses e) { Log.e(Constants.TAG, "TooManyResponses", e); - entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(entryList, e); + mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e); } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java deleted file mode 100644 index ac505adfb..000000000 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (C) 2013 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.adapter; - -import java.util.HashMap; -import java.util.Set; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.util.Log; - -import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.database.Cursor; -import android.graphics.Color; -import android.support.v4.widget.CursorAdapter; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.RelativeLayout; -import android.widget.TextView; - -/** - * Implements StickyListHeadersAdapter from library - */ -public class KeyListPublicAdapter extends CursorAdapter implements StickyListHeadersAdapter { - private LayoutInflater mInflater; - private int mSectionColumnIndex; - private int mIndexUserId; - private int mIndexIsRevoked; - - @SuppressLint("UseSparseArrays") - private HashMap<Integer, Boolean> mSelection = new HashMap<Integer, Boolean>(); - - public KeyListPublicAdapter(Context context, Cursor c, int flags, int sectionColumnIndex) { - super(context, c, flags); - - mInflater = LayoutInflater.from(context); - mSectionColumnIndex = sectionColumnIndex; - initIndex(c); - } - - @Override - public Cursor swapCursor(Cursor newCursor) { - initIndex(newCursor); - - return super.swapCursor(newCursor); - } - - /** - * Get column indexes for performance reasons just once in constructor and swapCursor. For a - * performance comparison see http://stackoverflow.com/a/17999582 - * - * @param cursor - */ - private void initIndex(Cursor cursor) { - if (cursor != null) { - mIndexUserId = cursor.getColumnIndexOrThrow(KeychainContract.UserIds.USER_ID); - mIndexIsRevoked = cursor.getColumnIndexOrThrow(KeychainContract.Keys.IS_REVOKED); - } - } - - /** - * Bind cursor data to the item list view - * <p/> - * NOTE: CursorAdapter already implements the ViewHolder pattern in its getView() method. Thus - * no ViewHolder is required here. - */ - @Override - public void bindView(View view, Context context, Cursor cursor) { - TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId); - TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); - TextView revoked = (TextView) view.findViewById(R.id.revoked); - - String userId = cursor.getString(mIndexUserId); - String[] userIdSplit = PgpKeyHelper.splitUserId(userId); - if (userIdSplit[0] != null) { - mainUserId.setText(userIdSplit[0]); - } else { - mainUserId.setText(R.string.user_id_no_name); - } - if (userIdSplit[1] != null) { - mainUserIdRest.setText(userIdSplit[1]); - mainUserIdRest.setVisibility(View.VISIBLE); - } else { - mainUserIdRest.setVisibility(View.GONE); - } - - boolean isRevoked = cursor.getInt(mIndexIsRevoked) > 0; - if (isRevoked) { - revoked.setVisibility(View.VISIBLE); - } else { - revoked.setVisibility(View.GONE); - } - } - - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - return mInflater.inflate(R.layout.key_list_public_item, null); - } - - /** - * Creates a new header view and binds the section headers to it. It uses the ViewHolder - * pattern. Most functionality is similar to getView() from Android's CursorAdapter. - * <p/> - * NOTE: The variables mDataValid and mCursor are available due to the super class - * CursorAdapter. - */ - @Override - public View getHeaderView(int position, View convertView, ViewGroup parent) { - HeaderViewHolder holder; - if (convertView == null) { - holder = new HeaderViewHolder(); - convertView = mInflater.inflate(R.layout.key_list_public_header, parent, false); - holder.text = (TextView) convertView.findViewById(R.id.stickylist_header_text); - convertView.setTag(holder); - } else { - holder = (HeaderViewHolder) convertView.getTag(); - } - - if (!mDataValid) { - // no data available at this point - Log.d(Constants.TAG, "getHeaderView: No data available at this point!"); - return convertView; - } - - if (!mCursor.moveToPosition(position)) { - throw new IllegalStateException("couldn't move cursor to position " + position); - } - - // set header text as first char in user id - String userId = mCursor.getString(mSectionColumnIndex); - String headerText = convertView.getResources().getString(R.string.user_id_no_name); - if (userId != null && userId.length() > 0) { - headerText = "" + mCursor.getString(mSectionColumnIndex).subSequence(0, 1).charAt(0); - } - holder.text.setText(headerText); - return convertView; - } - - /** - * Header IDs should be static, position=1 should always return the same Id that is. - */ - @Override - public long getHeaderId(int position) { - if (!mDataValid) { - // no data available at this point - Log.d(Constants.TAG, "getHeaderView: No data available at this point!"); - return -1; - } - - if (!mCursor.moveToPosition(position)) { - throw new IllegalStateException("couldn't move cursor to position " + position); - } - - // return the first character of the name as ID because this is what - // headers are based upon - String userId = mCursor.getString(mSectionColumnIndex); - if (userId != null && userId.length() > 0) { - return userId.subSequence(0, 1).charAt(0); - } else { - return Long.MAX_VALUE; - } - } - - class HeaderViewHolder { - TextView text; - } - - /** - * -------------------------- MULTI-SELECTION METHODS -------------- - */ - public void setNewSelection(int position, boolean value) { - mSelection.put(position, value); - notifyDataSetChanged(); - } - - public boolean isPositionChecked(int position) { - Boolean result = mSelection.get(position); - return result == null ? false : result; - } - - public Set<Integer> getCurrentCheckedPosition() { - return mSelection.keySet(); - } - - public void removeSelection(int position) { - mSelection.remove(position); - notifyDataSetChanged(); - } - - public void clearSelection() { - mSelection.clear(); - notifyDataSetChanged(); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - // let the adapter handle setting up the row views - View v = super.getView(position, convertView, parent); - - /** - * Change color for multi-selection - */ - // default color - v.setBackgroundColor(Color.TRANSPARENT); - if (mSelection.get(position) != null && mSelection.get(position).booleanValue()) { - // this is a selected position, change color! - v.setBackgroundColor(parent.getResources().getColor(R.color.emphasis)); - } - return v; - } - -} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java deleted file mode 100644 index 11d1e8c17..000000000 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2013 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.adapter; - -import java.util.HashMap; -import java.util.Set; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.database.Cursor; -import android.graphics.Color; -import android.support.v4.widget.CursorAdapter; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -public class KeyListSecretAdapter extends CursorAdapter { - private LayoutInflater mInflater; - - private int mIndexUserId; - - @SuppressLint("UseSparseArrays") - private HashMap<Integer, Boolean> mSelection = new HashMap<Integer, Boolean>(); - - public KeyListSecretAdapter(Context context, Cursor c, int flags) { - super(context, c, flags); - - mInflater = LayoutInflater.from(context); - initIndex(c); - } - - @Override - public Cursor swapCursor(Cursor newCursor) { - initIndex(newCursor); - - return super.swapCursor(newCursor); - } - - /** - * Get column indexes for performance reasons just once in constructor and swapCursor. For a - * performance comparison see http://stackoverflow.com/a/17999582 - * - * @param cursor - */ - private void initIndex(Cursor cursor) { - if (cursor != null) { - mIndexUserId = cursor.getColumnIndexOrThrow(UserIds.USER_ID); - } - } - - @Override - public void bindView(View view, Context context, Cursor cursor) { - TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId); - TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); - - String userId = cursor.getString(mIndexUserId); - String[] userIdSplit = PgpKeyHelper.splitUserId(userId); - - if (userIdSplit[0] != null) { - mainUserId.setText(userIdSplit[0]); - } else { - mainUserId.setText(R.string.user_id_no_name); - } - if (userIdSplit[1] != null) { - mainUserIdRest.setText(userIdSplit[1]); - } else { - mainUserIdRest.setText(""); - } - } - - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - return mInflater.inflate(R.layout.key_list_secret_item, null); - } - - /** -------------------------- MULTI-SELECTION METHODS -------------- */ - public void setNewSelection(int position, boolean value) { - mSelection.put(position, value); - notifyDataSetChanged(); - } - - public boolean isPositionChecked(int position) { - Boolean result = mSelection.get(position); - return result == null ? false : result; - } - - public Set<Integer> getCurrentCheckedPosition() { - return mSelection.keySet(); - } - - public void removeSelection(int position) { - mSelection.remove(position); - notifyDataSetChanged(); - } - - public void clearSelection() { - mSelection.clear(); - notifyDataSetChanged(); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - // let the adapter handle setting up the row views - View v = super.getView(position, convertView, parent); - - /** - * Change color for multi-selection - */ - // default color - v.setBackgroundColor(Color.TRANSPARENT); - if (mSelection.get(position) != null) { - // this is a selected position, change color! - v.setBackgroundColor(parent.getResources().getColor(R.color.emphasis)); - } - return v; - } - -} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java index 78f7b1f7e..c997599bd 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java @@ -17,15 +17,11 @@ package org.sufficientlysecure.keychain.ui.adapter; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Map; -import java.util.SortedSet; -import java.util.TreeSet; - import android.content.Context; import android.widget.ArrayAdapter; +import java.util.*; + public class KeyValueSpinnerAdapter extends ArrayAdapter<String> { private final HashMap<Integer, String> mData; private final int[] mKeys; @@ -98,4 +94,4 @@ public class KeyValueSpinnerAdapter extends ArrayAdapter<String> { } return -1; } -}
\ No newline at end of file +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java index d44dd5890..fbbb9caa4 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java @@ -17,23 +17,22 @@ package org.sufficientlysecure.keychain.ui.adapter; -import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; - import android.content.Context; import android.database.Cursor; -import android.support.v4.widget.CursorAdapter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.ListView; import android.widget.TextView; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; -public class SelectKeyCursorAdapter extends CursorAdapter { + +public class SelectKeyCursorAdapter extends HighlightQueryCursorAdapter { protected int mKeyType; @@ -45,17 +44,16 @@ public class SelectKeyCursorAdapter extends CursorAdapter { private int mIndexProjectionValid; private int mIndexProjectionAvailable; - public final static String PROJECTION_ROW_AVAILABLE = "available"; - public final static String PROJECTION_ROW_VALID = "valid"; + public static final String PROJECTION_ROW_AVAILABLE = "available"; + public static final String PROJECTION_ROW_VALID = "valid"; public SelectKeyCursorAdapter(Context context, Cursor c, int flags, ListView listView, - int keyType) { + int keyType) { super(context, c, flags); mInflater = LayoutInflater.from(context); mListView = listView; mKeyType = keyType; - initIndex(c); } @@ -69,7 +67,7 @@ public class SelectKeyCursorAdapter extends CursorAdapter { /** * Get column indexes for performance reasons just once in constructor and swapCursor. For a * performance comparison see http://stackoverflow.com/a/17999582 - * + * * @param cursor */ private void initIndex(Cursor cursor) { @@ -104,12 +102,12 @@ public class SelectKeyCursorAdapter extends CursorAdapter { String[] userIdSplit = PgpKeyHelper.splitUserId(userId); if (userIdSplit[0] != null) { - mainUserId.setText(userIdSplit[0]); + mainUserId.setText(highlightSearchQuery(userIdSplit[0])); } else { mainUserId.setText(R.string.user_id_no_name); } if (userIdSplit[1] != null) { - mainUserIdRest.setText(userIdSplit[1]); + mainUserIdRest.setText(highlightSearchQuery(userIdSplit[1])); } else { mainUserIdRest.setText(""); } @@ -117,7 +115,7 @@ public class SelectKeyCursorAdapter extends CursorAdapter { // TODO: needed to key id to no? keyId.setText(R.string.no_key); long masterKeyId = cursor.getLong(mIndexMasterKeyId); - keyId.setText(PgpKeyHelper.convertKeyIdToHex(masterKeyId)); + keyId.setText(PgpKeyHelper.convertKeyIdToHexShort(masterKeyId)); // TODO: needed to set unknown_status? status.setText(R.string.unknown_status); @@ -164,5 +162,4 @@ public class SelectKeyCursorAdapter extends CursorAdapter { public View newView(Context context, Cursor cursor, ViewGroup parent) { return mInflater.inflate(R.layout.select_key_item, null); } - } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java index 924a70897..f435d46ef 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2012-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.adapter; import android.content.Context; @@ -19,12 +36,12 @@ public class TabsAdapter extends FragmentStatePagerAdapter implements ActionBar. private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>(); static final class TabInfo { - private final Class<?> clss; - private final Bundle args; + private final Class<?> mClss; + private final Bundle mArgs; - TabInfo(Class<?> _class, Bundle _args) { - clss = _class; - args = _args; + TabInfo(Class<?> mClss, Bundle mArgs) { + this.mClss = mClss; + this.mArgs = mArgs; } } @@ -54,7 +71,7 @@ public class TabsAdapter extends FragmentStatePagerAdapter implements ActionBar. @Override public Fragment getItem(int position) { TabInfo info = mTabs.get(position); - return Fragment.instantiate(mContext, info.clss.getName(), info.args); + return Fragment.instantiate(mContext, info.mClss.getName(), info.mArgs); } public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { @@ -81,4 +98,4 @@ public class TabsAdapter extends FragmentStatePagerAdapter implements ActionBar. public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) { } -}
\ No newline at end of file +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java index 54c7eb60e..9d60c1530 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java @@ -17,18 +17,23 @@ package org.sufficientlysecure.keychain.ui.adapter; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; - import android.content.Context; +import android.content.res.ColorStateList; import android.database.Cursor; +import android.graphics.Color; import android.support.v4.widget.CursorAdapter; +import android.text.format.DateFormat; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.OtherHelper; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; + +import java.util.Date; public class ViewKeyKeysAdapter extends CursorAdapter { private LayoutInflater mInflater; @@ -40,6 +45,10 @@ public class ViewKeyKeysAdapter extends CursorAdapter { private int mIndexCanCertify; private int mIndexCanEncrypt; private int mIndexCanSign; + private int mIndexRevokedKey; + private int mIndexExpiry; + + private ColorStateList mDefaultTextColor; public ViewKeyKeysAdapter(Context context, Cursor c, int flags) { super(context, c, flags); @@ -59,7 +68,7 @@ public class ViewKeyKeysAdapter extends CursorAdapter { /** * Get column indexes for performance reasons just once in constructor and swapCursor. For a * performance comparison see http://stackoverflow.com/a/17999582 - * + * * @param cursor */ private void initIndex(Cursor cursor) { @@ -71,6 +80,8 @@ public class ViewKeyKeysAdapter extends CursorAdapter { mIndexCanCertify = cursor.getColumnIndexOrThrow(Keys.CAN_CERTIFY); mIndexCanEncrypt = cursor.getColumnIndexOrThrow(Keys.CAN_ENCRYPT); mIndexCanSign = cursor.getColumnIndexOrThrow(Keys.CAN_SIGN); + mIndexRevokedKey = cursor.getColumnIndexOrThrow(Keys.IS_REVOKED); + mIndexExpiry = cursor.getColumnIndexOrThrow(Keys.EXPIRY); } } @@ -78,17 +89,18 @@ public class ViewKeyKeysAdapter extends CursorAdapter { public void bindView(View view, Context context, Cursor cursor) { TextView keyId = (TextView) view.findViewById(R.id.keyId); TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails); + TextView keyExpiry = (TextView) view.findViewById(R.id.keyExpiry); ImageView masterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey); ImageView certifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey); ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey); ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey); + ImageView revokedKeyIcon = (ImageView) view.findViewById(R.id.ic_revokedKey); - String keyIdStr = "0x" + PgpKeyHelper.convertKeyIdToHex(cursor.getLong(mIndexKeyId)); + String keyIdStr = PgpKeyHelper.convertKeyIdToHexShort(cursor.getLong(mIndexKeyId)); String algorithmStr = PgpKeyHelper.getAlgorithmInfo(cursor.getInt(mIndexAlgorithm), cursor.getInt(mIndexKeySize)); keyId.setText(keyIdStr); - keyDetails.setText("(" + algorithmStr + ")"); if (cursor.getInt(mIndexIsMasterKey) != 1) { @@ -114,11 +126,52 @@ public class ViewKeyKeysAdapter extends CursorAdapter { } else { signIcon.setVisibility(View.VISIBLE); } + + boolean valid = true; + if (cursor.getInt(mIndexRevokedKey) > 0) { + revokedKeyIcon.setVisibility(View.VISIBLE); + + valid = false; + } else { + keyId.setTextColor(mDefaultTextColor); + keyDetails.setTextColor(mDefaultTextColor); + keyExpiry.setTextColor(mDefaultTextColor); + + revokedKeyIcon.setVisibility(View.GONE); + } + + if (!cursor.isNull(mIndexExpiry)) { + Date expiryDate = new Date(cursor.getLong(mIndexExpiry) * 1000); + + valid = valid && expiryDate.after(new Date()); + keyExpiry.setText("(" + + context.getString(R.string.label_expiry) + ": " + + DateFormat.getDateFormat(context).format(expiryDate) + ")"); + + keyExpiry.setVisibility(View.VISIBLE); + } + else { + keyExpiry.setVisibility(View.GONE); + } + // if key is expired or revoked, strike through text + if (!valid) { + keyId.setText(OtherHelper.strikeOutText(keyId.getText())); + keyDetails.setText(OtherHelper.strikeOutText(keyDetails.getText())); + keyExpiry.setText(OtherHelper.strikeOutText(keyExpiry.getText())); + } + keyId.setEnabled(valid); + keyDetails.setEnabled(valid); + keyExpiry.setEnabled(valid); } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - return mInflater.inflate(R.layout.view_key_keys_item, null); + View view = mInflater.inflate(R.layout.view_key_keys_item, null); + if (mDefaultTextColor == null) { + TextView keyId = (TextView) view.findViewById(R.id.keyId); + mDefaultTextColor = keyId.getTextColors(); + } + return view; } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java index cf8699417..61572b9ce 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java @@ -17,33 +17,54 @@ package org.sufficientlysecure.keychain.ui.adapter; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; - import android.content.Context; import android.database.Cursor; import android.support.v4.widget.CursorAdapter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.CompoundButton; import android.widget.TextView; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; + +import java.util.ArrayList; public class ViewKeyUserIdsAdapter extends CursorAdapter { private LayoutInflater mInflater; - private int mIndexUserId; + private int mIndexUserId, mIndexRank; - public ViewKeyUserIdsAdapter(Context context, Cursor c, int flags) { + final private ArrayList<Boolean> mCheckStates; + + public ViewKeyUserIdsAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes) { super(context, c, flags); mInflater = LayoutInflater.from(context); + mCheckStates = showCheckBoxes ? new ArrayList<Boolean>() : null; + initIndex(c); } + public ViewKeyUserIdsAdapter(Context context, Cursor c, int flags) { + this(context, c, flags, false); + } @Override public Cursor swapCursor(Cursor newCursor) { initIndex(newCursor); + if(mCheckStates != null) { + mCheckStates.clear(); + if(newCursor != null) { + int count = newCursor.getCount(); + mCheckStates.ensureCapacity(count); + // initialize to true (use case knowledge: we usually want to sign all uids) + for(int i = 0; i < count; i++) + mCheckStates.add(true); + } + } return super.swapCursor(newCursor); } @@ -51,26 +72,73 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter { /** * Get column indexes for performance reasons just once in constructor and swapCursor. For a * performance comparison see http://stackoverflow.com/a/17999582 - * + * * @param cursor */ private void initIndex(Cursor cursor) { if (cursor != null) { mIndexUserId = cursor.getColumnIndexOrThrow(UserIds.USER_ID); + mIndexRank = cursor.getColumnIndexOrThrow(UserIds.RANK); } } @Override public void bindView(View view, Context context, Cursor cursor) { - String userIdStr = cursor.getString(mIndexUserId); - TextView userId = (TextView) view.findViewById(R.id.userId); - userId.setText(userIdStr); + TextView vRank = (TextView) view.findViewById(R.id.rank); + TextView vUserId = (TextView) view.findViewById(R.id.userId); + TextView vAddress = (TextView) view.findViewById(R.id.address); + + vRank.setText(Integer.toString(cursor.getInt(mIndexRank))); + + String[] userId = PgpKeyHelper.splitUserId(cursor.getString(mIndexUserId)); + if (userId[0] != null) { + vUserId.setText(userId[0]); + } else { + vUserId.setText(R.string.user_id_no_name); + } + vAddress.setText(userId[1]); + + // don't care further if checkboxes aren't shown + if(mCheckStates == null) + return; + + final CheckBox vCheckBox = (CheckBox) view.findViewById(R.id.checkBox); + final int position = cursor.getPosition(); + vCheckBox.setClickable(false); + vCheckBox.setChecked(mCheckStates.get(position)); + vCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean b) { + mCheckStates.set(position, b); + } + }); + view.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + vCheckBox.toggle(); + } + }); + + } + + public ArrayList<String> getSelectedUserIds() { + ArrayList<String> result = new ArrayList<String>(); + for(int i = 0; i < mCheckStates.size(); i++) { + if(mCheckStates.get(i)) { + mCursor.moveToPosition(i); + result.add(mCursor.getString(mIndexUserId)); + } + } + return result; } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - return mInflater.inflate(R.layout.view_key_userids_item, null); + View view = mInflater.inflate(R.layout.view_key_userids_item, null); + // only need to do this once ever, since mShowCheckBoxes is final + view.findViewById(R.id.checkBox).setVisibility(mCheckStates != null ? View.VISIBLE : View.GONE); + return view; } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java index 2b8cba857..20b70658c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java @@ -23,7 +23,6 @@ import android.content.DialogInterface; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.support.v4.app.FragmentActivity; - import org.sufficientlysecure.keychain.R; public class BadImportKeyDialogFragment extends DialogFragment { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java index 98b677511..ffb45afc5 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java @@ -25,14 +25,14 @@ import android.support.v4.app.DialogFragment; import android.support.v4.app.FragmentActivity; import android.view.LayoutInflater; import android.view.View; +import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Spinner; - import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.util.Choice; -import java.util.Vector; +import java.util.ArrayList; public class CreateKeyDialogFragment extends DialogFragment { @@ -78,7 +78,7 @@ public class CreateKeyDialogFragment extends DialogFragment { boolean wouldBeMasterKey = (childCount == 0); final Spinner algorithm = (Spinner) view.findViewById(R.id.create_key_algorithm); - Vector<Choice> choices = new Vector<Choice>(); + ArrayList<Choice> choices = new ArrayList<Choice>(); choices.add(new Choice(Id.choice.algorithm.dsa, getResources().getString( R.string.dsa))); if (!wouldBeMasterKey) { @@ -114,21 +114,8 @@ public class CreateKeyDialogFragment extends DialogFragment { public void onClick(DialogInterface di, int id) { di.dismiss(); try { - int nKeyIndex = keySize.getSelectedItemPosition(); - switch (nKeyIndex) { - case 0: - mNewKeySize = 512; - break; - case 1: - mNewKeySize = 1024; - break; - case 2: - mNewKeySize = 2048; - break; - case 3: - mNewKeySize = 4096; - break; - } + final String selectedItem = (String) keySize.getSelectedItem(); + mNewKeySize = Integer.parseInt(selectedItem); } catch (NumberFormatException e) { mNewKeySize = 0; } @@ -146,7 +133,26 @@ public class CreateKeyDialogFragment extends DialogFragment { } }); - return dialog.create(); + final AlertDialog alertDialog = dialog.create(); + + final AdapterView.OnItemSelectedListener weakRsaListener = new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { + final Choice selectedAlgorithm = (Choice)algorithm.getSelectedItem(); + final int selectedKeySize = Integer.parseInt((String)keySize.getSelectedItem()); + final boolean isWeakRsa = (selectedAlgorithm.getId() == Id.choice.algorithm.rsa && selectedKeySize <= 1024); + alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(!isWeakRsa); + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + } + }; + + keySize.setOnItemSelectedListener(weakRsaListener); + algorithm.setOnItemSelectedListener(weakRsaListener); + + return alertDialog; } -}
\ No newline at end of file +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java index cd8bc79a9..b4c38184c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java @@ -17,10 +17,6 @@ package org.sufficientlysecure.keychain.ui.dialog; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; - import android.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; @@ -32,6 +28,9 @@ import android.os.Messenger; import android.support.v4.app.DialogFragment; import android.support.v4.app.FragmentActivity; import android.widget.Toast; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; public class DeleteFileDialogFragment extends DialogFragment { private static final String ARG_DELETE_FILE = "delete_file"; @@ -67,7 +66,7 @@ public class DeleteFileDialogFragment extends DialogFragment { alert.setMessage(this.getString(R.string.file_delete_confirmation, deleteFile)); alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - + @Override public void onClick(DialogInterface dialog, int id) { dismiss(); @@ -83,19 +82,23 @@ public class DeleteFileDialogFragment extends DialogFragment { intent.putExtra(KeychainIntentService.EXTRA_DATA, data); ProgressDialogFragment deletingDialog = ProgressDialogFragment.newInstance( - R.string.progress_deleting_securely, ProgressDialog.STYLE_HORIZONTAL, false, null); - - // Message is received after deleting is done in ApgService - KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(activity, deletingDialog) { + getString(R.string.progress_deleting_securely), + ProgressDialog.STYLE_HORIZONTAL, + false, + null); + + // Message is received after deleting is done in KeychainIntentService + KeychainIntentServiceHandler saveHandler = + new KeychainIntentServiceHandler(activity, deletingDialog) { public void handleMessage(Message message) { - // handle messages by standard ApgHandler first + // handle messages by standard KeychainIntentHandler first super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { Toast.makeText(activity, R.string.file_delete_successful, Toast.LENGTH_SHORT).show(); } - }; + } }; // Create a new Messenger for the communication back @@ -118,4 +121,4 @@ public class DeleteFileDialogFragment extends DialogFragment { return alert.create(); } -}
\ No newline at end of file +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java index dc40bab2a..3ff88aa2b 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2013-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 @@ -28,6 +28,11 @@ import android.os.Messenger; import android.os.RemoteException; import android.support.v4.app.DialogFragment; import android.support.v4.app.FragmentActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.CheckBox; +import android.widget.LinearLayout; +import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; @@ -41,140 +46,163 @@ import java.util.ArrayList; public class DeleteKeyDialogFragment extends DialogFragment { private static final String ARG_MESSENGER = "messenger"; - private static final String ARG_DELETE_KEY_RING_ROW_IDS = "delete_file"; - private static final String ARG_KEY_TYPE = "key_type"; + private static final String ARG_DELETE_KEY_RING_ROW_IDS = "delete_key_ring_row_ids"; public static final int MESSAGE_OKAY = 1; + public static final int MESSAGE_ERROR = 0; - public static final String MESSAGE_NOT_DELETED = "not_deleted"; + private boolean isSingleSelection = false; + + private TextView mainMessage; + private CheckBox checkDeleteSecret; + private LinearLayout deleteSecretKeyView; + private View inflateView; private Messenger mMessenger; /** * Creates new instance of this delete file dialog fragment */ - public static DeleteKeyDialogFragment newInstance(Messenger messenger, long[] keyRingRowIds, - int keyType) { + public static DeleteKeyDialogFragment newInstance(Messenger messenger, long[] keyRingRowIds + ) { DeleteKeyDialogFragment frag = new DeleteKeyDialogFragment(); Bundle args = new Bundle(); args.putParcelable(ARG_MESSENGER, messenger); args.putLongArray(ARG_DELETE_KEY_RING_ROW_IDS, keyRingRowIds); - args.putInt(ARG_KEY_TYPE, keyType); + //We don't need the key type frag.setArguments(args); return frag; } - /** - * Creates dialog - */ + @Override public Dialog onCreateDialog(Bundle savedInstanceState) { + final FragmentActivity activity = getActivity(); mMessenger = getArguments().getParcelable(ARG_MESSENGER); final long[] keyRingRowIds = getArguments().getLongArray(ARG_DELETE_KEY_RING_ROW_IDS); - final int keyType = getArguments().getInt(ARG_KEY_TYPE); AlertDialog.Builder builder = new AlertDialog.Builder(activity); + + //Setup custom View to display in AlertDialog + LayoutInflater inflater = activity.getLayoutInflater(); + inflateView = inflater.inflate(R.layout.view_key_delete_fragment, null); + builder.setView(inflateView); + + deleteSecretKeyView = (LinearLayout) inflateView.findViewById(R.id.deleteSecretKeyView); + mainMessage = (TextView) inflateView.findViewById(R.id.mainMessage); + checkDeleteSecret = (CheckBox) inflateView.findViewById(R.id.checkDeleteSecret); + builder.setTitle(R.string.warning); + //If only a single key has been selected if (keyRingRowIds.length == 1) { Uri dataUri; - if (keyType == Id.type.public_key) { - dataUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(String.valueOf(keyRingRowIds[0])); + ArrayList<Long> publicKeyRings; //Any one will do + isSingleSelection = true; + + long selectedRow = keyRingRowIds[0]; + long keyType; + publicKeyRings = ProviderHelper.getPublicKeyRingsRowIds(activity); + + if (publicKeyRings.contains(selectedRow)) { + //TODO Should be a better method to do this other than getting all the KeyRings + dataUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(String.valueOf(selectedRow)); + keyType = Id.type.public_key; } else { - dataUri = KeychainContract.KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowIds[0])); + dataUri = KeychainContract.KeyRings.buildSecretKeyRingsUri(String.valueOf(selectedRow)); + keyType = Id.type.secret_key; } + String userId = ProviderHelper.getUserId(activity, dataUri); + //Hide the Checkbox and TextView since this is a single selection,user will be notified thru message + deleteSecretKeyView.setVisibility(View.GONE); + //Set message depending on which key it is. + mainMessage.setText(getString(keyType == Id.type.secret_key ? R.string.secret_key_deletion_confirmation + : R.string.public_key_deletetion_confirmation, userId)); + - builder.setMessage(getString( - keyType == Id.type.public_key ? R.string.key_deletion_confirmation - : R.string.secret_key_deletion_confirmation, userId)); } else { - builder.setMessage(R.string.key_deletion_confirmation_multi); + deleteSecretKeyView.setVisibility(View.VISIBLE); + mainMessage.setText(R.string.key_deletion_confirmation_multi); } + builder.setIcon(R.drawable.ic_dialog_alert_holo_light); builder.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - ArrayList<String> notDeleted = new ArrayList<String>(); - - if (keyType == Id.type.public_key) { - Uri queryUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(); - String[] projection = new String[]{ - KeychainContract.KeyRings._ID, // 0 - KeychainContract.KeyRings.MASTER_KEY_ID, // 1 - KeychainContract.UserIds.USER_ID // 2 - }; - - // make selection with all entries where _ID is one of the given row ids - String selection = KeychainDatabase.Tables.KEY_RINGS + "." + - KeychainContract.KeyRings._ID + " IN("; - String selectionIDs = ""; - for (int i = 0; i < keyRingRowIds.length; i++) { - selectionIDs += "'" + String.valueOf(keyRingRowIds[i]) + "'"; - if (i+1 < keyRingRowIds.length) - selectionIDs += ","; - } - selection += selectionIDs + ")"; - - Cursor cursor = activity.getContentResolver().query(queryUri, projection, - selection, null, null); - - long rowId; - long masterKeyId; - String userId; - try { - while (cursor != null && cursor.moveToNext()) { - rowId = cursor.getLong(0); - masterKeyId = cursor.getLong(1); - userId = cursor.getString(2); - - Log.d(Constants.TAG, "rowId: " + rowId + ", masterKeyId: " + masterKeyId - + ", userId: " + userId); - - // check if a corresponding secret key exists... - Cursor secretCursor = activity.getContentResolver().query( - KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri(String.valueOf(masterKeyId)), - null, null, null, null - ); - if (secretCursor != null && secretCursor.getCount() > 0) { - notDeleted.add(userId); - } else { - // it is okay to delete this key, no secret key found! - ProviderHelper.deletePublicKeyRing(activity, rowId); - } - if (secretCursor != null) { - secretCursor.close(); + public void onClick(DialogInterface dialog, int which) { + Uri queryUri = KeychainContract.KeyRings.buildUnifiedKeyRingsUri(); + String[] projection = new String[]{ + KeychainContract.KeyRings.MASTER_KEY_ID, // 0 + KeychainContract.KeyRings.TYPE// 1 + }; + + // make selection with all entries where _ID is one of the given row ids + String selection = KeychainDatabase.Tables.KEY_RINGS + "." + + KeychainContract.KeyRings._ID + " IN("; + String selectionIDs = ""; + for (int i = 0; i < keyRingRowIds.length; i++) { + selectionIDs += "'" + String.valueOf(keyRingRowIds[i]) + "'"; + if (i + 1 < keyRingRowIds.length) + selectionIDs += ","; + } + selection += selectionIDs + ")"; + + Cursor cursor = activity.getContentResolver().query(queryUri, projection, + selection, null, null); + + + long masterKeyId; + long keyType; + boolean isSuccessfullyDeleted; + try { + isSuccessfullyDeleted = false; + while (cursor != null && cursor.moveToNext()) { + masterKeyId = cursor.getLong(0); + keyType = cursor.getLong(1); + + Log.d(Constants.TAG, "masterKeyId: " + masterKeyId + + ", keyType:" + (keyType == KeychainContract.KeyTypes.PUBLIC ? "Public" : "Private")); + + + if (keyType == KeychainContract.KeyTypes.SECRET) { + if (checkDeleteSecret.isChecked() || isSingleSelection) { + ProviderHelper.deleteUnifiedKeyRing(activity, String.valueOf(masterKeyId), true); } - } - } finally { - if (cursor != null) { - cursor.close(); + } else { + ProviderHelper.deleteUnifiedKeyRing(activity, String.valueOf(masterKeyId), false); } } - } else { - for (long keyRowId : keyRingRowIds) { - ProviderHelper.deleteSecretKeyRing(activity, keyRowId); + + //Check if the selected rows have actually been deleted + cursor = activity.getContentResolver().query(queryUri, projection, selection, null, null); + if (cursor == null || cursor.getCount() == 0 || !checkDeleteSecret.isChecked()) { + isSuccessfullyDeleted = true; + } + + } finally { + if (cursor != null) { + cursor.close(); } + } dismiss(); - if (notDeleted.size() > 0) { - Bundle data = new Bundle(); - data.putStringArrayList(MESSAGE_NOT_DELETED, notDeleted); - sendMessageToHandler(MESSAGE_OKAY, data); - } else { + if (isSuccessfullyDeleted) { sendMessageToHandler(MESSAGE_OKAY, null); + } else { + sendMessageToHandler(MESSAGE_ERROR, null); } } - }); + + } + ); builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { @Override @@ -196,7 +224,6 @@ public class DeleteKeyDialogFragment extends DialogFragment { if (data != null) { msg.setData(data); } - try { mMessenger.send(msg); } catch (RemoteException e) { @@ -205,4 +232,5 @@ public class DeleteKeyDialogFragment extends DialogFragment { Log.w(Constants.TAG, "Messenger is null!", e); } } + }
\ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java index 39ce63b5f..a4285c8e9 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java @@ -17,11 +17,6 @@ package org.sufficientlysecure.keychain.ui.dialog; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.FileHelper; -import org.sufficientlysecure.keychain.util.Log; - import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; @@ -33,14 +28,16 @@ import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.support.v4.app.DialogFragment; -import android.text.Html; import android.view.LayoutInflater; import android.view.View; import android.widget.CheckBox; 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.helper.FileHelper; +import org.sufficientlysecure.keychain.util.Log; public class FileDialogFragment extends DialogFragment { private static final String ARG_MESSENGER = "messenger"; @@ -67,7 +64,7 @@ public class FileDialogFragment extends DialogFragment { * Creates new instance of this file dialog fragment */ public static FileDialogFragment newInstance(Messenger messenger, String title, String message, - String defaultFile, String checkboxText) { + String defaultFile, String checkboxText) { FileDialogFragment frag = new FileDialogFragment(); Bundle args = new Bundle(); args.putParcelable(ARG_MESSENGER, messenger); @@ -177,34 +174,33 @@ public class FileDialogFragment extends DialogFragment { @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode & 0xFFFF) { - case REQUEST_CODE: { - if (resultCode == Activity.RESULT_OK && data != null) { - try { - String path = data.getData().getPath(); - Log.d(Constants.TAG, "path=" + path); - - // set filename used in export/import dialogs - setFilename(path); - } catch (NullPointerException e) { - Log.e(Constants.TAG, "Nullpointer while retrieving path!", e); + case REQUEST_CODE: { + if (resultCode == Activity.RESULT_OK && data != null) { + try { + String path = data.getData().getPath(); + Log.d(Constants.TAG, "path=" + path); + + // set filename used in export/import dialogs + setFilename(path); + } catch (NullPointerException e) { + Log.e(Constants.TAG, "Nullpointer while retrieving path!", e); + } } - } - break; - } + break; + } - default: - super.onActivityResult(requestCode, resultCode, data); + default: + super.onActivityResult(requestCode, resultCode, data); - break; + break; } } /** * Send message back to handler which is initialized in a activity - * - * @param what - * Message integer you want to send + * + * @param what Message integer you want to send */ private void sendMessageToHandler(Integer what, Bundle data) { Message msg = Message.obtain(); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java index c00232fd1..271219fa3 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java @@ -17,20 +17,6 @@ package org.sufficientlysecure.keychain.ui.dialog; -import org.spongycastle.openpgp.PGPException; -import org.spongycastle.openpgp.PGPPrivateKey; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; -import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.service.PassphraseCacheService; -import org.sufficientlysecure.keychain.util.Log; - import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; @@ -52,6 +38,19 @@ import android.widget.EditText; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; import android.widget.Toast; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.util.Log; public class PassphraseDialogFragment extends DialogFragment implements OnEditorActionListener { private static final String ARG_MESSENGER = "messenger"; @@ -62,20 +61,18 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor private Messenger mMessenger; private EditText mPassphraseEditText; - private boolean canKB; + private boolean mCanKB; /** * Creates new instance of this dialog fragment - * - * @param secretKeyId - * secret key id you want to use - * @param messenger - * to communicate back after caching the passphrase + * + * @param secretKeyId secret key id you want to use + * @param messenger to communicate back after caching the passphrase * @return * @throws PgpGeneralException */ public static PassphraseDialogFragment newInstance(Context context, Messenger messenger, - long secretKeyId) throws PgpGeneralException { + long secretKeyId) throws PgpGeneralException { // check if secret key has a passphrase if (!(secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none)) { if (!PassphraseCacheService.hasPassphrase(context, secretKeyId)) { @@ -131,7 +128,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor } }); alert.setCancelable(false); - canKB = false; + mCanKB = false; return alert.create(); } String userId = PgpKeyHelper.getMainUserIdSafe(activity, secretKey); @@ -158,7 +155,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor PGPSecretKey clickSecretKey = secretKey; if (clickSecretKey != null) { - while (keyOK == true) { + while (keyOK) { if (clickSecretKey != null) { // check again for loop try { PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() @@ -171,7 +168,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor Toast.makeText(activity, R.string.error_could_not_extract_private_key, Toast.LENGTH_SHORT).show(); - + sendMessageToHandler(MESSAGE_CANCEL); return; } else { @@ -187,14 +184,14 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor } catch (PGPException e) { Toast.makeText(activity, R.string.wrong_passphrase, Toast.LENGTH_SHORT).show(); - + sendMessageToHandler(MESSAGE_CANCEL); return; } } else { Toast.makeText(activity, R.string.error_could_not_extract_private_key, Toast.LENGTH_SHORT).show(); - + sendMessageToHandler(MESSAGE_CANCEL); return; // ran out of keys to try } @@ -207,7 +204,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor // cache the new passphrase Log.d(Constants.TAG, "Everything okay! Caching entered passphrase"); PassphraseCacheService.addCachedPassphrase(activity, keyId, passphrase); - if (keyOK == false && clickSecretKey.getKeyID() != keyId) { + if (!keyOK && clickSecretKey.getKeyID() != keyId) { PassphraseCacheService.addCachedPassphrase(activity, clickSecretKey.getKeyID(), passphrase); } @@ -224,14 +221,14 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor } }); - canKB = true; + mCanKB = true; return alert.create(); } @Override public void onActivityCreated(Bundle arg0) { super.onActivityCreated(arg0); - if (canKB) { + if (mCanKB) { // request focus and open soft keyboard mPassphraseEditText.requestFocus(); getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE); @@ -265,9 +262,8 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor /** * Send message back to handler which is initialized in a activity - * - * @param what - * Message integer you want to send + * + * @param what Message integer you want to send */ private void sendMessageToHandler(Integer what) { Message msg = Message.obtain(); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java index 6c62d14e0..132a2ce86 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java @@ -26,11 +26,10 @@ import android.content.DialogInterface.OnKeyListener; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.view.KeyEvent; - import org.sufficientlysecure.keychain.R; public class ProgressDialogFragment extends DialogFragment { - private static final String ARG_MESSAGE_ID = "message_id"; + private static final String ARG_MESSAGE = "message"; private static final String ARG_STYLE = "style"; private static final String ARG_CANCELABLE = "cancelable"; @@ -39,16 +38,16 @@ public class ProgressDialogFragment extends DialogFragment { /** * Creates new instance of this fragment * - * @param messageId + * @param message * @param style * @param cancelable * @return */ - public static ProgressDialogFragment newInstance(int messageId, int style, boolean cancelable, + public static ProgressDialogFragment newInstance(String message, int style, boolean cancelable, OnCancelListener onCancelListener) { ProgressDialogFragment frag = new ProgressDialogFragment(); Bundle args = new Bundle(); - args.putInt(ARG_MESSAGE_ID, messageId); + args.putString(ARG_MESSAGE, message); args.putInt(ARG_STYLE, style); args.putBoolean(ARG_CANCELABLE, cancelable); @@ -101,8 +100,9 @@ public class ProgressDialogFragment extends DialogFragment { public void onCancel(DialogInterface dialog) { super.onCancel(dialog); - if (this.mOnCancelListener != null) + if (this.mOnCancelListener != null) { this.mOnCancelListener.onCancel(dialog); + } } /** @@ -117,22 +117,22 @@ public class ProgressDialogFragment extends DialogFragment { dialog.setCancelable(false); dialog.setCanceledOnTouchOutside(false); - int messageId = getArguments().getInt(ARG_MESSAGE_ID); + String message = getArguments().getString(ARG_MESSAGE); int style = getArguments().getInt(ARG_STYLE); boolean cancelable = getArguments().getBoolean(ARG_CANCELABLE); - dialog.setMessage(getString(messageId)); + dialog.setMessage(message); dialog.setProgressStyle(style); if (cancelable) { dialog.setButton(DialogInterface.BUTTON_NEGATIVE, activity.getString(R.string.progress_cancel), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.cancel(); - } - }); + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }); } // Disable the back button @@ -140,7 +140,6 @@ public class ProgressDialogFragment extends DialogFragment { @Override public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK) { return true; } @@ -152,4 +151,4 @@ public class ProgressDialogFragment extends DialogFragment { return dialog; } -}
\ No newline at end of file +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java index e406547b3..ae61c1470 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java @@ -17,10 +17,6 @@ package org.sufficientlysecure.keychain.ui.dialog; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.util.Log; - import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; @@ -40,6 +36,9 @@ import android.widget.EditText; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; import android.widget.Toast; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.util.Log; public class SetPassphraseDialogFragment extends DialogFragment implements OnEditorActionListener { private static final String ARG_MESSENGER = "messenger"; @@ -55,11 +54,9 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi /** * Creates new instance of this dialog fragment - * - * @param title - * title of dialog - * @param messenger - * to communicate back after setting the passphrase + * + * @param title title of dialog + * @param messenger to communicate back after setting the passphrase * @return */ public static SetPassphraseDialogFragment newInstance(Messenger messenger, int title) { @@ -96,7 +93,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi mPassphraseAgainEditText = (EditText) view.findViewById(R.id.passphrase_passphrase_again); alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - + @Override public void onClick(DialogInterface dialog, int id) { dismiss(); @@ -130,7 +127,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi }); alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - + @Override public void onClick(DialogInterface dialog, int id) { dismiss(); @@ -168,9 +165,8 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi /** * Send message back to handler which is initialized in a activity - * - * @param what - * Message integer you want to send + * + * @param what Message integer you want to send */ private void sendMessageToHandler(Integer what, Bundle data) { Message msg = Message.obtain(); @@ -187,4 +183,4 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi Log.w(Constants.TAG, "Messenger is null!", e); } } -}
\ No newline at end of file +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java index b850638a6..741530b1d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java @@ -17,9 +17,6 @@ package org.sufficientlysecure.keychain.ui.dialog; -import org.sufficientlysecure.htmltextview.HtmlTextView; -import org.sufficientlysecure.keychain.R; - import android.annotation.TargetApi; import android.app.AlertDialog; import android.app.Dialog; @@ -31,6 +28,8 @@ import android.os.Bundle; import android.provider.Settings; import android.support.v4.app.DialogFragment; import android.support.v4.app.FragmentActivity; +import org.sufficientlysecure.htmltextview.HtmlTextView; +import org.sufficientlysecure.keychain.R; @TargetApi(Build.VERSION_CODES.JELLY_BEAN) public class ShareNfcDialogFragment extends DialogFragment { @@ -97,4 +96,4 @@ public class ShareNfcDialogFragment extends DialogFragment { return alert.create(); } -}
\ No newline at end of file +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java index f26cd35c0..94586810e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java @@ -28,7 +28,6 @@ import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; - import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; @@ -91,7 +90,7 @@ public class ShareQrCodeDialogFragment extends DialogFragment { alert.setPositiveButton(R.string.btn_okay, null); byte[] fingerprintBlob = ProviderHelper.getFingerprint(getActivity(), dataUri); - String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, false); + String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob); mText.setText(getString(R.string.share_qr_code_dialog_fingerprint_text) + " " + fingerprint); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FixedListView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FixedListView.java index b9dcd0d25..da29f808a 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FixedListView.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FixedListView.java @@ -24,7 +24,7 @@ import android.widget.ListView; /** * Automatically calculate height of ListView based on contained items. This enables to put this * ListView into a ScrollView without messing up. - * + * <p/> * from * http://stackoverflow.com/questions/2419246/how-do-i-create-a-listview-thats-not-in-a-scrollview- * or-has-the-scrollview-dis @@ -52,4 +52,4 @@ public class FixedListView extends ListView { super.onMeasure(widthMeasureSpec, expandSpec); } -}
\ No newline at end of file +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java new file mode 100644 index 000000000..f9a5b92f3 --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java @@ -0,0 +1,203 @@ +/* + * 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.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.widget.LinearLayout; +import android.widget.TextView; +import com.beardedhen.androidbootstrap.FontAwesomeText; +import org.sufficientlysecure.keychain.R; + +/** + * Class representing a LinearLayout that can fold and hide it's content when pressed + * To use just add the following to your xml layout + + <org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + custom:foldedLabel="@string/TEXT_TO_DISPLAY_WHEN_FOLDED" + custom:unFoldedLabel="@string/TEXT_TO_DISPLAY_WHEN_UNFOLDED" + custom:foldedIcon="ICON_NAME_FROM_FontAwesomeText_TO_USE_WHEN_FOLDED" + custom:unFoldedIcon="ICON_NAME_FROM_FontAwesomeText_TO_USE_WHEN_UNFOLDED"> + + <include layout="@layout/ELEMENTS_TO_BE_FOLDED"/> + + </org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout> + + */ +public class FoldableLinearLayout extends LinearLayout { + + private FontAwesomeText mFoldableIcon; + private boolean mFolded; + private boolean mHasMigrated = false; + private Integer mShortAnimationDuration = null; + private TextView mFoldableTextView = null; + private LinearLayout mFoldableContainer = null; + private View mFoldableLayout = null; + + private String mFoldedIconName; + private String mUnFoldedIconName; + private String mFoldedLabel; + private String mUnFoldedLabel; + + public FoldableLinearLayout(Context context) { + super(context); + processAttributes(context, null); + } + + public FoldableLinearLayout(Context context, AttributeSet attrs) { + super(context, attrs); + processAttributes(context, attrs); + } + + public FoldableLinearLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs); + processAttributes(context, attrs); + } + + /** + * Load given attributes to inner variables, + * @param context + * @param attrs + */ + private void processAttributes(Context context, AttributeSet attrs) { + if(attrs != null) { + TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.FoldableLinearLayout, 0, 0); + mFoldedIconName = a.getString(R.styleable.FoldableLinearLayout_foldedIcon); + mUnFoldedIconName = a.getString(R.styleable.FoldableLinearLayout_unFoldedIcon); + mFoldedLabel = a.getString(R.styleable.FoldableLinearLayout_foldedLabel); + mUnFoldedLabel = a.getString(R.styleable.FoldableLinearLayout_unFoldedLabel); + a.recycle(); + } + // If any attribute isn't found then set a default one + mFoldedIconName = (mFoldedIconName == null) ? "fa-chevron-right" : mFoldedIconName; + mUnFoldedIconName = (mUnFoldedIconName == null) ? "fa-chevron-down" : mUnFoldedIconName; + mFoldedLabel = (mFoldedLabel == null) ? context.getString(R.id.none) : mFoldedLabel; + mUnFoldedLabel = (mUnFoldedLabel == null) ? context.getString(R.id.none) : mUnFoldedLabel; + } + + @Override + protected void onFinishInflate() { + // if the migration has already happened + // there is no need to move any children + if(!mHasMigrated) { + migrateChildrenToContainer(); + mHasMigrated = true; + } + + initialiseInnerViews(); + + super.onFinishInflate(); + } + + /** + * Migrates Child views as declared in xml to the inner foldableContainer + */ + private void migrateChildrenToContainer() { + // Collect children of FoldableLinearLayout as declared in XML + int childNum = getChildCount(); + View[] children = new View[childNum]; + + for(int i = 0; i < childNum; i++) { + children[i] = getChildAt(i); + } + if(children[0].getId() == R.id.foldableControl) { + + } + + // remove all of them from FoldableLinearLayout + detachAllViewsFromParent(); + + // Inflate the inner foldable_linearlayout.xml + LayoutInflater inflator = (LayoutInflater)getContext().getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + + mFoldableLayout = inflator.inflate(R.layout.foldable_linearlayout, this, true); + mFoldableContainer = (LinearLayout) mFoldableLayout.findViewById(R.id.foldableContainer); + + // Push previously collected children into foldableContainer. + for(int i = 0; i < childNum; i++) { + addView(children[i]); + } + } + + private void initialiseInnerViews() { + mFoldableIcon = (FontAwesomeText) mFoldableLayout.findViewById(R.id.foldableIcon); + mFoldableIcon.setIcon(mFoldedIconName); + mFoldableTextView = (TextView) mFoldableLayout.findViewById(R.id.foldableText); + mFoldableTextView.setText(mFoldedLabel); + + // retrieve and cache the system's short animation time + mShortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime); + + LinearLayout foldableControl = (LinearLayout) mFoldableLayout.findViewById(R.id.foldableControl); + foldableControl.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + mFolded = !mFolded; + if (mFolded) { + mFoldableIcon.setIcon(mUnFoldedIconName); + mFoldableContainer.setVisibility(View.VISIBLE); + AlphaAnimation animation = new AlphaAnimation(0f, 1f); + animation.setDuration(mShortAnimationDuration); + mFoldableContainer.startAnimation(animation); + mFoldableTextView.setText(mUnFoldedLabel); + + } else { + mFoldableIcon.setIcon(mFoldedIconName); + AlphaAnimation animation = new AlphaAnimation(1f, 0f); + animation.setDuration(mShortAnimationDuration); + animation.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { } + + @Override + public void onAnimationEnd(Animation animation) { + // making sure that at the end the container is completely removed from view + mFoldableContainer.setVisibility(View.GONE); + } + + @Override + public void onAnimationRepeat(Animation animation) { } + }); + mFoldableContainer.startAnimation(animation); + mFoldableTextView.setText(mFoldedLabel); + } + } + }); + + } + + /** + * Adds provided child view to foldableContainer View + * @param child + */ + @Override + public void addView(View child) { + if(mFoldableContainer != null) { + mFoldableContainer.addView(child); + } + } +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/IntegerListPreference.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/IntegerListPreference.java index bc60b2adf..6e1e4c678 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/IntegerListPreference.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/IntegerListPreference.java @@ -25,7 +25,7 @@ import android.util.AttributeSet; * values should use {@link android.content.SharedPreferences#getInt}. When using XML-declared * arrays for entry values, the arrays should be regular string arrays containing valid integer * values. - * + * * @author Rodrigo Damazio */ public class IntegerListPreference extends ListPreference { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java index b1f97babf..0dfc6dc5e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java @@ -30,7 +30,7 @@ import org.spongycastle.openpgp.PGPSecretKey; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.util.Choice; - +import android.annotation.TargetApi; import android.app.DatePickerDialog; import android.app.Dialog; import android.content.Context; @@ -47,8 +47,16 @@ import android.widget.LinearLayout; import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; - import com.beardedhen.androidbootstrap.BootstrapButton; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPSecretKey; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.util.Choice; + +import java.text.DateFormat; +import java.util.*; public class KeyEditor extends LinearLayout implements Editor, OnClickListener { private PGPSecretKey mKey; @@ -85,7 +93,8 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { private int mDatePickerResultCount = 0; - private DatePickerDialog.OnDateSetListener mExpiryDateSetListener = new DatePickerDialog.OnDateSetListener() { + private DatePickerDialog.OnDateSetListener mExpiryDateSetListener = + new DatePickerDialog.OnDateSetListener() { public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { // Note: Ignore results after the first one - android sends multiples. if (mDatePickerResultCount++ == 0) { @@ -139,6 +148,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { setExpiryDate(null); mExpiryDateButton.setOnClickListener(new OnClickListener() { + @TargetApi(11) public void onClick(View v) { GregorianCalendar date = mExpiryDate; if (date == null) { @@ -169,16 +179,18 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { }); // setCalendarViewShown() is supported from API 11 onwards. - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) { // Hide calendarView in tablets because of the unix warparound bug. dialog.getDatePicker().setCalendarViewShown(false); - + } if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) { - if ( dialog != null && mCreatedDate != null ) { - dialog.getDatePicker().setMinDate(mCreatedDate.getTime().getTime()+ DateUtils.DAY_IN_MILLIS); + if (dialog != null && mCreatedDate != null) { + dialog.getDatePicker() + .setMinDate( + mCreatedDate.getTime().getTime() + DateUtils.DAY_IN_MILLIS); } else { //When created date isn't available - dialog.getDatePicker().setMinDate(date.getTime().getTime()+ DateUtils.DAY_IN_MILLIS); + dialog.getDatePicker().setMinDate(date.getTime().getTime() + DateUtils.DAY_IN_MILLIS); } } @@ -208,9 +220,8 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { } mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(key)); - String keyId1Str = PgpKeyHelper.convertKeyIdToHex(key.getKeyID()); - String keyId2Str = PgpKeyHelper.convertKeyIdToHex(key.getKeyID() >> 32); - mKeyId.setText(keyId1Str + " " + keyId2Str); + String keyIdStr = PgpKeyHelper.convertKeyIdToHex(key.getKeyID()); + mKeyId.setText(keyIdStr); Vector<Choice> choices = new Vector<Choice>(); boolean isElGamalKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT); @@ -353,9 +364,11 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { class ExpiryDatePickerDialog extends DatePickerDialog { - public ExpiryDatePickerDialog(Context context, OnDateSetListener callBack, int year, int monthOfYear, int dayOfMonth) { + public ExpiryDatePickerDialog(Context context, OnDateSetListener callBack, + int year, int monthOfYear, int dayOfMonth) { super(context, callBack, year, monthOfYear, dayOfMonth); } + //Set permanent title. public void setTitle(CharSequence title) { super.setTitle(getContext().getString(R.string.expiry_date_dialog_title)); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java index 47238cd56..171763672 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java @@ -16,8 +16,6 @@ package org.sufficientlysecure.keychain.ui.widget; -import org.sufficientlysecure.keychain.R; - import android.content.Context; import android.util.AttributeSet; import android.view.View; @@ -25,8 +23,8 @@ import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; - import com.beardedhen.androidbootstrap.BootstrapButton; +import org.sufficientlysecure.keychain.R; public class KeyServerEditor extends LinearLayout implements Editor, OnClickListener { private EditorListener mEditorListener = null; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java index 9758835d2..6c7737e6d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -48,9 +48,7 @@ import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; - import com.beardedhen.androidbootstrap.BootstrapButton; - import org.spongycastle.openpgp.PGPSecretKey; import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; @@ -75,10 +73,10 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor private Choice mNewKeyAlgorithmChoice; private int mNewKeySize; - private boolean canEdit = true; private boolean oldItemDeleted = false; private ArrayList<String> mDeletedIDs = new ArrayList<String>(); private ArrayList<PGPSecretKey> mDeletedKeys = new ArrayList<PGPSecretKey>(); + private boolean mCanEdit = true; private ActionBarActivity mActivity; @@ -105,30 +103,32 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor public void setType(int type) { mType = type; switch (type) { - case Id.type.user_id: { - mTitle.setText(R.string.section_user_ids); - break; - } + case Id.type.user_id: { + mTitle.setText(R.string.section_user_ids); + break; + } - case Id.type.key: { - mTitle.setText(R.string.section_keys); - break; - } + case Id.type.key: { + mTitle.setText(R.string.section_keys); + break; + } - default: { - break; - } + default: { + break; + } } } public void setCanEdit(boolean bCanEdit) { - canEdit = bCanEdit; - if (!canEdit) { + mCanEdit = bCanEdit; + if (!mCanEdit) { mPlusButton.setVisibility(View.INVISIBLE); } } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override protected void onFinishInflate() { mInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); @@ -146,7 +146,9 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor super.onFinishInflate(); } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ public void onDeleted(Editor editor, boolean wasNewItem) { oldItemDeleted |= !wasNewItem; if (oldItemDeleted) { @@ -262,39 +264,44 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor return mList; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ public void onClick(View v) { - if (canEdit) { + if (mCanEdit) { switch (mType) { - case Id.type.user_id: { - UserIdEditor view = (UserIdEditor) mInflater.inflate( - R.layout.edit_key_user_id_item, mEditors, false); - view.setEditorListener(this); - view.setValue("", mEditors.getChildCount() == 0, true); - mEditors.addView(view); - if (mEditorListener != null) { - mEditorListener.onEdited(); + case Id.type.user_id: { + UserIdEditor view = (UserIdEditor) mInflater.inflate( + R.layout.edit_key_user_id_item, mEditors, false); + view.setEditorListener(this); + view.setValue("", mEditors.getChildCount() == 0, true); + mEditors.addView(view); + if (mEditorListener != null) { + mEditorListener.onEdited(); + } + break; } - break; - } - case Id.type.key: { - CreateKeyDialogFragment mCreateKeyDialogFragment = CreateKeyDialogFragment.newInstance(mEditors.getChildCount()); - mCreateKeyDialogFragment.setOnAlgorithmSelectedListener(new CreateKeyDialogFragment.OnAlgorithmSelectedListener() { - @Override - public void onAlgorithmSelected(Choice algorithmChoice, int keySize) { - mNewKeyAlgorithmChoice = algorithmChoice; - mNewKeySize = keySize; - createKey(); - } - }); - mCreateKeyDialogFragment.show(mActivity.getSupportFragmentManager(), "createKeyDialog"); - break; - } + case Id.type.key: { + CreateKeyDialogFragment mCreateKeyDialogFragment = + CreateKeyDialogFragment.newInstance(mEditors.getChildCount()); + mCreateKeyDialogFragment + .setOnAlgorithmSelectedListener( + new CreateKeyDialogFragment.OnAlgorithmSelectedListener() { + @Override + public void onAlgorithmSelected(Choice algorithmChoice, int keySize) { + mNewKeyAlgorithmChoice = algorithmChoice; + mNewKeySize = keySize; + createKey(); + } + }); + mCreateKeyDialogFragment.show(mActivity.getSupportFragmentManager(), "createKeyDialog"); + break; + } - default: { - break; - } + default: { + break; + } } this.updateEditorsVisible(); } @@ -311,7 +318,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor mEditors, false); view.setEditorListener(this); view.setValue(userId, mEditors.getChildCount() == 0, false); - view.setCanEdit(canEdit); + view.setCanEdit(mCanEdit); mEditors.addView(view); } @@ -332,7 +339,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor view.setEditorListener(this); boolean isMasterKey = (mEditors.getChildCount() == 0); view.setValue(list.get(i), isMasterKey, usages.get(i), newKeys); - view.setCanEdit(canEdit); + view.setCanEdit(mCanEdit); mEditors.addView(view); } @@ -367,19 +374,22 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor intent.putExtra(KeychainIntentService.EXTRA_DATA, data); // show progress dialog - mGeneratingDialog = ProgressDialogFragment.newInstance(R.string.progress_generating, - ProgressDialog.STYLE_SPINNER, true, new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - mActivity.stopService(intent); - } - }); + mGeneratingDialog = ProgressDialogFragment.newInstance( + getResources().getQuantityString(R.plurals.progress_generating, 1), + ProgressDialog.STYLE_SPINNER, + true, + new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + mActivity.stopService(intent); + } + }); - // Message is received after generating is done in ApgService + // Message is received after generating is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(mActivity, mGeneratingDialog) { public void handleMessage(Message message) { - // handle messages by standard ApgHandler first + // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { @@ -390,7 +400,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor .getByteArray(KeychainIntentService.RESULT_NEW_KEY)); addGeneratedKeyToView(newKey); } - }; + } }; // Create a new Messenger for the communication back diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UnderlineTextView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UnderlineTextView.java index 752d44f89..937a48e48 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UnderlineTextView.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UnderlineTextView.java @@ -26,7 +26,7 @@ import android.widget.TextView; /** * Copied from StickyListHeaders lib example - * + * * @author Eric Frohnhoefer */ public class UnderlineTextView extends TextView { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java index 8760b72f7..d4b15613a 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java @@ -21,19 +21,18 @@ import java.util.regex.Pattern; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; - import android.content.Context; import android.text.Editable; import android.text.TextWatcher; import android.util.AttributeSet; +import android.util.Patterns; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; -import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.RadioButton; - +import android.widget.*; import com.beardedhen.androidbootstrap.BootstrapButton; +import org.sufficientlysecure.keychain.helper.ContactHelper; + public class UserIdEditor extends LinearLayout implements Editor, OnClickListener { private EditorListener mEditorListener = null; @@ -43,21 +42,13 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene private String mOriginalID; private EditText mName; private String mOriginalName; - private EditText mEmail; + private AutoCompleteTextView mEmail; private String mOriginalEmail; private EditText mComment; private String mOriginalComment; private boolean mOriginallyMainUserID; private boolean mIsNewId; - // see http://www.regular-expressions.info/email.html - // RFC 2822 if we omit the syntax using double quotes and square brackets - // android.util.Patterns.EMAIL_ADDRESS is only available as of Android 2.2+ - private static final Pattern EMAIL_PATTERN = Pattern - .compile( - "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", - Pattern.CASE_INSENSITIVE); - public void setCanEdit(boolean bCanEdit) { if (!bCanEdit) { mDeleteButton.setVisibility(View.INVISIBLE); @@ -114,11 +105,46 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene mName = (EditText) findViewById(R.id.name); mName.addTextChangedListener(mTextWatcher); - mEmail = (EditText) findViewById(R.id.email); - mEmail.addTextChangedListener(mTextWatcher); + mEmail = (AutoCompleteTextView) findViewById(R.id.email); mComment = (EditText) findViewById(R.id.comment); mComment.addTextChangedListener(mTextWatcher); + + mEmail.setThreshold(1); // Start working from first character + mEmail.setAdapter( + new ArrayAdapter<String> + (this.getContext(), android.R.layout.simple_dropdown_item_1line, + ContactHelper.getMailAccounts(getContext()) + )); + mEmail.addTextChangedListener(new TextWatcher(){ + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { } + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { } + + @Override + public void afterTextChanged(Editable editable) { + String email = editable.toString(); + if (email.length() > 0) { + Matcher emailMatcher = Patterns.EMAIL_ADDRESS.matcher(email); + if (emailMatcher.matches()) { + mEmail.setCompoundDrawablesWithIntrinsicBounds(0, 0, + android.R.drawable.presence_online, 0); + } else { + mEmail.setCompoundDrawablesWithIntrinsicBounds(0, 0, + android.R.drawable.presence_offline, 0); + } + } else { + // remove drawable if email is empty + mEmail.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); + } + if (mEditorListener != null) { + mEditorListener.onEdited(); + } + } + }); + super.onFinishInflate(); } @@ -151,19 +177,11 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene setIsMainUserId(isMainID); } - public String getValue() throws InvalidEmailException { + public String getValue() { String name = ("" + mName.getText()).trim(); String email = ("" + mEmail.getText()).trim(); String comment = ("" + mComment.getText()).trim(); - if (email.length() > 0) { - Matcher emailMatcher = EMAIL_PATTERN.matcher(email); - if (!emailMatcher.matches()) { - throw new InvalidEmailException(getContext().getString(R.string.error_invalid_email, - email)); - } - } - String userId = name; if (comment.length() > 0) { userId += " (" + comment + ")"; |