Android4.4 Telephony Process Analysis-call record loading process of dial-up applications (Dialer)

Source: Internet
Author: User
Tags addgroup

Android4.4 Telephony Process Analysis-call record loading process of dial-up applications (Dialer)

The code in this article takes the MTK platform Android 4.4 as the analysis object, which is somewhat different from Google's native AOSP. Please be aware of it.

The call history of the Android system is stored in the CALS table of the contact database contacts2.db. When the call record (calllog) is stored in the database, you can view my previous blog Android4.4 Telephony Process Analysis-call end step 39, the system provides the ContentProvider CallLogProvider for external access. Let's take a look at the code snippet of CallLogProvider that will be used in this article:

/*** Call log content provider. */public class CallLogProvider extends ContentProvider {...... private static final int CALLS_JION_DATA_VIEW = 5; private static final int CALLS_JION_DATA_VIEW_ID = 6 ;...... private static final UriMatcher sURIMatcher = new UriMatcher (UriMatcher. NO_MATCH); static {sURIMatcher. addURI (CallLog. AUTHORITY, "CILS", CILS); sURIMatcher. addURI (CallLog. AUTHORITY, "CILS/#", CALLS_ID); sURIMatcher. addURI (CallLog. AUTHORITY, "CILS/filter/*", CALLS_FILTER); sURIMatcher. addURI (CallLog. AUTHORITY, "CILS/search_filter/*", CALLS_SEARCH_FILTER); sURIMatcher. addURI (CallLog. AUTHORITY, "callsjoindataview", CALLS_JION_DATA_VIEW); sURIMatcher. addURI (CallLog. AUTHORITY, "callsjoindataview/#", CALLS_JION_DATA_VIEW_ID); sURIMatcher. addURI (CallLog. AUTHORITY, SearchManager. SUGGEST_URI_PATH_QUERY, SEARCH_SUGGESTIONS); sURIMatcher. addURI (CallLog. AUTHORITY, SearchManager. SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGESTIONS); sURIMatcher. addURI (CallLog. AUTHORITY, SearchManager. SUGGEST_URI_PATH_SHORTCUT + "/*", SEARCH_SHORTCUT);} private static final HashMap
SCallsProjectionMap ;...... private static final String mstableCallsJoinData = Tables. cballs + "left join" + "(SELECT * FROM" + Views. DATA + "WHERE" + Data. _ ID + "IN" + "(SELECT" + CILS. DATA_ID + "FROM" + Tables. cballs + ") AS" + Views. DATA + "ON (" + Tables. cballs + ". "+ CILS. DATA_ID + "=" + Views. DATA + ". "+ Data. _ ID + ")";...... private static final HashMap
SCallsJoinDataViewProjectionMap ;...... @ Override public Cursor query (Uri uri, String [] projection, String selection, String [] selectionArgs, String sortOrder) {final SQLiteQueryBuilder qb = new SQLiteQueryBuilder ();....... switch (match ){...... case CALLS_JION_DATA_VIEW: {qb. setTables (mstableCallsJoinData); qb. setProjectionMap (sCallsJoinDataViewProjectionMap); qb. setStrict (true); break;} case CALLS_JION_DATA_VIEW_ID: {qb. setTables (mstableCallsJoinData); // this data set will be queried. qb is defined before mstableCallsJoinData. setProjectionMap (sCallsJoinDataViewProjectionMap); qb. setStrict (true); selectionBuilder. addClause (getEqualityClause (Tables. cballs + ". "+ CILS. _ ID, parsecal1_fromuri (uri); break ;}......}......}......}

The following table lists the main fields and their data types in the CILS table:

The following figure shows the sequence of Loading records in Dialer. This figure only focuses on the processing of calllog data:

The Dialer module is processed independently after Android4.4, and most of the UI display of the entire module is implemented using Framgment. Call refresh and load are triggered many operations, such as Fragment onResume (), database update, and filtering of call records. These operations use refreshData () of step 2 () method to query the database.

Step 3 ~ Step 4: refresh the Contact Image cache of the call record. The Contact Image cache uses the LruCache Technology for asynchronous loading, and then sends a blog post for analysis.

Step 5: Read the SIM card filter settings and call type settings to start querying,

Public void startCallsQuery () {mAdapter. setLoading (true); // Step 6: Loading the contact. The contact list is not displayed as empty SharedPreferences prefs = PreferenceManager. getdefasharsharedpreferences (this. getActivity (); int simFilter = prefs. getInt (Constants. SIM_FILTER_PREF, Constants. FILTER_SIM_DEFAULT); // the SIM card int typeFilter = prefs for viewing calllog. getInt (Constants. TYPE_FILTER_PREF, Constants. FILTER_TYPE_DEFAULT); // call type: Incoming call? Power-off? Not answered? All? MCallLogQueryHandler. fetchCallsJionDataView (simFilter, typeFilter);/* add wait cursor */int count = this. getListView (). getCount (); Log. I (TAG, "*********************** count:" + count); mIsFinished = false; if (0 = count) {// The record in the list is empty, and the Log of the control waiting to be loaded is displayed. I (TAG, "call sendmessage"); mHandler. sendMessageDelayed (mHandler. obtainMessage (WAIT_CURSOR_START), WAIT_CURSOR_DELAY_TIME );}}

Step 7 ~ Step 11: Add query conditions and submit the query request to ContentProvider.

Step 9: Set the query Uri,

        if (QUERY_ALL_CALLS_JOIN_DATA_VIEW_TOKEN == token) {            queryUri = Uri.parse("content://call_log/callsjoindataview");            queryProjection = CallLogQueryEx.PROJECTION_CALLS_JOIN_DATAVIEW;        }
CallLogQueryHandlerEx, NoNullCursorAsyncQueryHandler abstract classes, AsyncQueryHandler abstract classes are inherited from Handler,

AsyncQueryHandler is an asynchronous Query Class provided by the Framework. It is defined in \ frameworks \ base \ core \ java \ android \ content. Step 10 submits the query request to it,

Public void startQuery (int token, Object cookie, Uri uri, String [] projection, String selection, String [] selectionArgs, String orderBy) {// Use the token as what so cancelOperations works properly Message msg = mWorkerThreadHandler. obtainMessage (token); // mWorkerThreadHandler is the WorkerHandler object and also a Handler, which communicates with the worker thread msg. arg1 = EVENT_ARG_QUERY; WorkerArgs args = new WorkerArgs (); args. handler = this; // this is AsyncQueryHandler, used by the working thread to return the query result Cursor args. uri = uri; args. projection = projection; args. selection = selection; args. selectionArgs = selectionArgs; args. orderBy = orderBy; args. cookie = cookie; msg. obj = args; mWorkerThreadHandler. sendMessage (msg); // the query will be performed in the work thread}

Step12 ~ Step 15, the working thread returns the query result to the handleMessage () Processing of AsyncQueryHandler.

Protected class WorkerHandler extends Handler {public WorkerHandler (low.lofter) {super (logoff) ;}@ Override public void handleMessage (Message msg) {final ContentResolver resolver = mResolver. get (); if (resolver = null) return; WorkerArgs args = (WorkerArgs) msg. obj; int token = msg. what; int event = msg. arg1; switch (event) {case EVENT_ARG_QUERY: Cursor cursor; try {cursor = resolver. query (Args. uri, args. projection, args. selection, args. selectionArgs, args. orderBy); // Calling getCount () causes the cursor window to be filled, // which will make the first access on the main thread a lot faster. if (cursor! = Null) {cursor. getCount () ;}} catch (Exception e) {Log. w (TAG, "Exception thrown during handling EVENT_ARG_QUERY", e); cursor = null;} args. result = cursor; // query result: cursor break ;......} // passing the original token value back to the caller // on top of the event values in arg1. Message reply = args. handler. obtainMessage (token); // args. handler is the this reply mentioned above. obj = args; reply. arg1 = msg. arg1; // EVENT_ARG_QUERY reply. sendToTarget ();}}

Step 16: After the query is complete, return the cursor and check whether the cursor is empty.

@ Override protected final void onQueryComplete (int token, Object cookie, Cursor cursor) {CookieWithProjection projectionCookie = (CookieWithProjection) cookie; super. onQueryComplete (token, projectionCookie. originalCookie, cursor); if (cursor = null) {// The call record is empty. Create an empty cursor and return cursor = new EmptyCursor (projectionCookie. projection);} onNotNullableQueryComplete (token, projectionCookie. originalCookie, cursor); // step17}

Step 18 ~ Step 19: return the result cursor to CallLogFragmentEx.

@ Override public void onCallsFetched (Cursor cursor ){....... mAdapter. setLoading (false); // corresponds to mAdapter in Step 6. changeCursor (cursor); // modify the cursor of CallLogListAdapter, refresh ListView // when dialpadfrangment is in forgoround, not update dial pad menu item. activity activity = getActivity (); // M: for refresh option menu; activity. invalidateOptionsMenu (); if (mScrollToTop) {// Modified by Lee 2014-06-30 for fl Ip sms and call start final HYListView listView = (HYListView) getListView (); // Modified by Lee 2014-06-30 for flip sms and call end if (listView. getFirstVisiblePosition ()> 5) {listView. setSelection (5);} listView. setSelection (0); mScrollToTop = false;} mCallLogFetched = true;/** M: add: Bug Fix for ALPS00115673 @ {*/Log. I (TAG, "onCallsFetched is call"); mIsFinished = true; mLoadingContainer. StartAnimation (AnimationUtils. loadAnimation (getActivity (), android. r. anim. fade_out); mLoadingContainer. setVisibility (View. GONE); mLoadingContact. setVisibility (View. GONE); mProgress. setVisibility (View. GONE); // hide calldetail view, let no call log warning show on all screen if (mCallDetail! = Null) {if (cursor = null | cursor. getCount () = 0) {mCallDetail. setVisibility (View. GONE);} else {mCallDetail. setVisibility (View. VISIBLE) ;}} mEmptyTitle. setText (R. string. recentCalls_empty);/** @} */destroyEmptyLoaderIfAllDataFetched (); // send message, the message will execute after the listview inflate handle. sendEmptyMessage (SETFIRSTTAG); // you can specify the first data that can be displayed in ListView}

Step24 ~ Step 32 is mainly used to process group display of call records.

The specific grouping rules and grouping process are shown in step 26:

Public void addGroups (Cursor cursor) {final int count = cursor. getCount (); if (count = 0) {return;} int currentGroupSize = 1; cursor. moveToFirst (); // The number of the first entry in the group. string firstNumber = cursor. getString (CallLogQueryEx. CALLS_JOIN_DATA_VIEW_NUMBER); // This is the type of the first call in the group. int firstCallType = cursor. getInt (CallLogQueryEx. CALLS_JOIN_DATA_VI EW_CALL_TYPE); // The following lines are provided and maintained by Mediatek Inc. int firstSimId = cursor. getInt (CallLogQueryEx. CALLS_JOIN_DATA_VIEW_SIM_ID); int firstVtCall = cursor. getInt (CallLogQueryEx. CALLS_JOIN_DATA_VIEW_VTCALL); long firstDate = cursor. getLong (CallLogQueryEx. CALLS_JOIN_DATA_VIEW_DATE); if (0! = Cursor. getCount () {setGroupHeaderPosition (cursor. getPosition ();} // @} while (cursor. moveToNext () {// The number of the current row in the cursor. final String currentNumber = cursor. getString (CallLogQueryEx. CALLS_JOIN_DATA_VIEW_NUMBER); final int callType = cursor. getInt (CallLogQueryEx. CALLS_JOIN_DATA_VIEW_CALL_TYPE); // @} final boolean sameNumber = specified numbers (firstNumber, currentNumb Er); final boolean shouldGroup; // M: add @ {final int simId = cursor. getInt (CallLogQueryEx. CALLS_JOIN_DATA_VIEW_SIM_ID); final int vtCall = cursor. getInt (CallLogQueryEx. CALLS_JOIN_DATA_VIEW_VTCALL); final long date = cursor. getLong (CallLogQueryEx. CALLS_JOIN_DATA_VIEW_DATE); final boolean isSameDay = CallLogDateFormatHelper. isSameDay (firstDate, date); // @} // M: [VVM] voice mail shocould not be Grouped. if (firstCallType = CILS. VOICEMAIL_TYPE |! SameNumber | firstCallType! = CallType | firstSimId! = SimId | firstVtCall! = VtCall |! IsSameDay) {// view comments // shoshould only group with callfrom the same number, the same // callType, the same simId and the same vtCall values. shouldGroup = false; // under this condition, ListView needs to display a record} else {shouldGroup = true; // only one record is displayed for the same group ListView, add the number of call records} // @} if (shouldGroup) {// Increment the size of the group to include the current call, but do not create // the group until we find a call that doe S not match. currentGroupSize ++; // accumulate} else {// Create a group for the previous set of cals, excluding the current one, but do // not create a group for a single call. addGroup (cursor. getPosition ()-currentGroupSize, currentGroupSize); if (! IsSameDay) {// not the call record for the same day. The Header (date) setGroupHeaderPosition (cursor. getPosition ();} // @} // Start a new group; it will include at least the current call. currentGroupSize = 1; // The current entry is now the first in the group. // compare firstNumber = currentNumber; firstCallType = callType; // M: add @ {firstCallType = callType; firstSimId = simId; firstVtCall = vtCall; firstDate = date; /// }} addGroup (count-currentGroupSize, currentGroupSize );///@}}

Step27 ~ Step 29: Set the Header location to the HashMap of mHeaderPositionList,

    public void setGroupHeaderPosition(int cursorPosition) {        mHeaderPositionList.put(Integer.valueOf(cursorPosition), Boolean.valueOf(true));    }

Step30 ~ Step 32: record the starting position and size of a Group (an item of ListView) (including the number of call records) at mGroupMetadata,

    protected void addGroup(int cursorPosition, int size, boolean expanded) {        if (mGroupCount >= mGroupMetadata.length) {            int newSize = idealLongArraySize(                    mGroupMetadata.length + GROUP_METADATA_ARRAY_INCREMENT);            long[] array = new long[newSize];            System.arraycopy(mGroupMetadata, 0, array, 0, mGroupCount);            mGroupMetadata = array;        }        long metadata = ((long)size << 32) | cursorPosition;        if (expanded) {            metadata |= EXPANDED_GROUP_MASK;        }        mGroupMetadata[mGroupCount++] = metadata;    }

MGroupMetadata is a long array with the initial size of GROUP_METADATA_ARRAY_INITIAL_SIZE, 16. When the space is insufficient, it increases with GROUP_METADATA_ARRAY_INCREMENT (128) each time.

The data binding between the call history ListView and the Adapter is in the getView () method of the GroupingListAdapter. This class is inherited from the BaseAdapter. Let's take a look at its inheritance structure:

Open the browser to view the large image.

Not yet resumed. please correct me if there is anything wrong.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.