Improved SectionedCursorAdapter, added support for the dummy key item view.
This commit is contained in:
@@ -21,12 +21,10 @@ package org.sufficientlysecure.keychain.ui;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.database.MergeCursor;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@@ -35,7 +33,6 @@ 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.RecyclerView;
|
||||
import android.support.v7.widget.SearchView;
|
||||
import android.text.TextUtils;
|
||||
import android.view.ActionMode;
|
||||
@@ -46,11 +43,8 @@ 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.Button;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.ViewAnimator;
|
||||
|
||||
import com.getbase.floatingactionbutton.FloatingActionButton;
|
||||
@@ -73,26 +67,22 @@ import org.sufficientlysecure.keychain.service.ConsolidateInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.ui.util.ContentDescriptionHint;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.ui.util.adapter.KeySectionedListAdapter;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.KeySectionedListAdapter;
|
||||
import org.sufficientlysecure.keychain.ui.util.recyclerview.RecyclerFragment;
|
||||
import org.sufficientlysecure.keychain.util.FabContainer;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
|
||||
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
|
||||
|
||||
import java.io.IOException;
|
||||
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 LoaderFragment
|
||||
public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
||||
implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener,
|
||||
LoaderManager.LoaderCallbacks<Cursor>, FabContainer,
|
||||
CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
|
||||
@@ -101,10 +91,6 @@ public class KeyListFragment extends LoaderFragment
|
||||
private static final int REQUEST_DELETE = 2;
|
||||
private static final int REQUEST_VIEW_KEY = 3;
|
||||
|
||||
//private KeyListAdapter mAdapter;
|
||||
private KeySectionedListAdapter mAdapter;
|
||||
private RecyclerView mStickyList;
|
||||
|
||||
// saves the mode object for multiselect, needed for reset at some point
|
||||
private ActionMode mActionMode = null;
|
||||
|
||||
@@ -126,12 +112,8 @@ public class KeyListFragment extends LoaderFragment
|
||||
* Load custom layout with StickyListView from library
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
|
||||
View root = super.onCreateView(inflater, superContainer, savedInstanceState);
|
||||
View view = inflater.inflate(R.layout.key_list_fragment, getContainer());
|
||||
|
||||
mStickyList = (RecyclerView) view.findViewById(R.id.key_list_list);
|
||||
//mStickyList.setOnItemClickListener(this);
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.key_list_fragment, container, false);
|
||||
|
||||
mFab = (FloatingActionsMenu) view.findViewById(R.id.fab_main);
|
||||
|
||||
@@ -162,7 +144,7 @@ public class KeyListFragment extends LoaderFragment
|
||||
});
|
||||
|
||||
|
||||
return root;
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,7 +164,7 @@ public class KeyListFragment extends LoaderFragment
|
||||
//mStickyList.setDrawingListUnderStickyHeader(false);
|
||||
//mStickyList.setFastScrollEnabled(true);
|
||||
|
||||
// Adds an empty footer view so that the Floating Action Button won't block content
|
||||
/* Adds an empty footer view so that the Floating Action Button won't block content
|
||||
// in last few rows.
|
||||
View footer = new View(activity);
|
||||
|
||||
@@ -197,6 +179,7 @@ public class KeyListFragment extends LoaderFragment
|
||||
|
||||
footer.setLayoutParams(params);
|
||||
//mStickyList.addFooterView(footer, null, false);
|
||||
*/
|
||||
|
||||
/*
|
||||
* Multi-selection
|
||||
@@ -269,7 +252,7 @@ public class KeyListFragment extends LoaderFragment
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
// Start out with a progress indicator.
|
||||
setContentShown(false);
|
||||
hideList(true);
|
||||
|
||||
// this view is made visible if no data is available
|
||||
// mStickyList.setEmptyView(activity.findViewById(R.id.key_list_empty));
|
||||
@@ -286,10 +269,9 @@ public class KeyListFragment extends LoaderFragment
|
||||
|
||||
// Create an empty adapter we will use to display the loaded data.
|
||||
//mAdapter = new KeyListAdapter(activity, null, 0);
|
||||
mAdapter = new KeySectionedListAdapter(getContext(), null);
|
||||
|
||||
mStickyList.setAdapter(mAdapter);
|
||||
mStickyList.setLayoutManager(new LayoutManager(getActivity()));
|
||||
setAdapter(new KeySectionedListAdapter(getContext(), null));
|
||||
setLayoutManager(new LayoutManager(getActivity()));
|
||||
|
||||
// Prepare the loader. Either re-connect with an existing one,
|
||||
// or start a new one.
|
||||
@@ -324,22 +306,22 @@ public class KeyListFragment extends LoaderFragment
|
||||
|
||||
// Now create and return a CursorLoader that will take care of
|
||||
// creating a Cursor for the data being displayed.
|
||||
return new CursorLoader(getActivity(), uri, KeyListAdapter.PROJECTION, null, null, ORDER);
|
||||
return new CursorLoader(getActivity(), uri, KeyAdapter.PROJECTION, null, null, 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(mQuery);
|
||||
getAdapter().setSearchQuery(mQuery);
|
||||
|
||||
if (data != null && (mQuery == null || TextUtils.isEmpty(mQuery))) {
|
||||
boolean isSecret = data.moveToFirst() && data.getInt(KeyListAdapter.INDEX_HAS_ANY_SECRET) != 0;
|
||||
boolean isSecret = data.moveToFirst() && data.getInt(KeyAdapter.INDEX_HAS_ANY_SECRET) != 0;
|
||||
if (!isSecret) {
|
||||
MatrixCursor headerCursor = new MatrixCursor(KeyListAdapter.PROJECTION);
|
||||
Long[] row = new Long[KeyListAdapter.PROJECTION.length];
|
||||
row[KeyListAdapter.INDEX_HAS_ANY_SECRET] = 1L;
|
||||
row[KeyListAdapter.INDEX_MASTER_KEY_ID] = 0L;
|
||||
MatrixCursor headerCursor = new MatrixCursor(KeyAdapter.PROJECTION);
|
||||
Long[] row = new Long[KeyAdapter.PROJECTION.length];
|
||||
row[KeyAdapter.INDEX_HAS_ANY_SECRET] = 1L;
|
||||
row[KeyAdapter.INDEX_MASTER_KEY_ID] = 0L;
|
||||
headerCursor.addRow(row);
|
||||
|
||||
Cursor dataCursor = data;
|
||||
@@ -348,7 +330,8 @@ public class KeyListFragment extends LoaderFragment
|
||||
});
|
||||
}
|
||||
}
|
||||
mAdapter.swapCursor(data);
|
||||
|
||||
getAdapter().swapCursor(data);
|
||||
|
||||
// end action mode, if any
|
||||
if (mActionMode != null) {
|
||||
@@ -357,9 +340,9 @@ public class KeyListFragment extends LoaderFragment
|
||||
|
||||
// The list should now be shown.
|
||||
if (isResumed()) {
|
||||
setContentShown(true);
|
||||
showList(true);
|
||||
} else {
|
||||
setContentShownNoAnimation(true);
|
||||
showList(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,7 +351,7 @@ public class KeyListFragment extends LoaderFragment
|
||||
// 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);
|
||||
getAdapter().swapCursor(null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -378,7 +361,7 @@ public class KeyListFragment extends LoaderFragment
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
|
||||
Intent viewIntent = new Intent(getActivity(), ViewKeyActivity.class);
|
||||
viewIntent.setData(
|
||||
KeyRings.buildGenericKeyRingUri(mAdapter.getMasterKeyId(position)));
|
||||
KeyRings.buildGenericKeyRingUri(getAdapter().getMasterKeyId(position)));
|
||||
startActivityForResult(viewIntent, REQUEST_VIEW_KEY);
|
||||
}
|
||||
|
||||
@@ -762,6 +745,7 @@ public class KeyListFragment extends LoaderFragment
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
public class KeyListAdapter extends KeyAdapter implements StickyListHeadersAdapter {
|
||||
|
||||
private HashMap<Integer, Boolean> mSelection = new HashMap<>();
|
||||
@@ -842,19 +826,13 @@ public class KeyListFragment extends LoaderFragment
|
||||
TextView mCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
convertView = mInflater.inflate(R.layout.key_list_header_public, parent, false);
|
||||
holder.mText = (TextView) convertView.findViewById(R.id.stickylist_header_text);
|
||||
holder.mCount = (TextView) convertView.findViewById(R.id.contacts_num);
|
||||
convertView.setTag(holder);
|
||||
@@ -899,9 +877,6 @@ public class KeyListFragment extends LoaderFragment
|
||||
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) {
|
||||
@@ -927,9 +902,6 @@ public class KeyListFragment extends LoaderFragment
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* -------------------------- MULTI-SELECTION METHODS --------------
|
||||
*/
|
||||
public void setNewSelection(int position, boolean value) {
|
||||
mSelection.put(position, value);
|
||||
notifyDataSetChanged();
|
||||
@@ -965,5 +937,6 @@ public class KeyListFragment extends LoaderFragment
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
@@ -94,7 +94,6 @@ public class KeyAdapter extends CursorAdapter {
|
||||
|
||||
public static class KeyItemViewHolder {
|
||||
public View mView;
|
||||
public View mLayoutDummy;
|
||||
public View mLayoutData;
|
||||
public Long mMasterKeyId;
|
||||
public TextView mMainUserId;
|
||||
@@ -109,7 +108,6 @@ public class KeyAdapter extends CursorAdapter {
|
||||
public KeyItemViewHolder(View view) {
|
||||
mView = view;
|
||||
mLayoutData = view.findViewById(R.id.key_list_item_data);
|
||||
mLayoutDummy = view.findViewById(R.id.key_list_item_dummy);
|
||||
mMainUserId = (TextView) view.findViewById(R.id.key_list_item_name);
|
||||
mMainUserIdRest = (TextView) view.findViewById(R.id.key_list_item_email);
|
||||
mStatus = (ImageView) view.findViewById(R.id.key_list_item_status_icon);
|
||||
@@ -119,10 +117,6 @@ public class KeyAdapter extends CursorAdapter {
|
||||
}
|
||||
|
||||
public void setData(Context context, KeyItem item, Highlighter highlighter, boolean enabled) {
|
||||
|
||||
mLayoutData.setVisibility(View.VISIBLE);
|
||||
mLayoutDummy.setVisibility(View.GONE);
|
||||
|
||||
mDisplayedItem = item;
|
||||
|
||||
{ // set name and stuff, common to both key types
|
||||
@@ -207,25 +201,8 @@ public class KeyAdapter extends CursorAdapter {
|
||||
} else {
|
||||
mCreationDate.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Shows the "you have no keys yet" dummy view, and sets an OnClickListener. */
|
||||
public void setDummy(OnClickListener listener) {
|
||||
|
||||
// just reset everything to display the dummy layout
|
||||
mLayoutDummy.setVisibility(View.VISIBLE);
|
||||
mLayoutData.setVisibility(View.GONE);
|
||||
mSlinger.setVisibility(View.GONE);
|
||||
mStatus.setVisibility(View.GONE);
|
||||
mView.setClickable(false);
|
||||
|
||||
mLayoutDummy.setOnClickListener(listener);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean isEnabled(Cursor cursor) {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package org.sufficientlysecure.keychain.ui.util.adapter;
|
||||
package org.sufficientlysecure.keychain.ui.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -14,15 +15,24 @@ import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.openintents.openpgp.util.OpenPgpUtils;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
|
||||
import org.sufficientlysecure.keychain.ui.util.Highlighter;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.ui.util.adapter.*;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
public class KeySectionedListAdapter extends SectionCursorAdapter<Character, SectionCursorAdapter.ViewHolder, KeySectionedListAdapter.KeyHeaderViewHolder> implements org.sufficientlysecure.keychain.ui.util.adapter.KeyAdapter {
|
||||
private static final short VIEW_ITEM_TYPE_KEY = 0x0;
|
||||
private static final short VIEW_ITEM_TYPE_DUMMY = 0x1;
|
||||
|
||||
private static final short VIEW_SECTION_TYPE_PRIVATE = 0x0;
|
||||
private static final short VIEW_SECTION_TYPE_PUBLIC = 0x1;
|
||||
|
||||
public class KeySectionedListAdapter extends SectionCursorAdapter<String, KeySectionedListAdapter.KeyItemViewHolder, KeySectionedListAdapter.KeyHeaderViewHolder> implements KeyAdapter {
|
||||
private String mQuery;
|
||||
private SparseBooleanArray mSelectionMap;
|
||||
|
||||
private boolean mHasDummy = false;
|
||||
|
||||
public KeySectionedListAdapter(Context context, Cursor cursor) {
|
||||
super(context, cursor, 0);
|
||||
@@ -67,57 +77,148 @@ public class KeySectionedListAdapter extends SectionCursorAdapter<String, KeySec
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSectionFromCursor(Cursor cursor) throws IllegalStateException {
|
||||
public void onContentChanged() {
|
||||
mHasDummy = false;
|
||||
super.onContentChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of database entries displayed.
|
||||
* @return The item count
|
||||
*/
|
||||
public int getCount() {
|
||||
if (getCursor() != null) {
|
||||
return getCursor().getCount() - (mHasDummy ? 1 : 0);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Character getSectionFromCursor(Cursor cursor) throws IllegalStateException {
|
||||
if (cursor.getInt(INDEX_HAS_ANY_SECRET) != 0) {
|
||||
return getContext().getString(R.string.my_keys);
|
||||
}
|
||||
if (cursor.getLong(INDEX_MASTER_KEY_ID) == 0L) {
|
||||
mHasDummy = true;
|
||||
}
|
||||
|
||||
String userId = cursor.getString(INDEX_USER_ID);
|
||||
String headerText = getContext().getString(R.string.user_id_no_name);
|
||||
|
||||
if (userId != null && userId.length() > 0) {
|
||||
headerText = "" + userId.charAt(0);
|
||||
}
|
||||
|
||||
return headerText;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected KeyHeaderViewHolder onCreateSectionViewHolder(ViewGroup parent) {
|
||||
return new KeyHeaderViewHolder(LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.key_list_header, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected KeyItemViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
|
||||
return new KeyItemViewHolder(LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.key_list_item, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindSectionViewHolder(KeyHeaderViewHolder holder, int sectionIndex, String section) {
|
||||
System.out.println("SIX: " + sectionIndex);
|
||||
if(sectionIndex == 0) {
|
||||
holder.bind(section, getCursor().getCount());
|
||||
return '#';
|
||||
} else {
|
||||
holder.bind(section);
|
||||
String userId = cursor.getString(INDEX_USER_ID);
|
||||
if(TextUtils.isEmpty(userId)) {
|
||||
return '?';
|
||||
} else {
|
||||
return Character.toUpperCase(userId.charAt(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindItemViewHolder(KeyItemViewHolder holder, Cursor cursor) {
|
||||
boolean isSecret = cursor.getInt(INDEX_HAS_ANY_SECRET) != 0;
|
||||
long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID);
|
||||
if (isSecret && masterKeyId == 0L) {
|
||||
holder.bindDummy();
|
||||
protected short getSectionHeaderViewType(int sectionIndex) {
|
||||
return (sectionIndex < 1) ?
|
||||
VIEW_SECTION_TYPE_PRIVATE :
|
||||
VIEW_SECTION_TYPE_PUBLIC;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected short getSectionItemViewType(int position) {
|
||||
if(moveCursor(position)) {
|
||||
boolean hasMaster = getCursor().getLong(INDEX_MASTER_KEY_ID) != 0L;
|
||||
boolean isSecret = getCursor().getInt(INDEX_HAS_ANY_SECRET) != 0;
|
||||
|
||||
if (isSecret && !hasMaster) {
|
||||
return VIEW_ITEM_TYPE_DUMMY;
|
||||
}
|
||||
} else {
|
||||
Log.w(Constants.TAG, "Unable to determine key view type. "
|
||||
+ "Reason: Could not move cursor over dataset.");
|
||||
}
|
||||
|
||||
return VIEW_ITEM_TYPE_KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected KeyHeaderViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
|
||||
switch (viewType) {
|
||||
case VIEW_SECTION_TYPE_PUBLIC:
|
||||
return new KeyHeaderViewHolder(LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.key_list_header_public, parent, false));
|
||||
|
||||
case VIEW_SECTION_TYPE_PRIVATE:
|
||||
return new KeyHeaderViewHolder(LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.key_list_header_private, parent, false));
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
|
||||
switch (viewType) {
|
||||
case VIEW_ITEM_TYPE_KEY:
|
||||
return new KeyItemViewHolder(LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.key_list_item, parent, false));
|
||||
|
||||
case VIEW_ITEM_TYPE_DUMMY:
|
||||
return new KeyDummyViewHolder(LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.key_list_dummy, parent, false));
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindSectionViewHolder(KeyHeaderViewHolder holder, Character section) {
|
||||
switch (holder.getItemViewTypeWithoutSections()) {
|
||||
case VIEW_SECTION_TYPE_PUBLIC: {
|
||||
String title = section.equals('?') ?
|
||||
getContext().getString(R.string.user_id_no_name) :
|
||||
String.valueOf(section);
|
||||
|
||||
holder.bind(title);
|
||||
break;
|
||||
}
|
||||
|
||||
case VIEW_SECTION_TYPE_PRIVATE: {
|
||||
int count = getCount();
|
||||
String title = getContext().getResources()
|
||||
.getQuantityString(R.plurals.n_keys, count, count);
|
||||
holder.bind(title);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindItemViewHolder(ViewHolder holder, Cursor cursor) {
|
||||
if (holder.getItemViewTypeWithoutSections() == VIEW_ITEM_TYPE_KEY) {
|
||||
Highlighter highlighter = new Highlighter(getContext(), mQuery);
|
||||
holder.bindKey(new KeyItem(cursor), highlighter);
|
||||
((KeyItemViewHolder) holder).bindKey(new KeyItem(cursor), highlighter);
|
||||
}
|
||||
}
|
||||
|
||||
static class KeyItemViewHolder extends RecyclerView.ViewHolder {
|
||||
private View mLayoutDummy;
|
||||
private static class KeyDummyViewHolder extends SectionCursorAdapter.ViewHolder
|
||||
implements View.OnClickListener{
|
||||
|
||||
public KeyDummyViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
||||
itemView.setClickable(true);
|
||||
itemView.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static class KeyItemViewHolder extends SectionCursorAdapter.ViewHolder
|
||||
implements View.OnClickListener, View.OnLongClickListener {
|
||||
|
||||
private View mLayoutData;
|
||||
private Long mMasterKeyId;
|
||||
private TextView mMainUserId;
|
||||
@@ -130,22 +231,23 @@ public class KeySectionedListAdapter extends SectionCursorAdapter<String, KeySec
|
||||
public KeyItemViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
||||
itemView.setOnClickListener(this);
|
||||
itemView.setOnLongClickListener(this);
|
||||
|
||||
mLayoutData = itemView.findViewById(R.id.key_list_item_data);
|
||||
mLayoutDummy = itemView.findViewById(R.id.key_list_item_dummy);
|
||||
mMainUserId = (TextView) itemView.findViewById(R.id.key_list_item_name);
|
||||
mMainUserIdRest = (TextView) itemView.findViewById(R.id.key_list_item_email);
|
||||
mStatus = (ImageView) itemView.findViewById(R.id.key_list_item_status_icon);
|
||||
mSlinger = itemView.findViewById(R.id.key_list_item_slinger_view);
|
||||
mSlingerButton = (ImageButton) itemView.findViewById(R.id.key_list_item_slinger_button);
|
||||
mCreationDate = (TextView) itemView.findViewById(R.id.key_list_item_creation);
|
||||
|
||||
mSlingerButton.setOnClickListener(this);
|
||||
}
|
||||
|
||||
public void bindKey(KeyItem keyItem, Highlighter highlighter) {
|
||||
Context ctx = itemView.getContext();
|
||||
|
||||
mLayoutData.setVisibility(View.VISIBLE);
|
||||
mLayoutDummy.setVisibility(View.GONE);
|
||||
|
||||
{ // set name and stuff, common to both key types
|
||||
OpenPgpUtils.UserId userIdSplit = keyItem.mUserId;
|
||||
if (userIdSplit.name != null) {
|
||||
@@ -177,12 +279,12 @@ public class KeySectionedListAdapter extends SectionCursorAdapter<String, KeySec
|
||||
.setStatusImage(ctx, mStatus, null, KeyFormattingUtils.State.REVOKED, R.color.key_flag_gray);
|
||||
mStatus.setVisibility(View.VISIBLE);
|
||||
mSlinger.setVisibility(View.GONE);
|
||||
textColor = ctx.getResources().getColor(R.color.key_flag_gray);
|
||||
textColor = ContextCompat.getColor(ctx, R.color.key_flag_gray);
|
||||
} else if (keyItem.mIsExpired) {
|
||||
KeyFormattingUtils.setStatusImage(ctx, mStatus, null, KeyFormattingUtils.State.EXPIRED, R.color.key_flag_gray);
|
||||
mStatus.setVisibility(View.VISIBLE);
|
||||
mSlinger.setVisibility(View.GONE);
|
||||
textColor = ctx.getResources().getColor(R.color.key_flag_gray);
|
||||
textColor = ContextCompat.getColor(ctx, R.color.key_flag_gray);
|
||||
} else if (keyItem.mIsSecret) {
|
||||
mStatus.setVisibility(View.GONE);
|
||||
if (mSlingerButton.hasOnClickListeners()) {
|
||||
@@ -228,41 +330,27 @@ public class KeySectionedListAdapter extends SectionCursorAdapter<String, KeySec
|
||||
}
|
||||
}
|
||||
|
||||
public void bindDummy() {
|
||||
// just reset everything to display the dummy layout
|
||||
mLayoutDummy.setVisibility(View.VISIBLE);
|
||||
mLayoutData.setVisibility(View.GONE);
|
||||
mSlinger.setVisibility(View.GONE);
|
||||
mStatus.setVisibility(View.GONE);
|
||||
itemView.setClickable(false);
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static class KeyHeaderViewHolder extends RecyclerView.ViewHolder {
|
||||
private TextView mHeaderText;
|
||||
private TextView mHeaderCount;
|
||||
static class KeyHeaderViewHolder extends SectionCursorAdapter.ViewHolder {
|
||||
private TextView mText1;
|
||||
|
||||
public KeyHeaderViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
||||
mHeaderText = (TextView) itemView.findViewById(R.id.stickylist_header_text);
|
||||
mHeaderCount = (TextView) itemView.findViewById(R.id.contacts_num);
|
||||
}
|
||||
|
||||
public void bind(String title, int count) {
|
||||
mHeaderText.setText(title);
|
||||
|
||||
String contactsTotal = itemView.getResources()
|
||||
.getQuantityString(R.plurals.n_keys, count, count);
|
||||
|
||||
mHeaderCount.setText(contactsTotal);
|
||||
mHeaderCount.setVisibility(View.VISIBLE);
|
||||
|
||||
mText1 = (TextView) itemView.findViewById(android.R.id.text1);
|
||||
}
|
||||
|
||||
public void bind(String title) {
|
||||
mHeaderText.setText(title);
|
||||
mHeaderCount.setVisibility(View.GONE);
|
||||
mText1.setText(title);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,14 +49,14 @@ public interface KeyAdapter {
|
||||
boolean isSecretAvailable(int position);
|
||||
|
||||
class KeyItem implements Serializable {
|
||||
final String mUserIdFull;
|
||||
final OpenPgpUtils.UserId mUserId;
|
||||
final long mKeyId;
|
||||
final boolean mHasDuplicate;
|
||||
final boolean mHasEncrypt;
|
||||
final Date mCreation;
|
||||
final String mFingerprint;
|
||||
final boolean mIsSecret, mIsRevoked, mIsExpired, mIsVerified;
|
||||
public final String mUserIdFull;
|
||||
public final OpenPgpUtils.UserId mUserId;
|
||||
public final long mKeyId;
|
||||
public final boolean mHasDuplicate;
|
||||
public final boolean mHasEncrypt;
|
||||
public final Date mCreation;
|
||||
public final String mFingerprint;
|
||||
public final boolean mIsSecret, mIsRevoked, mIsExpired, mIsVerified;
|
||||
|
||||
public KeyItem(Cursor cursor) {
|
||||
String userId = cursor.getString(INDEX_USER_ID);
|
||||
|
||||
@@ -4,35 +4,26 @@ import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.support.v4.util.SparseArrayCompat;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.SectionIndexer;
|
||||
|
||||
import com.tonicartos.superslim.GridSLM;
|
||||
import com.tonicartos.superslim.LayoutManager;
|
||||
import com.tonicartos.superslim.LinearSLM;
|
||||
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
/**
|
||||
* @param <T> section type.
|
||||
* @param <VH> the view holder extending {@code BaseViewHolder<Cursor>} that is bound to the cursor data.
|
||||
* @param <SH> the view holder extending {@code BaseViewHolder<<T>>} that is bound to the section data.
|
||||
*/
|
||||
public abstract class SectionCursorAdapter<T, VH extends RecyclerView.ViewHolder, SH extends RecyclerView.ViewHolder>
|
||||
extends CursorAdapter implements SectionIndexer {
|
||||
public abstract class SectionCursorAdapter<T, VH extends SectionCursorAdapter.ViewHolder,
|
||||
SH extends SectionCursorAdapter.ViewHolder> extends CursorAdapter {
|
||||
|
||||
public static final String TAG = "SectionCursorAdapter";
|
||||
|
||||
private static final int VIEW_TYPE_ITEM = 0x0;
|
||||
private static final int VIEW_TYPE_SECTION = 0x1;
|
||||
private static final short VIEW_TYPE_ITEM = 0x1;
|
||||
private static final short VIEW_TYPE_SECTION = 0x2;
|
||||
|
||||
private SparseArrayCompat<T> mSectionMap = new SparseArrayCompat<>();
|
||||
private ArrayList<Integer> mSectionIndexList = new ArrayList<>();
|
||||
private Comparator<T> mSectionComparator;
|
||||
private Object[] mFastScrollItems;
|
||||
|
||||
public SectionCursorAdapter(Context context, Cursor cursor, int flags) {
|
||||
this(context, cursor, flags, new Comparator<T>() {
|
||||
@@ -55,8 +46,6 @@ public abstract class SectionCursorAdapter<T, VH extends RecyclerView.ViewHolder
|
||||
buildSections();
|
||||
} else {
|
||||
mSectionMap.clear();
|
||||
mSectionIndexList.clear();
|
||||
mFastScrollItems = null;
|
||||
}
|
||||
|
||||
super.onContentChanged();
|
||||
@@ -81,8 +70,6 @@ public abstract class SectionCursorAdapter<T, VH extends RecyclerView.ViewHolder
|
||||
moveCursor(-1);
|
||||
try {
|
||||
mSectionMap.clear();
|
||||
mSectionIndexList.clear();
|
||||
mFastScrollItems = null;
|
||||
|
||||
appendSections(getCursor());
|
||||
} catch (IllegalStateException e) {
|
||||
@@ -91,13 +78,11 @@ public abstract class SectionCursorAdapter<T, VH extends RecyclerView.ViewHolder
|
||||
swapCursor(null);
|
||||
|
||||
mSectionMap.clear();
|
||||
mSectionIndexList.clear();
|
||||
mFastScrollItems = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void appendSections(Cursor cursor) throws IllegalStateException {
|
||||
private void appendSections(Cursor cursor) throws IllegalStateException {
|
||||
int cursorPosition = 0;
|
||||
while(hasValidData() && cursor.moveToNext()) {
|
||||
T section = getSectionFromCursor(cursor);
|
||||
@@ -125,9 +110,6 @@ public abstract class SectionCursorAdapter<T, VH extends RecyclerView.ViewHolder
|
||||
* This object will be passed to newSectionView and bindSectionView.
|
||||
*/
|
||||
protected abstract T getSectionFromCursor(Cursor cursor) throws IllegalStateException;
|
||||
protected String getTitleFromSection(T section) {
|
||||
return section != null ? section.toString() : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
@@ -174,57 +156,6 @@ public abstract class SectionCursorAdapter<T, VH extends RecyclerView.ViewHolder
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the section object for the index within the array of sections.
|
||||
* @param sectionPosition The section index.
|
||||
* @return The specified section object for this position.
|
||||
*/
|
||||
public T getSection(int sectionPosition) {
|
||||
if (mSectionIndexList.contains(sectionPosition)) {
|
||||
return mSectionMap.get(mSectionIndexList.get(sectionPosition));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all indices at which the first item of a section is placed.
|
||||
* @return The first index of each section.
|
||||
*/
|
||||
public List<Integer> getSectionListPositions() {
|
||||
return mSectionIndexList;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int getPositionForSection(int sectionIndex) {
|
||||
if (mSectionIndexList.isEmpty()) {
|
||||
for (int i = 0; i < mSectionMap.size(); i++) {
|
||||
mSectionIndexList.add(mSectionMap.keyAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
return sectionIndex < mSectionIndexList.size() ?
|
||||
mSectionIndexList.get(sectionIndex) : getItemCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the position of a section, returns its index in the array of sections.
|
||||
* @param sectionPosition The first position of the corresponding section in the array of items.
|
||||
* @return The section index in the array of sections.
|
||||
*/
|
||||
public int getSectionIndexForPosition(int sectionPosition) {
|
||||
if (mSectionIndexList.isEmpty()) {
|
||||
for (int i = 0; i < mSectionMap.size(); i++) {
|
||||
mSectionIndexList.add(mSectionMap.keyAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
return mSectionIndexList.indexOf(sectionPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the list position of an item in the adapter, returns the
|
||||
* adapter position of the first item of the section the given item belongs to.
|
||||
@@ -242,10 +173,7 @@ public abstract class SectionCursorAdapter<T, VH extends RecyclerView.ViewHolder
|
||||
return start;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
|
||||
public int getSectionForPosition(int listPosition) {
|
||||
boolean isSection = false;
|
||||
int numPrecedingSections = 0;
|
||||
@@ -264,27 +192,6 @@ public abstract class SectionCursorAdapter<T, VH extends RecyclerView.ViewHolder
|
||||
return isSection ? numPrecedingSections : Math.max(numPrecedingSections - 1, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getSections() {
|
||||
if(mFastScrollItems == null) {
|
||||
mFastScrollItems = getSectionLabels();
|
||||
}
|
||||
|
||||
return mFastScrollItems;
|
||||
}
|
||||
|
||||
private Object[] getSectionLabels() {
|
||||
if(mSectionMap == null)
|
||||
return new Object[0];
|
||||
|
||||
String[] ret = new String[mSectionMap.size()];
|
||||
for(int i = 0; i < ret.length; i++) {
|
||||
ret[i] = getTitleFromSection(mSectionMap.valueAt(i));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private boolean isListPositionBeforeFirstSection(int listPosition, int sectionIndex) {
|
||||
boolean hasSections = mSectionMap != null && mSectionMap.size() > 0;
|
||||
return sectionIndex == 0 && hasSections && listPosition < mSectionMap.keyAt(0);
|
||||
@@ -292,7 +199,21 @@ public abstract class SectionCursorAdapter<T, VH extends RecyclerView.ViewHolder
|
||||
|
||||
@Override
|
||||
public final int getItemViewType(int listPosition) {
|
||||
return isSection(listPosition) ? VIEW_TYPE_SECTION : VIEW_TYPE_ITEM;
|
||||
int sectionIndex = mSectionMap.indexOfKey(listPosition);
|
||||
if(sectionIndex < 0) {
|
||||
int cursorPosition = getCursorPositionWithoutSections(listPosition);
|
||||
return (getSectionItemViewType(cursorPosition) << 16) | VIEW_TYPE_ITEM;
|
||||
} else {
|
||||
return (getSectionHeaderViewType(sectionIndex) << 16) | VIEW_TYPE_SECTION;
|
||||
}
|
||||
}
|
||||
|
||||
protected short getSectionHeaderViewType(int sectionIndex) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected short getSectionItemViewType(int position) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -304,7 +225,8 @@ public abstract class SectionCursorAdapter<T, VH extends RecyclerView.ViewHolder
|
||||
// assign first position of section to each item
|
||||
layoutParams.setFirstPosition(getFirstSectionPosition(position));
|
||||
|
||||
switch (holder.getItemViewType()) {
|
||||
int viewType = holder.getItemViewType() & 0xFF;
|
||||
switch (viewType) {
|
||||
case VIEW_TYPE_ITEM :
|
||||
moveCursorOrThrow(getCursorPositionWithoutSections(position));
|
||||
onBindItemViewHolder((VH) holder, getCursor());
|
||||
@@ -314,8 +236,7 @@ public abstract class SectionCursorAdapter<T, VH extends RecyclerView.ViewHolder
|
||||
|
||||
case VIEW_TYPE_SECTION:
|
||||
T section = mSectionMap.get(position);
|
||||
int sectionIndex = getSectionIndexForPosition(position);
|
||||
onBindSectionViewHolder((SH) holder, sectionIndex, section);
|
||||
onBindSectionViewHolder((SH) holder, section);
|
||||
|
||||
layoutParams.isHeader = true;
|
||||
break;
|
||||
@@ -326,26 +247,45 @@ public abstract class SectionCursorAdapter<T, VH extends RecyclerView.ViewHolder
|
||||
|
||||
@Override
|
||||
public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
switch (viewType) {
|
||||
switch (viewType & 0xFF) {
|
||||
case VIEW_TYPE_SECTION:
|
||||
return onCreateSectionViewHolder(parent);
|
||||
return onCreateSectionViewHolder(parent, viewType >> 16);
|
||||
|
||||
case VIEW_TYPE_ITEM:
|
||||
return onCreateItemViewHolder(parent, viewType);
|
||||
return onCreateItemViewHolder(parent, viewType >> 16);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract SH onCreateSectionViewHolder(ViewGroup parent);
|
||||
protected abstract SH onCreateSectionViewHolder(ViewGroup parent, int viewType);
|
||||
protected abstract VH onCreateItemViewHolder(ViewGroup parent, int viewType);
|
||||
|
||||
protected abstract void onBindSectionViewHolder(SH holder, int sectionIndex, T section);
|
||||
protected abstract void onBindSectionViewHolder(SH holder, T section);
|
||||
protected abstract void onBindItemViewHolder(VH holder, Cursor cursor);
|
||||
|
||||
public interface Comparator<T> {
|
||||
boolean equal(T obj1, T obj2);
|
||||
}
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the view type assigned in
|
||||
* {@link SectionCursorAdapter#getSectionHeaderViewType(int)} or
|
||||
* {@link SectionCursorAdapter#getSectionItemViewType(int)}
|
||||
*
|
||||
* Note that a call to {@link #getItemViewType()} will return a value that contains
|
||||
* internal stuff necessary to distinguish sections from items.
|
||||
* @return The view type you set.
|
||||
*/
|
||||
public short getItemViewTypeWithoutSections(){
|
||||
return (short) (getItemViewType() >> 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
* Implementation of taken from the sourcecode of
|
||||
* android.support.v4.app.ListFragment from the
|
||||
* Android Open Source Project and changed to use
|
||||
* RecyclerView instead of ListView.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ui.util.recyclerview;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
public class RecyclerFragment<A extends RecyclerView.Adapter> extends Fragment {
|
||||
|
||||
private static final int INTERNAL_LIST_VIEW_ID = android.R.id.list;
|
||||
private static final int INTERNAL_EMPTY_VIEW_ID = android.R.id.empty;
|
||||
private static final int INTERNAL_LIST_CONTAINER_ID = android.R.id.widget_frame;
|
||||
private static final int INTERNAL_PROGRESS_CONTAINER_ID = android.R.id.progress;
|
||||
|
||||
private final Handler handler = new Handler();
|
||||
private final Runnable requestFocus = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listView.focusableViewAvailable(listView);
|
||||
}
|
||||
};
|
||||
|
||||
private boolean observerRegistered = false;
|
||||
private final RecyclerView.AdapterDataObserver dataObserver = new RecyclerView.AdapterDataObserver() {
|
||||
@Override
|
||||
public void onChanged() {
|
||||
super.onChanged();
|
||||
checkDataSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemRangeInserted(int positionStart, int itemCount) {
|
||||
super.onItemRangeInserted(positionStart, itemCount);
|
||||
checkDataSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemRangeRemoved(int positionStart, int itemCount) {
|
||||
super.onItemRangeRemoved(positionStart, itemCount);
|
||||
checkDataSet();
|
||||
}
|
||||
};
|
||||
|
||||
private final RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() {
|
||||
|
||||
@Override
|
||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||
RecyclerFragment.this.onScrolled(dx, dy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
|
||||
RecyclerFragment.this.onScrollStateChanged(newState);
|
||||
}
|
||||
};
|
||||
|
||||
private A adapter;
|
||||
private RecyclerView.LayoutManager layoutManager;
|
||||
private RecyclerView listView;
|
||||
private View emptyView;
|
||||
private View progressContainer;
|
||||
private View listContainer;
|
||||
private boolean listShown;
|
||||
|
||||
public RecyclerFragment() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
|
||||
|
||||
final Context context = parent.getContext();
|
||||
FrameLayout root = new FrameLayout(context);
|
||||
|
||||
LinearLayout progressContainer = new LinearLayout(context);
|
||||
progressContainer.setId(INTERNAL_PROGRESS_CONTAINER_ID);
|
||||
progressContainer.setOrientation(LinearLayout.VERTICAL);
|
||||
progressContainer.setGravity(Gravity.CENTER);
|
||||
progressContainer.setVisibility(View.GONE);
|
||||
|
||||
ProgressBar progressBar = new ProgressBar(context, null,
|
||||
android.R.attr.progressBarStyleLarge);
|
||||
|
||||
progressContainer.addView(progressBar, new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
root.addView(progressContainer, new FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
|
||||
|
||||
FrameLayout listContainer = new FrameLayout(context);
|
||||
listContainer.setId(INTERNAL_LIST_CONTAINER_ID);
|
||||
|
||||
TextView textView = new TextView(context);
|
||||
textView.setId(INTERNAL_EMPTY_VIEW_ID);
|
||||
textView.setGravity(Gravity.CENTER);
|
||||
|
||||
listContainer.addView(textView, new FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
|
||||
RecyclerView listView = new RecyclerView(context);
|
||||
listView.setId(INTERNAL_LIST_VIEW_ID);
|
||||
|
||||
listContainer.addView(listView, new FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
|
||||
|
||||
root.addView(listContainer, new FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
|
||||
|
||||
root.setLayoutParams(new FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
ensureList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
handler.removeCallbacks(requestFocus);
|
||||
listView.setLayoutManager(null);
|
||||
listView.removeOnScrollListener(scrollListener);
|
||||
|
||||
listView = null;
|
||||
listShown = false;
|
||||
listContainer = null;
|
||||
layoutManager = null;
|
||||
emptyView = null;
|
||||
progressContainer = null;
|
||||
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
setAdapter(null);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
public Handler getHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
public void onScrollStateChanged(int state) {
|
||||
// empty body
|
||||
}
|
||||
|
||||
public void onScrolled(int dx, int dy) {
|
||||
// empty body
|
||||
}
|
||||
|
||||
public void setAdapter(A adapter) {
|
||||
unregisterObserver();
|
||||
|
||||
boolean hadAdapter = this.adapter != null;
|
||||
this.adapter = adapter;
|
||||
|
||||
registerObserver();
|
||||
|
||||
if(listView != null) {
|
||||
listView.setAdapter(adapter);
|
||||
|
||||
if(!listShown && !hadAdapter) {
|
||||
if(getView() != null)
|
||||
setListShown(true, getView().getWindowToken() != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setLayoutManager(RecyclerView.LayoutManager manager) {
|
||||
if(!manager.isAttachedToWindow()) {
|
||||
layoutManager = manager;
|
||||
|
||||
if (listView != null) {
|
||||
listView.setLayoutManager(manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getItemCount() {
|
||||
if(adapter != null)
|
||||
return adapter.getItemCount();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
if(adapter != null)
|
||||
return adapter.getItemId(position);
|
||||
else
|
||||
return View.NO_ID;
|
||||
}
|
||||
|
||||
public RecyclerView getRecyclerView() {
|
||||
ensureList();
|
||||
return listView;
|
||||
}
|
||||
|
||||
public RecyclerView.LayoutManager getLayoutManager() {
|
||||
ensureList();
|
||||
return layoutManager;
|
||||
}
|
||||
|
||||
private void registerObserver() {
|
||||
if(!observerRegistered && adapter != null) {
|
||||
adapter.registerAdapterDataObserver(dataObserver);
|
||||
observerRegistered = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void unregisterObserver() {
|
||||
if(observerRegistered && adapter != null) {
|
||||
adapter.unregisterAdapterDataObserver(dataObserver);
|
||||
observerRegistered = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkDataSet() {
|
||||
boolean empty = treatAsEmpty(getItemCount());
|
||||
|
||||
Log.d("RecyclerFragment", "Dataset change detected! Count: "
|
||||
+ getItemCount() + ", Empty: " + empty);
|
||||
|
||||
if(emptyView != null) {
|
||||
emptyView.setVisibility(empty ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the data set of the recycler view should be treated as empty.
|
||||
* This is useful e.g. if you have an empty padding row and therefore the item
|
||||
* count is always greater than 0.
|
||||
*
|
||||
* @param itemCount the number of items in the data set.
|
||||
* @return Whether to treat this as an empty set of data
|
||||
*/
|
||||
protected boolean treatAsEmpty(int itemCount) {
|
||||
return itemCount < 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the recycler view should have a fixed size or not
|
||||
*/
|
||||
protected boolean isFixedSize() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void hideList(boolean animated) {
|
||||
setListShown(false, animated);
|
||||
}
|
||||
|
||||
public void showList(boolean animated) {
|
||||
setListShown(true, animated);
|
||||
}
|
||||
|
||||
private void setListShown(boolean shown, boolean animated) {
|
||||
ensureList();
|
||||
|
||||
if(progressContainer == null)
|
||||
throw new IllegalStateException("Can't be used with a custom content view");
|
||||
|
||||
if(listShown == shown)
|
||||
return;
|
||||
|
||||
listShown = shown;
|
||||
if(shown) {
|
||||
if (animated) {
|
||||
progressContainer.startAnimation(AnimationUtils.loadAnimation(
|
||||
getActivity(), android.R.anim.fade_out));
|
||||
listContainer.startAnimation(AnimationUtils.loadAnimation(
|
||||
getActivity(), android.R.anim.fade_in));
|
||||
} else {
|
||||
progressContainer.clearAnimation();
|
||||
listContainer.clearAnimation();
|
||||
}
|
||||
progressContainer.setVisibility(View.GONE);
|
||||
listContainer.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
if (animated) {
|
||||
progressContainer.startAnimation(AnimationUtils.loadAnimation(
|
||||
getActivity(), android.R.anim.fade_in));
|
||||
listContainer.startAnimation(AnimationUtils.loadAnimation(
|
||||
getActivity(), android.R.anim.fade_out));
|
||||
} else {
|
||||
progressContainer.clearAnimation();
|
||||
listContainer.clearAnimation();
|
||||
}
|
||||
progressContainer.setVisibility(View.VISIBLE);
|
||||
listContainer.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public A getAdapter() {
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void ensureList() {
|
||||
if (listView != null)
|
||||
return;
|
||||
|
||||
View root = getView();
|
||||
if (root == null)
|
||||
throw new IllegalStateException("Content view not yet created");
|
||||
|
||||
if (root instanceof RecyclerView) {
|
||||
listView = (RecyclerView) root;
|
||||
} else {
|
||||
emptyView = root.findViewById(INTERNAL_EMPTY_VIEW_ID);
|
||||
if(emptyView != null) {
|
||||
emptyView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
progressContainer = root.findViewById(INTERNAL_PROGRESS_CONTAINER_ID);
|
||||
listContainer = root.findViewById(INTERNAL_LIST_CONTAINER_ID);
|
||||
|
||||
View rawListView = root.findViewById(INTERNAL_LIST_VIEW_ID);
|
||||
if (!(rawListView instanceof RecyclerView)) {
|
||||
if (rawListView == null) {
|
||||
throw new RuntimeException(
|
||||
"Your content must have a RecyclerView whose id attribute is " +
|
||||
"'android.R.id.list'");
|
||||
}
|
||||
throw new RuntimeException(
|
||||
"Content has view with id attribute 'android.R.id.list' "
|
||||
+ "that is not a ListView class");
|
||||
}
|
||||
|
||||
listView = (RecyclerView) rawListView;
|
||||
}
|
||||
|
||||
if(layoutManager != null) {
|
||||
RecyclerView.LayoutManager manager = layoutManager;
|
||||
this.layoutManager = null;
|
||||
setLayoutManager(manager);
|
||||
}
|
||||
|
||||
listShown = true;
|
||||
listView.setHasFixedSize(isFixedSize());
|
||||
listView.addOnScrollListener(scrollListener);
|
||||
|
||||
if (this.adapter != null) {
|
||||
A adapter = this.adapter;
|
||||
this.adapter = null;
|
||||
setAdapter(adapter);
|
||||
} else {
|
||||
// We are starting without an adapter, so assume we won't
|
||||
// have our data right away and start with the progress indicator.
|
||||
if (progressContainer != null) {
|
||||
setListShown(false, false);
|
||||
}
|
||||
}
|
||||
|
||||
handler.post(requestFocus);
|
||||
}
|
||||
}
|
||||
47
OpenKeychain/src/main/res/layout/key_list_dummy.xml
Normal file
47
OpenKeychain/src/main/res/layout/key_list_dummy.xml
Normal file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:focusable="true"
|
||||
android:background="?android:selectableItemBackground">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingRight="4dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="4dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:text="You don't have any keys yet!"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:text="Click here to create or import one."
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_key_plus_grey600_24dp" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,115 +1,140 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:fab="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
<FrameLayout
|
||||
xmlns:fab="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:custom="http://schemas.android.com/apk/res-auto"
|
||||
>
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
|
||||
<!--rebuild functionality of ListFragment -->
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@android:id/progress"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center">
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/key_list_list"
|
||||
<ProgressBar
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@android:id/widget_frame"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:visibility="gone">
|
||||
|
||||
<!--rebuild functionality of ListFragment -->
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="32dp"
|
||||
android:scrollbarStyle="outsideOverlay" />
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/key_list_empty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="240dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:animateLayoutChanges="true"
|
||||
>
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@android:id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="32dp"
|
||||
android:clipToPadding="false"
|
||||
android:scrollbarStyle="outsideOverlay" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
<LinearLayout
|
||||
android:id="@android:id/empty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="240dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/key_list_empty_text1"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge" />
|
||||
android:orientation="vertical"
|
||||
android:animateLayoutChanges="true" >
|
||||
|
||||
<org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/search_container"
|
||||
android:inAnimation="@anim/fade_in_delayed"
|
||||
android:outAnimation="@anim/fade_out"
|
||||
android:measureAllChildren="true"
|
||||
custom:initialView="1">
|
||||
|
||||
<Space
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<Button
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:id="@+id/search_button"
|
||||
android:gravity="center"
|
||||
tools:text="@string/btn_search_for_query"
|
||||
/>
|
||||
android:text="@string/key_list_empty_text1"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge" />
|
||||
|
||||
</org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
|
||||
<org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/search_container"
|
||||
android:inAnimation="@anim/fade_in_delayed"
|
||||
android:outAnimation="@anim/fade_out"
|
||||
android:measureAllChildren="true"
|
||||
custom:initialView="1">
|
||||
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
<Space
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<com.getbase.floatingactionbutton.FloatingActionsMenu
|
||||
android:id="@+id/fab_main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
fab:fab_addButtonColorNormal="?attr/colorPrimary"
|
||||
fab:fab_addButtonColorPressed="?attr/colorPrimaryDark"
|
||||
fab:fab_addButtonSize="normal"
|
||||
fab:fab_addButtonPlusIconColor="@color/icons"
|
||||
fab:fab_expandDirection="up"
|
||||
fab:fab_labelStyle="@style/FabMenuStyle"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
>
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:id="@+id/search_button"
|
||||
android:gravity="center"
|
||||
tools:text="@string/btn_search_for_query"/>
|
||||
|
||||
<com.getbase.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab_add_qr_code"
|
||||
</org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
|
||||
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<com.getbase.floatingactionbutton.FloatingActionsMenu
|
||||
android:id="@+id/fab_main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
fab:fab_icon="@drawable/ic_qrcode_white_24dp"
|
||||
fab:fab_colorNormal="?attr/colorPrimary"
|
||||
fab:fab_colorPressed="?attr/colorPrimaryDark"
|
||||
fab:fab_title="@string/key_list_fab_qr_code"
|
||||
fab:fab_size="mini" />
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
fab:fab_addButtonColorNormal="?attr/colorPrimary"
|
||||
fab:fab_addButtonColorPressed="?attr/colorPrimaryDark"
|
||||
fab:fab_addButtonSize="normal"
|
||||
fab:fab_addButtonPlusIconColor="@color/icons"
|
||||
fab:fab_expandDirection="up"
|
||||
fab:fab_labelStyle="@style/FabMenuStyle"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginEnd="16dp">
|
||||
|
||||
<com.getbase.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab_add_cloud"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
fab:fab_icon="@drawable/ic_cloud_search_24dp"
|
||||
fab:fab_colorNormal="?attr/colorPrimary"
|
||||
fab:fab_colorPressed="?attr/colorPrimaryDark"
|
||||
fab:fab_title="@string/key_list_fab_search"
|
||||
fab:fab_size="mini" />
|
||||
<com.getbase.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab_add_qr_code"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
fab:fab_icon="@drawable/ic_qrcode_white_24dp"
|
||||
fab:fab_colorNormal="?attr/colorPrimary"
|
||||
fab:fab_colorPressed="?attr/colorPrimaryDark"
|
||||
fab:fab_title="@string/key_list_fab_qr_code"
|
||||
fab:fab_size="mini" />
|
||||
|
||||
<com.getbase.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab_add_cloud"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
fab:fab_icon="@drawable/ic_cloud_search_24dp"
|
||||
fab:fab_colorNormal="?attr/colorPrimary"
|
||||
fab:fab_colorPressed="?attr/colorPrimaryDark"
|
||||
fab:fab_title="@string/key_list_fab_search"
|
||||
fab:fab_size="mini" />
|
||||
|
||||
<com.getbase.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab_add_file"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
fab:fab_icon="@drawable/ic_folder_white_24dp"
|
||||
fab:fab_colorNormal="?attr/colorPrimary"
|
||||
fab:fab_colorPressed="?attr/colorPrimaryDark"
|
||||
fab:fab_title="@string/key_list_fab_import"
|
||||
fab:fab_size="mini" />
|
||||
|
||||
</com.getbase.floatingactionbutton.FloatingActionsMenu>
|
||||
</RelativeLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<com.getbase.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab_add_file"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
fab:fab_icon="@drawable/ic_folder_white_24dp"
|
||||
fab:fab_colorNormal="?attr/colorPrimary"
|
||||
fab:fab_colorPressed="?attr/colorPrimaryDark"
|
||||
fab:fab_title="@string/key_list_fab_import"
|
||||
fab:fab_size="mini" />
|
||||
|
||||
</com.getbase.floatingactionbutton.FloatingActionsMenu>
|
||||
</RelativeLayout>
|
||||
|
||||
@@ -12,25 +12,23 @@
|
||||
super:slm_section_sectionManager="linear"
|
||||
tools:ignore="ResAuto">
|
||||
|
||||
<TextView
|
||||
style="@style/SectionHeader"
|
||||
android:id="@+id/stickylist_header_text"
|
||||
<TextView style="@style/SectionHeader"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|left"
|
||||
android:text="header text" />
|
||||
android:text="@string/my_keys" />
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/text1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:text="key count"
|
||||
android:id="@+id/contacts_num"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginRight="8dp"
|
||||
android:visibility="gone"
|
||||
android:textColor="@android:color/darker_gray" />
|
||||
android:layout_marginLeft="8dp"
|
||||
android:textColor="@android:color/darker_gray"
|
||||
tools:text="11 Keys"/>
|
||||
|
||||
</RelativeLayout>
|
||||
22
OpenKeychain/src/main/res/layout/key_list_header_public.xml
Normal file
22
OpenKeychain/src/main/res/layout/key_list_header_public.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:super="http://schemas.android.com/apk/lib-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:colorBackground"
|
||||
|
||||
super:slm_headerDisplay="sticky|inline"
|
||||
super:slm_section_sectionManager="linear"
|
||||
tools:ignore="ResAuto">
|
||||
|
||||
<TextView style="@style/SectionHeader"
|
||||
android:id="@android:id/text1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|left"
|
||||
tools:text="A" />
|
||||
|
||||
</FrameLayout>
|
||||
@@ -1,63 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:gravity="center_vertical"
|
||||
android:singleLine="true"
|
||||
android:maxLines="1"
|
||||
android:orientation="horizontal"
|
||||
android:descendantFocusability="blocksDescendants"
|
||||
android:focusable="false">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/key_list_item_dummy"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:focusable="true"
|
||||
android:visibility="gone"
|
||||
android:background="?android:selectableItemBackground"
|
||||
tools:visibility="visible">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="4dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="4dp"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:text="You don't have any keys yet!"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:text="Click here to create or import one."
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_key_plus_grey600_24dp"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/key_list_item_data"
|
||||
android:layout_width="0dip"
|
||||
@@ -83,7 +37,7 @@
|
||||
android:id="@+id/key_list_item_email"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
tools:text="user@example.com"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
@@ -92,12 +46,11 @@
|
||||
android:id="@+id/key_list_item_creation"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:maxLines="1"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
tools:text="Created on 10/10/2010 10:00"
|
||||
/>
|
||||
tools:text="Created on 10/10/2010 10:00" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -134,7 +87,6 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:padding="16dp"
|
||||
tools:src="@drawable/status_signature_revoked_cutout_24dp"
|
||||
/>
|
||||
tools:src="@drawable/status_signature_revoked_cutout_24dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
Reference in New Issue
Block a user