0
0
mirror of https://github.com/ankidroid/Anki-Android.git synced 2024-09-19 19:42:17 +02:00

RecyclerView for DeckPicker

Replaced ListView with a RecyclerView in DeckPicker. This also replaces the anonymous SimpleAdapter with a custom RecyclerView.Adapter. Our UI is a simple list, which does not require any custom styling depending on whether the item is a deck or subdeck. The items have click & long click actions that function the same as before. There is also a new method inside DeckPicker for finding a deck node given a deck ID.
This commit is contained in:
Allison Van Pelt 2015-03-29 22:51:55 -07:00
parent d3b0ff05b1
commit 2e3665c0bf
9 changed files with 479 additions and 268 deletions

4
.gitignore vendored
View File

@ -52,3 +52,7 @@ Thumbs.db
# Crowdin files
ankidroid.zip
tools/crowdin_key.txt
.idea/misc.xml
.idea/vcs.xml

View File

@ -33,6 +33,7 @@ android {
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.0.0'
compile 'com.android.support:recyclerview-v7:22.0.0'
compile 'com.google.code.gson:gson:2.3'
compile 'com.afollestad:material-dialogs:0.6.6.2'
compile 'ch.acra:acra:4.6.1'

View File

@ -41,22 +41,20 @@ import android.os.Message;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import com.afollestad.materialdialogs.MaterialDialog;
@ -98,7 +96,6 @@ import org.json.JSONObject;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
@ -150,9 +147,11 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
private int mSyncMediaUsn = 0;
private SimpleAdapter mDeckListAdapter;
private ArrayList<HashMap<String, String>> mDeckList;
private ListView mDeckListView;
private RecyclerView mRecyclerView;
private LinearLayoutManager mRecyclerViewLayoutManager;
private RecyclerView.Adapter mDeckListAdapter;
private List<Sched.DeckDueTreeNode> mDeckList;
private TextView mTodayTextView;
private ImageButton mAddButton;
@ -165,7 +164,7 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
int mStatisticType;
boolean mCompletionBarRestrictToActive = false; // set this to true in order to calculate completion bar only for
// active cards
// active cards
boolean mShowShowcaseView = false;
// flag asking user to do a full sync which is used in upgrade path
boolean mRecommendFullSync = false;
@ -181,14 +180,33 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
// LISTENERS
// ----------------------------------------------------------------------------
private AdapterView.OnItemClickListener mDeckSelHandler = new AdapterView.OnItemClickListener() {
private final OnClickListener mDeckClickListener = new OnClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View v, int p, long id) {
Timber.i("DeckPicker:: Selected deck in position %d", p);
handleDeckSelection(p);
public void onClick(View v) {
long deckId = (long) v.getTag();
Timber.i("DeckPicker:: Selected deck with id %d", deckId);
handleDeckSelection(deckId);
}
};
private final View.OnLongClickListener mDeckLongClickListener = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
long deckId = (long) v.getTag();
Timber.i("DeckPicker:: Long tapped on deck with id %d", deckId);
if (mDeckList == null || mDeckList.size() == 0) {
return true;
}
mContextMenuDid = deckId;
String deckName = getCol().getDecks().name(mContextMenuDid);
boolean hasSubdecks = getCol().getDecks().children(mContextMenuDid).size() > 0;
boolean isCollapsed = getCol().getDecks().get(mContextMenuDid).
optBoolean("collapsed", false);
showDialogFragment(DeckPickerContextMenu.newInstance(deckName, hasSubdecks,
isCollapsed));
return true;
}
};
DeckTask.TaskListener mImportAddListener = new DeckTask.TaskListener() {
@SuppressWarnings("unchecked")
@ -377,62 +395,18 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
setTitle(getResources().getString(R.string.app_name));
mDeckList = new ArrayList<HashMap<String, String>>();
mDeckListView = (ListView) findViewById(R.id.files);
mDeckListAdapter = new SimpleAdapter(this, mDeckList, R.layout.deck_item, new String[] { "name", "new", "lrn",
"rev", "sep", "dyn" }, new int[] { R.id.DeckPickerName, R.id.deckpicker_new, R.id.deckpicker_lrn,
R.id.deckpicker_rev, R.id.deckpicker_deck, R.id.DeckPickerName });
mDeckListAdapter.setViewBinder(new SimpleAdapter.ViewBinder() {
@Override
public boolean setViewValue(View view, Object data, String text) {
if (view.getId() == R.id.deckpicker_deck) {
return true;
} else if (view.getId() == R.id.DeckPickerName) {
if (text.equals("d0")) {
((TextView) view).setTextColor(getResources().getColor(R.color.non_dyn_deck));
return true;
} else if (text.equals("d1")) {
((TextView) view).setTextColor(getResources().getColor(R.color.dyn_deck));
return true;
}
} else if (view.getId() == R.id.deckpicker_new) {
// Set the right color, light gray or blue.
((TextView) view).setTextColor((text.equals("0")) ? getResources().getColor(R.color.zero_count)
: getResources().getColor(R.color.new_count));
return false; // Let SimpleAdapter take care of binding the number to the TextView.
} else if (view.getId() == R.id.deckpicker_lrn) {
// ... or red.
((TextView) view).setTextColor((text.equals("0")) ? getResources().getColor(R.color.zero_count)
: getResources().getColor(R.color.learn_count));
return false;
} else if (view.getId() == R.id.deckpicker_rev) {
// ... or green.
((TextView) view).setTextColor((text.equals("0")) ? getResources().getColor(R.color.zero_count)
: getResources().getColor(R.color.review_count));
return false;
}
return false;
}
});
mDeckListView.setOnItemClickListener(mDeckSelHandler);
mDeckListView.setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int position, long id) {
Timber.i("DeckPicker:: Long tapped on deck in position %d", position);
if (mDeckList == null || mDeckList.size() == 0) {
return true;
}
mContextMenuDid = Long.parseLong(mDeckList.get(position).get("did"));
String deckName = getCol().getDecks().name(mContextMenuDid);
boolean hasSubdecks = getCol().getDecks().children(mContextMenuDid).size() > 0;
boolean isCollapsed = getCol().getDecks().get(mContextMenuDid).
optBoolean("collapsed", false);
showDialogFragment(DeckPickerContextMenu.newInstance(deckName, hasSubdecks,
isCollapsed));
return true;
}
});
mDeckListView.setAdapter(mDeckListAdapter);
mDeckList = new ArrayList<>();
mRecyclerView = (RecyclerView) findViewById(R.id.files);
mRecyclerView.addItemDecoration(new DividerItemDecoration(this, null, false, true));
// specify a LinearLayoutManager for the RecyclerView
mRecyclerViewLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mRecyclerViewLayoutManager);
// create and set an adapter for the RecyclerView
mDeckListAdapter = new DeckAdapter(mDeckList);
mRecyclerView.setAdapter(mDeckListAdapter);
mTodayTextView = (TextView) findViewById(R.id.today_stats_text_view);
final Resources res = getResources();
mAddButton = (ImageButton) findViewById(R.id.fab);
@ -441,12 +415,12 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
public void onClick(View v) {
new MaterialDialog.Builder(DeckPicker.this)
.items(new String[] {res.getString(R.string.menu_add_note),
res.getString(R.string.menu_get_shared_decks),
res.getString(R.string.new_deck)})
res.getString(R.string.menu_get_shared_decks),
res.getString(R.string.new_deck)})
.itemsCallback(new MaterialDialog.ListCallback() {
@Override
public void onSelection(MaterialDialog materialDialog, View view, int i,
CharSequence charSequence) {
CharSequence charSequence) {
switch(i) {
case 0:
hideShowcaseView();
@ -484,8 +458,7 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
.build().show();
}
});
mDeckListView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
mTodayTextView = (TextView) findViewById(R.id.today_stats_text_view);
// Show splash screen and load collection
showStartupScreensAndDialogs(preferences, 0);
}
@ -502,7 +475,7 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
menu.findItem(R.id.action_check_media).setEnabled(sdCardAvailable);
// Show the welcome screen here if col empty to be sure that the action bar exists
if (mShowShowcaseView && colIsOpen() && getCol().isEmpty() && mDeckList!= null && mDeckList.size() <=1) {
if (mShowShowcaseView && colIsOpen() && getCol().isEmpty() && mDeckList != null && mDeckList.size() <=1) {
mShowShowcaseView = false;
final Resources res = getResources();
try {
@ -803,7 +776,7 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
Message handlerMessage = Message.obtain();
handlerMessage.what = DialogHandler.MSG_SHOW_FORCE_FULL_SYNC_DIALOG;
Bundle handlerMessageData = new Bundle();
handlerMessageData.putString("message", res.getString(R.string.full_sync_confirmation_upgrade) +
handlerMessageData.putString("message", res.getString(R.string.full_sync_confirmation_upgrade) +
"\n\n" + res.getString(R.string.full_sync_confirmation));
handlerMessage.setData(handlerMessageData);
getDialogHandler().sendMessage(handlerMessage);
@ -1011,7 +984,7 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
showAsyncDialogFragment(newFragment);
}
/**
/**
* Show log message after sync, using "Sync Error" as the dialog title, and reload activity
* @param message
*/
@ -1020,7 +993,7 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
showSyncLogDialog(message, true);
}
/**
/**
* Show log message after sync, and reload activity
* @param message
* @param error Show "Sync Error" as dialog title if this flag is set, otherwise use no title
@ -1249,7 +1222,7 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
* The mother of all syncing attempts. This might be called from sync() as first attempt to sync a collection OR
* from the mSyncConflictResolutionListener if the first attempt determines that a full-sync is required. In the
* second case, we have passed the mediaUsn that was obtained during the first attempt.
*
*
* @param syncConflictResolution Either "upload" or "download", depending on the user's choice.
* @param syncMediaUsn The media Usn, as determined during the prior sync() attempt that determined that full
* syncing was required.
@ -1649,10 +1622,7 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
}
private void handleDeckSelection(int id) {
@SuppressWarnings("unchecked")
HashMap<String, String> data = (HashMap<String, String>) mDeckListAdapter.getItem(id);
long did = Long.parseLong(data.get("did"));
private void handleDeckSelection(long did) {
getCol().getDecks().select(did);
mFocusedDeck = did;
openStudyOptions(false);
@ -1676,14 +1646,67 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
/**
* Scroll the deck list so that it is centered on the current deck.
*
* @param did The deck ID of the deck to select.
*/
private void focusDecklistOnDeck(long did) {
for (int i = 0; i < mDeckList.size(); i++) {
if (Long.parseLong(mDeckList.get(i).get("did")) == did) {
mDeckListView.setSelectionFromTop(i, (mDeckListView.getHeight() / 2));
break;
private void scrollDecklistToDeck(long did) {
int position = findDeckPositionWithId(mDeckList, did, 0);
if (position > -1) {
mRecyclerViewLayoutManager.scrollToPositionWithOffset(position, (mRecyclerView.getHeight() / 2));
}
}
/**
* Given a list of {@link Sched.DeckDueTreeNode} objects, find the list position matching the id.
*
* @param nodes The list of {@link Sched.DeckDueTreeNode} objects
* @param did The deck id to find
* @param position The current deck's position in the list
*
* @return The position, if found, and -1 otherwise.
*/
private int findDeckPositionWithId(List<Sched.DeckDueTreeNode> nodes, long did, int position) {
for (int i=0; i<nodes.size(); i++) {
// return the position if the deck ids match
if (nodes.get(i).did == did) {
// if we're at depth zero, return this loop's value
// otherwise return the position (the cardview we want)
return (nodes.get(i).depth == 0) ? i : position;
}
// keep searching through this node's children
if (nodes.get(i).children.size() > 0) {
// if we find the deck id, return this child's parent position
int childPosition = findDeckPositionWithId(nodes.get(i).children, did, (nodes.get(i).depth == 0) ? i : position);
if (childPosition > -1) {
return childPosition;
}
}
}
return -1;
}
/**
* Given a list of {@link Sched.DeckDueTreeNode} objects, find the node matching the deck id.
*
* @param nodes The list of {@link Sched.DeckDueTreeNode} objects
* @param did The deck id to find
*
* @return The {@link Sched.DeckDueTreeNode}, if found, and null otherwise.
*/
private Sched.DeckDueTreeNode findDeckWithId(List<Sched.DeckDueTreeNode> nodes, long did) {
for (int i=0; i<nodes.size(); i++) {
if (nodes.get(i).did == did) {
return nodes.get(i);
}
if (nodes.get(i).children.size() > 0) {
return findDeckWithId(nodes.get(i).children, did);
}
}
return null;
}
@ -1732,9 +1755,9 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
Long current = getCol().getDecks().current().optLong("id");
if (!deckIsShown(nodes, current)) {
// clear selection if deck is hidden
mDeckListView.clearChoices();
// mDeckListView.clearChoices(); // TODO AVP
} else if (mFocusedDeck == null || !mFocusedDeck.equals(current)) {
focusDecklistOnDeck(current);
scrollDecklistToDeck(current);
mFocusedDeck = current;
}
@ -1762,7 +1785,17 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
private void _renderDeckTree(List<Sched.DeckDueTreeNode> nodes, int depth) {
for (Sched.DeckDueTreeNode node : nodes) {
_deckRow(node, depth, nodes.size());
// if this node's parent is collapsed, don't add it to the deck list
for (JSONObject parent : getCol().getDecks().parents(node.did)) {
if (parent.optBoolean("collapsed")) {
return;
}
}
// add node to deck
mDeckList.add(node);
_deckRow(node, depth);
}
}
@ -1778,20 +1811,11 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
*
* NOTE: Each row in the list also contains the deck ID (did) which is later fetched
* to perform operations on the correct deck when the user interacts with the list.
*
* @param node The row data.
* @param node The row data.
* @param depth The subdeck level this node is at.
* @param cnt The number of decks in the collection.
*/
private void _deckRow(Sched.DeckDueTreeNode node, int depth, int cnt) {
HashMap<String, String> m = new HashMap<String, String>();
boolean collapsed = getCol().getDecks().get(node.did).optBoolean("collapsed", false);
m.put("name", decoratedDeckName(node.names[0], depth, collapsed, node.children.size()));
m.put("did", Long.toString(node.did));
m.put("new", Integer.toString(node.newCount));
m.put("lrn", Integer.toString(node.lrnCount));
m.put("rev", Integer.toString(node.revCount));
m.put("dyn", getCol().getDecks().isDyn(node.did) ? "d1" : "d0");
private void _deckRow(Sched.DeckDueTreeNode node, int depth) {
node.depth = depth;
// Add this node's counts to the totals if it's a parent deck
if (depth == 0) {
@ -1800,76 +1824,39 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
nRev += node.revCount;
}
// If the default deck is empty, hide it
// We don't hide it if it's the only deck or if it has sub-decks
if (node.did == 1 && cnt > 1 && node.children.size() == 0) {
if (getCol().getDb().queryScalar("select 1 from cards where did = 1", false) == 0) {
return;
}
}
// Add the appropriate separators
if (depth == 0 && node.children.size() > 0 && !collapsed) {
// Top-level decks with expanded subdecks have only a top border
m.put("sep", "top");
} else if (depth > 0) {
// All subdecks begin with no borders (i.e., thin centered borders)
m.put("sep", "cen");
} else {
// Every other deck gets full borders
m.put("sep", "ful");
}
// Parent toggled for collapsing
for (JSONObject parent : getCol().getDecks().parents(node.did)) {
if (parent.optBoolean("collapsed")) {
return;
}
}
// If this deck is the current deck, highlight it in the deck list
if (node.did == getCol().getDecks().current().optLong("id")) {
mDeckListView.setItemChecked(mDeckList.size(), true);
}
mDeckList.add(m);
_renderDeckTree(node.children, depth + 1);
// If the deck contained expanded subdecks, ensure the last one has a bottom border
if (depth == 0 && node.children.size() > 0 && !collapsed) {
mDeckList.get(mDeckList.size() - 1).put("sep", "bot");
}
}
/**
* Returns the name of the deck to be displayed in the deck list.
*
* Various properties of a deck are indicated to the user by the deck name in the deck list
* (as opposed to additional native UI elements). This includes the amount of indenting
* for nested decks based on depth and an indicator of collapsed state.
*/
private String decoratedDeckName(String name, int depth, boolean collapsed, int children) {
if (collapsed) {
// add arrow pointing right if collapsed
name = "\u25B7 " + name;
} else if (children > 0) {
// add arrow pointing down if deck has children
name = "\u25BD " + name;
} else {
// add empty spaces
name = "\u2009\u2009\u2009 " + name;
}
if (depth == 0) {
return name;
} else {
// Add 4 spaces for every level of nesting
return new String(new char[depth]).replace("\0", "\u2009\u2009\u2009 ") + name;
}
}
}, new TaskData(getCol()));
}
/**
* Returns the name of the deck to be displayed in the deck list.
* <p/>
* Various properties of a deck are indicated to the user by the deck name in the deck list
* (as opposed to additional native UI elements). This includes the amount of indenting
* for nested decks based on depth and an indicator of collapsed state.
*/
private String decoratedDeckName(String name, int depth, boolean collapsed, int children) {
if (collapsed) {
// add arrow pointing right if collapsed
name = "\u25B7 " + name;
} else if (children > 0) {
// add arrow pointing down if deck has children
name = "\u25BD " + name;
} else {
// add empty spaces
name = "\u2009\u2009\u2009 " + name;
}
if (depth == 0) {
return name;
} else {
// Add 4 spaces for every level of nesting
return new String(new char[depth]).replace("\0", "\u2009\u2009\u2009 ") + name;
}
}
// Callback to collapse currently selected deck
public void collapseContextMenuDeck() {
try {
@ -1937,12 +1924,10 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
Collection col = getCol();
if (col != null) {
if (col.getDecks().rename(col.getDecks().get(mContextMenuDid), newName)) {
for (HashMap<String, String> d : mDeckList) {
if (d.get("did").equals(Long.toString(mContextMenuDid))) {
d.put("name", newName);
}
Sched.DeckDueTreeNode node = findDeckWithId(mDeckList, mContextMenuDid);
if (node != null) {
node.names = new String[]{newName};
}
mDeckListAdapter.notifyDataSetChanged();
updateDeckList();
} else {
try {
@ -1955,7 +1940,8 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
}
}
}
dismissAllDialogFragments();
mDeckListAdapter.notifyDataSetChanged();
updateDeckList();
}
@Override
@ -2058,7 +2044,7 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
if (CompatHelper.isHoneycomb()) {
final float alpha = 1f;
mTodayTextView.setAlpha(alpha);
mDeckListView.setAlpha(alpha);
mRecyclerView.setAlpha(alpha);
}
}
@ -2075,7 +2061,7 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
if (CompatHelper.isHoneycomb()) {
final float alpha = 0.1f;
mTodayTextView.setAlpha(alpha);
mDeckListView.setAlpha(alpha);
mRecyclerView.setAlpha(alpha);
}
}
@ -2103,4 +2089,95 @@ public class DeckPicker extends NavigationDrawerActivity implements OnShowcaseEv
public void createFilteredDeck(JSONArray delays, Object[] terms, Boolean resched) {
getFragment().createFilteredDeck(delays, terms, resched);
}
}
public class DeckAdapter extends RecyclerView.Adapter<DeckAdapter.ViewHolder> {
private LayoutInflater mLayoutInflater;
private List<Sched.DeckDueTreeNode> mDeckList;
private boolean mHideDefaultDeck;
// ViewHolder class to save inflated views for recycling
public class ViewHolder extends RecyclerView.ViewHolder {
public RelativeLayout mDeckLayout;
public TextView mDeckName;
public TextView mDeckNew, mDeckLearn, mDeckRev;
public ViewHolder(View v) {
super(v);
mDeckLayout = (RelativeLayout) v.findViewById(R.id.DeckPickerHoriz);
mDeckName = (TextView) v.findViewById(R.id.DeckPickerName);
mDeckNew = (TextView) v.findViewById(R.id.deckpicker_new);
mDeckLearn = (TextView) v.findViewById(R.id.deckpicker_lrn);
mDeckRev = (TextView) v.findViewById(R.id.deckpicker_rev);
}
}
public DeckAdapter(List<Sched.DeckDueTreeNode> deckList) {
mLayoutInflater = getLayoutInflater();
mDeckList = deckList;
}
@Override
public DeckAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.deck_item, parent, false);
// return ViewHolder with new view
return new ViewHolder(v);
}
// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
// update views for this node
Sched.DeckDueTreeNode node = mDeckList.get(position);
boolean collapsed = getCol().getDecks().get(node.did).optBoolean("collapsed", false);
// set deck name
holder.mDeckName.setText(decoratedDeckName(node.names[0], node.depth, collapsed, node.children.size()));
// set deck new card count and color
holder.mDeckNew.setText(String.valueOf(node.newCount));
holder.mDeckNew.setTextColor((node.newCount == 0) ?
getResources().getColor(R.color.zero_count) : getResources().getColor(R.color.new_count));
// set deck learn card count and color
holder.mDeckLearn.setText(String.valueOf(node.lrnCount));
holder.mDeckLearn.setTextColor((node.lrnCount == 0) ?
getResources().getColor(R.color.zero_count) : getResources().getColor(R.color.learn_count));
// set deck review card count and color
holder.mDeckRev.setText(String.valueOf(node.revCount));
holder.mDeckRev.setTextColor((node.revCount == 0) ?
getResources().getColor(R.color.zero_count) : getResources().getColor(R.color.review_count));
// store deck ID in layout's tag, for easy retrieval in our click listeners
holder.mDeckLayout.setTag(node.did);
// set click listeners
holder.mDeckLayout.setOnClickListener(mDeckClickListener);
holder.mDeckLayout.setOnLongClickListener(mDeckLongClickListener);
// if this deck is the current deck, highlight it
if (node.did == getCol().getDecks().current().optLong("id")) {
// mDeckListView.setItemChecked(mOldDeckList.size(), true); // TODO
}
}
@Override
public int getItemCount() {
// TODO
// If the default deck is empty, hide it
// We don't hide it if it's the only deck or if it has sub-decks
// if (node.did == 1 && cnt > 1 && node.children.size() == 0) {
// if (getCol().getDb().queryScalar("select 1 from cards where did = 1", false) == 0) {
// mHideDefaultDeck = true;
// }
// }
return mDeckList.size();
}
}
}

View File

@ -0,0 +1,130 @@
package com.ichi2.anki;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.View;
/*
* DividerItemDecoration, from https://gist.github.com/lapastillaroja/858caf1a82791b6c1a36
*/
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private Drawable mDivider;
private boolean mShowFirstDivider = false;
private boolean mShowLastDivider = false;
public DividerItemDecoration(Context context, AttributeSet attrs) {
final TypedArray a = context.obtainStyledAttributes(attrs, new int[]{android.R.attr.listDivider});
mDivider = a.getDrawable(0);
a.recycle();
}
public DividerItemDecoration(Context context, AttributeSet attrs,
boolean showFirstDivider, boolean showLastDivider) {
this(context, attrs);
mShowFirstDivider = showFirstDivider;
mShowLastDivider = showLastDivider;
}
public DividerItemDecoration(Drawable divider) {
mDivider = divider;
}
public DividerItemDecoration(Drawable divider,
boolean showFirstDivider, boolean showLastDivider) {
this(divider);
mShowFirstDivider = showFirstDivider;
mShowLastDivider = showLastDivider;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if (mDivider == null) {
return;
}
if (parent.getChildPosition(view) < 1) {
return;
}
if (getOrientation(parent) == LinearLayoutManager.VERTICAL) {
outRect.top = mDivider.getIntrinsicHeight();
} else {
outRect.left = mDivider.getIntrinsicWidth();
}
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (mDivider == null) {
super.onDrawOver(c, parent, state);
return;
}
// Initialization needed to avoid compiler warning
int left = 0, right = 0, top = 0, bottom = 0, size;
int orientation = getOrientation(parent);
int childCount = parent.getChildCount();
if (orientation == LinearLayoutManager.VERTICAL) {
size = mDivider.getIntrinsicHeight();
left = parent.getPaddingLeft();
right = parent.getWidth() - parent.getPaddingRight();
} else { //horizontal
size = mDivider.getIntrinsicWidth();
top = parent.getPaddingTop();
bottom = parent.getHeight() - parent.getPaddingBottom();
}
for (int i = mShowFirstDivider ? 0 : 1; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
if (orientation == LinearLayoutManager.VERTICAL) {
top = child.getTop() - params.topMargin;
bottom = top + size;
} else { //horizontal
left = child.getLeft() - params.leftMargin;
right = left + size;
}
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
// show last divider
if (mShowLastDivider && childCount > 0) {
View child = parent.getChildAt(childCount - 1);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
if (orientation == LinearLayoutManager.VERTICAL) {
top = child.getBottom() + params.bottomMargin;
bottom = top + size;
} else { // horizontal
left = child.getRight() + params.rightMargin;
right = left + size;
}
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
private int getOrientation(RecyclerView parent) {
if (parent.getLayoutManager() instanceof LinearLayoutManager) {
LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager();
return layoutManager.getOrientation();
} else {
throw new IllegalStateException(
"DividerItemDecoration can only be used with a LinearLayoutManager.");
}
}
}

View File

@ -39,7 +39,7 @@ import timber.log.Timber;
public class NavigationDrawerActivity extends AnkiActivity {
/** Navigation Drawer */
protected CharSequence mTitle;
protected Boolean mFragmented = false;
@ -76,7 +76,7 @@ public class NavigationDrawerActivity extends AnkiActivity {
protected int getSelfNavDrawerItem() {
return DRAWER_INVALID;
}
// Navigation drawer initialisation
protected void initNavigationDrawer(View mainView){
// Create inherited navigation drawer layout here so that it can be used by parent class
@ -98,7 +98,7 @@ public class NavigationDrawerActivity extends AnkiActivity {
// enable ActionBar app icon to behave as action to toggle nav drawer
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
// ActionBarDrawerToggle ties together the the proper interactions
// between the sliding drawer and the action bar app icon
mDrawerToggle = new ActionBarDrawerToggle(
@ -107,7 +107,7 @@ public class NavigationDrawerActivity extends AnkiActivity {
R.drawable.ic_menu_white_24dp, /* nav drawer image to replace 'Up' caret */
R.string.drawer_open, /* "open drawer" description for accessibility */
R.string.drawer_close /* "close drawer" description for accessibility */
) {
) {
public void onDrawerClosed(View view) {
getSupportActionBar().setTitle(mTitle);
supportInvalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
@ -231,17 +231,17 @@ public class NavigationDrawerActivity extends AnkiActivity {
mOldColPath = CollectionHelper.getCurrentAnkiDroidDirectory(this);
startActivityForResultWithAnimation(new Intent(this, Preferences.class), REQUEST_PREFERENCES_UPDATE, ActivityTransitionAnimation.FADE);
break;
case DRAWER_HELP:
Intent helpIntent = new Intent("android.intent.action.VIEW", Uri.parse(AnkiDroidApp.getManualUrl()));
startActivityWithoutAnimation(helpIntent);
break;
case DRAWER_FEEDBACK:
Intent feedbackIntent = new Intent("android.intent.action.VIEW", Uri.parse(AnkiDroidApp.getFeedbackUrl()));
startActivityWithoutAnimation(feedbackIntent);
break;
default:
break;
}
@ -287,15 +287,15 @@ public class NavigationDrawerActivity extends AnkiActivity {
/* Members not related directly to navigation drawer */
@Override
protected void onDestroy() {
protected void onDestroy() {
super.onDestroy();
mNavigationImages.recycle();
}
public ActionBarDrawerToggle getDrawerToggle() {
return mDrawerToggle;
}
/**
* This function locks the navigation drawer closed in regards to swipes,
* but continues to allowed it to be opened via it's indicator button. This
@ -306,11 +306,11 @@ public class NavigationDrawerActivity extends AnkiActivity {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
}
}
/**
* This function allows swipes to open the navigation drawer. This
* function in a noop if the drawer hasn't been initialized.
*/
*/
protected void enableDrawerSwipe() {
if (mDrawerLayout != null) {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
@ -354,4 +354,4 @@ public class NavigationDrawerActivity extends AnkiActivity {
public static boolean isWholeCollection() {
return sIsWholeCollection;
}
}
}

View File

@ -52,6 +52,7 @@ import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.TreeSet;
import timber.log.Timber;
@ -2603,6 +2604,7 @@ public class Sched {
public class DeckDueTreeNode implements Comparable {
public String[] names;
public long did;
public int depth;
public int revCount;
public int lrnCount;
public int newCount;
@ -2636,8 +2638,8 @@ public class Sched {
@Override
public String toString() {
return String.format("%s, %d, %d, %d, %d, %s",
Arrays.toString(names), did, revCount, lrnCount, newCount, children);
return String.format("%s, %d, %d, %d, %d, %d, %s",
Arrays.toString(names), did, depth, revCount, lrnCount, newCount, children);
}
}
}

View File

@ -189,18 +189,19 @@ public class Themes {
break;
case CALLER_DECKPICKER:
ListView lv = (ListView) view.findViewById(R.id.files);
switch (mCurrentTheme) {
case THEME_WHITE:
lv.setSelector(R.drawable.white_deckpicker_list_selector);
CompatHelper.getCompat().setOverScrollModeNever(lv);
lv.setFadingEdgeLength(60);
lv.getDivider().setAlpha(ALPHA_DIVIDER);
//lv.setVerticalFadingEdgeEnabled(true);
break;
default:
break;
}
// TODO
// ListView lv = (ListView) view.findViewById(R.id.files);
// switch (mCurrentTheme) {
// case THEME_WHITE:
// lv.setSelector(R.drawable.white_deckpicker_list_selector);
// CompatHelper.getCompat().setOverScrollModeNever(lv);
// lv.setFadingEdgeLength(60);
// lv.getDivider().setAlpha(ALPHA_DIVIDER);
// //lv.setVerticalFadingEdgeEnabled(true);
// break;
// default:
// break;
// }
break;
case CALLER_CARDBROWSER:
@ -241,9 +242,9 @@ public class Themes {
// } else if (view.getId() == R.id.deckpicker_bar_all) {
// // view.setBackgroundResource(mProgressbarsDeckpickerYoungColor);
// } else
if (view.getId() == R.id.deckpicker_deck) {
view.setBackgroundResource(mDeckpickerItemBorder);
}
// if (view.getId() == R.id.deckpicker_deck) {
// view.setBackgroundResource(mDeckpickerItemBorder);
// } // TODO AVP
break;
case CALLER_REVIEWER:

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2009 Daniel Svärd <daniel.svard@gmail.com>
~ Copyright (c) 2011 Norbert Nagold <norbert.nagold@gmail.com>
~
@ -15,67 +14,63 @@
~ You should have received a copy of the GNU General Public License along with
~ this program. If not, see <http://www.gnu.org/licenses/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/deckpicker_deck"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/DeckPickerHoriz"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingBottom="7dip"
android:paddingLeft="10dip"
android:paddingRight="5dip"
android:paddingTop="5dip">
<RelativeLayout
android:id="@+id/DeckPickerHoriz"
android:layout_width="fill_parent"
<TextView
android:id="@+id/DeckPickerName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="7dip"
android:layout_marginLeft="10dip"
android:layout_marginRight="5dip"
android:layout_marginTop="5dip"
android:gravity="center_vertical"
android:orientation="horizontal" >
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/deckpicker_new"
android:background="@color/transparent"
android:gravity="left"
android:maxLines="2"
android:textColor="?android:textColorPrimary"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:id="@+id/DeckPickerName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/deckpicker_new"
android:gravity="left"
android:maxLines="2"
android:textColor="#000000"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:id="@+id/deckpicker_new"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/deckpicker_lrn"
android:background="@color/transparent"
android:gravity="center"
android:paddingRight="5dip"
android:textSize="14sp" />
<TextView
android:id="@+id/deckpicker_new"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/deckpicker_lrn"
android:gravity="center"
android:paddingRight="5dip"
android:textSize="14sp" />
<TextView
android:id="@+id/deckpicker_lrn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_gravity="center"
android:layout_toLeftOf="@+id/deckpicker_rev"
android:background="@color/transparent"
android:gravity="center"
android:paddingRight="5dip"
android:textSize="14sp" />
<TextView
android:id="@+id/deckpicker_lrn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_gravity="center"
android:layout_toLeftOf="@+id/deckpicker_rev"
android:gravity="center"
android:paddingRight="5dip"
android:textSize="14sp" />
<TextView
android:id="@+id/deckpicker_rev"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_gravity="center"
android:layout_marginRight="6dip"
android:gravity="center"
android:textSize="14sp" />
</RelativeLayout>
</LinearLayout>
<TextView
android:id="@+id/deckpicker_rev"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_gravity="center"
android:layout_marginRight="6dip"
android:background="@color/transparent"
android:gravity="center"
android:textSize="14sp" />
</RelativeLayout>

View File

@ -13,9 +13,10 @@
<include layout="@layout/toolbar" />
<ListView
<android.support.v7.widget.RecyclerView
android:id="@+id/files"
android:layout_width="fill_parent"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawSelectorOnTop="true"
android:fastScrollEnabled="true"