Home | History | Annotate | Download | only in model
      1 /*
      2  * Copyright (C) 2009 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.loaderapp.model;
     18 
     19 import com.android.loaderapp.R;
     20 import com.android.loaderapp.model.EntityDelta.ValuesDelta;
     21 import com.google.android.collect.Lists;
     22 
     23 import android.accounts.Account;
     24 import android.content.ContentProviderOperation;
     25 import android.content.ContentProviderResult;
     26 import android.content.ContentResolver;
     27 import android.content.ContentUris;
     28 import android.content.ContentValues;
     29 import android.content.Context;
     30 import android.content.OperationApplicationException;
     31 import android.database.Cursor;
     32 import android.os.RemoteException;
     33 import android.provider.ContactsContract;
     34 import android.provider.ContactsContract.Groups;
     35 import android.provider.ContactsContract.RawContacts;
     36 import android.provider.ContactsContract.CommonDataKinds.Email;
     37 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
     38 import android.provider.ContactsContract.CommonDataKinds.Phone;
     39 import android.provider.ContactsContract.Contacts.Data;
     40 
     41 import java.util.ArrayList;
     42 
     43 public class GoogleSource extends FallbackSource {
     44     public static final String ACCOUNT_TYPE = "com.google";
     45     public GoogleSource(String resPackageName) {
     46         this.accountType = ACCOUNT_TYPE;
     47         this.resPackageName = null;
     48         this.summaryResPackageName = resPackageName;
     49     }
     50 
     51     @Override
     52     protected void inflate(Context context, int inflateLevel) {
     53 
     54         inflateStructuredName(context, inflateLevel);
     55         inflateNickname(context, inflateLevel);
     56         inflatePhone(context, inflateLevel);
     57         inflateEmail(context, inflateLevel);
     58         inflateStructuredPostal(context, inflateLevel);
     59         inflateIm(context, inflateLevel);
     60         inflateOrganization(context, inflateLevel);
     61         inflatePhoto(context, inflateLevel);
     62         inflateNote(context, inflateLevel);
     63         inflateWebsite(context, inflateLevel);
     64         inflateEvent(context, inflateLevel);
     65 
     66         // TODO: GOOGLE: GROUPMEMBERSHIP
     67 
     68         setInflatedLevel(inflateLevel);
     69 
     70     }
     71 
     72     @Override
     73     protected DataKind inflateStructuredName(Context context, int inflateLevel) {
     74         return super.inflateStructuredName(context, inflateLevel);
     75     }
     76 
     77     @Override
     78     protected DataKind inflateNickname(Context context, int inflateLevel) {
     79         return super.inflateNickname(context, inflateLevel);
     80     }
     81 
     82     @Override
     83     protected DataKind inflatePhone(Context context, int inflateLevel) {
     84         final DataKind kind = super.inflatePhone(context, ContactsSource.LEVEL_MIMETYPES);
     85 
     86         if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
     87             kind.typeColumn = Phone.TYPE;
     88             kind.typeList = Lists.newArrayList();
     89             kind.typeList.add(buildPhoneType(Phone.TYPE_HOME));
     90             kind.typeList.add(buildPhoneType(Phone.TYPE_MOBILE));
     91             kind.typeList.add(buildPhoneType(Phone.TYPE_WORK));
     92             kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_WORK).setSecondary(true));
     93             kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_HOME).setSecondary(true));
     94             kind.typeList.add(buildPhoneType(Phone.TYPE_PAGER).setSecondary(true));
     95             kind.typeList.add(buildPhoneType(Phone.TYPE_OTHER));
     96             kind.typeList.add(buildPhoneType(Phone.TYPE_CUSTOM).setSecondary(true).setCustomColumn(
     97                     Phone.LABEL));
     98 
     99             kind.fieldList = Lists.newArrayList();
    100             kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup, FLAGS_PHONE));
    101         }
    102 
    103         return kind;
    104     }
    105 
    106     @Override
    107     protected DataKind inflateEmail(Context context, int inflateLevel) {
    108         final DataKind kind = super.inflateEmail(context, ContactsSource.LEVEL_MIMETYPES);
    109 
    110         if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
    111             kind.typeColumn = Email.TYPE;
    112             kind.typeList = Lists.newArrayList();
    113             kind.typeList.add(buildEmailType(Email.TYPE_HOME));
    114             kind.typeList.add(buildEmailType(Email.TYPE_WORK));
    115             kind.typeList.add(buildEmailType(Email.TYPE_OTHER));
    116             kind.typeList.add(buildEmailType(Email.TYPE_CUSTOM).setSecondary(true).setCustomColumn(
    117                     Email.LABEL));
    118 
    119             kind.fieldList = Lists.newArrayList();
    120             kind.fieldList.add(new EditField(Email.DATA, R.string.emailLabelsGroup, FLAGS_EMAIL));
    121         }
    122 
    123         return kind;
    124     }
    125 
    126     @Override
    127     protected DataKind inflateStructuredPostal(Context context, int inflateLevel) {
    128         return super.inflateStructuredPostal(context, inflateLevel);
    129     }
    130 
    131     @Override
    132     protected DataKind inflateIm(Context context, int inflateLevel) {
    133         return super.inflateIm(context, inflateLevel);
    134     }
    135 
    136     @Override
    137     protected DataKind inflateOrganization(Context context, int inflateLevel) {
    138         return super.inflateOrganization(context, inflateLevel);
    139     }
    140 
    141     @Override
    142     protected DataKind inflatePhoto(Context context, int inflateLevel) {
    143         return super.inflatePhoto(context, inflateLevel);
    144     }
    145 
    146     @Override
    147     protected DataKind inflateNote(Context context, int inflateLevel) {
    148         return super.inflateNote(context, inflateLevel);
    149     }
    150 
    151     @Override
    152     protected DataKind inflateWebsite(Context context, int inflateLevel) {
    153         return super.inflateWebsite(context, inflateLevel);
    154     }
    155 
    156     // TODO: this should come from resource in the future
    157     // Note that frameworks/base/core/java/android/pim/vcard/ContactStruct.java also wants
    158     // this String.
    159     private static final String GOOGLE_MY_CONTACTS_GROUP = "System Group: My Contacts";
    160 
    161     public static final void attemptMyContactsMembership(EntityDelta state, Context context) {
    162         final ValuesDelta stateValues = state.getValues();
    163 	stateValues.setFromTemplate(true);
    164         final String accountName = stateValues.getAsString(RawContacts.ACCOUNT_NAME);
    165         final String accountType = stateValues.getAsString(RawContacts.ACCOUNT_TYPE);
    166         attemptMyContactsMembership(state, accountName, accountType, context, true);
    167     }
    168 
    169     public static final void createMyContactsIfNotExist(Account account, Context context) {
    170         attemptMyContactsMembership(null, account.name, account.type, context, true);
    171     }
    172 
    173     /**
    174      *
    175      * @param allowRecur If the group is created between querying/about to create, we recur.  But
    176      *     to prevent excess recursion, we provide a flag to make sure we only do the recursion loop
    177      *     once
    178      */
    179     private static final void attemptMyContactsMembership(EntityDelta state,
    180                 final String accountName, final String accountType, Context context,
    181                 boolean allowRecur) {
    182         final ContentResolver resolver = context.getContentResolver();
    183 
    184         Cursor cursor = resolver.query(Groups.CONTENT_URI,
    185                 new String[] {Groups.TITLE, Groups.SOURCE_ID, Groups.SHOULD_SYNC},
    186                 Groups.ACCOUNT_NAME + " =? AND " + Groups.ACCOUNT_TYPE + " =?",
    187                 new String[] {accountName, accountType}, null);
    188 
    189         boolean myContactsExists = false;
    190         long assignToGroupSourceId = -1;
    191         while (cursor.moveToNext()) {
    192             if (GOOGLE_MY_CONTACTS_GROUP.equals(cursor.getString(0))) {
    193                 myContactsExists = true;
    194             }
    195             if (assignToGroupSourceId == -1 && cursor.getInt(2) != 0) {
    196                 assignToGroupSourceId = cursor.getInt(1);
    197             }
    198 
    199             if (myContactsExists && assignToGroupSourceId != -1) {
    200                 break;
    201             }
    202         }
    203 
    204         if (myContactsExists && state == null) {
    205             return;
    206         }
    207 
    208         try {
    209             final ContentValues values = new ContentValues();
    210             values.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
    211 
    212             if (!myContactsExists) {
    213                 // create the group if it doesn't exist
    214                 final ContentValues newGroup = new ContentValues();
    215                 newGroup.put(Groups.TITLE, GOOGLE_MY_CONTACTS_GROUP);
    216 
    217                 newGroup.put(Groups.ACCOUNT_NAME, accountName);
    218                 newGroup.put(Groups.ACCOUNT_TYPE, accountType);
    219                 newGroup.put(Groups.GROUP_VISIBLE, "1");
    220 
    221                 ArrayList<ContentProviderOperation> operations =
    222                     new ArrayList<ContentProviderOperation>();
    223 
    224                 operations.add(ContentProviderOperation
    225                         .newAssertQuery(Groups.CONTENT_URI)
    226                         .withSelection(Groups.TITLE + "=?",
    227                                 new String[] { GOOGLE_MY_CONTACTS_GROUP })
    228                         .withExpectedCount(0).build());
    229                 operations.add(ContentProviderOperation
    230 
    231                         .newInsert(Groups.CONTENT_URI)
    232                         .withValues(newGroup)
    233                         .build());
    234                 try {
    235                     ContentProviderResult[] results = resolver.applyBatch(
    236                             ContactsContract.AUTHORITY, operations);
    237                     values.put(GroupMembership.GROUP_ROW_ID, ContentUris.parseId(results[1].uri));
    238                 } catch (RemoteException e) {
    239                     throw new IllegalStateException("Problem querying for groups", e);
    240                 } catch (OperationApplicationException e) {
    241                     // the group was created after the query but before we tried to create it
    242                     if (allowRecur) {
    243                         attemptMyContactsMembership(
    244                                 state, accountName, accountType, context, false);
    245                     }
    246                     return;
    247                 }
    248             } else {
    249                 if (assignToGroupSourceId != -1) {
    250                     values.put(GroupMembership.GROUP_SOURCE_ID, assignToGroupSourceId);
    251                 } else {
    252                     // there are no Groups to add this contact to, so don't apply any membership
    253                     // TODO: alert user that their contact will be dropped?
    254                 }
    255             }
    256             if (state != null) {
    257                 state.addEntry(ValuesDelta.fromAfter(values));
    258             }
    259         } finally {
    260             cursor.close();
    261         }
    262     }
    263 
    264     @Override
    265     public int getHeaderColor(Context context) {
    266         return 0xff89c2c2;
    267     }
    268 
    269     @Override
    270     public int getSideBarColor(Context context) {
    271         return 0xff5bb4b4;
    272     }
    273 }
    274