1 /*
2  * Copyright (c) 2016, 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 package com.android.car.stream.telecom;
17 
18 import android.content.Context;
19 import android.content.CursorLoader;
20 import android.content.Loader;
21 import android.content.pm.PackageManager;
22 import android.database.Cursor;
23 import android.net.Uri;
24 import android.provider.CallLog;
25 import android.text.TextUtils;
26 import android.text.format.DateUtils;
27 import android.util.Log;
28 import com.android.car.stream.StreamCard;
29 import com.android.car.stream.StreamProducer;
30 
31 import java.util.ArrayList;
32 import java.util.List;
33 
34 /**
35  * Loads recent calls from the call log and produces a {@link StreamCard} for each entry.
36  */
37 public class RecentCallStreamProducer extends StreamProducer
38         implements Loader.OnLoadCompleteListener<Cursor> {
39     private static final String TAG = "RecentCallProducer";
40     private static final long RECENT_CALL_TIME_RANGE = 6 * DateUtils.HOUR_IN_MILLIS;
41 
42     /** Number of call log items to query for */
43     private static final int CALL_LOG_QUERY_LIMIT = 1;
44     private static final String[] EMPTY_STRING_ARRAY = new String[0];
45 
46     private CursorLoader mCursorLoader;
47     private StreamCard mCurrentStreamCard;
48     private long mCurrentNumber;
49     private RecentCallConverter mConverter = new RecentCallConverter();
50 
RecentCallStreamProducer(Context context)51     public RecentCallStreamProducer(Context context) {
52         super(context);
53         mCursorLoader = createCallLogLoader();
54     }
55 
56     @Override
start()57     public void start() {
58         super.start();
59         if (!hasReadCallLogPermission()) {
60             if (Log.isLoggable(TAG, Log.DEBUG)) {
61                 Log.d(TAG, "Could not onStart RecentCallStreamProducer, permissions not granted");
62             }
63             return;
64         }
65 
66         if (!mCursorLoader.isStarted()) {
67             mCursorLoader.startLoading();
68         }
69     }
70 
71     @Override
stop()72     public void stop() {
73         if (mCursorLoader.isStarted()) {
74             mCursorLoader.stopLoading();
75             removeCard(mCurrentStreamCard);
76             mCurrentStreamCard = null;
77             mCurrentNumber = 0;
78         }
79         super.stop();
80     }
81 
82     @Override
onLoadComplete(Loader<Cursor> loader, Cursor cursor)83     public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
84         if (cursor == null || !cursor.moveToFirst()) {
85             return;
86         }
87 
88         int column = cursor.getColumnIndex(CallLog.Calls.NUMBER);
89         String number = cursor.getString(column);
90         column = cursor.getColumnIndex(CallLog.Calls.DATE);
91         long callTimeMs = cursor.getLong(column);
92         // Display if we have a phone number, and the call was within 6hours.
93         number = number.replaceAll("[^0-9]", "");
94         long timestamp = System.currentTimeMillis();
95         long digits = Long.parseLong(number);
96 
97         if (!TextUtils.isEmpty(number) &&
98                 (timestamp - callTimeMs) < RECENT_CALL_TIME_RANGE) {
99             if (mCurrentStreamCard == null || mCurrentNumber != digits) {
100                 removeCard(mCurrentStreamCard);
101                 mCurrentStreamCard = mConverter.createStreamCard(mContext, number, timestamp);
102                 mCurrentNumber = digits;
103                 postCard(mCurrentStreamCard);
104             }
105         }
106     }
107 
hasReadCallLogPermission()108     private boolean hasReadCallLogPermission() {
109         return mContext.checkSelfPermission(android.Manifest.permission.READ_CALL_LOG)
110                 == PackageManager.PERMISSION_GRANTED;
111     }
112 
113     /**
114      * Creates a CursorLoader for Call data.
115      * Note: NOT to be used with LoaderManagers.
116      */
createCallLogLoader()117     private CursorLoader createCallLogLoader() {
118         // We need to check for NULL explicitly otherwise entries with where READ is NULL
119         // may not match either the query or its negation.
120         // We consider the calls that are not yet consumed (i.e. IS_READ = 0) as "new".
121         StringBuilder where = new StringBuilder();
122         List<String> selectionArgs = new ArrayList<String>();
123 
124         String selection = where.length() > 0 ? where.toString() : null;
125         Uri uri = CallLog.Calls.CONTENT_URI.buildUpon()
126                 .appendQueryParameter(CallLog.Calls.LIMIT_PARAM_KEY,
127                         Integer.toString(CALL_LOG_QUERY_LIMIT))
128                 .build();
129         CursorLoader loader = new CursorLoader(mContext, uri, null, selection,
130                 selectionArgs.toArray(EMPTY_STRING_ARRAY), CallLog.Calls.DEFAULT_SORT_ORDER);
131         loader.registerListener(0, this /* OnLoadCompleteListener */);
132         return loader;
133     }
134 
135 }
136