1 /* 2 * Copyright (C) 2010 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.phone; 18 import android.content.Context; 19 import android.net.Uri; 20 import android.os.AsyncTask; 21 import android.os.Looper; 22 import android.provider.CallLog.Calls; 23 import android.util.Log; 24 import com.android.internal.telephony.CallerInfo; 25 26 /** 27 * Class to access the call logs database asynchronously since 28 * database ops can take a long time depending on the system's load. 29 * It uses AsyncTask which has its own thread pool. 30 * 31 * <pre class="prettyprint"> 32 * Typical usage: 33 * ============== 34 * 35 * // From an activity... 36 * String mLastNumber = ""; 37 * 38 * CallLogAsync log = new CallLogAsync(); 39 * 40 * CallLogAsync.AddCallArgs addCallArgs = new CallLogAsync.AddCallArgs( 41 * this, ci, number, presentation, type, timestamp, duration); 42 * 43 * log.addCall(addCallArgs); 44 * 45 * CallLogAsync.GetLastOutgoingCallArgs lastCallArgs = new CallLogAsync.GetLastOutgoingCallArgs( 46 * this, new CallLogAsync.OnLastOutgoingCallComplete() { 47 * public void lastOutgoingCall(String number) { mLastNumber = number; } 48 * }); 49 * log.getLastOutgoingCall(lastCallArgs); 50 * </pre> 51 * 52 */ 53 54 public class CallLogAsync { 55 private static final String TAG = "CallLogAsync"; 56 57 /** 58 * Parameter object to hold the args to add a call in the call log DB. 59 */ 60 public static class AddCallArgs { 61 /** 62 * @param ci CallerInfo. 63 * @param number To be logged. 64 * @param presentation Of the number. 65 * @param callType The type of call (e.g INCOMING_TYPE). @see 66 * android.provider.CallLog for the list of values. 67 * @param timestamp Of the call (millisecond since epoch). 68 * @param durationInMillis Of the call (millisecond). 69 */ 70 public AddCallArgs(Context context, 71 CallerInfo ci, 72 String number, 73 int presentation, 74 int callType, 75 long timestamp, 76 long durationInMillis) { 77 // Note that the context is passed each time. We could 78 // have stored it in a member but we've run into a bunch 79 // of memory leaks in the past that resulted from storing 80 // references to contexts in places that were long lived 81 // when the contexts were expected to be short lived. For 82 // example, if you initialize this class with an Activity 83 // instead of an Application the Activity can't be GCed 84 // until this class can, and Activities tend to hold 85 // references to large amounts of RAM for things like the 86 // bitmaps in their views. 87 // 88 // Having hit more than a few of those bugs in the past 89 // we've grown cautious of storing references to Contexts 90 // when it's not very clear that the thing holding the 91 // references is tightly tied to the Context, for example 92 // Views the Activity is displaying. 93 94 this.context = context; 95 this.ci = ci; 96 this.number = number; 97 this.presentation = presentation; 98 this.callType = callType; 99 this.timestamp = timestamp; 100 this.durationInSec = (int)(durationInMillis / 1000); 101 } 102 // Since the members are accessed directly, we don't use the 103 // mXxxx notation. 104 public final Context context; 105 public final CallerInfo ci; 106 public final String number; 107 public final int presentation; 108 public final int callType; 109 public final long timestamp; 110 public final int durationInSec; 111 } 112 113 /** 114 * Parameter object to hold the args to get the last outgoing call 115 * from the call log DB. 116 */ 117 public static class GetLastOutgoingCallArgs { 118 public GetLastOutgoingCallArgs(Context context, 119 OnLastOutgoingCallComplete callback) { 120 this.context = context; 121 this.callback = callback; 122 } 123 public final Context context; 124 public final OnLastOutgoingCallComplete callback; 125 } 126 127 /** 128 * Non blocking version of CallLog.addCall(...) 129 */ 130 public AsyncTask addCall(AddCallArgs args) { 131 assertUiThread(); 132 return new AddCallTask().execute(args); 133 } 134 135 /** Interface to retrieve the last dialed number asynchronously. */ 136 public interface OnLastOutgoingCallComplete { 137 /** @param number The last dialed number or an empty string if 138 * none exists yet. */ 139 void lastOutgoingCall(String number); 140 } 141 142 /** 143 * CallLog.getLastOutgoingCall(...) 144 */ 145 public AsyncTask getLastOutgoingCall(GetLastOutgoingCallArgs args) { 146 assertUiThread(); 147 return new GetLastOutgoingCallTask(args.callback).execute(args); 148 } 149 150 /** 151 * AsyncTask to save calls in the DB. 152 */ 153 private class AddCallTask extends AsyncTask<AddCallArgs, Void, Uri[]> { 154 @Override 155 protected Uri[] doInBackground(AddCallArgs... callList) { 156 int count = callList.length; 157 Uri[] result = new Uri[count]; 158 for (int i = 0; i < count; i++) { 159 AddCallArgs c = callList[i]; 160 161 // May block. 162 result[i] = Calls.addCall( 163 c.ci, c.context, c.number, c.presentation, 164 c.callType, c.timestamp, c.durationInSec); 165 } 166 return result; 167 } 168 169 // Perform a simple sanity check to make sure the call was 170 // written in the database. Typically there is only one result 171 // per call so it is easy to identify which one failed. 172 @Override 173 protected void onPostExecute(Uri[] result) { 174 for (Uri uri : result) { 175 if (uri == null) { 176 Log.e(TAG, "Failed to write call to the log."); 177 } 178 } 179 } 180 } 181 182 /** 183 * AsyncTask to get the last outgoing call from the DB. 184 */ 185 private class GetLastOutgoingCallTask extends AsyncTask<GetLastOutgoingCallArgs, Void, String> { 186 private final OnLastOutgoingCallComplete mCallback; 187 private String mNumber; 188 public GetLastOutgoingCallTask(OnLastOutgoingCallComplete callback) { 189 mCallback = callback; 190 } 191 192 // Happens on a background thread. We cannot run the callback 193 // here because only the UI thread can modify the view 194 // hierarchy (e.g enable/disable the dial button). The 195 // callback is ran rom the post execute method. 196 @Override 197 protected String doInBackground(GetLastOutgoingCallArgs... list) { 198 int count = list.length; 199 String number = ""; 200 for (GetLastOutgoingCallArgs args : list) { 201 // May block. Select only the last one. 202 number = Calls.getLastOutgoingCall(args.context); 203 } 204 return number; // passed to the onPostExecute method. 205 } 206 207 // Happens on the UI thread, it is safe to run the callback 208 // that may do some work on the views. 209 @Override 210 protected void onPostExecute(String number) { 211 assertUiThread(); 212 mCallback.lastOutgoingCall(number); 213 } 214 } 215 216 private void assertUiThread() { 217 if (!Looper.getMainLooper().equals(Looper.myLooper())) { 218 throw new RuntimeException("Not on the UI thread!"); 219 } 220 } 221 } 222