1d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian/*
2d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Copyright (C) 2015 The Android Open Source Project
3d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian *
4d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Licensed under the Apache License, Version 2.0 (the "License");
5d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * you may not use this file except in compliance with the License.
6d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * You may obtain a copy of the License at
7d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian *
8d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian *      http://www.apache.org/licenses/LICENSE-2.0
9d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian *
10d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Unless required by applicable law or agreed to in writing, software
11d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * distributed under the License is distributed on an "AS IS" BASIS,
12d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * See the License for the specific language governing permissions and
14d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * limitations under the License
15d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */
16d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianpackage com.android.voicemail.impl.sync;
17d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
18d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.annotation.TargetApi;
19d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.content.ContentResolver;
20d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.content.ContentUris;
21d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.content.ContentValues;
22d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.content.Context;
23d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.database.Cursor;
24d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.net.Uri;
25d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.os.Build.VERSION_CODES;
26d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.provider.VoicemailContract;
27d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.provider.VoicemailContract.Voicemails;
288369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanianimport android.support.annotation.NonNull;
29d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.telecom.PhoneAccountHandle;
30d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.dialer.common.Assert;
31d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.voicemail.impl.Voicemail;
32d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.util.ArrayList;
33d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.util.List;
34d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
35d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian/** Construct queries to interact with the voicemails table. */
36d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianpublic class VoicemailsQueryHelper {
37d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  static final String[] PROJECTION =
38d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      new String[] {
39d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        Voicemails._ID, // 0
40d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        Voicemails.SOURCE_DATA, // 1
41d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        Voicemails.IS_READ, // 2
42d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        Voicemails.DELETED, // 3
43d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        Voicemails.TRANSCRIPTION // 4
44d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      };
45d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
46d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public static final int _ID = 0;
47d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public static final int SOURCE_DATA = 1;
48d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public static final int IS_READ = 2;
49d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public static final int DELETED = 3;
50d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public static final int TRANSCRIPTION = 4;
51d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
52d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  static final String READ_SELECTION =
53d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      Voicemails.DIRTY + "=1 AND " + Voicemails.DELETED + "!=1 AND " + Voicemails.IS_READ + "=1";
54d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  static final String DELETED_SELECTION = Voicemails.DELETED + "=1";
55d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  static final String ARCHIVED_SELECTION = Voicemails.ARCHIVED + "=0";
56d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
57d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  private Context mContext;
58d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  private ContentResolver mContentResolver;
59d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  private Uri mSourceUri;
60d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
61d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public VoicemailsQueryHelper(Context context) {
62d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    mContext = context;
63d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    mContentResolver = context.getContentResolver();
64d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    mSourceUri = VoicemailContract.Voicemails.buildSourceUri(mContext.getPackageName());
65d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
66d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
67d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  /**
68d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * Get all the local read voicemails that have not been synced to the server.
69d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   *
70d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * @return A list of read voicemails.
71d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   */
728369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian  public List<Voicemail> getReadVoicemails(@NonNull PhoneAccountHandle phoneAccountHandle) {
738369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian    return getLocalVoicemails(phoneAccountHandle, READ_SELECTION);
74d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
75d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
76d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  /**
77d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * Get all the locally deleted voicemails that have not been synced to the server.
78d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   *
79d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * @return A list of deleted voicemails.
80d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   */
818369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian  public List<Voicemail> getDeletedVoicemails(@NonNull PhoneAccountHandle phoneAccountHandle) {
828369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian    return getLocalVoicemails(phoneAccountHandle, DELETED_SELECTION);
83d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
84d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
85d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  /**
86d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * Get all voicemails locally stored.
87d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   *
88d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * @return A list of all locally stored voicemails.
89d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   */
908369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian  public List<Voicemail> getAllVoicemails(@NonNull PhoneAccountHandle phoneAccountHandle) {
918369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian    return getLocalVoicemails(phoneAccountHandle, null);
92d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
93d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
94d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  /**
95d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * Utility method to make queries to the voicemail database.
96d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   *
9710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian   * <p>TODO(b/36588206) add PhoneAccountHandle filtering back
9810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian   *
99d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * @param selection A filter declaring which rows to return. {@code null} returns all rows.
100d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * @return A list of voicemails according to the selection statement.
101d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   */
1028369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian  private List<Voicemail> getLocalVoicemails(
10310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian      @NonNull PhoneAccountHandle unusedPhoneAccountHandle, String selection) {
10410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian    Cursor cursor = mContentResolver.query(mSourceUri, PROJECTION, selection, null, null);
105d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    if (cursor == null) {
106d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      return null;
107d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    }
108d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    try {
109d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      List<Voicemail> voicemails = new ArrayList<Voicemail>();
110d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      while (cursor.moveToNext()) {
111d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        final long id = cursor.getLong(_ID);
112d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        final String sourceData = cursor.getString(SOURCE_DATA);
113d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        final boolean isRead = cursor.getInt(IS_READ) == 1;
114d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        final String transcription = cursor.getString(TRANSCRIPTION);
115d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        Voicemail voicemail =
116d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian            Voicemail.createForUpdate(id, sourceData)
117d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian                .setIsRead(isRead)
118d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian                .setTranscription(transcription)
119d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian                .build();
120d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        voicemails.add(voicemail);
121d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      }
122d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      return voicemails;
123d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    } finally {
124d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      cursor.close();
125d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    }
126d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
127d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
128d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  /**
129d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * Deletes a list of voicemails from the voicemail content provider.
130d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   *
131d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * @param voicemails The list of voicemails to delete
132d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * @return The number of voicemails deleted
133d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   */
134d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public int deleteFromDatabase(List<Voicemail> voicemails) {
135d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    int count = voicemails.size();
136d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    if (count == 0) {
137d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      return 0;
138d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    }
139d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
140d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    StringBuilder sb = new StringBuilder();
141d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    for (int i = 0; i < count; i++) {
142d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      if (i > 0) {
143d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        sb.append(",");
144d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      }
145d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      sb.append(voicemails.get(i).getId());
146d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    }
147d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
148d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    String selectionStatement = String.format(Voicemails._ID + " IN (%s)", sb.toString());
149d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    return mContentResolver.delete(Voicemails.CONTENT_URI, selectionStatement, null);
150d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
151d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
152d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  /** Utility method to delete a single voicemail that is not archived. */
153d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public void deleteNonArchivedFromDatabase(Voicemail voicemail) {
154d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    mContentResolver.delete(
155d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        Voicemails.CONTENT_URI,
156d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        Voicemails._ID + "=? AND " + Voicemails.ARCHIVED + "= 0",
157d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        new String[] {Long.toString(voicemail.getId())});
158d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
159d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
160d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public int markReadInDatabase(List<Voicemail> voicemails) {
161d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    int count = voicemails.size();
162d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    for (int i = 0; i < count; i++) {
163d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      markReadInDatabase(voicemails.get(i));
164d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    }
165d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    return count;
166d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
167d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
168d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  /** Utility method to mark single message as read. */
169d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public void markReadInDatabase(Voicemail voicemail) {
170d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    Uri uri = ContentUris.withAppendedId(mSourceUri, voicemail.getId());
171d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    ContentValues contentValues = new ContentValues();
172d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    contentValues.put(Voicemails.IS_READ, "1");
173d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    mContentResolver.update(uri, contentValues, null, null);
174d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
175d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
176d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  /**
177d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * Sends an update command to the voicemail content provider for a list of voicemails. From the
178d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * view of the provider, since the updater is the owner of the entry, a blank "update" means that
179d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * the voicemail source is indicating that the server has up-to-date information on the voicemail.
180d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * This flips the "dirty" bit to "0".
181d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   *
182d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * @param voicemails The list of voicemails to update
183d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * @return The number of voicemails updated
184d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   */
185d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public int markCleanInDatabase(List<Voicemail> voicemails) {
186d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    int count = voicemails.size();
187d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    for (int i = 0; i < count; i++) {
188d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      markCleanInDatabase(voicemails.get(i));
189d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    }
190d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    return count;
191d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
192d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
193d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  /** Utility method to mark single message as clean. */
194d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public void markCleanInDatabase(Voicemail voicemail) {
195d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    Uri uri = ContentUris.withAppendedId(mSourceUri, voicemail.getId());
196d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    ContentValues contentValues = new ContentValues();
197d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    mContentResolver.update(uri, contentValues, null, null);
198d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
199d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
200d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  /** Utility method to add a transcription to the voicemail. */
201d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public void updateWithTranscription(Voicemail voicemail, String transcription) {
202d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    Uri uri = ContentUris.withAppendedId(mSourceUri, voicemail.getId());
203d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    ContentValues contentValues = new ContentValues();
204d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    contentValues.put(Voicemails.TRANSCRIPTION, transcription);
205d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    mContentResolver.update(uri, contentValues, null, null);
206d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
207d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
208d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  /**
209d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * Voicemail is unique if the tuple of (phone account component name, phone account id, source
210d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * data) is unique. If the phone account is missing, we also consider this unique since it's
211d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * simply an "unknown" account.
212d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   *
213d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * @param voicemail The voicemail to check if it is unique.
214d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * @return {@code true} if the voicemail is unique, {@code false} otherwise.
215d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   */
216d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public boolean isVoicemailUnique(Voicemail voicemail) {
217d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    Cursor cursor = null;
218d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    PhoneAccountHandle phoneAccount = voicemail.getPhoneAccount();
219d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    if (phoneAccount != null) {
220d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      String phoneAccountComponentName = phoneAccount.getComponentName().flattenToString();
221d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      String phoneAccountId = phoneAccount.getId();
222d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      String sourceData = voicemail.getSourceData();
223d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      if (phoneAccountComponentName == null || phoneAccountId == null || sourceData == null) {
224d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        return true;
225d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      }
226d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      try {
227d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        String whereClause =
228d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian            Voicemails.PHONE_ACCOUNT_COMPONENT_NAME
229d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian                + "=? AND "
230d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian                + Voicemails.PHONE_ACCOUNT_ID
231d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian                + "=? AND "
232d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian                + Voicemails.SOURCE_DATA
233d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian                + "=?";
234d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        String[] whereArgs = {phoneAccountComponentName, phoneAccountId, sourceData};
235d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        cursor = mContentResolver.query(mSourceUri, PROJECTION, whereClause, whereArgs, null);
236d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        if (cursor.getCount() == 0) {
237d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian          return true;
238d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        } else {
239d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian          return false;
240d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        }
241d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      } finally {
242d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        if (cursor != null) {
243d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian          cursor.close();
244d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        }
245d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      }
246d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    }
247d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    return true;
248d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
249d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
250d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  /**
251d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * Marks voicemails in the local database as archived. This indicates that the voicemails from the
252d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * server were removed automatically to make space for new voicemails, and are stored locally on
253d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * the users devices, without a corresponding server copy.
254d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   */
255d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public void markArchivedInDatabase(List<Voicemail> voicemails) {
256d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    for (Voicemail voicemail : voicemails) {
257d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      markArchiveInDatabase(voicemail);
258d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    }
259d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
260d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
261d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  /** Utility method to mark single voicemail as archived. */
262d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public void markArchiveInDatabase(Voicemail voicemail) {
263d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    Uri uri = ContentUris.withAppendedId(mSourceUri, voicemail.getId());
264d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    ContentValues contentValues = new ContentValues();
265d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    contentValues.put(Voicemails.ARCHIVED, "1");
266d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    mContentResolver.update(uri, contentValues, null, null);
267d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
268d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
269d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  /** Find the oldest voicemails that are on the device, and also on the server. */
270d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  @TargetApi(VERSION_CODES.M) // used for try with resources
271d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public List<Voicemail> oldestVoicemailsOnServer(int numVoicemails) {
272d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    if (numVoicemails <= 0) {
273d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      Assert.fail("Query for remote voicemails cannot be <= 0");
274d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    }
275d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
276d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    String sortAndLimit = "date ASC limit " + numVoicemails;
277d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
278d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    try (Cursor cursor =
2799a090c89e7a0c43a668bd6e2ac5e5e680f323aedEric Erfanian        mContentResolver.query(mSourceUri, PROJECTION, ARCHIVED_SELECTION, null, sortAndLimit)) {
280d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
281d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      Assert.isNotNull(cursor);
282d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
283d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      List<Voicemail> voicemails = new ArrayList<>();
284d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      while (cursor.moveToNext()) {
2859a090c89e7a0c43a668bd6e2ac5e5e680f323aedEric Erfanian        final long id = cursor.getLong(_ID);
286d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        final String sourceData = cursor.getString(SOURCE_DATA);
2879a090c89e7a0c43a668bd6e2ac5e5e680f323aedEric Erfanian        Voicemail voicemail = Voicemail.createForUpdate(id, sourceData).build();
288d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        voicemails.add(voicemail);
289d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      }
290d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
291d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      if (voicemails.size() != numVoicemails) {
292d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        Assert.fail(
293d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian            String.format(
294d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian                "voicemail count (%d) doesn't matched expected (%d)",
295d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian                voicemails.size(), numVoicemails));
296d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      }
297d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      return voicemails;
298d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    }
299d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
300d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian}
301