Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a012aec6 authored by Daisuke Miyakawa's avatar Daisuke Miyakawa
Browse files

Make a Singleton for ContactListFilterController

Better fix for issue 5165507 "Contacts to Display filter loses its
setting when going from People to Phone app". Now the whole app
has one instance and People and Phone UIs share it. All notification
will be delivered to both Activities, and thus no hack on onStart()
will be needed.

Also fixes issue 5299160 "Account filter header not updated when account
is removed from device". We need to update filter after
AccountTypeManager finishes its reload operation.

Now filter settings should be saved only from the controller, so this
change removes the code saving filter settings to SharedPreferences from
Activity/Fragment.

Bug: 5165507
Bug: 5299160
Change-Id: I4118271f1a78976af6cb3d432b1dd7b30c18eb7a
parent 7f8d6a5e
Loading
Loading
Loading
Loading
+10 −2
Original line number Diff line number Diff line
@@ -16,14 +16,13 @@

package com.android.contacts;

import com.android.contacts.list.ContactListFilterController;
import com.android.contacts.model.AccountTypeManager;
import com.android.contacts.test.InjectedServices;
import com.android.contacts.util.Constants;
import com.google.common.annotations.VisibleForTesting;

import android.app.Application;
import android.app.FragmentManager;
import android.app.LoaderManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
@@ -35,6 +34,7 @@ public final class ContactsApplication extends Application {
    private static InjectedServices sInjectedServices;
    private AccountTypeManager mAccountTypeManager;
    private ContactPhotoManager mContactPhotoManager;
    private ContactListFilterController mContactListFilterController;

    /**
     * Overrides the system services with mocks for testing.
@@ -95,6 +95,14 @@ public final class ContactsApplication extends Application {
            return mContactPhotoManager;
        }

        if (ContactListFilterController.CONTACT_LIST_FILTER_SERVICE.equals(name)) {
            if (mContactListFilterController == null) {
                mContactListFilterController =
                        ContactListFilterController.createContactListFilterController(this);
            }
            return mContactListFilterController;
        }

        return super.getSystemService(name);
    }

+32 −29
Original line number Diff line number Diff line
@@ -214,6 +214,30 @@ public class DialtactsActivity extends Activity {
    private CallLogFragment mCallLogFragment;
    private PhoneFavoriteFragment mPhoneFavoriteFragment;

    private final ContactListFilterListener mContactListFilterListener =
            new ContactListFilterListener() {
        @Override
        public void onContactListFilterChanged() {
            boolean doInvalidateOptionsMenu = false;

            if (mPhoneFavoriteFragment != null && mPhoneFavoriteFragment.isAdded()) {
                mPhoneFavoriteFragment.setFilter(mContactListFilterController.getFilter());
                doInvalidateOptionsMenu = true;
            }

            if (mSearchFragment != null && mSearchFragment.isAdded()) {
                mSearchFragment.setFilter(mContactListFilterController.getFilter());
                doInvalidateOptionsMenu = true;
            } else {
                Log.w(TAG, "Search Fragment isn't available when ContactListFilter is changed");
            }

            if (doInvalidateOptionsMenu) {
                invalidateOptionsMenu();
            }
        }
    };

    private final TabListener mTabListener = new TabListener() {
        @Override
        public void onTabUnselected(Tab tab, FragmentTransaction ft) {
@@ -371,29 +395,8 @@ public class DialtactsActivity extends Activity {

        setContentView(R.layout.dialtacts_activity);

        mContactListFilterController = new ContactListFilterController(this);
        mContactListFilterController.addListener(new ContactListFilterListener() {
            @Override
            public void onContactListFilterChanged() {
                boolean doInvalidateOptionsMenu = false;

                if (mPhoneFavoriteFragment != null && mPhoneFavoriteFragment.isAdded()) {
                    mPhoneFavoriteFragment.setFilter(mContactListFilterController.getFilter());
                    doInvalidateOptionsMenu = true;
                }

                if (mSearchFragment != null && mSearchFragment.isAdded()) {
                    mSearchFragment.setFilter(mContactListFilterController.getFilter());
                    doInvalidateOptionsMenu = true;
                } else {
                    Log.w(TAG, "Search Fragment isn't available when ContactListFilter is changed");
                }

                if (doInvalidateOptionsMenu) {
                    invalidateOptionsMenu();
                }
            }
        });
        mContactListFilterController = ContactListFilterController.getInstance(this);
        mContactListFilterController.addListener(mContactListFilterListener);

        mViewPager = (ViewPager) findViewById(R.id.pager);
        mViewPager.setAdapter(new ViewPagerAdapter(getFragmentManager()));
@@ -429,12 +432,6 @@ public class DialtactsActivity extends Activity {
    @Override
    public void onStart() {
        super.onStart();
        // Force filter reload to reflect possible filter changes done via People UI.
        //
        // Ideally both (People/Phone) UI should share the same instance for
        // ContactListFilterController and they should be able to receive filter change event
        // from the same controller (Bug 5165507)
        mContactListFilterController.onStart(true);
        if (mPhoneFavoriteFragment != null) {
            mPhoneFavoriteFragment.setFilter(mContactListFilterController.getFilter());
        }
@@ -443,6 +440,12 @@ public class DialtactsActivity extends Activity {
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mContactListFilterController.removeListener(mContactListFilterListener);
    }

    private void prepareSearchView() {
        final View searchViewLayout =
                getLayoutInflater().inflate(R.layout.dialtacts_custom_action_bar, null);
+8 −7
Original line number Diff line number Diff line
@@ -184,8 +184,6 @@ public class PeopleActivity extends ContactsActivity
    public PeopleActivity() {
        mInstanceId = sNextInstanceId.getAndIncrement();
        mIntentResolver = new ContactsIntentResolver(this);
        mContactListFilterController = new ContactListFilterController(this);
        mContactListFilterController.addListener(this);
        mProviderStatusLoader = new ProviderStatusLoader(this);
    }

@@ -246,6 +244,9 @@ public class PeopleActivity extends ContactsActivity
            return;
        }

        mContactListFilterController = ContactListFilterController.getInstance(this);
        mContactListFilterController.addListener(this);

        mIsRecreatedInstance = (savedState != null);
        createViewsAndFragments(savedState);
        if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
@@ -436,7 +437,6 @@ public class PeopleActivity extends ContactsActivity
             */
            configureFragments(!mIsRecreatedInstance);
        }
        mContactListFilterController.onStart(false);
        super.onStart();
    }

@@ -473,6 +473,7 @@ public class PeopleActivity extends ContactsActivity
        if (mActionBarAdapter != null) {
            mActionBarAdapter.setListener(null);
        }
        mContactListFilterController.removeListener(this);
        super.onDestroy();
    }

@@ -859,6 +860,7 @@ public class PeopleActivity extends ContactsActivity
            mAllFragment.setSelectedContactUri(contactUri);
        }

        mAllFragment.setFilter(mContactListFilterController.getFilter());
        mAllFragment.setSearchMode(mActionBarAdapter.isSearchMode());
        mAllFragment.setQueryString(mActionBarAdapter.getQueryString(), false);

@@ -867,13 +869,12 @@ public class PeopleActivity extends ContactsActivity
        } else {
            mAllFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_NONE);
        }

        if (mContactListFilterController.isInitialized()) {
            mAllFragment.setFilter(mContactListFilterController.getFilter());
        }
    }

    private void configureContactListFragment() {
        // Filter may be changed when this Activity is in background.
        mAllFragment.setFilter(mContactListFilterController.getFilter());

        final boolean showSearchResult = mActionBarAdapter.shouldShowSearchResult();
        mAllFragment.setSearchMode(showSearchResult);

+41 −1
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.ContactsContract.Contacts;
import android.text.TextUtils;

/**
@@ -296,4 +295,45 @@ public final class ContactListFilter implements Comparable<ContactListFilter>, P
        }
        return mId;
    }

    public String toDebugString() {
        final StringBuilder builder = new StringBuilder();
        builder.append("[filter type: " + filterType + " (" + filterTypeToString(filterType) + ")");
        if (filterType == FILTER_TYPE_ACCOUNT) {
            builder.append(", accountType: " + accountType)
                    .append(", accountName: " + accountName)
                    .append(", dataSet: " + dataSet);
        }
        if (filterType == FILTER_TYPE_GROUP) {
            builder.append(", groupId: " + groupId)
                    .append(", groupSourceId: " + groupSourceId)
                    .append(", groupReadOnly: " + groupReadOnly)
                    .append("title: " + title);
        }
        builder.append(", icon: " + icon + "]");
        return builder.toString();
    }

    public static final String filterTypeToString(int filterType) {
        switch (filterType) {
            case FILTER_TYPE_DEFAULT:
                return "FILTER_TYPE_DEFAULT";
            case FILTER_TYPE_ALL_ACCOUNTS:
                return "FILTER_TYPE_ALL_ACCOUNTS";
            case FILTER_TYPE_CUSTOM:
                return "FILTER_TYPE_CUSTOM";
            case FILTER_TYPE_STARRED:
                return "FILTER_TYPE_STARRED";
            case FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY:
                return "FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY";
            case FILTER_TYPE_SINGLE_CONTACT:
                return "FILTER_TYPE_SINGLE_CONTACT";
            case FILTER_TYPE_ACCOUNT:
                return "FILTER_TYPE_ACCOUNT";
            case FILTER_TYPE_GROUP:
                return "FILTER_TYPE_GROUP";
            default:
                return "(unknown)";
        }
    }
}
+79 −23
Original line number Diff line number Diff line
@@ -15,7 +15,9 @@
 */
package com.android.contacts.list;

import android.app.Activity;
import com.android.contacts.model.AccountTypeManager;
import com.android.contacts.model.AccountWithDataSet;

import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
@@ -24,48 +26,77 @@ import java.util.ArrayList;
import java.util.List;

/**
 * Stores the {@link ContactListFilter} selected by the user and saves it to
 * {@link SharedPreferences} if necessary.
 * Manages {@link ContactListFilter}. All methods must be called from UI thread.
 */
public class ContactListFilterController {
public abstract class ContactListFilterController {

    public static final String CONTACT_LIST_FILTER_SERVICE = "contactListFilter";

    public interface ContactListFilterListener {
        void onContactListFilterChanged();
    }

    private Context mContext;
    private List<ContactListFilterListener> mListeners = new ArrayList<ContactListFilterListener>();
    private ContactListFilter mFilter;

    private boolean mIsInitialized;
    public static ContactListFilterController getInstance(Context context) {
        return (ContactListFilterController)
                context.getApplicationContext().getSystemService(CONTACT_LIST_FILTER_SERVICE);
    }

    public ContactListFilterController(Activity activity) {
        mContext = activity;
    public static ContactListFilterController
            createContactListFilterController(Context context) {
        return new ContactListFilterControllerImpl(context);
    }

    public abstract void addListener(ContactListFilterListener listener);

    public abstract void removeListener(ContactListFilterListener listener);

    public abstract ContactListFilter getFilter();

    /**
     * @param forceFilterReload when true filter is reloaded even when there's already a cache
     * for it.
     * @param filter the filter
     * @param persistent True when the given filter should be saved soon. False when the filter
     * should not be saved. The latter case may happen when some Intent requires a certain type of
     * UI (e.g. single contact) temporarily.
     */
    public void onStart(boolean forceFilterReload) {
        if (mFilter == null || forceFilterReload) {
            mFilter = ContactListFilter.restoreDefaultPreferences(getSharedPreferences());
        }
        mIsInitialized = true;
    public abstract void setContactListFilter(ContactListFilter filter, boolean persistent);

    public abstract void selectCustomFilter();

    /**
     * Checks if the current filter is valid and reset the filter if not. It may happen when
     * an account is removed while the filter points to the account with
     * {@link ContactListFilter#FILTER_TYPE_ACCOUNT} type, for example.
     */
    public abstract void checkFilterValidity();
}

    public boolean isInitialized() {
        return mIsInitialized;
/**
 * Stores the {@link ContactListFilter} selected by the user and saves it to
 * {@link SharedPreferences} if necessary.
 */
class ContactListFilterControllerImpl extends ContactListFilterController {
    private final Context mContext;
    private final List<ContactListFilterListener> mListeners =
            new ArrayList<ContactListFilterListener>();
    private ContactListFilter mFilter;

    public ContactListFilterControllerImpl(Context context) {
        mContext = context;
        mFilter = ContactListFilter.restoreDefaultPreferences(getSharedPreferences());
        checkFilterValidity();
    }

    @Override
    public void addListener(ContactListFilterListener listener) {
        mListeners.add(listener);
    }

    @Override
    public void removeListener(ContactListFilterListener listener) {
        mListeners.remove(listener);
    }

    @Override
    public ContactListFilter getFilter() {
        return mFilter;
    }
@@ -74,18 +105,20 @@ public class ContactListFilterController {
        return PreferenceManager.getDefaultSharedPreferences(mContext);
    }

    @Override
    public void setContactListFilter(ContactListFilter filter, boolean persistent) {
        if (!filter.equals(mFilter)) {
            mFilter = filter;
            if (persistent) {
                ContactListFilter.storeToPreferences(getSharedPreferences(), mFilter);
            }
            if (mListeners != null) {
            if (!mListeners.isEmpty()) {
                notifyContactListFilterChanged();
            }
        }
    }

    @Override
    public void selectCustomFilter() {
        setContactListFilter(ContactListFilter.createFilterWithType(
                ContactListFilter.FILTER_TYPE_CUSTOM), true);
@@ -97,4 +130,27 @@ public class ContactListFilterController {
        }
    }

    @Override
    public void checkFilterValidity() {
        if (mFilter == null || mFilter.filterType != ContactListFilter.FILTER_TYPE_ACCOUNT) {
            return;
        }

        if (!filterAccountExists()) {
            // The current account filter points to invalid account. Use "all" filter instead.
            setContactListFilter(
                    ContactListFilter.createFilterWithType(
                            ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS), true);
        }
    }

    /**
     * @return true if the Account for the current filter exists.
     */
    private boolean filterAccountExists() {
        final AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(mContext);
        final AccountWithDataSet filterAccount = new AccountWithDataSet(
                mFilter.accountName, mFilter.accountType, mFilter.dataSet);
        return accountTypeManager.contains(filterAccount, false);
    }
}
Loading