重新设计实现CSipSimple呼叫记录分组功能
2016-09-08 10:06
225 查看
CSipSimple 原有的分组功能只能针对连续相同被叫号码,如果中间有间隔,相同的号码就不会被分成一组。这个实现很弱,也失去了分组的意义。下面针对这块功能的设计实现做下简单记录。 | |
#### 1. 自己封装一个CursorLoader | |
这里取名为CalllogCursorLoader,在CallLogListFragment -> OnCreateLoader中: | |
~~~.java | |
// Loader | |
public Loader<Cursor> onCreateLoader(int id, Bundle args) { | |
return new CalllogCursorLoader(getActivity()); | |
} | |
~~~ | |
#### 2. CalllogCursorLoader.java 代码: | |
~~~.java | |
package org.phoneos.db; | |
import org.phoneos.api.SipManager; | |
import android.content.Context; | |
import android.database.Cursor; | |
import android.net.Uri; | |
import android.provider.CallLog; | |
import android.support.v4.content.AsyncTaskLoader; | |
public class CalllogCursorLoader extends AsyncTaskLoader<Cursor> { | |
final ForceLoadContentObserver mObserver; | |
private FastCursor fastCursor = null; | |
private Cursor mObserverCursor = null; | |
/** | |
* Creates an empty unspecified CursorLoader. You must follow this with | |
* calls to {@link #setUri(Uri)}, {@link #setSelection(String)}, etc to | |
* specify the query to perform. | |
*/ | |
public CalllogCursorLoader(Context context) { | |
super(context); | |
mObserver = new ForceLoadContentObserver(); | |
} | |
/* Runs on a worker thread */ | |
@Override | |
public Cursor loadInBackground() { | |
String[] fields = new String[] { CallLog.Calls._ID, | |
CallLog.Calls.CACHED_NAME, CallLog.Calls.CACHED_NUMBER_LABEL, | |
CallLog.Calls.CACHED_NUMBER_TYPE, CallLog.Calls.DURATION, | |
CallLog.Calls.DATE, CallLog.Calls.NEW, CallLog.Calls.NUMBER, | |
CallLog.Calls.TYPE, SipManager.CALLLOG_PROFILE_ID_FIELD }; | |
try { | |
if (mObserverCursor != null) { | |
mObserverCursor.close(); | |
mObserverCursor = null; | |
} | |
// get last inserted, a trick for observer data | |
mObserverCursor = getContext().getContentResolver().query( | |
SipManager.CALLLOG_URI, fields, null, null, | |
"date desc limit 1"); | |
if (mObserverCursor != null) { | |
mObserverCursor.registerContentObserver(mObserver); | |
} | |
// if (fastCursor == null) { | |
Cursor cursor = getContext().getContentResolver().query( | |
SipManager.CALLLOG_URI, fields, null, null, "date asc"); | |
fastCursor = new FastCursor(cursor); | |
cursor.close(); | |
cursor = null; | |
// } else { | |
// fastCursor.addCursor(mObserverCursor); | |
// } | |
// int min = fastCursor.getCount(); | |
// if (min > 100) | |
// min = 100; | |
// for (int i = 0; i < min; i++) { | |
// fastCursor.moveToPosition(i); | |
// Log.d("LOADER", i + ", " + fastCursor.getString(fastCursor.getColumnIndex(CallLog.Calls.NUMBER))); | |
// } | |
return fastCursor; | |
} finally { | |
} | |
} | |
/* Runs on the UI thread */ | |
@Override | |
public void deliverResult(Cursor cursor) { | |
if (isReset()) { | |
if (fastCursor != null) { | |
fastCursor.close(); | |
} | |
} | |
Cursor oldCursor = fastCursor; | |
fastCursor = (FastCursor)cursor; | |
if (isStarted()) { | |
super.deliverResult(cursor); | |
} | |
if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) { | |
oldCursor.close(); | |
} | |
} | |
/** | |
* Starts an asynchronous load of the contacts list data. When the result is | |
* ready the callbacks will be called on the UI thread. If a previous load | |
* has been completed and is still valid the result may be passed to the | |
* callbacks immediately. | |
* | |
* Must be called from the UI thread | |
*/ | |
@Override | |
protected void onStartLoading() { | |
if (fastCursor != null) { | |
deliverResult(fastCursor); | |
} | |
if (takeContentChanged() || fastCursor == null) { | |
forceLoad(); | |
} | |
} | |
/** | |
* Must be called from the UI thread | |
*/ | |
@Override | |
protected void onStopLoading() { | |
// Attempt to cancel the current load task if possible. | |
cancelLoad(); | |
} | |
@Override | |
public void onCanceled(Cursor data) { | |
if (fastCursor != null && !fastCursor.isClosed()) { | |
fastCursor.close(); | |
} | |
} | |
@Override | |
protected void onReset() { | |
super.onReset(); | |
// Ensure the loader is stopped | |
onStopLoading(); | |
if (fastCursor != null && !fastCursor.isClosed()) { | |
fastCursor.close(); | |
} | |
fastCursor = null; | |
} | |
} | |
~~~ | |
#### 3. FastCursor.java 代码: | |
~~~.java | |
package org.phoneos.db; | |
import java.util.ArrayList; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import android.database.AbstractCursor; | |
import android.database.Cursor; | |
import android.database.MatrixCursor; | |
import android.provider.CallLog; | |
import android.util.Log; | |
// Custom a cursor, better group call logs, better performace | |
public class FastCursor extends AbstractCursor { | |
private HashMap<String, ArrayList<Integer>> groupHashMap = new HashMap<String, ArrayList<Integer>>(); | |
private ArrayList<ArrayList<Integer>> groupList; | |
private MatrixCursor mCursor; | |
public FastCursor(Cursor cursor) { | |
mCursor = new MatrixCursor(cursor.getColumnNames()); | |
int capacity = cursor.getCount() >> 3; | |
if (capacity < 10) | |
capacity = 10; | |
groupList = new ArrayList<ArrayList<Integer>>(capacity); | |
addCursor(cursor); | |
} | |
// Cursor order by date asc | |
public void addCursor(Cursor cursor) { | |
for (int index = 0; index < cursor.getCount(); index++) { | |
cursor.moveToPosition(index); | |
Object[] columnValues = new Object[cursor.getColumnCount()]; | |
columnValues[0] = cursor.getInt(0); | |
columnValues[1] = cursor.getString(1); | |
columnValues[2] = cursor.getString(2); | |
columnValues[3] = cursor.getInt(3); | |
columnValues[4] = cursor.getInt(4); | |
columnValues[5] = cursor.getLong(5); | |
columnValues[6] = cursor.getInt(6); | |
columnValues[7] = cursor.getString(7); | |
columnValues[8] = cursor.getInt(8); | |
columnValues[9] = cursor.getInt(9); | |
mCursor.addRow(columnValues); | |
String number = cursor.getString(cursor.getColumnIndex(CallLog.Calls.NUMBER)); | |
ArrayList<Integer> list = groupHashMap.get(number); | |
if (list == null) { | |
list = new ArrayList<Integer>(4); | |
groupHashMap.put(number, list); | |
} | |
else { | |
groupList.remove(list); | |
} | |
list.add(mCursor.getCount() - 1); | |
groupList.add(list); | |
} | |
// MUST reset mPos, workaroud for AbstractCursor.moveToPosition issue | |
mPos = -1; | |
Log.d("LOADER", groupHashMap.toString()); | |
Log.d("LOADER", groupList.toString()); | |
} | |
@Override | |
public boolean onMove(int oldPosition, int newPosition) { | |
int cursorStartPos = 0; | |
int length = groupList.size(); | |
ArrayList<Integer> list = null; | |
for (int i = length - 1; i >= 0; i--) { | |
if (newPosition < (cursorStartPos + groupList.get(i).size())) { | |
list = groupList.get(i); | |
break; | |
} | |
cursorStartPos += groupList.get(i).size(); | |
} | |
/* Move it to the right position */ | |
if (list != null) { | |
int index = list.size() - (newPosition - cursorStartPos) - 1; | |
Boolean ret = mCursor.moveToPosition(list.get(index)); | |
return ret; | |
} | |
return false; | |
} | |
// AbstractCursor implementation. | |
@Override | |
public void close() { | |
super.close(); | |
mCursor.close(); | |
groupHashMap.clear(); | |
groupList.clear(); | |
mCursor = null; | |
groupHashMap = null; | |
groupList = null; | |
} | |
@Override | |
public int getCount() { | |
return mCursor.getCount(); | |
} | |
@Override | |
public String[] getColumnNames() { | |
return mCursor.getColumnNames(); | |
} | |
@Override | |
public String getString(int column) { | |
return mCursor.getString(column); | |
} | |
@Override | |
public short getShort(int column) { | |
return mCursor.getShort(column); | |
} | |
@Override | |
public int getInt(int column) { | |
return mCursor.getInt(column); | |
} | |
@Override | |
public long getLong(int column) { | |
return mCursor.getLong(column); | |
} | |
@Override | |
public float getFloat(int column) { | |
return mCursor.getFloat(column); | |
} | |
@Override | |
public double getDouble(int column) { | |
return mCursor.getDouble(column); | |
} | |
@Override | |
public byte[] getBlob(int column) { | |
return mCursor.getBlob(column); | |
} | |
@Override | |
public int getType(int column) { | |
return mCursor.getType(column); | |
} | |
@Override | |
public boolean isNull(int column) { | |
return mCursor.isNull(column); | |
} | |
} | |
~~~ | |
#### 4. 修改CallLogGroupBuilder.java | |
~~~.diff | |
+++ b/src/org/phoneos/ui/calllog/CallLogGroupBuilder.java | |
@@ -81,16 +81,17 @@ public class CallLogGroupBuilder { | |
final boolean sameNumber = equalNumbers(firstNumber, currentNumber); | |
final boolean shouldGroup; | |
- if (!sameNumber) { | |
- // Should only group with calls from the same number. | |
- shouldGroup = false; | |
- } else if ( firstCallType == Calls.MISSED_TYPE) { | |
- // Voicemail and missed calls should only be grouped with subsequent missed calls. | |
- shouldGroup = callType == Calls.MISSED_TYPE; | |
- } else { | |
- // Incoming and outgoing calls group together. | |
- shouldGroup = callType == Calls.INCOMING_TYPE || callType == Calls.OUTGOING_TYPE; | |
- } | |
+ shouldGroup = sameNumber; | |
~~~ |
相关文章推荐
- 重新设计实现CSipSimple呼叫记录分组功能
- 实现GridView控件的删除多条记录功能系列(1)
- 实现GridView控件的删除多条记录功能系列(2)
- 基于ArcEngine实现分组统计面积的功能
- sqlserver2005实现oracle中的rownum(记录号)功能
- 基于Asterisk的VoIP开发指南——(1)实现基本呼叫功能
- 使用DATATGRID实现分组小计功能
- 基于Asterisk的VoIP开发指南——(1)实现基本呼叫功能
- [学习笔记]小型论坛功能——实现按照指定每页的行数来分页显示记录[3]
- 在J2ME和WAP中实现电话呼叫功能
- 使用Session记录页面地址和实现页面返回功能
- 使用Session记录页面地址和实现页面返回功能
- 实现GridView控件的删除多条记录功能系列
- 实现GridView控件的删除多条记录功能系列(1)
- 在J2ME和WAP中实现电话呼叫功能
- 基于ArcEngine实现分组统计面积的功能
- 使用DATATGRID实现分组小计功能
- 在J2ME和WAP中实现电话呼叫功能转载自j2medev
- 基于Asterisk的VoIP开发指南——(1)实现基本呼叫功能
- 动态获取Nhibernate的ORM配置信息,实现系统历史记录功能