diff options
Diffstat (limited to 'OpenKeychain/src/main/java')
6 files changed, 181 insertions, 186 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index ecf68890e..7be61d9c8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -219,7 +219,7 @@ public class UncachedKeyRing { Iterator<PGPPublicKey> it = mRing.getPublicKeys(); while (it.hasNext()) { if (KeyFormattingUtils.convertFingerprintToHex( - it.next().getFingerprint()).equals(expectedFingerprint)) { + it.next().getFingerprint()).equalsIgnoreCase(expectedFingerprint)) { return true; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index a91eca453..3346926ec 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -430,6 +430,9 @@ public class KeychainDatabase extends SQLiteOpenHelper { // DANGEROUS, use in test code ONLY! public void clearDatabase() { getWritableDatabase().execSQL("delete from " + Tables.KEY_RINGS_PUBLIC); + getWritableDatabase().execSQL("delete from " + Tables.API_ACCOUNTS); + getWritableDatabase().execSQL("delete from " + Tables.API_ALLOWED_KEYS); + getWritableDatabase().execSQL("delete from " + Tables.API_APPS); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java index b880525ca..caa173f03 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java @@ -17,10 +17,11 @@ package org.sufficientlysecure.keychain.remote.ui; -import android.content.Context; + +import java.util.Set; + import android.content.OperationApplicationException; import android.database.Cursor; -import android.database.DatabaseUtils; import android.net.Uri; import android.os.Bundle; import android.os.RemoteException; @@ -35,23 +36,17 @@ import android.widget.ListView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.ListFragmentWorkaround; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; +import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.ui.adapter.SelectKeyCursorAdapter; +import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter; +import org.sufficientlysecure.keychain.ui.adapter.KeySelectableAdapter; import org.sufficientlysecure.keychain.ui.widget.FixedListView; import org.sufficientlysecure.keychain.util.Log; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; -import java.util.Vector; - public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround implements LoaderManager.LoaderCallbacks<Cursor> { private static final String ARG_DATA_URI = "uri"; - private SelectKeyCursorAdapter mAdapter; - private Set<Long> mSelectedMasterKeyIds; + private KeySelectableAdapter mAdapter; private ProviderHelper mProviderHelper; private Uri mDataUri; @@ -80,8 +75,7 @@ public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround i @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View layout = super.onCreateView(inflater, container, - savedInstanceState); + View layout = super.onCreateView(inflater, container, savedInstanceState); ListView lv = (ListView) layout.findViewById(android.R.id.list); ViewGroup parent = (ViewGroup) lv.getParent(); @@ -109,67 +103,29 @@ public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround i mDataUri = getArguments().getParcelable(ARG_DATA_URI); - getListView().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 SecretKeyCursorAdapter(getActivity(), null, 0, getListView()); - + Set<Long> checked = mProviderHelper.getAllKeyIdsForApp(mDataUri); + mAdapter = new KeySelectableAdapter(getActivity(), null, 0, checked); setListAdapter(mAdapter); + getListView().setOnItemClickListener(mAdapter); // Start out with a progress indicator. setListShown(false); - mSelectedMasterKeyIds = mProviderHelper.getAllKeyIdsForApp(mDataUri); - Log.d(Constants.TAG, "allowed: " + mSelectedMasterKeyIds.toString()); - // Prepare the loader. Either re-connect with an existing one, // or start a new one. getLoaderManager().initLoader(0, null, this); - } - /** - * Selects items based on master key ids in list view - * - * @param masterKeyIds - */ - private void preselectMasterKeyIds(Set<Long> masterKeyIds) { - for (int i = 0; i < getListView().getCount(); ++i) { - long listKeyId = mAdapter.getMasterKeyId(i); - for (long keyId : masterKeyIds) { - if (listKeyId == keyId) { - getListView().setItemChecked(i, true); - break; - } - } - } } - - /** - * Returns all selected master key ids - * - * @return - */ + /** Returns all selected master key ids. */ public Set<Long> getSelectedMasterKeyIds() { - // mListView.getCheckedItemIds() would give the row ids of the KeyRings not the master key - // ids! - Set<Long> keyIds = new HashSet<>(); - for (int i = 0; i < getListView().getCount(); ++i) { - if (getListView().isItemChecked(i)) { - keyIds.add(mAdapter.getMasterKeyId(i)); - } - } - - return keyIds; + return mAdapter.getSelectedMasterKeyIds(); } - /** - * Returns all selected user ids - * - * @return - */ + /** Returns all selected user ids. public String[] getSelectedUserIds() { Vector<String> userIds = new Vector<>(); for (int i = 0; i < getListView().getCount(); ++i) { @@ -181,7 +137,7 @@ public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround i // make empty array to not return null String userIdArray[] = new String[0]; return userIds.toArray(userIdArray); - } + } */ public void saveAllowedKeys() { try { @@ -192,46 +148,11 @@ public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround i } @Override - public Loader<Cursor> onCreateLoader(int id, Bundle args) { - Uri baseUri = KeyRings.buildUnifiedKeyRingsUri(); - - // These are the rows that we will retrieve. - String[] projection = new String[]{ - KeyRings._ID, - KeyRings.MASTER_KEY_ID, - KeyRings.USER_ID, - KeyRings.IS_EXPIRED, - KeyRings.IS_REVOKED, - KeyRings.HAS_ENCRYPT, - KeyRings.VERIFIED, - KeyRings.HAS_ANY_SECRET, - KeyRings.HAS_DUPLICATE_USER_ID, - KeyRings.CREATION, - }; - - String inMasterKeyList = null; - if (mSelectedMasterKeyIds != null && mSelectedMasterKeyIds.size() > 0) { - inMasterKeyList = Tables.KEYS + "." + KeyRings.MASTER_KEY_ID + " IN ("; - Iterator iter = mSelectedMasterKeyIds.iterator(); - while (iter.hasNext()) { - inMasterKeyList += DatabaseUtils.sqlEscapeString("" + iter.next()); - if (iter.hasNext()) { - inMasterKeyList += ", "; - } - } - inMasterKeyList += ")"; - } - - String selection = KeyRings.HAS_ANY_SECRET + " != 0"; + public Loader<Cursor> onCreateLoader(int loaderId, Bundle data) { + Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingsUri(); + String where = KeychainContract.KeyRings.HAS_ANY_SECRET + " = 1"; - String orderBy = KeyRings.USER_ID + " ASC"; - if (inMasterKeyList != null) { - // sort by selected master keys - orderBy = inMasterKeyList + " DESC, " + orderBy; - } - // 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, selection, null, orderBy); + return new CursorLoader(getActivity(), baseUri, KeyAdapter.PROJECTION, where, null, null); } @Override @@ -246,9 +167,6 @@ public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround i } else { setListShownNoAnimation(true); } - - // preselect given master keys - preselectMasterKeyIds(mSelectedMasterKeyIds); } @Override @@ -259,36 +177,4 @@ public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround i mAdapter.swapCursor(null); } - private class SecretKeyCursorAdapter extends SelectKeyCursorAdapter { - - public SecretKeyCursorAdapter(Context context, Cursor c, int flags, ListView listView) { - super(context, c, flags, listView); - } - - @Override - protected void initIndex(Cursor cursor) { - super.initIndex(cursor); - } - - @Override - public void bindView(View view, Context context, Cursor cursor) { - super.bindView(view, context, cursor); - ViewHolderItem h = (ViewHolderItem) view.getTag(); - - // We care about the checkbox - h.selected.setVisibility(View.VISIBLE); - // the getListView works because this is not a static subclass! - h.selected.setChecked(getListView().isItemChecked(cursor.getPosition())); - - boolean enabled = false; - if ((Boolean) h.statusIcon.getTag()) { - h.statusIcon.setVisibility(View.GONE); - enabled = true; - } - - h.setEnabled(enabled); - } - - } - } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java index dbbfe3133..a0b470add 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java @@ -17,6 +17,9 @@ package org.sufficientlysecure.keychain.service; + +import java.util.Date; + import android.app.AlarmManager; import android.app.Notification; import android.app.PendingIntent; @@ -25,8 +28,13 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; import android.os.Binder; import android.os.Build; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; @@ -46,8 +54,6 @@ import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Preferences; -import java.util.Date; - /** * This service runs in its own process, but is available to all other processes as the main * passphrase cache. Use the static methods addCachedPassphrase and getCachedPassphrase for @@ -317,7 +323,7 @@ public class PassphraseCacheService extends Service { if (action.equals(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE)) { long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1); - timeout(context, keyId); + timeout(keyId); } } }; @@ -452,7 +458,7 @@ public class PassphraseCacheService extends Service { /** * Called when one specific passphrase for keyId timed out */ - private void timeout(Context context, long keyId) { + private void timeout(long keyId) { CachedPassphrase cPass = mPassphraseCache.get(keyId); // clean internal char[] from memory! cPass.getPassphrase().removeFromMemory(); @@ -474,59 +480,67 @@ public class PassphraseCacheService extends Service { } } + // from de.azapps.mirakel.helper.Helpers from https://github.com/MirakelX/mirakel-android + private static Bitmap getBitmap(int resId, Context context) { + int mLargeIconWidth = (int) context.getResources().getDimension( + android.R.dimen.notification_large_icon_width); + int mLargeIconHeight = (int) context.getResources().getDimension( + android.R.dimen.notification_large_icon_height); + Drawable d; + if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) { + // noinspection deprecation (can't help it at this api level) + d = context.getResources().getDrawable(resId); + } else { + d = context.getDrawable(resId); + } + if (d == null) { + return null; + } + Bitmap b = Bitmap.createBitmap(mLargeIconWidth, mLargeIconHeight, Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(b); + d.setBounds(0, 0, mLargeIconWidth, mLargeIconHeight); + d.draw(c); + return b; + } + private Notification getNotification() { NotificationCompat.Builder builder = new NotificationCompat.Builder(this); + builder.setSmallIcon(R.drawable.ic_stat_notify_24dp) + .setLargeIcon(getBitmap(R.drawable.ic_launcher, getBaseContext())) + .setContentTitle(getResources().getQuantityString(R.plurals.passp_cache_notif_n_keys, + mPassphraseCache.size(), mPassphraseCache.size())) + .setContentText(getString(R.string.passp_cache_notif_click_to_clear)); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - builder.setSmallIcon(R.drawable.ic_launcher) - .setContentTitle(getString(R.string.app_name)) - .setContentText(String.format(getString(R.string.passp_cache_notif_n_keys), - mPassphraseCache.size())); + NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle(); - NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle(); + inboxStyle.setBigContentTitle(getString(R.string.passp_cache_notif_keys)); - inboxStyle.setBigContentTitle(getString(R.string.passp_cache_notif_keys)); + // Moves events into the big view + for (int i = 0; i < mPassphraseCache.size(); i++) { + inboxStyle.addLine(mPassphraseCache.valueAt(i).getPrimaryUserID()); + } - // Moves events into the big view - for (int i = 0; i < mPassphraseCache.size(); i++) { - inboxStyle.addLine(mPassphraseCache.valueAt(i).getPrimaryUserID()); - } + // Moves the big view style object into the notification object. + builder.setStyle(inboxStyle); - // Moves the big view style object into the notification object. - builder.setStyle(inboxStyle); - - // Add purging action - Intent intent = new Intent(getApplicationContext(), PassphraseCacheService.class); - intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR); - builder.addAction( - R.drawable.abc_ic_clear_mtrl_alpha, - getString(R.string.passp_cache_notif_clear), - PendingIntent.getService( - getApplicationContext(), - 0, - intent, - PendingIntent.FLAG_UPDATE_CURRENT - ) - ); - } else { - // Fallback, since expandable notifications weren't available back then - builder.setSmallIcon(R.drawable.ic_launcher) - .setContentTitle(String.format(getString(R.string.passp_cache_notif_n_keys), - mPassphraseCache.size())) - .setContentText(getString(R.string.passp_cache_notif_click_to_clear)); - - Intent intent = new Intent(getApplicationContext(), PassphraseCacheService.class); - intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR); - - builder.setContentIntent( - PendingIntent.getService( - getApplicationContext(), - 0, - intent, - PendingIntent.FLAG_UPDATE_CURRENT - ) - ); - } + Intent intent = new Intent(getApplicationContext(), PassphraseCacheService.class); + intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR); + PendingIntent clearCachePi = PendingIntent.getService( + getApplicationContext(), + 0, + intent, + PendingIntent.FLAG_UPDATE_CURRENT + ); + + // Add cache clear PI to normal touch + builder.setContentIntent(clearCachePi); + + // Add clear PI action below text + builder.addAction( + R.drawable.abc_ic_clear_mtrl_alpha, + getString(R.string.passp_cache_notif_clear), + clearCachePi + ); return builder.build(); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java index 1fc24775b..e545b007b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java @@ -95,6 +95,8 @@ public class KeyAdapter extends CursorAdapter { public View mSlinger; public ImageButton mSlingerButton; + public KeyItem mDisplayedItem; + public KeyItemViewHolder(View view) { mView = view; mMainUserId = (TextView) view.findViewById(R.id.key_list_item_name); @@ -107,6 +109,8 @@ public class KeyAdapter extends CursorAdapter { public void setData(Context context, KeyItem item, Highlighter highlighter) { + mDisplayedItem = item; + { // set name and stuff, common to both key types KeyRing.UserId userIdSplit = item.mUserId; if (userIdSplit.name != null) { @@ -143,6 +147,9 @@ public class KeyAdapter extends CursorAdapter { } else if (item.mIsSecret) { mStatus.setVisibility(View.GONE); if (mSlingerButton.hasOnClickListeners()) { + mSlingerButton.setColorFilter( + context.getResources().getColor(R.color.tertiary_text_light), + PorterDuff.Mode.SRC_IN); mSlinger.setVisibility(View.VISIBLE); } else { mSlinger.setVisibility(View.GONE); @@ -192,8 +199,6 @@ public class KeyAdapter extends CursorAdapter { View view = mInflater.inflate(R.layout.key_list_item, parent, false); KeyItemViewHolder holder = new KeyItemViewHolder(view); view.setTag(holder); - holder.mSlingerButton.setColorFilter(context.getResources().getColor(R.color.tertiary_text_light), - PorterDuff.Mode.SRC_IN); return view; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeySelectableAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeySelectableAdapter.java new file mode 100644 index 000000000..471a20411 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeySelectableAdapter.java @@ -0,0 +1,87 @@ +package org.sufficientlysecure.keychain.ui.adapter; + + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import android.content.Context; +import android.database.Cursor; +import android.support.v7.internal.widget.AdapterViewCompat; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.CheckBox; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.util.Log; + + +public class KeySelectableAdapter extends KeyAdapter implements OnItemClickListener { + + HashSet<Long> mSelectedItems = new HashSet<>(); + + public KeySelectableAdapter(Context context, Cursor c, int flags, Set<Long> initialChecked) { + super(context, c, flags); + if (initialChecked != null) { + mSelectedItems.addAll(initialChecked); + } + } + + public static class KeySelectableItemViewHolder extends KeyItemViewHolder { + + public CheckBox mCheckbox; + + public KeySelectableItemViewHolder(View view) { + super(view); + mCheckbox = (CheckBox) view.findViewById(R.id.selected); + } + + public void setCheckedState(boolean checked) { + mCheckbox.setChecked(checked); + } + + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + View view = mInflater.inflate(R.layout.key_list_selectable_item, parent, false); + KeySelectableItemViewHolder holder = new KeySelectableItemViewHolder(view); + view.setTag(holder); + return view; + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + super.bindView(view, context, cursor); + + KeySelectableItemViewHolder h = (KeySelectableItemViewHolder) view.getTag(); + h.setCheckedState(mSelectedItems.contains(h.mDisplayedItem.mKeyId)); + + } + + public void setCheckedStates(Set<Long> checked) { + mSelectedItems.clear(); + mSelectedItems.addAll(checked); + notifyDataSetChanged(); + } + + public Set<Long> getSelectedMasterKeyIds() { + return Collections.unmodifiableSet(mSelectedItems); + } + + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + Log.d(Constants.TAG, "clicked id: " + id); + long masterKeyId = getMasterKeyId(position); + if (mSelectedItems.contains(masterKeyId)) { + mSelectedItems.remove(masterKeyId); + } else { + mSelectedItems.add(masterKeyId); + } + notifyDataSetChanged(); + } + +} |