1 /*
2  * Copyright (C) 2015 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.phone.vvm.omtp.sync;
17 
18 import android.content.ContentResolver;
19 import android.content.ContentUris;
20 import android.content.ContentValues;
21 import android.content.Context;
22 import android.database.Cursor;
23 import android.net.Uri;
24 import android.provider.VoicemailContract;
25 import android.provider.VoicemailContract.Voicemails;
26 import android.telecom.PhoneAccountHandle;
27 import android.telecom.Voicemail;
28 
29 import java.util.ArrayList;
30 import java.util.List;
31 
32 /**
33  * Construct queries to interact with the voicemails table.
34  */
35 public class VoicemailsQueryHelper {
36     final static String[] PROJECTION = new String[] {
37             Voicemails._ID,              // 0
38             Voicemails.SOURCE_DATA,      // 1
39             Voicemails.IS_READ,          // 2
40             Voicemails.DELETED,          // 3
41             Voicemails.TRANSCRIPTION     // 4
42     };
43 
44     public static final int _ID = 0;
45     public static final int SOURCE_DATA = 1;
46     public static final int IS_READ = 2;
47     public static final int DELETED = 3;
48     public static final int TRANSCRIPTION = 4;
49 
50     final static String READ_SELECTION = Voicemails.DIRTY + "=1 AND "
51                 + Voicemails.DELETED + "!=1 AND " + Voicemails.IS_READ + "=1";
52     final static String DELETED_SELECTION = Voicemails.DELETED + "=1";
53 
54     private Context mContext;
55     private ContentResolver mContentResolver;
56     private Uri mSourceUri;
57 
VoicemailsQueryHelper(Context context)58     public VoicemailsQueryHelper(Context context) {
59         mContext = context;
60         mContentResolver = context.getContentResolver();
61         mSourceUri = VoicemailContract.Voicemails.buildSourceUri(mContext.getPackageName());
62     }
63 
64     /**
65      * Get all the local read voicemails that have not been synced to the server.
66      *
67      * @return A list of read voicemails.
68      */
getReadVoicemails()69     public List<Voicemail> getReadVoicemails() {
70         return getLocalVoicemails(READ_SELECTION);
71     }
72 
73     /**
74      * Get all the locally deleted voicemails that have not been synced to the server.
75      *
76      * @return A list of deleted voicemails.
77      */
getDeletedVoicemails()78     public List<Voicemail> getDeletedVoicemails() {
79         return getLocalVoicemails(DELETED_SELECTION);
80     }
81 
82     /**
83      * Get all voicemails locally stored.
84      *
85      * @return A list of all locally stored voicemails.
86      */
getAllVoicemails()87     public List<Voicemail> getAllVoicemails() {
88         return getLocalVoicemails(null);
89     }
90 
91     /**
92      * Utility method to make queries to the voicemail database.
93      *
94      * @param selection A filter declaring which rows to return. {@code null} returns all rows.
95      * @return A list of voicemails according to the selection statement.
96      */
getLocalVoicemails(String selection)97     private List<Voicemail> getLocalVoicemails(String selection) {
98         Cursor cursor = mContentResolver.query(mSourceUri, PROJECTION, selection, null, null);
99         if (cursor == null) {
100             return null;
101         }
102         try {
103             List<Voicemail> voicemails = new ArrayList<Voicemail>();
104             while (cursor.moveToNext()) {
105                 final long id = cursor.getLong(_ID);
106                 final String sourceData = cursor.getString(SOURCE_DATA);
107                 final boolean isRead = cursor.getInt(IS_READ) == 1;
108                 final String transcription = cursor.getString(TRANSCRIPTION);
109                 Voicemail voicemail = Voicemail
110                         .createForUpdate(id, sourceData)
111                         .setIsRead(isRead)
112                         .setTranscription(transcription).build();
113                 voicemails.add(voicemail);
114             }
115             return voicemails;
116         } finally {
117             cursor.close();
118         }
119     }
120 
121     /**
122      * Deletes a list of voicemails from the voicemail content provider.
123      *
124      * @param voicemails The list of voicemails to delete
125      * @return The number of voicemails deleted
126      */
deleteFromDatabase(List<Voicemail> voicemails)127     public int deleteFromDatabase(List<Voicemail> voicemails) {
128         int count = voicemails.size();
129         if (count == 0) {
130             return 0;
131         }
132 
133         StringBuilder sb = new StringBuilder();
134         for (int i = 0; i < count; i++) {
135             if (i > 0) {
136                 sb.append(",");
137             }
138             sb.append(voicemails.get(i).getId());
139         }
140 
141         String selectionStatement = String.format(Voicemails._ID + " IN (%s)", sb.toString());
142         return mContentResolver.delete(Voicemails.CONTENT_URI, selectionStatement, null);
143     }
144 
145     /**
146      * Utility method to delete a single voicemail.
147      */
deleteFromDatabase(Voicemail voicemail)148     public void deleteFromDatabase(Voicemail voicemail) {
149         mContentResolver.delete(Voicemails.CONTENT_URI, Voicemails._ID + "=?",
150                 new String[] { Long.toString(voicemail.getId()) });
151     }
152 
153     /**
154      * Sends an update command to the voicemail content provider for a list of voicemails.
155      * From the view of the provider, since the updater is the owner of the entry, a blank
156      * "update" means that the voicemail source is indicating that the server has up-to-date
157      * information on the voicemail. This flips the "dirty" bit to "0".
158      *
159      * @param voicemails The list of voicemails to update
160      * @return The number of voicemails updated
161      */
markReadInDatabase(List<Voicemail> voicemails)162     public int markReadInDatabase(List<Voicemail> voicemails) {
163         int count = voicemails.size();
164         for (int i = 0; i < count; i++) {
165             markReadInDatabase(voicemails.get(i));
166         }
167         return count;
168     }
169 
170     /**
171      * Utility method to mark single message as read.
172      */
markReadInDatabase(Voicemail voicemail)173     public void markReadInDatabase(Voicemail voicemail) {
174         Uri uri = ContentUris.withAppendedId(mSourceUri, voicemail.getId());
175         ContentValues contentValues = new ContentValues();
176         contentValues.put(Voicemails.IS_READ, "1");
177         mContentResolver.update(uri, contentValues, null, null);
178     }
179 
180     /**
181      * Utility method to add a transcription to the voicemail.
182      */
updateWithTranscription(Voicemail voicemail, String transcription)183     public void updateWithTranscription(Voicemail voicemail, String transcription) {
184         Uri uri = ContentUris.withAppendedId(mSourceUri, voicemail.getId());
185         ContentValues contentValues = new ContentValues();
186         contentValues.put(Voicemails.TRANSCRIPTION, transcription);
187         mContentResolver.update(uri, contentValues, null, null);
188     }
189 
190     /**
191      * Voicemail is unique if the tuple of (phone account component name, phone account id, source
192      * data) is unique. If the phone account is missing, we also consider this unique since it's
193      * simply an "unknown" account.
194      * @param voicemail The voicemail to check if it is unique.
195      * @return {@code true} if the voicemail is unique, {@code false} otherwise.
196      */
isVoicemailUnique(Voicemail voicemail)197     public boolean isVoicemailUnique(Voicemail voicemail) {
198         Cursor cursor = null;
199         PhoneAccountHandle phoneAccount = voicemail.getPhoneAccount();
200         if (phoneAccount != null) {
201             String phoneAccountComponentName = phoneAccount.getComponentName().flattenToString();
202             String phoneAccountId = phoneAccount.getId();
203             String sourceData = voicemail.getSourceData();
204             if (phoneAccountComponentName == null || phoneAccountId == null || sourceData == null) {
205                 return true;
206             }
207             try {
208                 String whereClause =
209                         Voicemails.PHONE_ACCOUNT_COMPONENT_NAME + "=? AND " +
210                         Voicemails.PHONE_ACCOUNT_ID + "=? AND " + Voicemails.SOURCE_DATA + "=?";
211                 String[] whereArgs = { phoneAccountComponentName, phoneAccountId, sourceData };
212                 cursor = mContentResolver.query(
213                         mSourceUri, PROJECTION, whereClause, whereArgs, null);
214                 if (cursor.getCount() == 0) {
215                     return true;
216                 } else {
217                     return false;
218                 }
219             }
220             finally {
221                 if (cursor != null) {
222                     cursor.close();
223                 }
224             }
225         }
226         return true;
227     }
228 }
229