package what.whatandroid.subscriptions; import android.content.Context; import android.os.AsyncTask; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.*; import api.subscriptions.ForumThread; import api.subscriptions.Subscriptions; import api.util.Tuple; import what.whatandroid.R; import what.whatandroid.callbacks.ViewForumCallbacks; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; /** * Adapter for displaying the list of subscriptions, * grouped by forum category */ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnItemClickListener, View.OnClickListener { private final int VIEW_HEADER = 0, VIEW_ITEM = 1; /** * Map of category -> [threads] of all our subscribed threads */ private Map<String, List<ForumThread>> threads; /** * The list of categories we're showing */ private List<String> categories; /** * Callbacks so we can go view the threads we're subscribed to */ private final ViewForumCallbacks viewForum; private final LayoutInflater inflater; private final Context context; /** * We also need the no content notice to show it if all the items are * removed from the list */ private final TextView noContent; public SubscriptionsAdapter(Context context, TextView noContent){ threads = new TreeMap<String, List<ForumThread>>(); categories = new ArrayList<String>(); this.context = context; this.noContent = noContent; inflater = LayoutInflater.from(context); try { viewForum = (ViewForumCallbacks) context; } catch (ClassCastException e){ throw new ClassCastException(context.toString() + " must implement ViewForumCallbacks"); } } /** * Add the subscriptions forum threads and categories to be shown */ public void addSubscriptions(Subscriptions subscriptions){ Map<String, List<ForumThread>> groups = subscriptions.groupThreadsBySection(); //Update the category listing categories.clear(); for (Map.Entry<String, List<ForumThread>> e : groups.entrySet()){ if (!e.getValue().isEmpty()){ categories.add(e.getKey()); threads.put(e.getKey(), e.getValue()); } } } @Override public View getView(int position, View convertView, ViewGroup parent){ switch (getItemViewType(position)){ case VIEW_HEADER: return getCategoryView(position, convertView, parent); default: return getThreadView(position, convertView, parent); } } private View getCategoryView(int position, View convertView, ViewGroup parent){ HeaderViewHolder holder; if (convertView != null){ holder = (HeaderViewHolder) convertView.getTag(); } else { convertView = inflater.inflate(R.layout.list_forum_category, parent, false); holder = new HeaderViewHolder(); holder.name = (TextView) convertView.findViewById(R.id.category); convertView.setTag(holder); } String name = (String) getItem(position); holder.name.setText(name); return convertView; } private View getThreadView(int position, View convertView, ViewGroup parent){ ItemViewHolder holder; if (convertView != null){ holder = (ItemViewHolder) convertView.getTag(); } else { convertView = inflater.inflate(R.layout.list_subscription, parent, false); holder = new ItemViewHolder(); holder.name = (TextView) convertView.findViewById(R.id.title); holder.unsubscribe = (ImageButton) convertView.findViewById(R.id.unsubscribe); holder.unsubscribe.setImageResource(R.drawable.ic_visibility_24dp); convertView.setTag(holder); } ForumThread thread = (ForumThread) getItem(position); holder.name.setText(thread.getThreadTitle()); holder.unsubscribe.setOnClickListener(this); holder.unsubscribe.setTag(position); return convertView; } @Override public Object getItem(int position){ Tuple<Integer, Integer> indices = getIndices(position); return indices.getB() == null ? categories.get(indices.getA()) : threads.get(categories.get(indices.getA())).get(indices.getB()); } @Override public int getViewTypeCount(){ return 2; } @Override public long getItemId(int position){ return position; } @Override public int getCount(){ int count = 0; for (Map.Entry<String, List<ForumThread>> e : threads.entrySet()){ count += 1 + e.getValue().size(); } return count; } /** * Resolve the position into the proper view type, category header or thread */ @Override public int getItemViewType(int position){ for (int i = 0; position > 0; ++i){ position -= 1 + threads.get(categories.get(i)).size(); } return position == 0 ? VIEW_HEADER : VIEW_ITEM; } @Override public boolean areAllItemsEnabled(){ return false; } @Override public boolean isEnabled(int position){ return getItemViewType(position) == VIEW_ITEM; } /** * Clear all the data being shown */ public void clear(){ threads.clear(); categories.clear(); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id){ ForumThread thread = (ForumThread) getItem(position); viewForum.viewThread(thread.getThreadId().intValue(), thread.getPostId().intValue()); } @Override public void onClick(View v){ Integer position = (Integer) v.getTag(); new UnsubscribeTask().execute((ForumThread) getItem(position)); //Also remove the item from the displayed data Tuple<Integer, Integer> indices = getIndices(position); List<ForumThread> t = threads.get(categories.get(indices.getA())); t.remove(indices.getB().intValue()); //If that was the only item in the category then also remove the category if (t.isEmpty()){ threads.remove(categories.get(indices.getA())); categories.remove(indices.getA().intValue()); } notifyDataSetChanged(); if (getCount() == 0){ noContent.setVisibility(View.VISIBLE); } } /** * Get the category and thread indices for the element at some position, * if the element is a header then the thread index will be null * * @return { category index, thread index } */ private Tuple<Integer, Integer> getIndices(int position){ int category = 0, thread, p = position; while (true){ int listLen = threads.get(categories.get(category)).size(); //If we're in the block for the current category if (p - 1 - listLen < 0){ thread = p - 1; break; } else { ++category; p -= 1 + listLen; } } return p == 0 ? new Tuple<Integer, Integer>(category, null) : new Tuple<Integer, Integer>(category, thread); } private static class HeaderViewHolder { public TextView name; } private static class ItemViewHolder { public TextView name; public ImageButton unsubscribe; } /** * Task used to launch an async request to unsubscribe from some forum thread */ private class UnsubscribeTask extends AsyncTask<ForumThread, Void, Boolean> { @Override protected Boolean doInBackground(ForumThread... params){ return api.forum.thread.ForumThread.unsubscribe(params[0].getThreadId().intValue()); } @Override protected void onPostExecute(Boolean status){ if (!status){ Toast.makeText(context, "Could not unsubscribe from thread", Toast.LENGTH_LONG).show(); } } } }