/* * Tencent is pleased to support the open source community by making * Tencent GT (Version 2.4 and subsequent versions) available. * * Notwithstanding anything to the contrary herein, any previous version * of Tencent GT shall not be subject to the license hereunder. * All right, title, and interest, including all intellectual property rights, * in and to the previous version of Tencent GT (including any and all copies thereof) * shall be owned and retained by Tencent and subject to the license under the * Tencent GT End User License Agreement (http://gt.qq.com/wp-content/EULA_EN.html). * * Copyright (C) 2015 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the MIT License (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License at * * http://opensource.org/licenses/MIT * * 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.tencent.wstt.gt.log; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import android.content.Context; import android.graphics.Color; import android.text.SpannableString; import android.text.Spanned; import android.text.style.ForegroundColorSpan; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Filter; import android.widget.Filterable; import android.widget.TextView; import com.tencent.wstt.gt.R; import com.tencent.wstt.gt.ui.model.LogEntry; /** * 扩展可用于日志过滤展示的适配 */ public class NormalLogAdapter extends BaseAdapter implements Filterable { private Context context; private int queryLevel = GTLogInternal.LOG_VERBOSE; private String queryTag = ""; private String queryMsg = ""; // 是否过滤条件变化了 private boolean flag_filter_condition_changed = false; // 是否自动刷新 private boolean flag_auto_refresh = true; // 实际显示用的数据源 private List<LogEntry> dataSet; // 用于过滤中间状态的数据源,必须有这个,否则过滤会和getView UI冲突 private RemoveRangeArrayList<LogEntry> tempDataSet; // TODO 针对dataSet的读写锁,暂时不用 protected ReadWriteLock dataSetLock = new ReentrantReadWriteLock(false); private Filter filter; public NormalLogAdapter(Context context) { super(); this.context = context; this.dataSet = GTLogInternal.getNormalLogList(); } /** * 查询,暂时只支持level和tag查询 * @param queryLevel * @param queryGroup * @param queryTag * @param queryMsg */ public void query(int queryLevel, String queryTag, String queryMsg) { this.queryLevel = queryLevel; this.queryTag = queryTag; this.queryMsg = queryMsg.trim().toLowerCase(Locale.CHINA); this.getFilter().filter(null); // 查询条件重新设置时,需要重新加载数据显示 // notifyDataSetChanged(); 在filter里控制刷新了 } /** * TODO 由UI驱动的清理,暂不加锁 */ public void clear() { this.dataSet.clear(); if (tempDataSet != null) { this.tempDataSet.clear(); } } public void setFilter() { this.filter = new LogFilter(); } // 返回false后Item间的分割线消失 @Override public boolean areAllItemsEnabled() { return false; } /* * 让ListView的item不可点击 */ @Override public boolean isEnabled(int position) { return false; } @Override public int getCount() { return dataSet.size(); } @Override public LogEntry getItem(int position) { return dataSet.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { TextView tv; if (convertView == null) { tv = (TextView) LayoutInflater.from(context).inflate( R.layout.gt_loglist_item, parent,false); } else { tv = (TextView) convertView; tv.setTextColor(Color.GREEN); // 恢复绿色底 } try{ String target; target = getItem(position).msg; int tagStart = target.indexOf("/") + 1; int tagEnd = target.indexOf("(", tagStart + 1); int pidStart = tagEnd + 1; int pidEnd = target.indexOf(")", pidStart + 1); if (tagStart < tagEnd && pidStart < pidEnd) { SpannableString ss = new SpannableString(target); ss.setSpan(new ForegroundColorSpan(Color.argb(0xff, 0x9f, 0x9f, 0x9e)), 0, 19, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // 日期浅灰色 ss.setSpan(new ForegroundColorSpan(Color.argb(0xff, 0xcb, 0x74, 0x18)), 20, 21, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // 级别橘红色 ss.setSpan(new ForegroundColorSpan(Color.argb(0xff, 0xcb, 0x74, 0x18)), tagStart, tagEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // TAG橘红色 ss.setSpan(new ForegroundColorSpan(Color.argb(0xff, 0xcb, 0x74, 0x18)), pidStart, pidEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // 线程号橘红色 tv.setText(ss); return tv; } // 如果无法解析,直接返回文本内容 tv.setText(target); } catch(Exception e) { tv.setText("can't parse log text!"); tv.setTextColor(Color.RED); // 红色 } return tv; } @Override public Filter getFilter() { return filter; } // TODO 返回可见的日志集合,如果频繁使用可能需要加dataSet自己的锁 public List<LogEntry> getUIEntryList(){ return new ArrayList<LogEntry>(dataSet); } class LogFilter extends Filter { private int lastLevel; private CharSequence lastTag; private CharSequence lastMsg; public LogFilter() { } /** * 这里的constraint参数不用,满足不了需求,还是用query方法的参数 * NOTE: this function is always called from a background thread, and * not the UI thread. */ @Override protected FilterResults performFiltering(CharSequence constraint) { FilterResults results = new FilterResults(); // 先重置查询条件变化标志位 flag_filter_condition_changed = false; if (queryLevel == GTLogInternal.LOG_VERBOSE && (null == NormalLogAdapter.this.queryTag || NormalLogAdapter.this.queryTag.length() == 0) && (null == NormalLogAdapter.this.queryMsg || NormalLogAdapter.this.queryMsg.length() == 0)) { GTLogInternal.getLogListReadLock().lock(); results.values = GTLogInternal.getNormalLogList(); results.count = GTLogInternal.getNormalLogList().size(); GTLogInternal.getLogListReadLock().unlock(); } // 查询条件没变化的情况,需要增量查询数据源并增量追加FilterResults else if ((queryLevel == lastLevel) && (queryTag == lastTag || null != queryTag && queryTag.equals(lastTag)) && (queryMsg == lastMsg || null != queryMsg && queryMsg.equals(lastMsg))) { GTLogInternal.getLogListReadLock().lock(); try { int i = Math.max(GTLogInternal.getNormalLogLastFilterEndLocation(), 0); for (; i < GTLogInternal.getNormalLogList().size(); i++) { LogEntry entry = GTLogInternal.getNormalLogList().get(i); if ((null == lastTag || null == entry.tag || entry.tag.contains(lastTag)) && (null == lastMsg || null == entry.msg || entry.msg.toLowerCase(Locale.CHINA).contains(lastMsg)) // level是不可缺的 && entry.level >= lastLevel) { tempDataSet.add(entry); } } // 要保证UI展示时也不超过1000条 int length = tempDataSet.size(); if (length > LogUtils.CACHE) { tempDataSet.remove(0, length - LogUtils.CACHE); } GTLogInternal.resetNormalLogLastFilterEndLocation(); results.values = tempDataSet; results.count = tempDataSet.size(); } finally { GTLogInternal.getLogListReadLock().unlock(); } } // 查询条件有变化,需要重新在数据源中查询 else { flag_filter_condition_changed = true; lastLevel = queryLevel; lastTag = queryTag; lastMsg = queryMsg; if (tempDataSet != null) { tempDataSet.clear(); } tempDataSet = new RemoveRangeArrayList<LogEntry>(); GTLogInternal.getLogListReadLock().lock(); try { for (LogEntry entry : GTLogInternal.getNormalLogList()) { if ((null == lastTag || null == entry.tag || (null != entry.tag && entry.tag.contains(lastTag))) && (null == lastMsg || null == entry.msg || (null != entry.msg && entry.msg.toLowerCase( Locale.CHINA).contains(lastMsg))) // level是不可缺的 && entry.level >= lastLevel) { tempDataSet.add(entry); } } GTLogInternal.resetNormalLogLastFilterEndLocation(); results.values = tempDataSet; results.count = tempDataSet.size(); } finally { GTLogInternal.getLogListReadLock().unlock(); } } return results; } @SuppressWarnings("unchecked") @Override /* * NOTE: this function is always called from the UI thread. */ protected void publishResults(CharSequence constraint, FilterResults results) { if (results.count == 0) { dataSet = Collections.EMPTY_LIST; GTLogInternal.setFilterdLogList(dataSet); notifyDataSetInvalidated(); } else { dataSet = new ArrayList<LogEntry>((List<LogEntry>) results.values); GTLogInternal.setFilterdLogList(dataSet); if(flag_auto_refresh || flag_filter_condition_changed){ notifyDataSetChanged(); } } } } // TODO 是否自动刷新,包子新增,待检视 public void setAutoRefresh(boolean flag){ flag_auto_refresh = flag; } }