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.animation.ObjectAnimator;
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.MatrixCursor;
|
import android.database.MatrixCursor;
|
||||||
import android.database.MergeCursor;
|
import android.database.MergeCursor;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
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.CursorLoader;
|
||||||
import android.support.v4.content.Loader;
|
import android.support.v4.content.Loader;
|
||||||
import android.support.v4.view.MenuItemCompat;
|
import android.support.v4.view.MenuItemCompat;
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.support.v7.widget.SearchView;
|
import android.support.v7.widget.SearchView;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.ActionMode;
|
import android.view.ActionMode;
|
||||||
@@ -46,11 +43,8 @@ import android.view.MenuItem;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.AbsListView.MultiChoiceModeListener;
|
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ListView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.ViewAnimator;
|
import android.widget.ViewAnimator;
|
||||||
|
|
||||||
import com.getbase.floatingactionbutton.FloatingActionButton;
|
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.service.ImportKeyringParcel;
|
||||||
import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter;
|
import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter;
|
||||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
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.KeyFormattingUtils;
|
||||||
import org.sufficientlysecure.keychain.ui.util.ContentDescriptionHint;
|
|
||||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
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.FabContainer;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
import org.sufficientlysecure.keychain.util.Preferences;
|
import org.sufficientlysecure.keychain.util.Preferences;
|
||||||
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
|
|
||||||
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public key list with sticky list headers. It does _not_ extend ListFragment because it uses
|
* Public key list with sticky list headers. It does _not_ extend ListFragment because it uses
|
||||||
* StickyListHeaders library which does not extend upon ListView.
|
* StickyListHeaders library which does not extend upon ListView.
|
||||||
*/
|
*/
|
||||||
public class KeyListFragment extends LoaderFragment
|
public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
||||||
implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener,
|
implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener,
|
||||||
LoaderManager.LoaderCallbacks<Cursor>, FabContainer,
|
LoaderManager.LoaderCallbacks<Cursor>, FabContainer,
|
||||||
CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
|
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_DELETE = 2;
|
||||||
private static final int REQUEST_VIEW_KEY = 3;
|
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
|
// saves the mode object for multiselect, needed for reset at some point
|
||||||
private ActionMode mActionMode = null;
|
private ActionMode mActionMode = null;
|
||||||
|
|
||||||
@@ -126,12 +112,8 @@ public class KeyListFragment extends LoaderFragment
|
|||||||
* Load custom layout with StickyListView from library
|
* Load custom layout with StickyListView from library
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
View root = super.onCreateView(inflater, superContainer, savedInstanceState);
|
View view = inflater.inflate(R.layout.key_list_fragment, container, false);
|
||||||
View view = inflater.inflate(R.layout.key_list_fragment, getContainer());
|
|
||||||
|
|
||||||
mStickyList = (RecyclerView) view.findViewById(R.id.key_list_list);
|
|
||||||
//mStickyList.setOnItemClickListener(this);
|
|
||||||
|
|
||||||
mFab = (FloatingActionsMenu) view.findViewById(R.id.fab_main);
|
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.setDrawingListUnderStickyHeader(false);
|
||||||
//mStickyList.setFastScrollEnabled(true);
|
//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.
|
// in last few rows.
|
||||||
View footer = new View(activity);
|
View footer = new View(activity);
|
||||||
|
|
||||||
@@ -197,6 +179,7 @@ public class KeyListFragment extends LoaderFragment
|
|||||||
|
|
||||||
footer.setLayoutParams(params);
|
footer.setLayoutParams(params);
|
||||||
//mStickyList.addFooterView(footer, null, false);
|
//mStickyList.addFooterView(footer, null, false);
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Multi-selection
|
* Multi-selection
|
||||||
@@ -269,7 +252,7 @@ public class KeyListFragment extends LoaderFragment
|
|||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
|
|
||||||
// Start out with a progress indicator.
|
// Start out with a progress indicator.
|
||||||
setContentShown(false);
|
hideList(true);
|
||||||
|
|
||||||
// this view is made visible if no data is available
|
// this view is made visible if no data is available
|
||||||
// mStickyList.setEmptyView(activity.findViewById(R.id.key_list_empty));
|
// 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.
|
// Create an empty adapter we will use to display the loaded data.
|
||||||
//mAdapter = new KeyListAdapter(activity, null, 0);
|
//mAdapter = new KeyListAdapter(activity, null, 0);
|
||||||
mAdapter = new KeySectionedListAdapter(getContext(), null);
|
|
||||||
|
|
||||||
mStickyList.setAdapter(mAdapter);
|
setAdapter(new KeySectionedListAdapter(getContext(), null));
|
||||||
mStickyList.setLayoutManager(new LayoutManager(getActivity()));
|
setLayoutManager(new LayoutManager(getActivity()));
|
||||||
|
|
||||||
// Prepare the loader. Either re-connect with an existing one,
|
// Prepare the loader. Either re-connect with an existing one,
|
||||||
// or start a new 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
|
// Now create and return a CursorLoader that will take care of
|
||||||
// creating a Cursor for the data being displayed.
|
// 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
|
@Override
|
||||||
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
||||||
// Swap the new cursor in. (The framework will take care of closing the
|
// Swap the new cursor in. (The framework will take care of closing the
|
||||||
// old cursor once we return.)
|
// old cursor once we return.)
|
||||||
mAdapter.setSearchQuery(mQuery);
|
getAdapter().setSearchQuery(mQuery);
|
||||||
|
|
||||||
if (data != null && (mQuery == null || TextUtils.isEmpty(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) {
|
if (!isSecret) {
|
||||||
MatrixCursor headerCursor = new MatrixCursor(KeyListAdapter.PROJECTION);
|
MatrixCursor headerCursor = new MatrixCursor(KeyAdapter.PROJECTION);
|
||||||
Long[] row = new Long[KeyListAdapter.PROJECTION.length];
|
Long[] row = new Long[KeyAdapter.PROJECTION.length];
|
||||||
row[KeyListAdapter.INDEX_HAS_ANY_SECRET] = 1L;
|
row[KeyAdapter.INDEX_HAS_ANY_SECRET] = 1L;
|
||||||
row[KeyListAdapter.INDEX_MASTER_KEY_ID] = 0L;
|
row[KeyAdapter.INDEX_MASTER_KEY_ID] = 0L;
|
||||||
headerCursor.addRow(row);
|
headerCursor.addRow(row);
|
||||||
|
|
||||||
Cursor dataCursor = data;
|
Cursor dataCursor = data;
|
||||||
@@ -348,7 +330,8 @@ public class KeyListFragment extends LoaderFragment
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mAdapter.swapCursor(data);
|
|
||||||
|
getAdapter().swapCursor(data);
|
||||||
|
|
||||||
// end action mode, if any
|
// end action mode, if any
|
||||||
if (mActionMode != null) {
|
if (mActionMode != null) {
|
||||||
@@ -357,9 +340,9 @@ public class KeyListFragment extends LoaderFragment
|
|||||||
|
|
||||||
// The list should now be shown.
|
// The list should now be shown.
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
setContentShown(true);
|
showList(true);
|
||||||
} else {
|
} else {
|
||||||
setContentShownNoAnimation(true);
|
showList(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,7 +351,7 @@ public class KeyListFragment extends LoaderFragment
|
|||||||
// This is called when the last Cursor provided to onLoadFinished()
|
// This is called when the last Cursor provided to onLoadFinished()
|
||||||
// above is about to be closed. We need to make sure we are no
|
// above is about to be closed. We need to make sure we are no
|
||||||
// longer using it.
|
// 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) {
|
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
|
||||||
Intent viewIntent = new Intent(getActivity(), ViewKeyActivity.class);
|
Intent viewIntent = new Intent(getActivity(), ViewKeyActivity.class);
|
||||||
viewIntent.setData(
|
viewIntent.setData(
|
||||||
KeyRings.buildGenericKeyRingUri(mAdapter.getMasterKeyId(position)));
|
KeyRings.buildGenericKeyRingUri(getAdapter().getMasterKeyId(position)));
|
||||||
startActivityForResult(viewIntent, REQUEST_VIEW_KEY);
|
startActivityForResult(viewIntent, REQUEST_VIEW_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -762,6 +745,7 @@ public class KeyListFragment extends LoaderFragment
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
public class KeyListAdapter extends KeyAdapter implements StickyListHeadersAdapter {
|
public class KeyListAdapter extends KeyAdapter implements StickyListHeadersAdapter {
|
||||||
|
|
||||||
private HashMap<Integer, Boolean> mSelection = new HashMap<>();
|
private HashMap<Integer, Boolean> mSelection = new HashMap<>();
|
||||||
@@ -842,19 +826,13 @@ public class KeyListFragment extends LoaderFragment
|
|||||||
TextView mCount;
|
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
|
@Override
|
||||||
public View getHeaderView(int position, View convertView, ViewGroup parent) {
|
public View getHeaderView(int position, View convertView, ViewGroup parent) {
|
||||||
HeaderViewHolder holder;
|
HeaderViewHolder holder;
|
||||||
if (convertView == null) {
|
if (convertView == null) {
|
||||||
holder = new HeaderViewHolder();
|
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.mText = (TextView) convertView.findViewById(R.id.stickylist_header_text);
|
||||||
holder.mCount = (TextView) convertView.findViewById(R.id.contacts_num);
|
holder.mCount = (TextView) convertView.findViewById(R.id.contacts_num);
|
||||||
convertView.setTag(holder);
|
convertView.setTag(holder);
|
||||||
@@ -899,9 +877,6 @@ public class KeyListFragment extends LoaderFragment
|
|||||||
return convertView;
|
return convertView;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Header IDs should be static, position=1 should always return the same Id that is.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public long getHeaderId(int position) {
|
public long getHeaderId(int position) {
|
||||||
if (!mDataValid) {
|
if (!mDataValid) {
|
||||||
@@ -927,9 +902,6 @@ public class KeyListFragment extends LoaderFragment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* -------------------------- MULTI-SELECTION METHODS --------------
|
|
||||||
*/
|
|
||||||
public void setNewSelection(int position, boolean value) {
|
public void setNewSelection(int position, boolean value) {
|
||||||
mSelection.put(position, value);
|
mSelection.put(position, value);
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
@@ -965,5 +937,6 @@ public class KeyListFragment extends LoaderFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,7 +94,6 @@ public class KeyAdapter extends CursorAdapter {
|
|||||||
|
|
||||||
public static class KeyItemViewHolder {
|
public static class KeyItemViewHolder {
|
||||||
public View mView;
|
public View mView;
|
||||||
public View mLayoutDummy;
|
|
||||||
public View mLayoutData;
|
public View mLayoutData;
|
||||||
public Long mMasterKeyId;
|
public Long mMasterKeyId;
|
||||||
public TextView mMainUserId;
|
public TextView mMainUserId;
|
||||||
@@ -109,7 +108,6 @@ public class KeyAdapter extends CursorAdapter {
|
|||||||
public KeyItemViewHolder(View view) {
|
public KeyItemViewHolder(View view) {
|
||||||
mView = view;
|
mView = view;
|
||||||
mLayoutData = view.findViewById(R.id.key_list_item_data);
|
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);
|
mMainUserId = (TextView) view.findViewById(R.id.key_list_item_name);
|
||||||
mMainUserIdRest = (TextView) view.findViewById(R.id.key_list_item_email);
|
mMainUserIdRest = (TextView) view.findViewById(R.id.key_list_item_email);
|
||||||
mStatus = (ImageView) view.findViewById(R.id.key_list_item_status_icon);
|
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) {
|
public void setData(Context context, KeyItem item, Highlighter highlighter, boolean enabled) {
|
||||||
|
|
||||||
mLayoutData.setVisibility(View.VISIBLE);
|
|
||||||
mLayoutDummy.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
mDisplayedItem = item;
|
mDisplayedItem = item;
|
||||||
|
|
||||||
{ // set name and stuff, common to both key types
|
{ // set name and stuff, common to both key types
|
||||||
@@ -207,25 +201,8 @@ public class KeyAdapter extends CursorAdapter {
|
|||||||
} else {
|
} else {
|
||||||
mCreationDate.setVisibility(View.GONE);
|
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) {
|
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.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.graphics.PorterDuff;
|
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.text.format.DateUtils;
|
||||||
import android.util.SparseBooleanArray;
|
import android.util.SparseBooleanArray;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -14,15 +15,24 @@ import android.widget.ImageView;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.openintents.openpgp.util.OpenPgpUtils;
|
import org.openintents.openpgp.util.OpenPgpUtils;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
|
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
|
||||||
import org.sufficientlysecure.keychain.ui.util.Highlighter;
|
import org.sufficientlysecure.keychain.ui.util.Highlighter;
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
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 String mQuery;
|
||||||
private SparseBooleanArray mSelectionMap;
|
private SparseBooleanArray mSelectionMap;
|
||||||
|
private boolean mHasDummy = false;
|
||||||
|
|
||||||
public KeySectionedListAdapter(Context context, Cursor cursor) {
|
public KeySectionedListAdapter(Context context, Cursor cursor) {
|
||||||
super(context, cursor, 0);
|
super(context, cursor, 0);
|
||||||
@@ -67,57 +77,148 @@ public class KeySectionedListAdapter extends SectionCursorAdapter<String, KeySec
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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) {
|
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);
|
return '#';
|
||||||
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());
|
|
||||||
} else {
|
} else {
|
||||||
holder.bind(section);
|
String userId = cursor.getString(INDEX_USER_ID);
|
||||||
|
if(TextUtils.isEmpty(userId)) {
|
||||||
|
return '?';
|
||||||
|
} else {
|
||||||
|
return Character.toUpperCase(userId.charAt(0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onBindItemViewHolder(KeyItemViewHolder holder, Cursor cursor) {
|
protected short getSectionHeaderViewType(int sectionIndex) {
|
||||||
boolean isSecret = cursor.getInt(INDEX_HAS_ANY_SECRET) != 0;
|
return (sectionIndex < 1) ?
|
||||||
long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID);
|
VIEW_SECTION_TYPE_PRIVATE :
|
||||||
if (isSecret && masterKeyId == 0L) {
|
VIEW_SECTION_TYPE_PUBLIC;
|
||||||
holder.bindDummy();
|
}
|
||||||
|
|
||||||
|
@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 {
|
} 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);
|
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 static class KeyDummyViewHolder extends SectionCursorAdapter.ViewHolder
|
||||||
private View mLayoutDummy;
|
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 View mLayoutData;
|
||||||
private Long mMasterKeyId;
|
private Long mMasterKeyId;
|
||||||
private TextView mMainUserId;
|
private TextView mMainUserId;
|
||||||
@@ -130,22 +231,23 @@ public class KeySectionedListAdapter extends SectionCursorAdapter<String, KeySec
|
|||||||
public KeyItemViewHolder(View itemView) {
|
public KeyItemViewHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
|
|
||||||
|
itemView.setOnClickListener(this);
|
||||||
|
itemView.setOnLongClickListener(this);
|
||||||
|
|
||||||
mLayoutData = itemView.findViewById(R.id.key_list_item_data);
|
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);
|
mMainUserId = (TextView) itemView.findViewById(R.id.key_list_item_name);
|
||||||
mMainUserIdRest = (TextView) itemView.findViewById(R.id.key_list_item_email);
|
mMainUserIdRest = (TextView) itemView.findViewById(R.id.key_list_item_email);
|
||||||
mStatus = (ImageView) itemView.findViewById(R.id.key_list_item_status_icon);
|
mStatus = (ImageView) itemView.findViewById(R.id.key_list_item_status_icon);
|
||||||
mSlinger = itemView.findViewById(R.id.key_list_item_slinger_view);
|
mSlinger = itemView.findViewById(R.id.key_list_item_slinger_view);
|
||||||
mSlingerButton = (ImageButton) itemView.findViewById(R.id.key_list_item_slinger_button);
|
mSlingerButton = (ImageButton) itemView.findViewById(R.id.key_list_item_slinger_button);
|
||||||
mCreationDate = (TextView) itemView.findViewById(R.id.key_list_item_creation);
|
mCreationDate = (TextView) itemView.findViewById(R.id.key_list_item_creation);
|
||||||
|
|
||||||
|
mSlingerButton.setOnClickListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bindKey(KeyItem keyItem, Highlighter highlighter) {
|
public void bindKey(KeyItem keyItem, Highlighter highlighter) {
|
||||||
Context ctx = itemView.getContext();
|
Context ctx = itemView.getContext();
|
||||||
|
|
||||||
mLayoutData.setVisibility(View.VISIBLE);
|
|
||||||
mLayoutDummy.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
{ // set name and stuff, common to both key types
|
{ // set name and stuff, common to both key types
|
||||||
OpenPgpUtils.UserId userIdSplit = keyItem.mUserId;
|
OpenPgpUtils.UserId userIdSplit = keyItem.mUserId;
|
||||||
if (userIdSplit.name != null) {
|
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);
|
.setStatusImage(ctx, mStatus, null, KeyFormattingUtils.State.REVOKED, R.color.key_flag_gray);
|
||||||
mStatus.setVisibility(View.VISIBLE);
|
mStatus.setVisibility(View.VISIBLE);
|
||||||
mSlinger.setVisibility(View.GONE);
|
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) {
|
} else if (keyItem.mIsExpired) {
|
||||||
KeyFormattingUtils.setStatusImage(ctx, mStatus, null, KeyFormattingUtils.State.EXPIRED, R.color.key_flag_gray);
|
KeyFormattingUtils.setStatusImage(ctx, mStatus, null, KeyFormattingUtils.State.EXPIRED, R.color.key_flag_gray);
|
||||||
mStatus.setVisibility(View.VISIBLE);
|
mStatus.setVisibility(View.VISIBLE);
|
||||||
mSlinger.setVisibility(View.GONE);
|
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) {
|
} else if (keyItem.mIsSecret) {
|
||||||
mStatus.setVisibility(View.GONE);
|
mStatus.setVisibility(View.GONE);
|
||||||
if (mSlingerButton.hasOnClickListeners()) {
|
if (mSlingerButton.hasOnClickListeners()) {
|
||||||
@@ -228,41 +330,27 @@ public class KeySectionedListAdapter extends SectionCursorAdapter<String, KeySec
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bindDummy() {
|
@Override
|
||||||
// just reset everything to display the dummy layout
|
public void onClick(View v) {
|
||||||
mLayoutDummy.setVisibility(View.VISIBLE);
|
|
||||||
mLayoutData.setVisibility(View.GONE);
|
}
|
||||||
mSlinger.setVisibility(View.GONE);
|
|
||||||
mStatus.setVisibility(View.GONE);
|
@Override
|
||||||
itemView.setClickable(false);
|
public boolean onLongClick(View v) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class KeyHeaderViewHolder extends RecyclerView.ViewHolder {
|
static class KeyHeaderViewHolder extends SectionCursorAdapter.ViewHolder {
|
||||||
private TextView mHeaderText;
|
private TextView mText1;
|
||||||
private TextView mHeaderCount;
|
|
||||||
|
|
||||||
public KeyHeaderViewHolder(View itemView) {
|
public KeyHeaderViewHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
|
mText1 = (TextView) itemView.findViewById(android.R.id.text1);
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bind(String title) {
|
public void bind(String title) {
|
||||||
mHeaderText.setText(title);
|
mText1.setText(title);
|
||||||
mHeaderCount.setVisibility(View.GONE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,14 +49,14 @@ public interface KeyAdapter {
|
|||||||
boolean isSecretAvailable(int position);
|
boolean isSecretAvailable(int position);
|
||||||
|
|
||||||
class KeyItem implements Serializable {
|
class KeyItem implements Serializable {
|
||||||
final String mUserIdFull;
|
public final String mUserIdFull;
|
||||||
final OpenPgpUtils.UserId mUserId;
|
public final OpenPgpUtils.UserId mUserId;
|
||||||
final long mKeyId;
|
public final long mKeyId;
|
||||||
final boolean mHasDuplicate;
|
public final boolean mHasDuplicate;
|
||||||
final boolean mHasEncrypt;
|
public final boolean mHasEncrypt;
|
||||||
final Date mCreation;
|
public final Date mCreation;
|
||||||
final String mFingerprint;
|
public final String mFingerprint;
|
||||||
final boolean mIsSecret, mIsRevoked, mIsExpired, mIsVerified;
|
public final boolean mIsSecret, mIsRevoked, mIsExpired, mIsVerified;
|
||||||
|
|
||||||
public KeyItem(Cursor cursor) {
|
public KeyItem(Cursor cursor) {
|
||||||
String userId = cursor.getString(INDEX_USER_ID);
|
String userId = cursor.getString(INDEX_USER_ID);
|
||||||
|
|||||||
@@ -4,35 +4,26 @@ import android.content.Context;
|
|||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.support.v4.util.SparseArrayCompat;
|
import android.support.v4.util.SparseArrayCompat;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.SectionIndexer;
|
|
||||||
|
|
||||||
import com.tonicartos.superslim.GridSLM;
|
|
||||||
import com.tonicartos.superslim.LayoutManager;
|
import com.tonicartos.superslim.LayoutManager;
|
||||||
import com.tonicartos.superslim.LinearSLM;
|
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
import java.lang.annotation.Inherited;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
/**
|
/**
|
||||||
* @param <T> section type.
|
* @param <T> section type.
|
||||||
* @param <VH> the view holder extending {@code BaseViewHolder<Cursor>} that is bound to the cursor data.
|
* @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.
|
* @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>
|
public abstract class SectionCursorAdapter<T, VH extends SectionCursorAdapter.ViewHolder,
|
||||||
extends CursorAdapter implements SectionIndexer {
|
SH extends SectionCursorAdapter.ViewHolder> extends CursorAdapter {
|
||||||
|
|
||||||
public static final String TAG = "SectionCursorAdapter";
|
public static final String TAG = "SectionCursorAdapter";
|
||||||
|
|
||||||
private static final int VIEW_TYPE_ITEM = 0x0;
|
private static final short VIEW_TYPE_ITEM = 0x1;
|
||||||
private static final int VIEW_TYPE_SECTION = 0x1;
|
private static final short VIEW_TYPE_SECTION = 0x2;
|
||||||
|
|
||||||
private SparseArrayCompat<T> mSectionMap = new SparseArrayCompat<>();
|
private SparseArrayCompat<T> mSectionMap = new SparseArrayCompat<>();
|
||||||
private ArrayList<Integer> mSectionIndexList = new ArrayList<>();
|
|
||||||
private Comparator<T> mSectionComparator;
|
private Comparator<T> mSectionComparator;
|
||||||
private Object[] mFastScrollItems;
|
|
||||||
|
|
||||||
public SectionCursorAdapter(Context context, Cursor cursor, int flags) {
|
public SectionCursorAdapter(Context context, Cursor cursor, int flags) {
|
||||||
this(context, cursor, flags, new Comparator<T>() {
|
this(context, cursor, flags, new Comparator<T>() {
|
||||||
@@ -55,8 +46,6 @@ public abstract class SectionCursorAdapter<T, VH extends RecyclerView.ViewHolder
|
|||||||
buildSections();
|
buildSections();
|
||||||
} else {
|
} else {
|
||||||
mSectionMap.clear();
|
mSectionMap.clear();
|
||||||
mSectionIndexList.clear();
|
|
||||||
mFastScrollItems = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onContentChanged();
|
super.onContentChanged();
|
||||||
@@ -81,8 +70,6 @@ public abstract class SectionCursorAdapter<T, VH extends RecyclerView.ViewHolder
|
|||||||
moveCursor(-1);
|
moveCursor(-1);
|
||||||
try {
|
try {
|
||||||
mSectionMap.clear();
|
mSectionMap.clear();
|
||||||
mSectionIndexList.clear();
|
|
||||||
mFastScrollItems = null;
|
|
||||||
|
|
||||||
appendSections(getCursor());
|
appendSections(getCursor());
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
@@ -91,13 +78,11 @@ public abstract class SectionCursorAdapter<T, VH extends RecyclerView.ViewHolder
|
|||||||
swapCursor(null);
|
swapCursor(null);
|
||||||
|
|
||||||
mSectionMap.clear();
|
mSectionMap.clear();
|
||||||
mSectionIndexList.clear();
|
|
||||||
mFastScrollItems = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void appendSections(Cursor cursor) throws IllegalStateException {
|
private void appendSections(Cursor cursor) throws IllegalStateException {
|
||||||
int cursorPosition = 0;
|
int cursorPosition = 0;
|
||||||
while(hasValidData() && cursor.moveToNext()) {
|
while(hasValidData() && cursor.moveToNext()) {
|
||||||
T section = getSectionFromCursor(cursor);
|
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.
|
* This object will be passed to newSectionView and bindSectionView.
|
||||||
*/
|
*/
|
||||||
protected abstract T getSectionFromCursor(Cursor cursor) throws IllegalStateException;
|
protected abstract T getSectionFromCursor(Cursor cursor) throws IllegalStateException;
|
||||||
protected String getTitleFromSection(T section) {
|
|
||||||
return section != null ? section.toString() : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
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
|
* 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.
|
* 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;
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public int getSectionForPosition(int listPosition) {
|
public int getSectionForPosition(int listPosition) {
|
||||||
boolean isSection = false;
|
boolean isSection = false;
|
||||||
int numPrecedingSections = 0;
|
int numPrecedingSections = 0;
|
||||||
@@ -264,27 +192,6 @@ public abstract class SectionCursorAdapter<T, VH extends RecyclerView.ViewHolder
|
|||||||
return isSection ? numPrecedingSections : Math.max(numPrecedingSections - 1, 0);
|
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) {
|
private boolean isListPositionBeforeFirstSection(int listPosition, int sectionIndex) {
|
||||||
boolean hasSections = mSectionMap != null && mSectionMap.size() > 0;
|
boolean hasSections = mSectionMap != null && mSectionMap.size() > 0;
|
||||||
return sectionIndex == 0 && hasSections && listPosition < mSectionMap.keyAt(0);
|
return sectionIndex == 0 && hasSections && listPosition < mSectionMap.keyAt(0);
|
||||||
@@ -292,7 +199,21 @@ public abstract class SectionCursorAdapter<T, VH extends RecyclerView.ViewHolder
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int getItemViewType(int listPosition) {
|
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
|
@Override
|
||||||
@@ -304,7 +225,8 @@ public abstract class SectionCursorAdapter<T, VH extends RecyclerView.ViewHolder
|
|||||||
// assign first position of section to each item
|
// assign first position of section to each item
|
||||||
layoutParams.setFirstPosition(getFirstSectionPosition(position));
|
layoutParams.setFirstPosition(getFirstSectionPosition(position));
|
||||||
|
|
||||||
switch (holder.getItemViewType()) {
|
int viewType = holder.getItemViewType() & 0xFF;
|
||||||
|
switch (viewType) {
|
||||||
case VIEW_TYPE_ITEM :
|
case VIEW_TYPE_ITEM :
|
||||||
moveCursorOrThrow(getCursorPositionWithoutSections(position));
|
moveCursorOrThrow(getCursorPositionWithoutSections(position));
|
||||||
onBindItemViewHolder((VH) holder, getCursor());
|
onBindItemViewHolder((VH) holder, getCursor());
|
||||||
@@ -314,8 +236,7 @@ public abstract class SectionCursorAdapter<T, VH extends RecyclerView.ViewHolder
|
|||||||
|
|
||||||
case VIEW_TYPE_SECTION:
|
case VIEW_TYPE_SECTION:
|
||||||
T section = mSectionMap.get(position);
|
T section = mSectionMap.get(position);
|
||||||
int sectionIndex = getSectionIndexForPosition(position);
|
onBindSectionViewHolder((SH) holder, section);
|
||||||
onBindSectionViewHolder((SH) holder, sectionIndex, section);
|
|
||||||
|
|
||||||
layoutParams.isHeader = true;
|
layoutParams.isHeader = true;
|
||||||
break;
|
break;
|
||||||
@@ -326,26 +247,45 @@ public abstract class SectionCursorAdapter<T, VH extends RecyclerView.ViewHolder
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
switch (viewType) {
|
switch (viewType & 0xFF) {
|
||||||
case VIEW_TYPE_SECTION:
|
case VIEW_TYPE_SECTION:
|
||||||
return onCreateSectionViewHolder(parent);
|
return onCreateSectionViewHolder(parent, viewType >> 16);
|
||||||
|
|
||||||
case VIEW_TYPE_ITEM:
|
case VIEW_TYPE_ITEM:
|
||||||
return onCreateItemViewHolder(parent, viewType);
|
return onCreateItemViewHolder(parent, viewType >> 16);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return null;
|
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 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);
|
protected abstract void onBindItemViewHolder(VH holder, Cursor cursor);
|
||||||
|
|
||||||
public interface Comparator<T> {
|
public interface Comparator<T> {
|
||||||
boolean equal(T obj1, T obj2);
|
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"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:fab="http://schemas.android.com/apk/res-auto"
|
<FrameLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:fab="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
xmlns:custom="http://schemas.android.com/apk/res-auto"
|
xmlns:custom="http://schemas.android.com/apk/res-auto"
|
||||||
>
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
|
||||||
<!--rebuild functionality of ListFragment -->
|
android:layout_width="match_parent"
|
||||||
<FrameLayout
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@android:id/progress"
|
||||||
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
<android.support.v7.widget.RecyclerView
|
<ProgressBar
|
||||||
android:id="@+id/key_list_list"
|
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_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:paddingLeft="16dp"
|
|
||||||
android:paddingRight="32dp"
|
|
||||||
android:scrollbarStyle="outsideOverlay" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<android.support.v7.widget.RecyclerView
|
||||||
android:id="@+id/key_list_empty"
|
android:id="@android:id/list"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="240dp"
|
android:layout_height="match_parent"
|
||||||
android:gravity="center"
|
android:paddingLeft="16dp"
|
||||||
android:orientation="vertical"
|
android:paddingRight="32dp"
|
||||||
android:animateLayoutChanges="true"
|
android:clipToPadding="false"
|
||||||
>
|
android:scrollbarStyle="outsideOverlay" />
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:id="@android:id/empty"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="240dp"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:text="@string/key_list_empty_text1"
|
android:orientation="vertical"
|
||||||
android:textAppearance="?android:attr/textAppearanceLarge" />
|
android:animateLayoutChanges="true" >
|
||||||
|
|
||||||
<org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
|
<TextView
|
||||||
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
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:id="@+id/search_button"
|
|
||||||
android:gravity="center"
|
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>
|
<Space
|
||||||
</FrameLayout>
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
<com.getbase.floatingactionbutton.FloatingActionsMenu
|
<Button
|
||||||
android:id="@+id/fab_main"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_marginTop="24dp"
|
||||||
android:layout_alignParentRight="true"
|
android:id="@+id/search_button"
|
||||||
android:layout_alignParentEnd="true"
|
android:gravity="center"
|
||||||
android:layout_alignParentBottom="true"
|
tools:text="@string/btn_search_for_query"/>
|
||||||
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
|
</org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
|
||||||
android:id="@+id/fab_add_qr_code"
|
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<com.getbase.floatingactionbutton.FloatingActionsMenu
|
||||||
|
android:id="@+id/fab_main"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
fab:fab_icon="@drawable/ic_qrcode_white_24dp"
|
android:layout_alignParentRight="true"
|
||||||
fab:fab_colorNormal="?attr/colorPrimary"
|
android:layout_alignParentEnd="true"
|
||||||
fab:fab_colorPressed="?attr/colorPrimaryDark"
|
android:layout_alignParentBottom="true"
|
||||||
fab:fab_title="@string/key_list_fab_qr_code"
|
fab:fab_addButtonColorNormal="?attr/colorPrimary"
|
||||||
fab:fab_size="mini" />
|
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
|
<com.getbase.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/fab_add_cloud"
|
android:id="@+id/fab_add_qr_code"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
fab:fab_icon="@drawable/ic_cloud_search_24dp"
|
fab:fab_icon="@drawable/ic_qrcode_white_24dp"
|
||||||
fab:fab_colorNormal="?attr/colorPrimary"
|
fab:fab_colorNormal="?attr/colorPrimary"
|
||||||
fab:fab_colorPressed="?attr/colorPrimaryDark"
|
fab:fab_colorPressed="?attr/colorPrimaryDark"
|
||||||
fab:fab_title="@string/key_list_fab_search"
|
fab:fab_title="@string/key_list_fab_qr_code"
|
||||||
fab:fab_size="mini" />
|
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"
|
super:slm_section_sectionManager="linear"
|
||||||
tools:ignore="ResAuto">
|
tools:ignore="ResAuto">
|
||||||
|
|
||||||
<TextView
|
<TextView style="@style/SectionHeader"
|
||||||
style="@style/SectionHeader"
|
|
||||||
android:id="@+id/stickylist_header_text"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="start|left"
|
android:layout_gravity="start|left"
|
||||||
android:text="header text" />
|
android:text="@string/my_keys" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
android:id="@android:id/text1"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:text="key count"
|
|
||||||
android:id="@+id/contacts_num"
|
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:layout_alignParentRight="true"
|
android:layout_alignParentRight="true"
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_marginRight="8dp"
|
android:layout_marginRight="8dp"
|
||||||
android:visibility="gone"
|
android:layout_marginLeft="8dp"
|
||||||
android:textColor="@android:color/darker_gray" />
|
android:textColor="@android:color/darker_gray"
|
||||||
|
tools:text="11 Keys"/>
|
||||||
|
|
||||||
</RelativeLayout>
|
</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"?>
|
<?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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:singleLine="true"
|
android:maxLines="1"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:descendantFocusability="blocksDescendants"
|
android:descendantFocusability="blocksDescendants"
|
||||||
android:focusable="false">
|
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
|
<LinearLayout
|
||||||
android:id="@+id/key_list_item_data"
|
android:id="@+id/key_list_item_data"
|
||||||
android:layout_width="0dip"
|
android:layout_width="0dip"
|
||||||
@@ -83,7 +37,7 @@
|
|||||||
android:id="@+id/key_list_item_email"
|
android:id="@+id/key_list_item_email"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:singleLine="true"
|
android:maxLines="1"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
tools:text="user@example.com"
|
tools:text="user@example.com"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
@@ -92,12 +46,11 @@
|
|||||||
android:id="@+id/key_list_item_creation"
|
android:id="@+id/key_list_item_creation"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:singleLine="true"
|
android:maxLines="1"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible"
|
tools:visibility="visible"
|
||||||
tools:text="Created on 10/10/2010 10:00"
|
tools:text="Created on 10/10/2010 10:00" />
|
||||||
/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@@ -134,7 +87,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
tools:src="@drawable/status_signature_revoked_cutout_24dp"
|
tools:src="@drawable/status_signature_revoked_cutout_24dp" />
|
||||||
/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
Reference in New Issue
Block a user