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