blob: 348a6c7436cd1a53e032fcebf3f3cc43e3bc1fc7 [file] [log] [blame]
Hung-ying Tyan831f9ac2010-06-23 17:07:07 -07001/*
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
17package com.android.phone2;
18import android.content.Context;
19import android.net.Uri;
20import android.os.AsyncTask;
21import android.os.Looper;
22import android.provider.CallLog.Calls;
23import android.util.Log;
24import 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
54public 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}