Home | History | Annotate | Download | only in preference
      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  */
     16 
     17 package com.android.contacts.preference;
     18 
     19 import android.app.Activity;
     20 import android.app.LoaderManager;
     21 import android.content.BroadcastReceiver;
     22 import android.content.ContentUris;
     23 import android.content.Context;
     24 import android.content.CursorLoader;
     25 import android.content.Intent;
     26 import android.content.IntentFilter;
     27 import android.content.Loader;
     28 import android.content.res.Resources;
     29 import android.database.Cursor;
     30 import android.net.Uri;
     31 import android.os.Bundle;
     32 import android.preference.Preference;
     33 import android.preference.PreferenceFragment;
     34 import android.provider.BlockedNumberContract;
     35 import android.provider.ContactsContract.Contacts;
     36 import android.provider.ContactsContract.Profile;
     37 import android.support.design.widget.Snackbar;
     38 import android.support.v4.content.LocalBroadcastManager;
     39 import android.telecom.TelecomManager;
     40 import android.telephony.TelephonyManager;
     41 import android.view.LayoutInflater;
     42 import android.view.View;
     43 import android.view.ViewGroup;
     44 import android.widget.FrameLayout;
     45 
     46 import com.android.contacts.ContactsUtils;
     47 import com.android.contacts.R;
     48 import com.android.contacts.SimImportService;
     49 import com.android.contacts.compat.TelecomManagerUtil;
     50 import com.android.contacts.compat.TelephonyManagerCompat;
     51 import com.android.contacts.interactions.ExportDialogFragment;
     52 import com.android.contacts.interactions.ImportDialogFragment;
     53 import com.android.contacts.list.ContactListFilter;
     54 import com.android.contacts.list.ContactListFilterController;
     55 import com.android.contacts.logging.ScreenEvent.ScreenType;
     56 import com.android.contacts.model.AccountTypeManager;
     57 import com.android.contacts.model.account.AccountInfo;
     58 import com.android.contacts.model.account.AccountsLoader;
     59 import com.android.contacts.util.AccountFilterUtil;
     60 import com.android.contacts.util.ImplicitIntentsUtil;
     61 import com.android.contactsbind.HelpUtils;
     62 
     63 import java.util.List;
     64 
     65 /**
     66  * This fragment shows the preferences for "display options"
     67  */
     68 public class DisplayOptionsPreferenceFragment extends PreferenceFragment
     69         implements Preference.OnPreferenceClickListener, AccountsLoader.AccountsListener {
     70 
     71     private static final int REQUEST_CODE_CUSTOM_CONTACTS_FILTER = 0;
     72 
     73     private static final String ARG_CONTACTS_AVAILABLE = "are_contacts_available";
     74     private static final String ARG_NEW_LOCAL_PROFILE = "new_local_profile";
     75 
     76     private static final String KEY_ABOUT = "about";
     77     private static final String KEY_ACCOUNTS = "accounts";
     78     private static final String KEY_DEFAULT_ACCOUNT = "defaultAccount";
     79     private static final String KEY_BLOCKED_NUMBERS = "blockedNumbers";
     80     private static final String KEY_DISPLAY_ORDER = "displayOrder";
     81     private static final String KEY_CUSTOM_CONTACTS_FILTER = "customContactsFilter";
     82     private static final String KEY_IMPORT = "import";
     83     private static final String KEY_EXPORT = "export";
     84     private static final String KEY_MY_INFO = "myInfo";
     85     private static final String KEY_SORT_ORDER = "sortOrder";
     86     private static final String KEY_PHONETIC_NAME_DISPLAY = "phoneticNameDisplay";
     87 
     88     private static final int LOADER_PROFILE = 0;
     89     private static final int LOADER_ACCOUNTS = 1;
     90 
     91     /**
     92      * Callbacks for hosts of the {@link DisplayOptionsPreferenceFragment}.
     93      */
     94     public interface ProfileListener  {
     95         /**
     96          * Invoked after profile has been loaded.
     97          */
     98         void onProfileLoaded(Cursor data);
     99     }
    100 
    101     /**
    102      * The projections that are used to obtain user profile
    103      */
    104     public static class ProfileQuery {
    105         /**
    106          * Not instantiable.
    107          */
    108         private ProfileQuery() {}
    109 
    110         private static final String[] PROFILE_PROJECTION_PRIMARY = new String[] {
    111                 Contacts._ID,                           // 0
    112                 Contacts.DISPLAY_NAME_PRIMARY,          // 1
    113                 Contacts.IS_USER_PROFILE,               // 2
    114         };
    115 
    116         private static final String[] PROFILE_PROJECTION_ALTERNATIVE = new String[] {
    117                 Contacts._ID,                           // 0
    118                 Contacts.DISPLAY_NAME_ALTERNATIVE,      // 1
    119                 Contacts.IS_USER_PROFILE,               // 2
    120         };
    121 
    122         public static final int CONTACT_ID               = 0;
    123         public static final int CONTACT_DISPLAY_NAME     = 1;
    124         public static final int CONTACT_IS_USER_PROFILE  = 2;
    125     }
    126 
    127     private String mNewLocalProfileExtra;
    128     private boolean mAreContactsAvailable;
    129 
    130     private boolean mHasProfile;
    131     private long mProfileContactId;
    132 
    133     private Preference mMyInfoPreference;
    134 
    135     private ProfileListener mListener;
    136 
    137     private ViewGroup mRootView;
    138     private SaveServiceResultListener mSaveServiceListener;
    139 
    140     private final LoaderManager.LoaderCallbacks<Cursor> mProfileLoaderListener =
    141             new LoaderManager.LoaderCallbacks<Cursor>() {
    142 
    143         @Override
    144         public CursorLoader onCreateLoader(int id, Bundle args) {
    145             final CursorLoader loader = createCursorLoader(getContext());
    146             loader.setUri(Profile.CONTENT_URI);
    147             loader.setProjection(getProjection(getContext()));
    148             return loader;
    149         }
    150 
    151         @Override
    152         public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    153             if (mListener != null) {
    154                 mListener.onProfileLoaded(data);
    155             }
    156         }
    157 
    158         public void onLoaderReset(Loader<Cursor> loader) {
    159         }
    160     };
    161 
    162     public static DisplayOptionsPreferenceFragment newInstance(String newLocalProfileExtra,
    163             boolean areContactsAvailable) {
    164         final DisplayOptionsPreferenceFragment fragment = new DisplayOptionsPreferenceFragment();
    165         final Bundle args = new Bundle();
    166         args.putString(ARG_NEW_LOCAL_PROFILE, newLocalProfileExtra);
    167         args.putBoolean(ARG_CONTACTS_AVAILABLE, areContactsAvailable);
    168         fragment.setArguments(args);
    169         return fragment;
    170     }
    171 
    172     @Override
    173     public void onAttach(Activity activity) {
    174         super.onAttach(activity);
    175         try {
    176             mListener = (ProfileListener) activity;
    177         } catch (ClassCastException e) {
    178             throw new ClassCastException(activity.toString() + " must implement ProfileListener");
    179         }
    180     }
    181 
    182     @Override
    183     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    184         // Wrap the preference view in a FrameLayout so we can show a snackbar
    185         mRootView = new FrameLayout(getActivity());
    186         final View list = super.onCreateView(inflater, mRootView, savedInstanceState);
    187         mRootView.addView(list);
    188         return mRootView;
    189     }
    190 
    191     @Override
    192     public void onViewCreated(View view, Bundle savedInstanceState) {
    193         super.onViewCreated(view, savedInstanceState);
    194 
    195         mSaveServiceListener = new SaveServiceResultListener();
    196         LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
    197                 mSaveServiceListener,
    198                 new IntentFilter(SimImportService.BROADCAST_SIM_IMPORT_COMPLETE));
    199     }
    200 
    201     @Override
    202     public void onCreate(Bundle savedInstanceState) {
    203         super.onCreate(savedInstanceState);
    204 
    205         // Load the preferences from an XML resource
    206         addPreferencesFromResource(R.xml.preference_display_options);
    207 
    208         final Bundle args = getArguments();
    209         mNewLocalProfileExtra = args.getString(ARG_NEW_LOCAL_PROFILE);
    210         mAreContactsAvailable = args.getBoolean(ARG_CONTACTS_AVAILABLE);
    211 
    212         removeUnsupportedPreferences();
    213 
    214         mMyInfoPreference = findPreference(KEY_MY_INFO);
    215 
    216         final Preference accountsPreference = findPreference(KEY_ACCOUNTS);
    217         accountsPreference.setOnPreferenceClickListener(this);
    218 
    219         final Preference importPreference = findPreference(KEY_IMPORT);
    220         importPreference.setOnPreferenceClickListener(this);
    221 
    222         final Preference exportPreference = findPreference(KEY_EXPORT);
    223         if (exportPreference != null) {
    224             exportPreference.setOnPreferenceClickListener(this);
    225         }
    226 
    227         final Preference blockedNumbersPreference = findPreference(KEY_BLOCKED_NUMBERS);
    228         if (blockedNumbersPreference != null) {
    229             blockedNumbersPreference.setOnPreferenceClickListener(this);
    230         }
    231 
    232         final Preference aboutPreference = findPreference(KEY_ABOUT);
    233         if (aboutPreference != null) {
    234             aboutPreference.setOnPreferenceClickListener(this);
    235         }
    236 
    237         final Preference customFilterPreference = findPreference(KEY_CUSTOM_CONTACTS_FILTER);
    238         if (customFilterPreference != null) {
    239             customFilterPreference.setOnPreferenceClickListener(this);
    240             setCustomContactsFilterSummary();
    241         }
    242     }
    243 
    244     @Override
    245     public void onActivityCreated(Bundle savedInstanceState) {
    246         super.onActivityCreated(savedInstanceState);
    247         getLoaderManager().initLoader(LOADER_PROFILE, null, mProfileLoaderListener);
    248         AccountsLoader.loadAccounts(this, LOADER_ACCOUNTS, AccountTypeManager.writableFilter());
    249     }
    250 
    251     @Override
    252     public void onDestroyView() {
    253         super.onDestroyView();
    254         LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(mSaveServiceListener);
    255         mRootView = null;
    256     }
    257 
    258     public void updateMyInfoPreference(boolean hasProfile, String displayName, long contactId) {
    259         final CharSequence summary = hasProfile ? displayName : getString(R.string.set_up_profile);
    260         mMyInfoPreference.setSummary(summary);
    261         mHasProfile = hasProfile;
    262         mProfileContactId = contactId;
    263         mMyInfoPreference.setOnPreferenceClickListener(this);
    264     }
    265 
    266     private void removeUnsupportedPreferences() {
    267         // Disable sort order for CJK locales where it is not supported
    268         final Resources resources = getResources();
    269         if (!resources.getBoolean(R.bool.config_sort_order_user_changeable)) {
    270             getPreferenceScreen().removePreference(findPreference(KEY_SORT_ORDER));
    271         }
    272 
    273         if (!resources.getBoolean(R.bool.config_phonetic_name_display_user_changeable)) {
    274             getPreferenceScreen().removePreference(findPreference(KEY_PHONETIC_NAME_DISPLAY));
    275         }
    276 
    277         if (HelpUtils.isHelpAndFeedbackAvailable()) {
    278             getPreferenceScreen().removePreference(findPreference(KEY_ABOUT));
    279         }
    280 
    281         // Disable display order for CJK locales as well
    282         if (!resources.getBoolean(R.bool.config_display_order_user_changeable)) {
    283             getPreferenceScreen().removePreference(findPreference(KEY_DISPLAY_ORDER));
    284         }
    285 
    286         final boolean isPhone = TelephonyManagerCompat.isVoiceCapable(
    287                 (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE));
    288         final boolean showBlockedNumbers = isPhone && ContactsUtils.FLAG_N_FEATURE
    289                 && BlockedNumberContract.canCurrentUserBlockNumbers(getContext());
    290         if (!showBlockedNumbers) {
    291             getPreferenceScreen().removePreference(findPreference(KEY_BLOCKED_NUMBERS));
    292         }
    293 
    294         if (!mAreContactsAvailable) {
    295             getPreferenceScreen().removePreference(findPreference(KEY_EXPORT));
    296         }
    297     }
    298 
    299     @Override
    300     public void onAccountsLoaded(List<AccountInfo> accounts) {
    301         // Hide accounts preferences if no writable accounts exist
    302         final DefaultAccountPreference preference =
    303                 (DefaultAccountPreference) findPreference(KEY_DEFAULT_ACCOUNT);
    304         preference.setAccounts(accounts);
    305     }
    306 
    307     @Override
    308     public Context getContext() {
    309         return getActivity();
    310     }
    311 
    312     private CursorLoader createCursorLoader(Context context) {
    313         return new CursorLoader(context) {
    314             @Override
    315             protected Cursor onLoadInBackground() {
    316                 try {
    317                     return super.onLoadInBackground();
    318                 } catch (RuntimeException e) {
    319                     return null;
    320                 }
    321             }
    322         };
    323     }
    324 
    325     private String[] getProjection(Context context) {
    326         final ContactsPreferences contactsPrefs = new ContactsPreferences(context);
    327         final int displayOrder = contactsPrefs.getDisplayOrder();
    328         if (displayOrder == ContactsPreferences.DISPLAY_ORDER_PRIMARY) {
    329             return ProfileQuery.PROFILE_PROJECTION_PRIMARY;
    330         }
    331         return ProfileQuery.PROFILE_PROJECTION_ALTERNATIVE;
    332     }
    333 
    334     @Override
    335     public boolean onPreferenceClick(Preference p) {
    336         final String prefKey = p.getKey();
    337 
    338         if (KEY_ABOUT.equals(prefKey)) {
    339             ((ContactsPreferenceActivity) getActivity()).showAboutFragment();
    340             return true;
    341         } else if (KEY_IMPORT.equals(prefKey)) {
    342             ImportDialogFragment.show(getFragmentManager());
    343             return true;
    344         } else if (KEY_EXPORT.equals(prefKey)) {
    345             ExportDialogFragment.show(getFragmentManager(), ContactsPreferenceActivity.class,
    346                     ExportDialogFragment.EXPORT_MODE_ALL_CONTACTS);
    347             return true;
    348         } else if (KEY_MY_INFO.equals(prefKey)) {
    349             if (mHasProfile) {
    350                 final Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, mProfileContactId);
    351                 ImplicitIntentsUtil.startQuickContact(getActivity(), uri, ScreenType.ME_CONTACT);
    352             } else {
    353                 final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
    354                 intent.putExtra(mNewLocalProfileExtra, true);
    355                 ImplicitIntentsUtil.startActivityInApp(getActivity(), intent);
    356             }
    357             return true;
    358         } else if (KEY_ACCOUNTS.equals(prefKey)) {
    359             ImplicitIntentsUtil.startActivityOutsideApp(getContext(),
    360                     ImplicitIntentsUtil.getIntentForAddingAccount());
    361             return true;
    362         } else if (KEY_BLOCKED_NUMBERS.equals(prefKey)) {
    363             final Intent intent = TelecomManagerUtil.createManageBlockedNumbersIntent(
    364                     (TelecomManager) getContext().getSystemService(Context.TELECOM_SERVICE));
    365             startActivity(intent);
    366             return true;
    367         } else if (KEY_CUSTOM_CONTACTS_FILTER.equals(prefKey)) {
    368             final ContactListFilter filter =
    369                     ContactListFilterController.getInstance(getContext()).getFilter();
    370             AccountFilterUtil.startAccountFilterActivityForResult(
    371                     this, REQUEST_CODE_CUSTOM_CONTACTS_FILTER, filter);
    372         }
    373         return false;
    374     }
    375 
    376     @Override
    377     public void onActivityResult(int requestCode, int resultCode, Intent data) {
    378         if (requestCode == REQUEST_CODE_CUSTOM_CONTACTS_FILTER
    379                 && resultCode == Activity.RESULT_OK) {
    380             AccountFilterUtil.handleAccountFilterResult(
    381                     ContactListFilterController.getInstance(getContext()), resultCode, data);
    382             setCustomContactsFilterSummary();
    383         } else {
    384             super.onActivityResult(requestCode, resultCode, data);
    385         }
    386     }
    387 
    388     private void setCustomContactsFilterSummary() {
    389         final Preference customFilterPreference = findPreference(KEY_CUSTOM_CONTACTS_FILTER);
    390         if (customFilterPreference != null) {
    391             final ContactListFilter filter =
    392                     ContactListFilterController.getInstance(getContext()).getPersistedFilter();
    393             if (filter != null) {
    394                 if (filter.filterType == ContactListFilter.FILTER_TYPE_DEFAULT ||
    395                         filter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
    396                     customFilterPreference.setSummary(R.string.list_filter_all_accounts);
    397                 } else if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
    398                     customFilterPreference.setSummary(R.string.listCustomView);
    399                 } else {
    400                     customFilterPreference.setSummary(null);
    401                 }
    402             }
    403         }
    404     }
    405 
    406     private class SaveServiceResultListener extends BroadcastReceiver {
    407         @Override
    408         public void onReceive(Context context, Intent intent) {
    409             final long now = System.currentTimeMillis();
    410             final long opStart = intent.getLongExtra(
    411                     SimImportService.EXTRA_OPERATION_REQUESTED_AT_TIME, now);
    412 
    413             // If it's been over 30 seconds the user is likely in a different context so suppress
    414             // the toast message.
    415             if (now - opStart > 30*1000) return;
    416 
    417             final int code = intent.getIntExtra(SimImportService.EXTRA_RESULT_CODE,
    418                     SimImportService.RESULT_UNKNOWN);
    419             final int count = intent.getIntExtra(SimImportService.EXTRA_RESULT_COUNT, -1);
    420             if (code == SimImportService.RESULT_SUCCESS && count > 0) {
    421                 Snackbar.make(mRootView, getResources().getQuantityString(
    422                         R.plurals.sim_import_success_toast_fmt, count, count),
    423                         Snackbar.LENGTH_LONG).show();
    424             } else if (code == SimImportService.RESULT_FAILURE) {
    425                 Snackbar.make(mRootView, R.string.sim_import_failed_toast,
    426                         Snackbar.LENGTH_LONG).show();
    427             }
    428         }
    429     }
    430 }
    431 
    432