• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.contacts.calllog;
18 
19 import com.android.common.widget.GroupingListAdapter;
20 
21 import android.database.CharArrayBuffer;
22 import android.database.Cursor;
23 import android.provider.CallLog.Calls;
24 import android.telephony.PhoneNumberUtils;
25 
26 /**
27  * Groups together calls in the call log.
28  * <p>
29  * This class is meant to be used in conjunction with {@link GroupingListAdapter}.
30  */
31 public class CallLogGroupBuilder {
32     public interface GroupCreator {
addGroup(int cursorPosition, int size, boolean expanded)33         public void addGroup(int cursorPosition, int size, boolean expanded);
34     }
35 
36     /** Reusable char array buffer. */
37     private CharArrayBuffer mBuffer1 = new CharArrayBuffer(128);
38     /** Reusable char array buffer. */
39     private CharArrayBuffer mBuffer2 = new CharArrayBuffer(128);
40 
41     /** The object on which the groups are created. */
42     private final GroupCreator mGroupCreator;
43 
CallLogGroupBuilder(GroupCreator groupCreator)44     public CallLogGroupBuilder(GroupCreator groupCreator) {
45         mGroupCreator = groupCreator;
46     }
47 
48     /**
49      * Finds all groups of adjacent entries in the call log which should be grouped together and
50      * calls {@link CallLogFragment.GroupCreator#addGroup(int, int, boolean)} on
51      * {@link #mGroupCreator} for each of them.
52      * <p>
53      * For entries that are not grouped with others, we do not need to create a group of size one.
54      * <p>
55      * It assumes that the cursor will not change during its execution.
56      *
57      * @see GroupingListAdapter#addGroups(Cursor)
58      */
addGroups(Cursor cursor)59     public void addGroups(Cursor cursor) {
60         final int count = cursor.getCount();
61         if (count == 0) {
62             return;
63         }
64 
65         int currentGroupSize = 1;
66         // The number of the first entry in the group.
67         CharArrayBuffer firstNumber = mBuffer1;
68         // The number of the current row in the cursor.
69         CharArrayBuffer currentNumber = mBuffer2;
70         cursor.moveToFirst();
71         cursor.copyStringToBuffer(CallLogQuery.NUMBER, firstNumber);
72         // This is the type of the first call in the group.
73         int firstCallType = cursor.getInt(CallLogQuery.CALL_TYPE);
74         while (cursor.moveToNext()) {
75             cursor.copyStringToBuffer(CallLogQuery.NUMBER, currentNumber);
76             final int callType = cursor.getInt(CallLogQuery.CALL_TYPE);
77             final boolean sameNumber = equalPhoneNumbers(firstNumber, currentNumber);
78             final boolean shouldGroup;
79 
80             if (CallLogQuery.isSectionHeader(cursor)) {
81                 // Cannot group headers.
82                 shouldGroup = false;
83             } else if (!sameNumber) {
84                 // Should only group with calls from the same number.
85                 shouldGroup = false;
86             } else if (firstCallType == Calls.VOICEMAIL_TYPE
87                     || firstCallType == Calls.MISSED_TYPE) {
88                 // Voicemail and missed calls should only be grouped with subsequent missed calls.
89                 shouldGroup = callType == Calls.MISSED_TYPE;
90             } else {
91                 // Incoming and outgoing calls group together.
92                 shouldGroup = callType == Calls.INCOMING_TYPE || callType == Calls.OUTGOING_TYPE;
93             }
94 
95             if (shouldGroup) {
96                 // Increment the size of the group to include the current call, but do not create
97                 // the group until we find a call that does not match.
98                 currentGroupSize++;
99             } else {
100                 // Create a group for the previous set of calls, excluding the current one, but do
101                 // not create a group for a single call.
102                 if (currentGroupSize > 1) {
103                     addGroup(cursor.getPosition() - currentGroupSize, currentGroupSize);
104                 }
105                 // Start a new group; it will include at least the current call.
106                 currentGroupSize = 1;
107                 // The current entry is now the first in the group. For the CharArrayBuffers, we
108                 // need to swap them.
109                 firstCallType = callType;
110                 CharArrayBuffer temp = firstNumber;  // Used to swap.
111                 firstNumber = currentNumber;
112                 currentNumber = temp;
113             }
114         }
115         // If the last set of calls at the end of the call log was itself a group, create it now.
116         if (currentGroupSize > 1) {
117             addGroup(count - currentGroupSize, currentGroupSize);
118         }
119     }
120 
121     /**
122      * Creates a group of items in the cursor.
123      * <p>
124      * The group is always unexpanded.
125      *
126      * @see CallLogAdapter#addGroup(int, int, boolean)
127      */
addGroup(int cursorPosition, int size)128     private void addGroup(int cursorPosition, int size) {
129         mGroupCreator.addGroup(cursorPosition, size, false);
130     }
131 
equalPhoneNumbers(CharArrayBuffer buffer1, CharArrayBuffer buffer2)132     private boolean equalPhoneNumbers(CharArrayBuffer buffer1, CharArrayBuffer buffer2) {
133         // TODO add PhoneNumberUtils.compare(CharSequence, CharSequence) to avoid
134         // string allocation
135         return PhoneNumberUtils.compare(new String(buffer1.data, 0, buffer1.sizeCopied),
136                 new String(buffer2.data, 0, buffer2.sizeCopied));
137     }
138 }
139