1/*
2 * Copyright (C) 2015 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
17package com.android.messaging.datamodel.data;
18
19import android.app.LoaderManager;
20import android.content.Context;
21import android.content.Loader;
22import android.database.Cursor;
23import android.os.Bundle;
24
25import com.android.messaging.datamodel.BoundCursorLoader;
26import com.android.messaging.datamodel.FrequentContactsCursorBuilder;
27import com.android.messaging.datamodel.MessagingContentProvider;
28import com.android.messaging.datamodel.binding.BindableData;
29import com.android.messaging.datamodel.binding.BindingBase;
30import com.android.messaging.sms.MmsConfig;
31import com.android.messaging.util.Assert;
32import com.android.messaging.util.ContactUtil;
33import com.android.messaging.util.LogUtil;
34
35/**
36 * Class to access phone contacts.
37 * The caller is responsible for ensuring that the app has READ_CONTACTS permission (see
38 * {@link ContactUtil#hasReadContactsPermission()}) before instantiating this class.
39 */
40public class ContactPickerData extends BindableData implements
41        LoaderManager.LoaderCallbacks<Cursor> {
42    public interface ContactPickerDataListener {
43        void onAllContactsCursorUpdated(Cursor data);
44        void onFrequentContactsCursorUpdated(Cursor data);
45        void onContactCustomColorLoaded(ContactPickerData data);
46    }
47
48    private static final String BINDING_ID = "bindingId";
49    private final Context mContext;
50    private LoaderManager mLoaderManager;
51    private ContactPickerDataListener mListener;
52    private final FrequentContactsCursorBuilder mFrequentContactsCursorBuilder;
53
54    public ContactPickerData(final Context context, final ContactPickerDataListener listener) {
55        mListener = listener;
56        mContext = context;
57        mFrequentContactsCursorBuilder = new FrequentContactsCursorBuilder();
58    }
59
60    private static final int ALL_CONTACTS_LOADER = 1;
61    private static final int FREQUENT_CONTACTS_LOADER = 2;
62    private static final int PARTICIPANT_LOADER = 3;
63
64    @Override
65    public Loader<Cursor> onCreateLoader(final int id, final Bundle args) {
66        final String bindingId = args.getString(BINDING_ID);
67        // Check if data still bound to the requesting ui element
68        if (isBound(bindingId)) {
69            switch (id) {
70                case ALL_CONTACTS_LOADER:
71                    return ContactUtil.getPhones(mContext)
72                            .createBoundCursorLoader(bindingId);
73                case FREQUENT_CONTACTS_LOADER:
74                    return ContactUtil.getFrequentContacts(mContext)
75                            .createBoundCursorLoader(bindingId);
76                case PARTICIPANT_LOADER:
77                    return new BoundCursorLoader(bindingId, mContext,
78                            MessagingContentProvider.PARTICIPANTS_URI,
79                            ParticipantData.ParticipantsQuery.PROJECTION, null, null, null);
80                default:
81                    Assert.fail("Unknown loader id for contact picker!");
82                    break;
83            }
84        } else {
85            LogUtil.w(LogUtil.BUGLE_TAG, "Loader created after unbinding the contacts list");
86        }
87        return null;
88    }
89
90    /**
91     * {@inheritDoc}
92     */
93    @Override
94    public void onLoadFinished(final Loader<Cursor> loader, final Cursor data) {
95        final BoundCursorLoader cursorLoader = (BoundCursorLoader) loader;
96        if (isBound(cursorLoader.getBindingId())) {
97            switch (loader.getId()) {
98                case ALL_CONTACTS_LOADER:
99                    mListener.onAllContactsCursorUpdated(data);
100                    mFrequentContactsCursorBuilder.setAllContacts(data);
101                    break;
102                case FREQUENT_CONTACTS_LOADER:
103                    mFrequentContactsCursorBuilder.setFrequents(data);
104                    break;
105                case PARTICIPANT_LOADER:
106                    mListener.onContactCustomColorLoaded(this);
107                    break;
108                default:
109                    Assert.fail("Unknown loader id for contact picker!");
110                    break;
111            }
112
113            if (loader.getId() != PARTICIPANT_LOADER) {
114                // The frequent contacts cursor to be used in the UI depends on results from both
115                // all contacts and frequent contacts loader, and we don't know which will finish
116                // first. Therefore, try to build the cursor and notify the listener if it's
117                // successfully built.
118                final Cursor frequentContactsCursor = mFrequentContactsCursorBuilder.build();
119                if (frequentContactsCursor != null) {
120                    mListener.onFrequentContactsCursorUpdated(frequentContactsCursor);
121                }
122            }
123        } else {
124            LogUtil.w(LogUtil.BUGLE_TAG, "Loader finished after unbinding the contacts list");
125        }
126    }
127
128    /**
129     * {@inheritDoc}
130     */
131    @Override
132    public void onLoaderReset(final Loader<Cursor> loader) {
133        final BoundCursorLoader cursorLoader = (BoundCursorLoader) loader;
134        if (isBound(cursorLoader.getBindingId())) {
135            switch (loader.getId()) {
136                case ALL_CONTACTS_LOADER:
137                    mListener.onAllContactsCursorUpdated(null);
138                    mFrequentContactsCursorBuilder.setAllContacts(null);
139                    break;
140                case FREQUENT_CONTACTS_LOADER:
141                    mListener.onFrequentContactsCursorUpdated(null);
142                    mFrequentContactsCursorBuilder.setFrequents(null);
143                    break;
144                case PARTICIPANT_LOADER:
145                    mListener.onContactCustomColorLoaded(this);
146                    break;
147                default:
148                    Assert.fail("Unknown loader id for contact picker!");
149                    break;
150            }
151        } else {
152            LogUtil.w(LogUtil.BUGLE_TAG, "Loader reset after unbinding the contacts list");
153        }
154    }
155
156    public void init(final LoaderManager loaderManager,
157                     final BindingBase<ContactPickerData> binding) {
158        final Bundle args = new Bundle();
159        args.putString(BINDING_ID, binding.getBindingId());
160        mLoaderManager = loaderManager;
161        mLoaderManager.initLoader(ALL_CONTACTS_LOADER, args, this);
162        mLoaderManager.initLoader(FREQUENT_CONTACTS_LOADER, args, this);
163        mLoaderManager.initLoader(PARTICIPANT_LOADER, args, this);
164    }
165
166    @Override
167    protected void unregisterListeners() {
168        mListener = null;
169
170
171        // This could be null if we bind but the caller doesn't init the BindableData
172        if (mLoaderManager != null) {
173            mLoaderManager.destroyLoader(ALL_CONTACTS_LOADER);
174            mLoaderManager.destroyLoader(FREQUENT_CONTACTS_LOADER);
175            mLoaderManager.destroyLoader(PARTICIPANT_LOADER);
176            mLoaderManager = null;
177        }
178        mFrequentContactsCursorBuilder.resetBuilder();
179    }
180
181    public static boolean isTooManyParticipants(final int participantCount) {
182        // When creating a conversation, the conversation will be created using the system's
183        // default SIM, so use the default MmsConfig's recipient limit.
184        return (participantCount > MmsConfig.get(ParticipantData.DEFAULT_SELF_SUB_ID)
185                .getRecipientLimit());
186    }
187
188    public static boolean getCanAddMoreParticipants(final int participantCount) {
189        // When creating a conversation, the conversation will be created using the system's
190        // default SIM, so use the default MmsConfig's recipient limit.
191        return (participantCount < MmsConfig.get(ParticipantData.DEFAULT_SELF_SUB_ID)
192                .getRecipientLimit());
193    }
194}
195