Java tutorial
/* * Copyright (c) 2015 IRCCloud, Ltd. * * 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 com.irccloud.android.fragment; import android.app.Activity; import android.content.res.Resources; import android.graphics.Typeface; import android.os.Bundle; import android.support.v4.app.ListFragment; import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.AdapterView; import android.widget.AdapterView.OnItemLongClickListener; import android.widget.BaseAdapter; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import com.crashlytics.android.Crashlytics; import com.fasterxml.jackson.databind.JsonNode; import com.irccloud.android.AsyncTaskEx; import com.irccloud.android.IRCCloudApplication; import com.irccloud.android.IRCCloudJSONObject; import com.irccloud.android.NetworkConnection; import com.irccloud.android.R; import com.irccloud.android.data.BuffersDataSource; import com.irccloud.android.data.ChannelsDataSource; import com.irccloud.android.data.EventsDataSource; import com.irccloud.android.data.ServersDataSource; import com.squareup.leakcanary.RefWatcher; import org.json.JSONException; import org.json.JSONObject; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.Map; public class BuffersListFragment extends ListFragment implements NetworkConnection.IRCEventHandler { private static final int TYPE_SERVER = 0; private static final int TYPE_CHANNEL = 1; private static final int TYPE_CONVERSATION = 2; private static final int TYPE_ARCHIVES_HEADER = 3; private static final int TYPE_JOIN_CHANNEL = 4; private static final int TYPE_ADD_NETWORK = 5; private static final int TYPE_REORDER = 6; NetworkConnection conn; BufferListAdapter adapter; OnBufferSelectedListener mListener; View view; ListView listView = null; LinearLayout topUnreadIndicator = null; LinearLayout topUnreadIndicatorColor = null; LinearLayout topUnreadIndicatorBorder = null; LinearLayout bottomUnreadIndicator = null; LinearLayout bottomUnreadIndicatorColor = null; LinearLayout bottomUnreadIndicatorBorder = null; int selected_bid = -1; RefreshTask refreshTask = null; private boolean ready = false; public boolean readOnly = false; int firstUnreadPosition = -1; int lastUnreadPosition = -1; int firstHighlightPosition = -1; int lastHighlightPosition = -1; int firstFailurePosition = -1; int lastFailurePosition = -1; SparseBooleanArray mExpandArchives = new SparseBooleanArray(); private static class BufferListEntry implements Serializable { private static final long serialVersionUID = 1848168221883194028L; int cid; int bid; int type; int unread; int highlights; int key; long last_seen_eid; long min_eid; int joined; int archived; int timeout; String name; String status; JsonNode fail_info; int ssl; int count; String contentDescription; } private class BufferListAdapter extends BaseAdapter { ArrayList<BufferListEntry> data; private ListFragment ctx; int progressRow = -1; private class ViewHolder { int type; TextView label; TextView highlights; LinearLayout unread; LinearLayout bufferbg; ImageView icon; ProgressBar progress; ImageButton addBtn; } public void showProgress(int row) { progressRow = row; notifyDataSetChanged(); } public int positionForBid(int bid) { for (int i = 0; i < data.size(); i++) { BufferListEntry e = data.get(i); if (e.bid == bid) return i; } return -1; } private int eightdp = 0; public BufferListAdapter(ListFragment context) { ctx = context; data = new ArrayList<>( BuffersDataSource.getInstance().count() + ServersDataSource.getInstance().count() + 10); eightdp = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, getSafeResources().getDisplayMetrics()); } public void setItems(ArrayList<BufferListEntry> items) { data = items; } public void updateBuffer(BuffersDataSource.Buffer b) { int pos = positionForBid(b.bid); if (pos >= 0 && data != null && pos < data.size()) { BufferListEntry e = data.get(pos); JSONObject channelDisabledMap = null; JSONObject bufferDisabledMap = null; if (conn != null && conn.getUserInfo() != null && conn.getUserInfo().prefs != null) { try { if (conn.getUserInfo().prefs.has("channel-disableTrackUnread")) channelDisabledMap = conn.getUserInfo().prefs .getJSONObject("channel-disableTrackUnread"); if (conn.getUserInfo().prefs.has("buffer-disableTrackUnread")) bufferDisabledMap = conn.getUserInfo().prefs.getJSONObject("buffer-disableTrackUnread"); } catch (JSONException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } int unread = 0; int highlights = 0; if (conn.getState() == NetworkConnection.STATE_CONNECTED && conn.ready) { unread = b.unread; highlights = b.highlights; } try { if (b.type.equalsIgnoreCase("channel")) { if (b.bid == selected_bid || (channelDisabledMap != null && channelDisabledMap.has(String.valueOf(b.bid)) && channelDisabledMap.getBoolean(String.valueOf(b.bid)))) unread = 0; if (b.bid == selected_bid) highlights = 0; } else { if (b.bid == selected_bid || (bufferDisabledMap != null && bufferDisabledMap.has(String.valueOf(b.bid)) && bufferDisabledMap.getBoolean(String.valueOf(b.bid)))) unread = 0; if (b.bid == selected_bid || (b.type.equalsIgnoreCase("conversation") && (bufferDisabledMap != null && bufferDisabledMap.has(String.valueOf(b.bid)) && bufferDisabledMap.getBoolean(String.valueOf(b.bid))))) highlights = 0; } } catch (JSONException e1) { e1.printStackTrace(); } e.unread = unread; e.highlights = highlights; ServersDataSource.Server s = ServersDataSource.getInstance().getServer(e.cid); if (s != null) { e.status = s.status; e.fail_info = s.fail_info; } if (b.type.equalsIgnoreCase("channel")) { ChannelsDataSource.Channel c = ChannelsDataSource.getInstance().getChannelForBuffer(b.bid); if (c == null) { e.joined = 0; e.key = 0; } else if (c.key) { e.key = 1; } else { e.key = 0; } } if (unread > 0) { if (firstUnreadPosition == -1 || firstUnreadPosition > pos) firstUnreadPosition = pos; if (lastUnreadPosition == -1 || lastUnreadPosition < pos) lastUnreadPosition = pos; } else { if (firstUnreadPosition == pos) { firstUnreadPosition = -1; for (int i = 0; i < data.size(); i++) { if (data.get(i).unread > 0) { firstUnreadPosition = i; break; } } } if (lastUnreadPosition == pos) { lastUnreadPosition = -1; for (int i = pos; i >= 0; i--) { if (data.get(i).unread > 0) { lastUnreadPosition = i; break; } } } } if (highlights > 0) { if (firstHighlightPosition == -1 || firstHighlightPosition > pos) firstHighlightPosition = pos; if (lastHighlightPosition == -1 || lastHighlightPosition < pos) lastHighlightPosition = pos; } else { if (firstHighlightPosition == pos) { firstHighlightPosition = -1; for (int i = 0; i < data.size(); i++) { if (data.get(i).highlights > 0) { firstHighlightPosition = i; break; } } } if (lastHighlightPosition == pos) { lastHighlightPosition = -1; for (int i = pos; i >= 0; i--) { if (data.get(i).highlights > 0) { lastHighlightPosition = i; break; } } } } if (e.type == TYPE_SERVER) { if (e.fail_info != null && e.fail_info.has("type")) { if (firstFailurePosition == -1 || firstFailurePosition > pos) firstFailurePosition = pos; if (lastFailurePosition == -1 || lastFailurePosition < pos) lastFailurePosition = pos; } else { if (firstFailurePosition == pos) { firstFailurePosition = -1; for (int i = 0; i < data.size(); i++) { BufferListEntry j = data.get(i); if (j.type == TYPE_SERVER && j.fail_info != null && j.fail_info.has("type")) { firstFailurePosition = i; break; } } } if (lastFailurePosition == pos) { lastFailurePosition = -1; for (int i = pos; i >= 0; i--) { BufferListEntry j = data.get(i); if (j.type == TYPE_SERVER && j.fail_info != null && j.fail_info.has("type")) { lastFailurePosition = i; break; } } } } } if (BuffersListFragment.this.getActivity() != null) { BuffersListFragment.this.getActivity().runOnUiThread(new Runnable() { @Override public void run() { notifyDataSetChanged(); if (listView != null) updateUnreadIndicators(listView.getFirstVisiblePosition(), listView.getLastVisiblePosition()); } }); } } else { if (BuffersListFragment.this.getActivity() != null) { BuffersListFragment.this.getActivity().runOnUiThread(new Runnable() { @Override public void run() { refresh(); } }); } } } int unreadPositionAbove(int pos) { if (pos > 0) { for (int i = pos - 1; i >= 0; i--) { BufferListEntry e = data.get(i); if (e.unread > 0 || e.highlights > 0 || (e.type == TYPE_SERVER && e.fail_info != null && e.fail_info.has("type"))) return i; } } return 0; } int unreadPositionBelow(int pos) { if (pos >= 0) { for (int i = pos; i < data.size(); i++) { BufferListEntry e = data.get(i); if (e.unread > 0 || e.highlights > 0 || (e.type == TYPE_SERVER && e.fail_info != null && e.fail_info.has("type"))) return i; } } return data.size() - 1; } public BufferListEntry buildItem(int cid, int bid, int type, String name, int key, int unread, int highlights, long last_seen_eid, long min_eid, int joined, int archived, String status, int timeout, int ssl, int count, String contentDescription, JsonNode fail_info) { BufferListEntry e = new BufferListEntry(); e.cid = cid; e.bid = bid; e.type = type; e.name = name; e.key = key; e.unread = unread; e.highlights = highlights; e.last_seen_eid = last_seen_eid; e.min_eid = min_eid; e.joined = joined; e.archived = archived; e.status = status; e.timeout = timeout; e.ssl = ssl; e.count = count; e.contentDescription = contentDescription; e.fail_info = fail_info; return e; } @Override public int getCount() { return data.size(); } @Override public Object getItem(int position) { if (position < data.size()) return data.get(position); else return null; } @Override public long getItemId(int position) { if (position < data.size()) { BufferListEntry e = data.get(position); return e.bid; } else { return -1; } } @SuppressWarnings("deprecation") @Override public View getView(int position, View convertView, ViewGroup parent) { BufferListEntry e = data.get(position); View row = convertView; ViewHolder holder; if (row == null) { LayoutInflater inflater = ctx.getLayoutInflater(null); row = inflater.inflate(R.layout.row_buffer, null); holder = new ViewHolder(); holder.label = (TextView) row.findViewById(R.id.label); holder.highlights = (TextView) row.findViewById(R.id.highlights); holder.unread = (LinearLayout) row.findViewById(R.id.unread); holder.bufferbg = (LinearLayout) row.findViewById(R.id.bufferbg); holder.icon = (ImageView) row.findViewById(R.id.icon); holder.progress = (ProgressBar) row.findViewById(R.id.progressBar); holder.addBtn = (ImageButton) row.findViewById(R.id.addBtn); holder.type = e.type; row.setTag(holder); } else { holder = (ViewHolder) row.getTag(); } row.setContentDescription(e.contentDescription); holder.label.setText(e.name); if (e.type == TYPE_ARCHIVES_HEADER) { holder.label.setTypeface(null); holder.label.setTextColor(getSafeResources().getColorStateList(R.color.row_label_archives_heading)); holder.unread.setBackgroundDrawable(null); } else if (e.type == TYPE_JOIN_CHANNEL) { holder.label.setTypeface(null); holder.label.setTextColor(getSafeResources().getColorStateList(R.color.row_label_join)); holder.unread.setBackgroundDrawable(null); } else if (e.archived == 1 && holder.bufferbg != null) { holder.label.setTypeface(null); holder.label.setTextColor(getSafeResources().getColorStateList(R.color.row_label_archived)); holder.unread.setBackgroundDrawable(null); } else if ((e.type == TYPE_CHANNEL && e.joined == 0) || !e.status.equals("connected_ready")) { if (selected_bid == e.bid) { holder.label.setTypeface(null, Typeface.BOLD); holder.unread.setBackgroundResource(R.drawable.selected_blue); } else { holder.label.setTypeface(null); holder.unread.setBackgroundDrawable(null); } holder.label.setTextColor(getSafeResources().getColorStateList(R.color.row_label_inactive)); } else if ((e.unread > 0 && !readOnly) || selected_bid == e.bid) { holder.label.setTypeface(null, Typeface.BOLD); holder.label.setTextColor(getSafeResources().getColorStateList(R.color.row_label_unread)); holder.unread.setBackgroundResource(R.drawable.selected_blue); row.setContentDescription(row.getContentDescription() + ", unread"); } else { holder.label.setTypeface(null); holder.label.setTextColor(getSafeResources().getColorStateList(R.color.row_label)); holder.unread.setBackgroundDrawable(null); } if (holder.icon != null) { if (e.type == TYPE_JOIN_CHANNEL) { holder.icon.setImageResource(R.drawable.add); holder.icon.setVisibility(View.VISIBLE); } else if (e.type == TYPE_ADD_NETWORK) { holder.icon.setImageResource(R.drawable.world_add); holder.icon.setVisibility(View.VISIBLE); } else if (e.type == TYPE_REORDER) { holder.icon.setImageResource(R.drawable.move); holder.icon.setVisibility(View.VISIBLE); } else if (e.type == TYPE_SERVER) { if (e.ssl > 0) holder.icon.setImageResource(R.drawable.world_shield); else holder.icon.setImageResource(R.drawable.world); holder.icon.setVisibility(View.VISIBLE); } else { if (e.key > 0) { holder.icon.setImageResource(R.drawable.lock); holder.icon.setVisibility(View.VISIBLE); } else { holder.icon.setVisibility(View.INVISIBLE); } } } if (holder.progress != null) { if (progressRow == position || e.timeout > 0 || (e.type == TYPE_SERVER && !(e.status.equals("connected_ready") || e.status.equals("quitting") || e.status.equals("disconnected")))) { if (selected_bid == -1 || progressRow != position) { holder.progress.setVisibility(View.VISIBLE); if (holder.bufferbg != null) holder.bufferbg.setSelected(false); } else { if (holder.bufferbg != null) holder.bufferbg.setSelected(true); holder.progress.setVisibility(View.GONE); } } else { holder.progress.setVisibility(View.GONE); if (e.type != TYPE_ARCHIVES_HEADER && holder.bufferbg != null) holder.bufferbg.setSelected(false); } } if (holder.bufferbg != null) { if (e.type == TYPE_ARCHIVES_HEADER) { if (mExpandArchives.get(e.cid, false)) { holder.bufferbg.setBackgroundResource(R.drawable.row_buffer_bg_archived); holder.bufferbg.setSelected(true); row.setContentDescription(e.contentDescription + ". Double-tap to collapse."); } else { holder.bufferbg.setBackgroundResource(R.drawable.row_buffer_bg); holder.bufferbg.setSelected(false); row.setContentDescription(e.contentDescription + ". Double-tap to expand."); } } else if (e.type == TYPE_SERVER) { if (e.status.equals("waiting_to_retry") || e.status.equals("pool_unavailable") || ((e.status.equals("disconnected") && e.fail_info != null && e.fail_info.has("type")))) { holder.bufferbg.setBackgroundResource(R.drawable.row_failed_bg); holder.label.setTextColor(getSafeResources().getColorStateList(R.color.row_label_failed)); if (e.bid == selected_bid) holder.unread.setBackgroundResource(R.drawable.status_fail_bg); } else { holder.bufferbg.setBackgroundResource(R.drawable.row_buffergroup_bg); if (e.status.equals("connected_ready")) holder.label.setTextColor(getSafeResources().getColorStateList(R.color.row_label)); else holder.label .setTextColor(getSafeResources().getColorStateList(R.color.row_label_inactive)); } } else if (e.type == TYPE_ADD_NETWORK || e.type == TYPE_REORDER) { holder.bufferbg.setBackgroundResource(R.drawable.row_buffergroup_bg); } else if (e.type == TYPE_JOIN_CHANNEL) { holder.bufferbg.setBackgroundResource(R.drawable.row_buffer_bg_join); } else if (e.archived == 1) { holder.bufferbg.setBackgroundResource(R.drawable.row_buffer_bg_archived); } else { holder.bufferbg.setBackgroundResource(R.drawable.row_buffer_bg); } } if (holder.highlights != null) { if (e.highlights > 0) { holder.highlights.setVisibility(View.VISIBLE); holder.highlights.setText(String.valueOf(e.highlights)); row.setContentDescription(row.getContentDescription() + ", " + e.highlights + " highlights"); } else { holder.highlights.setVisibility(View.GONE); holder.highlights.setText(""); } } if (holder.addBtn != null) { if (e.count > 1 && !readOnly) { holder.addBtn.setVisibility(View.VISIBLE); holder.addBtn.setTag(e); holder.addBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { BufferListEntry e = (BufferListEntry) v.getTag(); AddChannelFragment newFragment = new AddChannelFragment(); newFragment.setDefaultCid(e.cid); newFragment.show(getActivity().getSupportFragmentManager(), "dialog"); mListener.addButtonPressed(e.cid); } }); } else { holder.addBtn.setVisibility(View.GONE); } } LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) holder.unread.getLayoutParams(); if (lp != null) { if (e.type == TYPE_SERVER || e.type == TYPE_ADD_NETWORK || e.type == TYPE_REORDER) { lp.setMargins(0, eightdp, eightdp, 0); row.setMinimumHeight(eightdp * 7); } else { lp.setMargins(0, 0, eightdp, 0); row.setMinimumHeight(eightdp * 6); } holder.unread.setLayoutParams(lp); } return row; } } private class RefreshTask extends AsyncTaskEx<Void, Void, Void> { ArrayList<BufferListEntry> entries = new ArrayList<BufferListEntry>(); @Override protected synchronized Void doInBackground(Void... params) { if (!ready || isCancelled()) { Crashlytics.log(Log.WARN, "IRCCloud", "BuffersListFragment not ready or cancelled"); return null; } SparseArray<ServersDataSource.Server> serversArray = ServersDataSource.getInstance().getServers(); ArrayList<ServersDataSource.Server> servers = new ArrayList<ServersDataSource.Server>(); for (int i = 0; i < serversArray.size(); i++) { servers.add(serversArray.valueAt(i)); } Collections.sort(servers); if (adapter == null) { Crashlytics.log(Log.DEBUG, "IRCCloud", "Created new BufferListAdapter"); adapter = new BufferListAdapter(BuffersListFragment.this); } firstUnreadPosition = -1; lastUnreadPosition = -1; firstHighlightPosition = -1; lastHighlightPosition = -1; firstFailurePosition = -1; lastFailurePosition = -1; int position = 0; JSONObject channelDisabledMap = null; JSONObject bufferDisabledMap = null; if (conn != null && conn.getUserInfo() != null && conn.getUserInfo().prefs != null) { try { if (conn.getUserInfo().prefs.has("channel-disableTrackUnread")) channelDisabledMap = conn.getUserInfo().prefs.getJSONObject("channel-disableTrackUnread"); if (conn.getUserInfo().prefs.has("buffer-disableTrackUnread")) bufferDisabledMap = conn.getUserInfo().prefs.getJSONObject("buffer-disableTrackUnread"); } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (ServersDataSource.Server s : servers) { if (isCancelled()) return null; int archiveCount = 0; ArrayList<BuffersDataSource.Buffer> buffers = BuffersDataSource.getInstance() .getBuffersForServer(s.cid); for (int j = 0; j < buffers.size(); j++) { if (isCancelled()) return null; BuffersDataSource.Buffer b = buffers.get(j); if (b.type.equalsIgnoreCase("console")) { int unread = 0; int highlights = 0; if (conn.getState() == NetworkConnection.STATE_CONNECTED && conn.ready) { unread = b.unread; highlights = b.highlights; } if (s.name.length() == 0) s.name = s.hostname; try { if (b.bid == selected_bid || (bufferDisabledMap != null && bufferDisabledMap.has(String.valueOf(b.bid)) && bufferDisabledMap.getBoolean(String.valueOf(b.bid)))) unread = 0; } catch (JSONException e) { e.printStackTrace(); } entries.add(adapter.buildItem(b.cid, b.bid, TYPE_SERVER, s.name, 0, unread, highlights, b.last_seen_eid, b.min_eid, 1, b.archived, s.status, 0, s.ssl, buffers.size(), "Network " + s.name, s.fail_info)); if (unread > 0 && firstUnreadPosition == -1) firstUnreadPosition = position; if (unread > 0 && (lastUnreadPosition == -1 || lastUnreadPosition < position)) lastUnreadPosition = position; if (highlights > 0 && firstHighlightPosition == -1) firstHighlightPosition = position; if (highlights > 0 && (lastHighlightPosition == -1 || lastHighlightPosition < position)) lastHighlightPosition = position; if (s.fail_info != null && s.fail_info.has("type")) { if (firstFailurePosition == -1) firstFailurePosition = position; if (lastFailurePosition == -1 || lastFailurePosition < position) lastFailurePosition = position; } position++; break; } } for (BuffersDataSource.Buffer b : buffers) { if (isCancelled()) return null; int type = -1; int key = 0; int joined = 1; if (b.type.equalsIgnoreCase("channel")) { type = TYPE_CHANNEL; ChannelsDataSource.Channel c = ChannelsDataSource.getInstance().getChannelForBuffer(b.bid); if (c == null) joined = 0; if (c != null && c.key) key = 1; } else if (b.type.equalsIgnoreCase("conversation")) type = TYPE_CONVERSATION; if (type > 0 && b.archived == 0) { int unread = 0; int highlights = 0; String contentDescription = null; if (conn.getState() == NetworkConnection.STATE_CONNECTED && conn.ready) { unread = b.unread; highlights = b.highlights; } try { if (b.type.equalsIgnoreCase("channel")) { contentDescription = "Channel " + b.normalizedName(); if (b.bid == selected_bid || (channelDisabledMap != null && channelDisabledMap.has(String.valueOf(b.bid)) && channelDisabledMap.getBoolean(String.valueOf(b.bid)))) unread = 0; } else { contentDescription = "Conversation with " + b.normalizedName(); if (b.bid == selected_bid || (bufferDisabledMap != null && bufferDisabledMap.has(String.valueOf(b.bid)) && bufferDisabledMap.getBoolean(String.valueOf(b.bid)))) unread = 0; if (b.type.equalsIgnoreCase("conversation") && (bufferDisabledMap != null && bufferDisabledMap.has(String.valueOf(b.bid)) && bufferDisabledMap.getBoolean(String.valueOf(b.bid)))) highlights = 0; } } catch (JSONException e) { e.printStackTrace(); } entries.add(adapter.buildItem(b.cid, b.bid, type, b.name, key, unread, highlights, b.last_seen_eid, b.min_eid, joined, b.archived, s.status, b.timeout, s.ssl, 0, contentDescription, null)); if (unread > 0 && firstUnreadPosition == -1) firstUnreadPosition = position; if (unread > 0 && (lastUnreadPosition == -1 || lastUnreadPosition < position)) lastUnreadPosition = position; if (highlights > 0 && firstHighlightPosition == -1) firstHighlightPosition = position; if (highlights > 0 && (lastHighlightPosition == -1 || lastHighlightPosition < position)) lastHighlightPosition = position; position++; } if (type > 0 && b.archived > 0) { archiveCount++; } } if (archiveCount > 0) { entries.add(adapter.buildItem(s.cid, 0, TYPE_ARCHIVES_HEADER, "Archives", 0, 0, 0, 0, 0, 0, 1, s.status, 0, s.ssl, 0, "Archives", null)); position++; if (mExpandArchives.get(s.cid, false)) { for (BuffersDataSource.Buffer b : buffers) { int type = -1; String contentDescription = null; if (b.archived == 1) { if (b.type.equalsIgnoreCase("channel")) { type = TYPE_CHANNEL; contentDescription = "Channel: " + b.normalizedName(); } else if (b.type.equalsIgnoreCase("conversation")) { type = TYPE_CONVERSATION; contentDescription = "Conversation with " + b.normalizedName(); } if (type > 0) { entries.add(adapter.buildItem(b.cid, b.bid, type, b.name, 0, 0, 0, b.last_seen_eid, b.min_eid, 0, b.archived, s.status, 0, s.ssl, 0, contentDescription, null)); position++; } } } } } if (buffers.size() == 1 && !readOnly) { entries.add(adapter.buildItem(s.cid, 0, TYPE_JOIN_CHANNEL, "Join a channel", 0, 0, 0, 0, 0, 0, 1, s.status, 0, s.ssl, 0, "Join a channel", null)); } } if (!readOnly) { entries.add(adapter.buildItem(0, 0, TYPE_ADD_NETWORK, "Add a network", 0, 0, 0, 0, 0, 0, 1, "connected_ready", 0, 0, 0, "Add a network", null)); entries.add(adapter.buildItem(0, 0, TYPE_REORDER, "Reorder", 0, 0, 0, 0, 0, 0, 1, "connected_ready", 0, 0, 0, "Reorder", null)); } Crashlytics.log(Log.DEBUG, "IRCCloud", "Buffers list adapter contains " + entries.size() + " entries"); return null; } @Override protected void onPostExecute(Void result) { if (isCancelled()) { Crashlytics.log(Log.WARN, "IRCCloud", "BuffersListFragment: OnPostExecute: This refresh task was cancelled"); return; } refreshTask = null; if (adapter == null) return; adapter.setItems(entries); if (getListAdapter() == null && entries.size() > 0) { setListAdapter(adapter); } else adapter.notifyDataSetChanged(); if (listView != null) updateUnreadIndicators(listView.getFirstVisiblePosition(), listView.getLastVisiblePosition()); else {//The activity view isn't ready yet, try again Crashlytics.log(Log.WARN, "IRCCloud", "BuffersListFragment: OnPostExecute: The activity isn't ready yet, will retry"); refreshTask = new RefreshTask(); refreshTask.execute((Void) null); } if (selected_bid > 0) adapter.showProgress(adapter.positionForBid(selected_bid)); } } public void setSelectedBid(int bid) { int last_bid = selected_bid; selected_bid = bid; if (adapter != null) { BuffersDataSource.Buffer b = BuffersDataSource.getInstance().getBuffer(last_bid); if (b != null) adapter.updateBuffer(b); b = BuffersDataSource.getInstance().getBuffer(bid); if (b != null) adapter.updateBuffer(b); adapter.showProgress(adapter.positionForBid(bid)); } else { Crashlytics.log(Log.WARN, "IRCCloud", "BufferListFragment: Request to set BID but I don't have an adapter yet, refreshing"); RefreshTask t = new RefreshTask(); t.doInBackground((Void) null); t.onPostExecute(null); Crashlytics.log(Log.DEBUG, "IRCCloud", "Done"); } } private void updateUnreadIndicators(int first, int last) { if (readOnly) { if (topUnreadIndicator != null) topUnreadIndicator.setVisibility(View.GONE); if (bottomUnreadIndicator != null) bottomUnreadIndicator.setVisibility(View.GONE); } else { if (topUnreadIndicator != null) { if (firstFailurePosition != -1 && first > firstFailurePosition) { topUnreadIndicator.setVisibility(View.VISIBLE); topUnreadIndicatorColor.setBackgroundResource(R.drawable.network_fail_bg); topUnreadIndicatorBorder.setBackgroundResource(R.drawable.networkErrorBorder); } else { topUnreadIndicator.setVisibility(View.GONE); } if (firstUnreadPosition != -1 && first > firstUnreadPosition) { topUnreadIndicator.setVisibility(View.VISIBLE); topUnreadIndicatorColor.setBackgroundResource(R.drawable.selected_blue); topUnreadIndicatorBorder.setBackgroundResource(R.drawable.unreadBorder); } if ((lastHighlightPosition != -1 && first > lastHighlightPosition) || (firstHighlightPosition != -1 && first > firstHighlightPosition)) { topUnreadIndicator.setVisibility(View.VISIBLE); topUnreadIndicatorColor.setBackgroundResource(R.drawable.highlight_red); topUnreadIndicatorBorder.setBackgroundResource(R.drawable.highlightBorder); } } if (bottomUnreadIndicator != null) { if (lastFailurePosition != -1 && last < lastFailurePosition) { bottomUnreadIndicator.setVisibility(View.VISIBLE); bottomUnreadIndicatorColor.setBackgroundResource(R.drawable.network_fail_bg); bottomUnreadIndicatorBorder.setBackgroundResource(R.drawable.networkErrorBorder); } else { bottomUnreadIndicator.setVisibility(View.GONE); } if (lastUnreadPosition != -1 && last < lastUnreadPosition) { bottomUnreadIndicator.setVisibility(View.VISIBLE); bottomUnreadIndicatorColor.setBackgroundResource(R.drawable.selected_blue); bottomUnreadIndicatorBorder.setBackgroundResource(R.drawable.unreadBorder); } if ((firstHighlightPosition != -1 && last < firstHighlightPosition) || (lastHighlightPosition != -1 && last < lastHighlightPosition)) { bottomUnreadIndicator.setVisibility(View.VISIBLE); bottomUnreadIndicatorColor.setBackgroundResource(R.drawable.highlight_red); bottomUnreadIndicatorBorder.setBackgroundResource(R.drawable.highlightBorder); } } } } @SuppressWarnings("unchecked") @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { conn = NetworkConnection.getInstance(); view = inflater.inflate(R.layout.bufferslist, null); topUnreadIndicator = (LinearLayout) view.findViewById(R.id.topUnreadIndicator); topUnreadIndicator.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { int scrollTo = adapter.unreadPositionAbove(getListView().getFirstVisiblePosition()); if (scrollTo > 0) getListView().setSelection(scrollTo - 1); else getListView().setSelection(0); updateUnreadIndicators(getListView().getFirstVisiblePosition(), getListView().getLastVisiblePosition()); } }); topUnreadIndicatorColor = (LinearLayout) view.findViewById(R.id.topUnreadIndicatorColor); topUnreadIndicatorBorder = (LinearLayout) view.findViewById(R.id.topUnreadIndicatorBorder); bottomUnreadIndicator = (LinearLayout) view.findViewById(R.id.bottomUnreadIndicator); bottomUnreadIndicator.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { int offset = getListView().getLastVisiblePosition() - getListView().getFirstVisiblePosition(); int scrollTo = adapter.unreadPositionBelow(getListView().getLastVisiblePosition()) - offset + 2; if (scrollTo < adapter.getCount()) getListView().setSelection(scrollTo); else getListView().setSelection(adapter.getCount() - 1); updateUnreadIndicators(getListView().getFirstVisiblePosition(), getListView().getLastVisiblePosition()); } }); bottomUnreadIndicatorColor = (LinearLayout) view.findViewById(R.id.bottomUnreadIndicatorColor); bottomUnreadIndicatorBorder = (LinearLayout) view.findViewById(R.id.bottomUnreadIndicatorBorder); listView = (ListView) view.findViewById(android.R.id.list); listView.setOnScrollListener(new OnScrollListener() { @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { updateUnreadIndicators(firstVisibleItem, firstVisibleItem + visibleItemCount - 1); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } }); listView.setOnItemLongClickListener(new OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int pos, long id) { return mListener .onBufferLongClicked(BuffersDataSource.getInstance().getBuffer(adapter.data.get(pos).bid)); } }); ready = NetworkConnection.getInstance().ready; if (ready) { if (savedInstanceState != null && savedInstanceState.containsKey("expandedArchives")) { ArrayList<Integer> expandedArchives = savedInstanceState.getIntegerArrayList("expandedArchives"); Iterator<Integer> i = expandedArchives.iterator(); while (i.hasNext()) { Integer cid = i.next(); mExpandArchives.put(cid, true); } } refreshTask = new RefreshTask(); refreshTask.doInBackground((Void) null); refreshTask.onPostExecute(null); if (savedInstanceState != null && savedInstanceState.containsKey("scrollPosition")) listView.setSelection(savedInstanceState.getInt("scrollPosition")); } return view; } @Override public void onSaveInstanceState(Bundle state) { if (adapter != null && adapter.data != null && adapter.data.size() > 0) { ArrayList<Integer> expandedArchives = new ArrayList<Integer>(); SparseArray<ServersDataSource.Server> servers = ServersDataSource.getInstance().getServers(); for (int i = 0; i < servers.size(); i++) { ServersDataSource.Server s = servers.valueAt(i); if (mExpandArchives.get(s.cid, false)) expandedArchives.add(s.cid); } state.putIntegerArrayList("expandedArchives", expandedArchives); if (listView != null) state.putInt("scrollPosition", listView.getFirstVisiblePosition()); } } public void onResume() { super.onResume(); conn.addHandler(this); ready = conn.ready; if (adapter != null) adapter.showProgress(-1); refresh(); } @Override public void onDestroy() { super.onDestroy(); RefWatcher refWatcher = IRCCloudApplication.getRefWatcher(getActivity()); if (refWatcher != null) refWatcher.watch(this); } @Override public void onPause() { super.onPause(); if (conn != null) conn.removeHandler(this); } @Override public void onAttach(Activity activity) { super.onAttach(activity); try { mListener = (OnBufferSelectedListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnBufferSelectedListener"); } } public void onListItemClick(ListView l, View v, int position, long id) { BufferListEntry e = (BufferListEntry) adapter.getItem(position); if (e != null) { switch (e.type) { case TYPE_ADD_NETWORK: mListener.addNetwork(); return; case TYPE_REORDER: mListener.reorder(); return; case TYPE_ARCHIVES_HEADER: mExpandArchives.put(e.cid, !mExpandArchives.get(e.cid, false)); refresh(); return; case TYPE_JOIN_CHANNEL: AddChannelFragment newFragment = new AddChannelFragment(); newFragment.setDefaultCid(e.cid); newFragment.show(getActivity().getSupportFragmentManager(), "dialog"); mListener.addButtonPressed(e.cid); return; } adapter.showProgress(position); mListener.onBufferSelected(e.bid); } } public void onIRCEvent(int what, Object obj) { BuffersDataSource.Buffer b; IRCCloudJSONObject object = null; try { object = (IRCCloudJSONObject) obj; } catch (ClassCastException e) { } EventsDataSource.Event event = null; try { event = (EventsDataSource.Event) obj; } catch (ClassCastException e) { } switch (what) { case NetworkConnection.EVENT_CHANNELMODE: b = BuffersDataSource.getInstance().getBuffer(object.bid()); if (b != null && adapter != null) adapter.updateBuffer(b); break; case NetworkConnection.EVENT_STATUSCHANGED: if (adapter != null) { ArrayList<BuffersDataSource.Buffer> buffers = BuffersDataSource.getInstance() .getBuffersForServer(object.cid()); for (BuffersDataSource.Buffer buffer : buffers) { adapter.updateBuffer(buffer); } } break; case NetworkConnection.EVENT_BUFFERMSG: if (adapter != null) { if (event.bid != selected_bid) { b = BuffersDataSource.getInstance().getBuffer(event.bid); if (b != null && event.isImportant(b.type)) adapter.updateBuffer(b); } } break; case NetworkConnection.EVENT_HEARTBEATECHO: if (adapter != null) { JsonNode seenEids = object.getJsonNode("seenEids"); Iterator<Map.Entry<String, JsonNode>> iterator = seenEids.fields(); int count = 0; while (iterator.hasNext()) { Map.Entry<String, JsonNode> entry = iterator.next(); JsonNode eids = entry.getValue(); Iterator<Map.Entry<String, JsonNode>> j = eids.fields(); while (j.hasNext()) { Map.Entry<String, JsonNode> eidentry = j.next(); Integer bid = Integer.valueOf(eidentry.getKey()); b = BuffersDataSource.getInstance().getBuffer(bid); if (b != null) adapter.updateBuffer(b); count++; } } if (count > 1) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { refresh(); } }); } } break; case NetworkConnection.EVENT_JOIN: case NetworkConnection.EVENT_PART: case NetworkConnection.EVENT_QUIT: case NetworkConnection.EVENT_KICK: if (object.type().startsWith("you_") && getActivity() != null) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { refresh(); } }); } break; case NetworkConnection.EVENT_USERINFO: case NetworkConnection.EVENT_CHANNELTOPIC: case NetworkConnection.EVENT_NICKCHANGE: case NetworkConnection.EVENT_MEMBERUPDATES: case NetworkConnection.EVENT_USERCHANNELMODE: case NetworkConnection.EVENT_AWAY: case NetworkConnection.EVENT_SELFBACK: case NetworkConnection.EVENT_CHANNELTIMESTAMP: case NetworkConnection.EVENT_SELFDETAILS: case NetworkConnection.EVENT_USERMODE: case NetworkConnection.EVENT_SETIGNORES: case NetworkConnection.EVENT_BADCHANNELKEY: case NetworkConnection.EVENT_OPENBUFFER: case NetworkConnection.EVENT_INVALIDNICK: case NetworkConnection.EVENT_BANLIST: case NetworkConnection.EVENT_WHOLIST: case NetworkConnection.EVENT_WHOIS: case NetworkConnection.EVENT_LINKCHANNEL: case NetworkConnection.EVENT_LISTRESPONSEFETCHING: case NetworkConnection.EVENT_LISTRESPONSE: case NetworkConnection.EVENT_LISTRESPONSETOOMANY: case NetworkConnection.EVENT_CONNECTIONLAG: case NetworkConnection.EVENT_GLOBALMSG: case NetworkConnection.EVENT_ACCEPTLIST: case NetworkConnection.EVENT_NAMESLIST: case NetworkConnection.EVENT_CHANNELTOPICIS: case NetworkConnection.EVENT_BACKLOG_FAILED: case NetworkConnection.EVENT_FAILURE_MSG: case NetworkConnection.EVENT_SUCCESS: case NetworkConnection.EVENT_PROGRESS: case NetworkConnection.EVENT_ALERT: case NetworkConnection.EVENT_DEBUG: break; case NetworkConnection.EVENT_CONNECTIVITY: if (getActivity() != null) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { if (adapter != null) adapter.notifyDataSetChanged(); } }); } break; case NetworkConnection.EVENT_BACKLOG_START: if (refreshTask != null) refreshTask.cancel(true); break; case NetworkConnection.EVENT_BACKLOG_END: ready = true; if (obj != null && adapter != null) { Integer bid = (Integer) obj; b = BuffersDataSource.getInstance().getBuffer(bid); if (b != null) { adapter.updateBuffer(b); break; } } else { if (getActivity() != null) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { if (refreshTask != null) refreshTask.cancel(true); Crashlytics.log(Log.DEBUG, "IRCCloud", "Refreshing buffers list"); RefreshTask t = new RefreshTask(); t.doInBackground((Void) null); t.onPostExecute(null); Crashlytics.log(Log.DEBUG, "IRCCloud", "Done"); } }); } } break; default: Crashlytics.log(Log.WARN, "IRCCloud", "Slow event: " + what); if (getActivity() != null) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { refresh(); } }); } break; } } public void refresh() { if (refreshTask != null) refreshTask.cancel(true); refreshTask = new RefreshTask(); refreshTask.execute((Void) null); } public Resources getSafeResources() { return IRCCloudApplication.getInstance().getApplicationContext().getResources(); } public interface OnBufferSelectedListener { public void onBufferSelected(int bid); public boolean onBufferLongClicked(BuffersDataSource.Buffer b); public void addButtonPressed(int cid); public void addNetwork(); public void reorder(); } }