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