package edu.kufpg.armatus.console;
import android.content.Context;
import android.graphics.Color;
import android.os.Parcel;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.BackgroundColorSpan;
import android.text.style.CharacterStyle;
import android.text.style.ForegroundColorSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.TextView;
import edu.kufpg.armatus.R;
import edu.kufpg.armatus.console.ConsoleWordSearcher.MatchParams;
import edu.kufpg.armatus.util.Views;
import edu.kufpg.armatus.Prefs;
import java.util.List;
import java.util.SortedSet;
public class ConsoleEntryAdapter extends BaseExpandableListAdapter {
private final LayoutInflater mInflater;
private final List<ConsoleEntry> mEntryList;
private ConsoleWordSearcher mSearcher;
private Context context;
public ConsoleEntryAdapter(Context context, List<ConsoleEntry> entryList) {
mInflater = LayoutInflater.from(context);
mEntryList = entryList;
this.context = context;
}
@Override
public int getGroupCount() {
return mEntryList.size();
}
@Override
public int getChildrenCount(int groupPosition) {
return getGroup(groupPosition).getContentLines().size();
}
@Override
public ConsoleEntry getGroup(int groupPosition) {
return mEntryList.get(groupPosition);
}
@Override
public CharSequence getChild(int groupPosition, int childPosition) {
return getGroup(groupPosition).getContentLines().get(childPosition);
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
return mInflater.inflate(R.layout.console_entry_group, null);
}
@Override
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
ConsoleEntryHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.console_entry_child, null);
holder = new ConsoleEntryHolder();
holder.contents = (TextView) convertView.findViewById(R.id.console_entry_contents);
holder.lineNumber = (TextView) convertView.findViewById(R.id.console_line_number);
convertView.setTag(holder);
} else {
holder = (ConsoleEntryHolder) convertView.getTag();
}
CharSequence entryContents = getChild(groupPosition, childPosition);
holder.contents.setTypeface(ConsoleActivity.TYPEFACE);
holder.contents.setText(entryContents);
if (Prefs.getShowLineNumbers(this.context)){
holder.lineNumber.setTypeface(ConsoleActivity.TYPEFACE);
holder.lineNumber.setText(Integer.toString(Views.getFlatListPosition((ExpandableListView) parent,
groupPosition, childPosition) - groupPosition));
}
else {
holder.lineNumber.setTypeface(ConsoleActivity.TYPEFACE);
holder.lineNumber.setText("");
}
//Highlights search matches
if (mSearcher != null) {
String criterion = mSearcher.getCriterion();
if (mSearcher.isSearching()) {
if (mSearcher.hasMatches(entryContents.toString())) {
removeHighlight(holder.contents);
Spannable contents = new SpannableString(holder.contents.getText());
SortedSet<Integer> offsets = mSearcher.getMatchOffsets(entryContents.toString());
for (int offset : offsets) {
MatchParams params = mSearcher.getSelectedMatch();
if (groupPosition == params.entryIndex
&& childPosition == params.lineIndex
&& offset == params.textViewOffset) {
MatchBackgroundSpan highlighted = new MatchBackgroundSpan(Color.parseColor("#FFC400"));
MatchForegroundSpan blackText = new MatchForegroundSpan(Color.BLACK);
setSpans(contents, offset, offset + criterion.length(), highlighted, blackText);
} else {
MatchBackgroundSpan unhighlighted = new MatchBackgroundSpan(Color.DKGRAY);
MatchForegroundSpan whiteText = new MatchForegroundSpan(Color.WHITE);
setSpans(contents, offset, offset + criterion.length(), unhighlighted, whiteText);
}
}
holder.contents.setText(contents);
}
} else {
removeHighlight(holder.contents);
}
}
return convertView;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
/**
* Restores the reference to the console searcher, which can be destroyed after
* device standby or rotation.
* @param searcher The {@link ConsoleWordSearcher} to reconnect to.
*/
void attachSearcher(ConsoleWordSearcher searcher) {
mSearcher = searcher;
}
/** Holds {@link TextView} reference for efficiency purposes. */
static class ConsoleEntryHolder {
public TextView contents;
public TextView lineNumber;
}
/**
* Strips any spans used to color {@link TextView} words during a console search.
* @param tv The {@code TextView} from which to remove all highlighting.
*/
private static void removeHighlight(TextView tv) {
Spannable noHighlight = new SpannableString(tv.getText());
CharacterStyle[] matchBackgrounds = noHighlight.getSpans(0, noHighlight.length(), MatchBackgroundSpan.class);
for (CharacterStyle span : matchBackgrounds) {
noHighlight.removeSpan(span);
}
CharacterStyle[] matchForegrounds = noHighlight.getSpans(0, noHighlight.length(), MatchForegroundSpan.class);
for (CharacterStyle span : matchForegrounds) {
noHighlight.removeSpan(span);
}
tv.setText(noHighlight);
}
/**
* Utility method for setting multiple spans in succession.
* @param spannable The object on which {@code spans} should be set.
* @param start The starting index (inclusive) for {@code spans}.
* @param end The ending index (exclusive) for {@code spans}.
* @param spans The series of {@link CharacterStyle}s to apply to {@code spannable}.
*/
private static void setSpans(Spannable spannable, int start, int end, CharacterStyle... spans) {
for (CharacterStyle span : spans) {
spannable.setSpan(span, start, end, 0);
}
}
private static class MatchBackgroundSpan extends BackgroundColorSpan {
public MatchBackgroundSpan(int color) {
super(color);
}
public MatchBackgroundSpan(Parcel src) {
super(src);
}
}
private static class MatchForegroundSpan extends ForegroundColorSpan {
public MatchForegroundSpan(int color) {
super(color);
}
public MatchForegroundSpan(Parcel src) {
super(src);
}
}
}