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