1 /* 2 * Copyright (C) 2011 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 17 package com.android.contacts.model.account; 18 19 import android.accounts.Account; 20 import android.content.ContentProviderOperation; 21 import android.content.Context; 22 import android.database.Cursor; 23 import android.net.Uri; 24 import android.os.Parcel; 25 import android.os.Parcelable; 26 import android.provider.BaseColumns; 27 import android.provider.ContactsContract; 28 import android.provider.ContactsContract.RawContacts; 29 import android.text.TextUtils; 30 31 import com.android.contacts.model.AccountTypeManager; 32 import com.android.contacts.preference.ContactsPreferences; 33 import com.google.common.base.Objects; 34 import com.google.common.collect.Lists; 35 36 import java.util.ArrayList; 37 import java.util.List; 38 import java.util.regex.Pattern; 39 40 /** 41 * Wrapper for an account that includes a data set (which may be null). 42 */ 43 public class AccountWithDataSet implements Parcelable { 44 private static final String STRINGIFY_SEPARATOR = "\u0001"; 45 private static final String ARRAY_STRINGIFY_SEPARATOR = "\u0002"; 46 47 private static final Pattern STRINGIFY_SEPARATOR_PAT = 48 Pattern.compile(Pattern.quote(STRINGIFY_SEPARATOR)); 49 private static final Pattern ARRAY_STRINGIFY_SEPARATOR_PAT = 50 Pattern.compile(Pattern.quote(ARRAY_STRINGIFY_SEPARATOR)); 51 52 public final String name; 53 public final String type; 54 public final String dataSet; 55 private final AccountTypeWithDataSet mAccountTypeWithDataSet; 56 57 private static final String[] ID_PROJECTION = new String[] {BaseColumns._ID}; 58 private static final Uri RAW_CONTACTS_URI_LIMIT_1 = RawContacts.CONTENT_URI.buildUpon() 59 .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, "1").build(); 60 61 public static final String LOCAL_ACCOUNT_SELECTION = RawContacts.ACCOUNT_TYPE + " IS NULL AND " 62 + RawContacts.ACCOUNT_NAME + " IS NULL AND " 63 + RawContacts.DATA_SET + " IS NULL"; 64 AccountWithDataSet(String name, String type, String dataSet)65 public AccountWithDataSet(String name, String type, String dataSet) { 66 this.name = emptyToNull(name); 67 this.type = emptyToNull(type); 68 this.dataSet = emptyToNull(dataSet); 69 mAccountTypeWithDataSet = AccountTypeWithDataSet.get(type, dataSet); 70 } 71 emptyToNull(String text)72 private static final String emptyToNull(String text) { 73 return TextUtils.isEmpty(text) ? null : text; 74 } 75 AccountWithDataSet(Parcel in)76 public AccountWithDataSet(Parcel in) { 77 this.name = in.readString(); 78 this.type = in.readString(); 79 this.dataSet = in.readString(); 80 mAccountTypeWithDataSet = AccountTypeWithDataSet.get(type, dataSet); 81 } 82 isNullAccount()83 public boolean isNullAccount() { 84 return name == null && type == null && dataSet == null; 85 } 86 getNullAccount()87 public static AccountWithDataSet getNullAccount() { 88 return new AccountWithDataSet(null, null, null); 89 } 90 getAccountOrNull()91 public Account getAccountOrNull() { 92 if (name != null && type != null) { 93 return new Account(name, type); 94 } 95 return null; 96 } 97 describeContents()98 public int describeContents() { 99 return 0; 100 } 101 writeToParcel(Parcel dest, int flags)102 public void writeToParcel(Parcel dest, int flags) { 103 dest.writeString(name); 104 dest.writeString(type); 105 dest.writeString(dataSet); 106 } 107 108 // For Parcelable 109 public static final Creator<AccountWithDataSet> CREATOR = new Creator<AccountWithDataSet>() { 110 public AccountWithDataSet createFromParcel(Parcel source) { 111 return new AccountWithDataSet(source); 112 } 113 114 public AccountWithDataSet[] newArray(int size) { 115 return new AccountWithDataSet[size]; 116 } 117 }; 118 getAccountTypeWithDataSet()119 public AccountTypeWithDataSet getAccountTypeWithDataSet() { 120 return mAccountTypeWithDataSet; 121 } 122 123 /** 124 * Return {@code true} if this account has any contacts in the database. 125 * Touches DB. Don't use in the UI thread. 126 */ hasData(Context context)127 public boolean hasData(Context context) { 128 String selection; 129 String[] args = null; 130 if (isNullAccount()) { 131 selection = LOCAL_ACCOUNT_SELECTION; 132 } else { 133 final String BASE_SELECTION = 134 RawContacts.ACCOUNT_TYPE + " = ?" + " AND " + RawContacts.ACCOUNT_NAME + " = ?"; 135 if (TextUtils.isEmpty(dataSet)) { 136 selection = BASE_SELECTION + " AND " + RawContacts.DATA_SET + " IS NULL"; 137 args = new String[] {type, name}; 138 } else { 139 selection = BASE_SELECTION + " AND " + RawContacts.DATA_SET + " = ?"; 140 args = new String[] {type, name, dataSet}; 141 } 142 } 143 selection += " AND " + RawContacts.DELETED + "=0"; 144 145 final Cursor c = context.getContentResolver().query(RAW_CONTACTS_URI_LIMIT_1, 146 ID_PROJECTION, selection, args, null); 147 if (c == null) return false; 148 try { 149 return c.moveToFirst(); 150 } finally { 151 c.close(); 152 } 153 } 154 equals(Object obj)155 public boolean equals(Object obj) { 156 if (obj instanceof AccountWithDataSet) { 157 AccountWithDataSet other = (AccountWithDataSet) obj; 158 return Objects.equal(name, other.name) 159 && Objects.equal(type, other.type) 160 && Objects.equal(dataSet, other.dataSet); 161 } 162 return false; 163 } 164 hashCode()165 public int hashCode() { 166 int result = 17; 167 result = 31 * result + (name != null ? name.hashCode() : 0); 168 result = 31 * result + (type != null ? type.hashCode() : 0); 169 result = 31 * result + (dataSet != null ? dataSet.hashCode() : 0); 170 return result; 171 } 172 toString()173 public String toString() { 174 return "AccountWithDataSet {name=" + name + ", type=" + type + ", dataSet=" + dataSet + "}"; 175 } 176 addStringified(StringBuilder sb, AccountWithDataSet account)177 private static StringBuilder addStringified(StringBuilder sb, AccountWithDataSet account) { 178 if (!TextUtils.isEmpty(account.name)) sb.append(account.name); 179 sb.append(STRINGIFY_SEPARATOR); 180 if (!TextUtils.isEmpty(account.type)) sb.append(account.type); 181 sb.append(STRINGIFY_SEPARATOR); 182 if (!TextUtils.isEmpty(account.dataSet)) sb.append(account.dataSet); 183 184 return sb; 185 } 186 187 /** 188 * Pack the instance into a string. 189 */ stringify()190 public String stringify() { 191 return addStringified(new StringBuilder(), this).toString(); 192 } 193 194 /** 195 * Returns a {@link ContentProviderOperation} that will create a RawContact in this account 196 */ newRawContactOperation()197 public ContentProviderOperation newRawContactOperation() { 198 final ContentProviderOperation.Builder builder = 199 ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) 200 .withValue(RawContacts.ACCOUNT_NAME, name) 201 .withValue(RawContacts.ACCOUNT_TYPE, type); 202 if (dataSet != null) { 203 builder.withValue(RawContacts.DATA_SET, dataSet); 204 } 205 return builder.build(); 206 } 207 208 /** 209 * Unpack a string created by {@link #stringify}. 210 * 211 * @throws IllegalArgumentException if it's an invalid string. 212 */ unstringify(String s)213 public static AccountWithDataSet unstringify(String s) { 214 final String[] array = STRINGIFY_SEPARATOR_PAT.split(s, 3); 215 if (array.length < 3) { 216 throw new IllegalArgumentException("Invalid string " + s); 217 } 218 return new AccountWithDataSet(array[0], array[1], 219 TextUtils.isEmpty(array[2]) ? null : array[2]); 220 } 221 222 /** 223 * Pack a list of {@link AccountWithDataSet} into a string. 224 */ stringifyList(List<AccountWithDataSet> accounts)225 public static String stringifyList(List<AccountWithDataSet> accounts) { 226 final StringBuilder sb = new StringBuilder(); 227 228 for (AccountWithDataSet account : accounts) { 229 if (sb.length() > 0) { 230 sb.append(ARRAY_STRINGIFY_SEPARATOR); 231 } 232 addStringified(sb, account); 233 } 234 235 return sb.toString(); 236 } 237 238 /** 239 * Unpack a list of {@link AccountWithDataSet} into a string. 240 * 241 * @throws IllegalArgumentException if it's an invalid string. 242 */ unstringifyList(String s)243 public static List<AccountWithDataSet> unstringifyList(String s) { 244 final ArrayList<AccountWithDataSet> ret = Lists.newArrayList(); 245 if (TextUtils.isEmpty(s)) { 246 return ret; 247 } 248 249 final String[] array = ARRAY_STRINGIFY_SEPARATOR_PAT.split(s); 250 251 for (int i = 0; i < array.length; i++) { 252 ret.add(unstringify(array[i])); 253 } 254 255 return ret; 256 } 257 } 258 259