diff options
| author | Vincent Breitmoser <valodim@mugenguild.com> | 2014-03-05 15:54:27 +0100 | 
|---|---|---|
| committer | Vincent Breitmoser <valodim@mugenguild.com> | 2014-03-06 01:32:18 +0100 | 
| commit | 4851f7f8fc5ca82d60a55e95730bf6c11675979c (patch) | |
| tree | 302da8a09f41ad9be444a46bb54ae8a1b0c7e747 /OpenPGP-Keychain/src | |
| parent | 1613a015d6497c4e70d3253b9456f791696a9cbf (diff) | |
| download | open-keychain-4851f7f8fc5ca82d60a55e95730bf6c11675979c.tar.gz open-keychain-4851f7f8fc5ca82d60a55e95730bf6c11675979c.tar.bz2 open-keychain-4851f7f8fc5ca82d60a55e95730bf6c11675979c.zip | |
copy public keylist as stub for unified keylist
Diffstat (limited to 'OpenPGP-Keychain/src')
8 files changed, 776 insertions, 5 deletions
| diff --git a/OpenPGP-Keychain/src/main/AndroidManifest.xml b/OpenPGP-Keychain/src/main/AndroidManifest.xml index 749229115..700e226e6 100644 --- a/OpenPGP-Keychain/src/main/AndroidManifest.xml +++ b/OpenPGP-Keychain/src/main/AndroidManifest.xml @@ -63,11 +63,6 @@              android:configChanges="orientation|screenSize|keyboardHidden|keyboard"              android:label="@string/app_name"              android:launchMode="singleTop"> -            <intent-filter> -                <action android:name="android.intent.action.MAIN" /> - -                <category android:name="android.intent.category.LAUNCHER" /> -            </intent-filter>              <!-- <intent-filter> -->              <!-- <action android:name="android.intent.action.SEARCH" /> --> @@ -79,6 +74,17 @@              <!-- android:resource="@xml/searchable_public_keys" /> -->          </activity>          <activity +            android:name=".ui.KeyListActivity" +            android:configChanges="orientation|screenSize|keyboardHidden|keyboard" +            android:label="@string/app_name" +            android:launchMode="singleTop"> +            <intent-filter> +                <action android:name="android.intent.action.MAIN" /> + +                <category android:name="android.intent.category.LAUNCHER" /> +            </intent-filter> +        </activity> +        <activity              android:name=".ui.KeyListSecretActivity"              android:configChanges="orientation|screenSize|keyboardHidden|keyboard"              android:label="@string/title_manage_secret_keys" diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java new file mode 100644 index 000000000..dd7aa9a26 --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java @@ -0,0 +1,71 @@ +/* + * 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 KeyListActivity 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/KeyListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java new file mode 100644 index 000000000..6f94ffd1a --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -0,0 +1,338 @@ +/* + * 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.KeyListAdapter; +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.view.ActionMode; +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.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 KeyListFragment extends Fragment implements AdapterView.OnItemClickListener, +        LoaderManager.LoaderCallbacks<Cursor> { + +    private KeyListAdapter mAdapter; +    private StickyListHeadersListView mStickyList; + +    // 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_fragment, container, false); + +        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() { + +                private int count = 0; + +                @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) { +                    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_multi_encrypt: { +                            encrypt(mode, ids); +                            break; +                        } +                        case R.id.menu_key_list_multi_delete: { +                            showDeleteKeyDialog(mode, ids); +                            break; +                        } +                    } +                    return true; +                } + +                @Override +                public void onDestroyActionMode(ActionMode mode) { +                    count = 0; +                    mAdapter.clearSelection(); +                } + +                @Override +                public void onItemCheckedStateChanged(ActionMode mode, int position, long id, +                                                      boolean checked) { +                    if (checked) { +                        count++; +                        mAdapter.setNewSelection(position, checked); +                    } else { +                        count--; +                        mAdapter.removeSelection(position); +                    } + +                    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 KeyListAdapter(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(); + +        // 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); +    } + +    @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"); +    } + +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java new file mode 100644 index 000000000..2e0d1496b --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java @@ -0,0 +1,232 @@ +/* + * 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 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; + +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 java.util.HashMap; +import java.util.Set; + +import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; + +/** + * Implements StickyListHeadersAdapter from library + */ +public class KeyListAdapter 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 KeyListAdapter(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_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_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) { +            // this is a selected position, change color! +            v.setBackgroundColor(parent.getResources().getColor(R.color.emphasis)); +        } +        return v; +    } + +} diff --git a/OpenPGP-Keychain/src/main/res/layout/key_list_activity.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_activity.xml new file mode 100644 index 000000000..65d246d7b --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout/key_list_activity.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:id="@+id/drawer_layout" +    android:layout_width="match_parent" +    android:layout_height="match_parent" > + +    <FrameLayout +        android:layout_width="match_parent" +        android:layout_height="match_parent" > + +        <fragment +            android:id="@+id/key_list_fragment" +            android:name="org.sufficientlysecure.keychain.ui.KeyListFragment" +            android:layout_width="match_parent" +            android:layout_height="match_parent" /> +    </FrameLayout> + +    <include layout="@layout/drawer_list" /> + +</android.support.v4.widget.DrawerLayout>
\ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/layout/key_list_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_fragment.xml new file mode 100644 index 000000000..f3a21e1bf --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout/key_list_fragment.xml @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="utf-8"?> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" +    xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto" +    android:layout_width="match_parent" +    android:layout_height="match_parent" +    android:orientation="vertical" > + +    <se.emilsjolander.stickylistheaders.StickyListHeadersListView +        android:id="@+id/list" +        android:layout_width="match_parent" +        android:layout_height="match_parent" +        android:clipToPadding="false" +        android:drawSelectorOnTop="true" +        android:fastScrollEnabled="true" +        android:paddingBottom="16dp" +        android:paddingLeft="16dp" +        android:paddingRight="32dp" +        android:scrollbarStyle="outsideOverlay" /> + +    <LinearLayout +        android:id="@+id/empty" +        android:layout_width="match_parent" +        android:layout_height="match_parent" +        android:gravity="center" +        android:orientation="vertical" +        android:visibility="gone" > + +        <TextView +            android:layout_width="wrap_content" +            android:layout_height="wrap_content" +            android:gravity="center" +            android:text="@string/key_list_empty_text1" +            android:textAppearance="?android:attr/textAppearanceLarge" /> + +        <TextView +            android:layout_width="wrap_content" +            android:layout_height="wrap_content" +            android:gravity="center" +            android:text="" +            android:textAppearance="?android:attr/textAppearanceLarge" /> + +        <TextView +            android:layout_width="wrap_content" +            android:layout_height="wrap_content" +            android:layout_margin="4dp" +            android:gravity="center" +            android:text="@string/key_list_empty_text2" +            android:textAppearance="?android:attr/textAppearanceSmall" /> + +        <com.beardedhen.androidbootstrap.BootstrapButton +            android:id="@+id/key_list_empty_button_create" +            android:layout_width="wrap_content" +            android:layout_height="wrap_content" +            android:layout_margin="4dp" +            android:text="@string/key_list_empty_button_create" +            bootstrapbutton:bb_icon_left="fa-plus" +            bootstrapbutton:bb_type="default" /> + +        <TextView +            android:layout_width="wrap_content" +            android:layout_height="wrap_content" +            android:layout_margin="4dp" +            android:gravity="center" +            android:text="@string/key_list_empty_text3" +            android:textAppearance="?android:attr/textAppearanceSmall" /> + +        <com.beardedhen.androidbootstrap.BootstrapButton +            android:id="@+id/key_list_empty_button_import" +            android:layout_width="wrap_content" +            android:layout_height="wrap_content" +            android:layout_margin="4dp" +            android:text="@string/key_list_empty_button_import" +            bootstrapbutton:bb_icon_left="fa-download" +            bootstrapbutton:bb_type="default" /> +    </LinearLayout> + +</FrameLayout>
\ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/menu/key_list.xml b/OpenPGP-Keychain/src/main/res/menu/key_list.xml new file mode 100644 index 000000000..23ab8c9f3 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/menu/key_list.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android" +    xmlns:app="http://schemas.android.com/apk/res-auto"> + +    <item +        android:id="@+id/menu_key_list_import" +        app:showAsAction="always|withText" +        android:icon="@drawable/ic_action_add_person" +        android:title="@string/menu_import" /> +    <item +        android:id="@+id/menu_key_list_export" +        app:showAsAction="never" +        android:title="@string/menu_export_keys" /> + +</menu>
\ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/menu/key_list_multi.xml b/OpenPGP-Keychain/src/main/res/menu/key_list_multi.xml new file mode 100644 index 000000000..bbed11f07 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/menu/key_list_multi.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + +    <item +        android:id="@+id/menu_key_list_multi_encrypt" +        android:title="@string/menu_encrypt_to" /> +    <item +        android:id="@+id/menu_key_list_multi_delete" +        android:icon="@drawable/ic_action_discard" +        android:title="@string/menu_delete_key" /> + +</menu>
\ No newline at end of file | 
