12b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro/*
22b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro * Copyright (C) 2011 The Android Open Source Project
32b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro *
42b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro * Licensed under the Apache License, Version 2.0 (the "License");
52b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro * you may not use this file except in compliance with the License.
62b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro * You may obtain a copy of the License at
72b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro *
82b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro *      http://www.apache.org/licenses/LICENSE-2.0
92b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro *
102b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro * Unless required by applicable law or agreed to in writing, software
112b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro * distributed under the License is distributed on an "AS IS" BASIS,
122b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro * See the License for the specific language governing permissions and
142b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro * limitations under the License.
152b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro */
162b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro
172b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoropackage com.android.contacts.model;
182b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro
192b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoroimport com.android.internal.util.Objects;
20558669dab4109afebd19eade1f95a396215fb44dMakoto Onukiimport com.google.common.collect.Lists;
212b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro
222b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoroimport android.accounts.Account;
233ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onukiimport android.content.Context;
243ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onukiimport android.database.Cursor;
253ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onukiimport android.net.Uri;
262b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoroimport android.os.Parcel;
27558669dab4109afebd19eade1f95a396215fb44dMakoto Onukiimport android.os.Parcelable.Creator;
283ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onukiimport android.provider.BaseColumns;
293ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onukiimport android.provider.ContactsContract;
303ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onukiimport android.provider.ContactsContract.RawContacts;
313ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onukiimport android.text.TextUtils;
322b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro
33558669dab4109afebd19eade1f95a396215fb44dMakoto Onukiimport java.util.ArrayList;
34558669dab4109afebd19eade1f95a396215fb44dMakoto Onukiimport java.util.List;
35558669dab4109afebd19eade1f95a396215fb44dMakoto Onukiimport java.util.regex.Pattern;
36558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki
372b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro/**
382b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro * Wrapper for an account that includes a data set (which may be null).
392b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro */
402b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoropublic class AccountWithDataSet extends Account {
41558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    private static final String STRINGIFY_SEPARATOR = "\u0001";
42558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    private static final String ARRAY_STRINGIFY_SEPARATOR = "\u0002";
43558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki
44558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    private static final Pattern STRINGIFY_SEPARATOR_PAT =
45558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki            Pattern.compile(Pattern.quote(STRINGIFY_SEPARATOR));
46558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    private static final Pattern ARRAY_STRINGIFY_SEPARATOR_PAT =
47558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki            Pattern.compile(Pattern.quote(ARRAY_STRINGIFY_SEPARATOR));
482b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro
492b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro    public final String dataSet;
506ad227f990265254864a04d3289292ca42330c71Makoto Onuki    private final AccountTypeWithDataSet mAccountTypeWithDataSet;
512b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro
523ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki    private static final String[] ID_PROJECTION = new String[] {BaseColumns._ID};
533ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki    private static final Uri RAW_CONTACTS_URI_LIMIT_1 = RawContacts.CONTENT_URI.buildUpon()
543ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki            .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, "1").build();
553ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki
563ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki
572b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro    public AccountWithDataSet(String name, String type, String dataSet) {
582b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro        super(name, type);
592b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro        this.dataSet = dataSet;
606ad227f990265254864a04d3289292ca42330c71Makoto Onuki        mAccountTypeWithDataSet = AccountTypeWithDataSet.get(type, dataSet);
612b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro    }
622b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro
63558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    public AccountWithDataSet(Parcel in) {
642b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro        super(in);
65558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        this.dataSet = in.readString();
666ad227f990265254864a04d3289292ca42330c71Makoto Onuki        mAccountTypeWithDataSet = AccountTypeWithDataSet.get(type, dataSet);
672b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro    }
682b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro
69558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    @Override
70558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    public void writeToParcel(Parcel dest, int flags) {
71558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        super.writeToParcel(dest, flags);
72558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        dest.writeString(dataSet);
73558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    }
74558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki
75558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    // For Parcelable
76558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    public static final Creator<AccountWithDataSet> CREATOR = new Creator<AccountWithDataSet>() {
77558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        public AccountWithDataSet createFromParcel(Parcel source) {
78558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki            return new AccountWithDataSet(source);
79558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        }
80558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki
81558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        public AccountWithDataSet[] newArray(int size) {
82558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki            return new AccountWithDataSet[size];
83558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        }
84558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    };
85558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki
863ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki    public AccountTypeWithDataSet getAccountTypeWithDataSet() {
876ad227f990265254864a04d3289292ca42330c71Makoto Onuki        return mAccountTypeWithDataSet;
882b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro    }
892b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro
903ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki    /**
913ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki     * Return {@code true} if this account has any contacts in the database.
923ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki     * Touches DB.  Don't use in the UI thread.
933ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki     */
943ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki    public boolean hasData(Context context) {
953ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki        final String BASE_SELECTION =
963ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki                RawContacts.ACCOUNT_TYPE + " = ?" + " AND " + RawContacts.ACCOUNT_NAME + " = ?";
973ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki        final String selection;
983ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki        final String[] args;
993ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki        if (TextUtils.isEmpty(dataSet)) {
1003ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki            selection = BASE_SELECTION + " AND " + RawContacts.DATA_SET + " IS NULL";
1013ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki            args = new String[] {type, name};
1023ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki        } else {
1033ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki            selection = BASE_SELECTION + " AND " + RawContacts.DATA_SET + " = ?";
1043ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki            args = new String[] {type, name, dataSet};
1053ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki        }
1063ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki
1073ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki        final Cursor c = context.getContentResolver().query(RAW_CONTACTS_URI_LIMIT_1,
1083ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki                ID_PROJECTION, selection, args, null);
1093ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki        if (c == null) return false;
1103ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki        try {
1113ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki            return c.moveToFirst();
1123ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki        } finally {
1133ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki            c.close();
1143ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki        }
1153ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki    }
1163ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki
1172b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro    @Override
1182b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro    public boolean equals(Object o) {
1192b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro        return (o instanceof AccountWithDataSet) && super.equals(o)
1202b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro                && Objects.equal(((AccountWithDataSet) o).dataSet, dataSet);
1212b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro    }
1222b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro
1232b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro    @Override
1242b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro    public int hashCode() {
1252b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro        return 31 * super.hashCode()
1262b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro                + (dataSet == null ? 0 : dataSet.hashCode());
1272b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro    }
1282b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro
1292b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro    @Override
1302b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro    public String toString() {
1312b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro        return "AccountWithDataSet {name=" + name + ", type=" + type + ", dataSet=" + dataSet + "}";
1322b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro    }
133558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki
134558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    private static StringBuilder addStringified(StringBuilder sb, AccountWithDataSet account) {
135558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        sb.append(account.name);
136558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        sb.append(STRINGIFY_SEPARATOR);
137558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        sb.append(account.type);
138558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        sb.append(STRINGIFY_SEPARATOR);
139558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        if (!TextUtils.isEmpty(account.dataSet)) sb.append(account.dataSet);
140558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki
141558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        return sb;
142558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    }
143558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki
144558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    /**
145558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki     * Pack the instance into a string.
146558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki     */
147558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    public String stringify() {
148558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        return addStringified(new StringBuilder(), this).toString();
149558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    }
150558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki
151558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    /**
152558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki     * Unpack a string created by {@link #stringify}.
153131e6ac666868645b48ae6932575d85751ff57c0Makoto Onuki     *
154131e6ac666868645b48ae6932575d85751ff57c0Makoto Onuki     * @throws IllegalArgumentException if it's an invalid string.
155558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki     */
156558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    public static AccountWithDataSet unstringify(String s) {
157558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        final String[] array = STRINGIFY_SEPARATOR_PAT.split(s, 3);
158558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        if (array.length < 3) {
159131e6ac666868645b48ae6932575d85751ff57c0Makoto Onuki            throw new IllegalArgumentException("Invalid string " + s);
160558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        }
161558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        return new AccountWithDataSet(array[0], array[1],
162558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki                TextUtils.isEmpty(array[2]) ? null : array[2]);
163558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    }
164558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki
165558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    /**
166558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki     * Pack a list of {@link AccountWithDataSet} into a string.
167558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki     */
168558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    public static String stringifyList(List<AccountWithDataSet> accounts) {
169558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        final StringBuilder sb = new StringBuilder();
170558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki
171558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        for (AccountWithDataSet account : accounts) {
172558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki            if (sb.length() > 0) {
173558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki                sb.append(ARRAY_STRINGIFY_SEPARATOR);
174558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki            }
175558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki            addStringified(sb, account);
176558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        }
177558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki
178558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        return sb.toString();
179558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    }
180558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki
181558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    /**
182558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki     * Unpack a list of {@link AccountWithDataSet} into a string.
183131e6ac666868645b48ae6932575d85751ff57c0Makoto Onuki     *
184131e6ac666868645b48ae6932575d85751ff57c0Makoto Onuki     * @throws IllegalArgumentException if it's an invalid string.
185558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki     */
186558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    public static List<AccountWithDataSet> unstringifyList(String s) {
187558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        final ArrayList<AccountWithDataSet> ret = Lists.newArrayList();
188558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        if (TextUtils.isEmpty(s)) {
189558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki            return ret;
190558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        }
191558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki
192558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        final String[] array = ARRAY_STRINGIFY_SEPARATOR_PAT.split(s);
193558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki
194558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        for (int i = 0; i < array.length; i++) {
195558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki            ret.add(unstringify(array[i]));
196558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        }
197558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki
198558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki        return ret;
199558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki    }
2002b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro}
201