1/*
2 * Copyright (C) 2010 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 */
16package com.android.contacts.list;
17
18import android.content.Context;
19import android.content.SharedPreferences;
20import android.preference.PreferenceManager;
21
22import com.android.contacts.model.AccountTypeManager;
23import com.android.contacts.model.account.AccountWithDataSet;
24
25import java.util.ArrayList;
26import java.util.List;
27
28/**
29 * Manages {@link ContactListFilter}. All methods must be called from UI thread.
30 */
31public abstract class ContactListFilterController {
32
33    public static final String CONTACT_LIST_FILTER_SERVICE = "contactListFilter";
34
35    public interface ContactListFilterListener {
36        void onContactListFilterChanged();
37    }
38
39    public static ContactListFilterController getInstance(Context context) {
40        return (ContactListFilterController)
41                context.getApplicationContext().getSystemService(CONTACT_LIST_FILTER_SERVICE);
42    }
43
44    public static ContactListFilterController
45            createContactListFilterController(Context context) {
46        return new ContactListFilterControllerImpl(context);
47    }
48
49    public abstract void addListener(ContactListFilterListener listener);
50
51    public abstract void removeListener(ContactListFilterListener listener);
52
53    /**
54     * Return the currently-active filter.
55     */
56    public abstract ContactListFilter getFilter();
57
58    /**
59     * @param filter the filter
60     * @param persistent True when the given filter should be saved soon. False when the filter
61     * should not be saved. The latter case may happen when some Intent requires a certain type of
62     * UI (e.g. single contact) temporarily.
63     */
64    public abstract void setContactListFilter(ContactListFilter filter, boolean persistent);
65
66    public abstract void selectCustomFilter();
67
68    /**
69     * Checks if the current filter is valid and reset the filter if not. It may happen when
70     * an account is removed while the filter points to the account with
71     * {@link ContactListFilter#FILTER_TYPE_ACCOUNT} type, for example. It may also happen if
72     * the current filter is {@link ContactListFilter#FILTER_TYPE_SINGLE_CONTACT}, in
73     * which case, we should switch to the last saved filter in {@link SharedPreferences}.
74     */
75    public abstract void checkFilterValidity(boolean notifyListeners);
76}
77
78/**
79 * Stores the {@link ContactListFilter} selected by the user and saves it to
80 * {@link SharedPreferences} if necessary.
81 */
82class ContactListFilterControllerImpl extends ContactListFilterController {
83    private final Context mContext;
84    private final List<ContactListFilterListener> mListeners =
85            new ArrayList<ContactListFilterListener>();
86    private ContactListFilter mFilter;
87
88    public ContactListFilterControllerImpl(Context context) {
89        mContext = context;
90        mFilter = ContactListFilter.restoreDefaultPreferences(getSharedPreferences());
91        checkFilterValidity(true /* notify listeners */);
92    }
93
94    @Override
95    public void addListener(ContactListFilterListener listener) {
96        mListeners.add(listener);
97    }
98
99    @Override
100    public void removeListener(ContactListFilterListener listener) {
101        mListeners.remove(listener);
102    }
103
104    @Override
105    public ContactListFilter getFilter() {
106        return mFilter;
107    }
108
109    private SharedPreferences getSharedPreferences() {
110        return PreferenceManager.getDefaultSharedPreferences(mContext);
111    }
112
113    @Override
114    public void setContactListFilter(ContactListFilter filter, boolean persistent) {
115        setContactListFilter(filter, persistent, true);
116    }
117
118    private void setContactListFilter(ContactListFilter filter, boolean persistent,
119            boolean notifyListeners) {
120        if (!filter.equals(mFilter)) {
121            mFilter = filter;
122            if (persistent) {
123                ContactListFilter.storeToPreferences(getSharedPreferences(), mFilter);
124            }
125            if (notifyListeners && !mListeners.isEmpty()) {
126                notifyContactListFilterChanged();
127            }
128        }
129    }
130
131    @Override
132    public void selectCustomFilter() {
133        setContactListFilter(ContactListFilter.createFilterWithType(
134                ContactListFilter.FILTER_TYPE_CUSTOM), true);
135    }
136
137    private void notifyContactListFilterChanged() {
138        for (ContactListFilterListener listener : mListeners) {
139            listener.onContactListFilterChanged();
140        }
141    }
142
143    @Override
144    public void checkFilterValidity(boolean notifyListeners) {
145        if (mFilter == null) {
146            return;
147        }
148
149        switch (mFilter.filterType) {
150            case ContactListFilter.FILTER_TYPE_SINGLE_CONTACT:
151                setContactListFilter(
152                        ContactListFilter.restoreDefaultPreferences(getSharedPreferences()),
153                        false, notifyListeners);
154                break;
155            case ContactListFilter.FILTER_TYPE_ACCOUNT:
156                if (!filterAccountExists()) {
157                    // The current account filter points to invalid account. Use "all" filter
158                    // instead.
159                    setContactListFilter(ContactListFilter.createFilterWithType(
160                            ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS), true, notifyListeners);
161                }
162        }
163    }
164
165    /**
166     * @return true if the Account for the current filter exists.
167     */
168    private boolean filterAccountExists() {
169        final AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(mContext);
170        final AccountWithDataSet filterAccount = new AccountWithDataSet(
171                mFilter.accountName, mFilter.accountType, mFilter.dataSet);
172        return accountTypeManager.contains(filterAccount, false);
173    }
174}
175