| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.contacts.activities; |
| |
| import android.app.ActionBar; |
| import android.app.ActionBar.LayoutParams; |
| import android.app.Activity; |
| import android.app.Fragment; |
| import android.content.ActivityNotFoundException; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.provider.ContactsContract.Contacts; |
| import android.provider.ContactsContract.Intents.Insert; |
| import android.text.TextUtils; |
| import android.util.Log; |
| import android.view.LayoutInflater; |
| import android.view.Menu; |
| import android.view.MenuInflater; |
| import android.view.MenuItem; |
| import android.view.View; |
| import android.view.View.OnClickListener; |
| import android.view.View.OnFocusChangeListener; |
| import android.view.inputmethod.InputMethodManager; |
| import android.widget.SearchView; |
| import android.widget.SearchView.OnCloseListener; |
| import android.widget.SearchView.OnQueryTextListener; |
| import android.widget.Toast; |
| |
| import com.android.contacts.ContactsActivity; |
| import com.android.contacts.R; |
| import com.android.contacts.common.activity.RequestPermissionsActivity; |
| import com.android.contacts.common.list.ContactEntryListFragment; |
| import com.android.contacts.editor.EditorIntents; |
| import com.android.contacts.list.ContactPickerFragment; |
| import com.android.contacts.list.ContactsIntentResolver; |
| import com.android.contacts.list.ContactsRequest; |
| import com.android.contacts.common.list.DirectoryListLoader; |
| import com.android.contacts.list.EmailAddressPickerFragment; |
| import com.android.contacts.list.JoinContactListFragment; |
| import com.android.contacts.list.LegacyPhoneNumberPickerFragment; |
| import com.android.contacts.list.OnContactPickerActionListener; |
| import com.android.contacts.list.OnEmailAddressPickerActionListener; |
| import com.android.contacts.list.UiIntentActions; |
| import com.android.contacts.common.list.OnPhoneNumberPickerActionListener; |
| import com.android.contacts.list.OnPostalAddressPickerActionListener; |
| import com.android.contacts.common.list.PhoneNumberPickerFragment; |
| import com.android.contacts.list.PostalAddressPickerFragment; |
| |
| import java.util.Set; |
| |
| /** |
| * Displays a list of contacts (or phone numbers or postal addresses) for the |
| * purposes of selecting one. |
| */ |
| public class ContactSelectionActivity extends ContactsActivity |
| implements View.OnCreateContextMenuListener, OnQueryTextListener, OnClickListener, |
| OnCloseListener, OnFocusChangeListener { |
| private static final String TAG = "ContactSelectionActivity"; |
| |
| private static final String KEY_ACTION_CODE = "actionCode"; |
| private static final String KEY_SEARCH_MODE = "searchMode"; |
| private static final int DEFAULT_DIRECTORY_RESULT_LIMIT = 20; |
| |
| private ContactsIntentResolver mIntentResolver; |
| protected ContactEntryListFragment<?> mListFragment; |
| |
| private int mActionCode = -1; |
| private boolean mIsSearchMode; |
| private boolean mIsSearchSupported; |
| |
| private ContactsRequest mRequest; |
| private SearchView mSearchView; |
| private View mSearchViewContainer; |
| |
| public ContactSelectionActivity() { |
| mIntentResolver = new ContactsIntentResolver(this); |
| } |
| |
| @Override |
| public void onAttachFragment(Fragment fragment) { |
| if (fragment instanceof ContactEntryListFragment<?>) { |
| mListFragment = (ContactEntryListFragment<?>) fragment; |
| setupActionListener(); |
| } |
| } |
| |
| @Override |
| protected void onCreate(Bundle savedState) { |
| super.onCreate(savedState); |
| |
| if (RequestPermissionsActivity.startPermissionActivity(this)) { |
| return; |
| } |
| |
| if (savedState != null) { |
| mActionCode = savedState.getInt(KEY_ACTION_CODE); |
| mIsSearchMode = savedState.getBoolean(KEY_SEARCH_MODE); |
| } |
| |
| // Extract relevant information from the intent |
| mRequest = mIntentResolver.resolveIntent(getIntent()); |
| if (!mRequest.isValid()) { |
| setResult(RESULT_CANCELED); |
| finish(); |
| return; |
| } |
| |
| configureActivityTitle(); |
| |
| setContentView(R.layout.contact_picker); |
| |
| if (mActionCode != mRequest.getActionCode()) { |
| mActionCode = mRequest.getActionCode(); |
| configureListFragment(); |
| } |
| |
| prepareSearchViewAndActionBar(); |
| } |
| |
| private void prepareSearchViewAndActionBar() { |
| final ActionBar actionBar = getActionBar(); |
| mSearchViewContainer = LayoutInflater.from(actionBar.getThemedContext()) |
| .inflate(R.layout.custom_action_bar, null); |
| mSearchView = (SearchView) mSearchViewContainer.findViewById(R.id.search_view); |
| |
| // Postal address pickers (and legacy pickers) don't support search, so just show |
| // "HomeAsUp" button and title. |
| if (mRequest.getActionCode() == ContactsRequest.ACTION_PICK_POSTAL || |
| mRequest.isLegacyCompatibilityMode()) { |
| mSearchView.setVisibility(View.GONE); |
| if (actionBar != null) { |
| actionBar.setDisplayShowHomeEnabled(true); |
| actionBar.setDisplayHomeAsUpEnabled(true); |
| actionBar.setDisplayShowTitleEnabled(true); |
| } |
| mIsSearchSupported = false; |
| configureSearchMode(); |
| return; |
| } |
| |
| actionBar.setDisplayShowHomeEnabled(true); |
| actionBar.setDisplayHomeAsUpEnabled(true); |
| |
| // In order to make the SearchView look like "shown via search menu", we need to |
| // manually setup its state. See also DialtactsActivity.java and ActionBarAdapter.java. |
| mSearchView.setIconifiedByDefault(true); |
| mSearchView.setQueryHint(getString(R.string.hint_findContacts)); |
| mSearchView.setIconified(false); |
| mSearchView.setFocusable(true); |
| |
| mSearchView.setOnQueryTextListener(this); |
| mSearchView.setOnCloseListener(this); |
| mSearchView.setOnQueryTextFocusChangeListener(this); |
| |
| actionBar.setCustomView(mSearchViewContainer, |
| new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); |
| actionBar.setDisplayShowCustomEnabled(true); |
| |
| mIsSearchSupported = true; |
| configureSearchMode(); |
| } |
| |
| private void configureSearchMode() { |
| final ActionBar actionBar = getActionBar(); |
| if (mIsSearchMode) { |
| actionBar.setDisplayShowTitleEnabled(false); |
| mSearchViewContainer.setVisibility(View.VISIBLE); |
| mSearchView.requestFocus(); |
| } else { |
| actionBar.setDisplayShowTitleEnabled(true); |
| mSearchViewContainer.setVisibility(View.GONE); |
| mSearchView.setQuery(null, true); |
| } |
| invalidateOptionsMenu(); |
| } |
| |
| @Override |
| public boolean onOptionsItemSelected(MenuItem item) { |
| switch (item.getItemId()) { |
| case android.R.id.home: |
| // Go back to previous screen, intending "cancel" |
| setResult(RESULT_CANCELED); |
| onBackPressed(); |
| return true; |
| case R.id.menu_search: |
| mIsSearchMode = !mIsSearchMode; |
| configureSearchMode(); |
| return true; |
| } |
| return super.onOptionsItemSelected(item); |
| } |
| |
| @Override |
| protected void onSaveInstanceState(Bundle outState) { |
| super.onSaveInstanceState(outState); |
| outState.putInt(KEY_ACTION_CODE, mActionCode); |
| outState.putBoolean(KEY_SEARCH_MODE, mIsSearchMode); |
| } |
| |
| private void configureActivityTitle() { |
| if (!TextUtils.isEmpty(mRequest.getActivityTitle())) { |
| setTitle(mRequest.getActivityTitle()); |
| return; |
| } |
| |
| int actionCode = mRequest.getActionCode(); |
| switch (actionCode) { |
| case ContactsRequest.ACTION_INSERT_OR_EDIT_CONTACT: { |
| setTitle(R.string.contactInsertOrEditActivityTitle); |
| break; |
| } |
| |
| case ContactsRequest.ACTION_PICK_CONTACT: { |
| setTitle(R.string.contactPickerActivityTitle); |
| break; |
| } |
| |
| case ContactsRequest.ACTION_PICK_OR_CREATE_CONTACT: { |
| setTitle(R.string.contactPickerActivityTitle); |
| break; |
| } |
| |
| case ContactsRequest.ACTION_CREATE_SHORTCUT_CONTACT: { |
| setTitle(R.string.shortcutActivityTitle); |
| break; |
| } |
| |
| case ContactsRequest.ACTION_PICK_PHONE: { |
| setTitle(R.string.contactPickerActivityTitle); |
| break; |
| } |
| |
| case ContactsRequest.ACTION_PICK_EMAIL: { |
| setTitle(R.string.contactPickerActivityTitle); |
| break; |
| } |
| |
| case ContactsRequest.ACTION_CREATE_SHORTCUT_CALL: { |
| setTitle(R.string.callShortcutActivityTitle); |
| break; |
| } |
| |
| case ContactsRequest.ACTION_CREATE_SHORTCUT_SMS: { |
| setTitle(R.string.messageShortcutActivityTitle); |
| break; |
| } |
| |
| case ContactsRequest.ACTION_PICK_POSTAL: { |
| setTitle(R.string.contactPickerActivityTitle); |
| break; |
| } |
| |
| case ContactsRequest.ACTION_PICK_JOIN: { |
| setTitle(R.string.titleJoinContactDataWith); |
| break; |
| } |
| } |
| } |
| |
| /** |
| * Creates the fragment based on the current request. |
| */ |
| public void configureListFragment() { |
| switch (mActionCode) { |
| case ContactsRequest.ACTION_INSERT_OR_EDIT_CONTACT: { |
| ContactPickerFragment fragment = new ContactPickerFragment(); |
| fragment.setEditMode(true); |
| fragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_NONE); |
| fragment.setCreateContactEnabled(!mRequest.isSearchMode()); |
| mListFragment = fragment; |
| break; |
| } |
| |
| case ContactsRequest.ACTION_DEFAULT: |
| case ContactsRequest.ACTION_PICK_CONTACT: { |
| ContactPickerFragment fragment = new ContactPickerFragment(); |
| fragment.setIncludeProfile(mRequest.shouldIncludeProfile()); |
| mListFragment = fragment; |
| break; |
| } |
| |
| case ContactsRequest.ACTION_PICK_OR_CREATE_CONTACT: { |
| ContactPickerFragment fragment = new ContactPickerFragment(); |
| fragment.setCreateContactEnabled(!mRequest.isSearchMode()); |
| mListFragment = fragment; |
| break; |
| } |
| |
| case ContactsRequest.ACTION_CREATE_SHORTCUT_CONTACT: { |
| ContactPickerFragment fragment = new ContactPickerFragment(); |
| fragment.setShortcutRequested(true); |
| mListFragment = fragment; |
| break; |
| } |
| |
| case ContactsRequest.ACTION_PICK_PHONE: { |
| PhoneNumberPickerFragment fragment = getPhoneNumberPickerFragment(mRequest); |
| mListFragment = fragment; |
| break; |
| } |
| |
| case ContactsRequest.ACTION_PICK_EMAIL: { |
| mListFragment = new EmailAddressPickerFragment(); |
| break; |
| } |
| |
| case ContactsRequest.ACTION_CREATE_SHORTCUT_CALL: { |
| PhoneNumberPickerFragment fragment = getPhoneNumberPickerFragment(mRequest); |
| fragment.setShortcutAction(Intent.ACTION_CALL); |
| |
| mListFragment = fragment; |
| break; |
| } |
| |
| case ContactsRequest.ACTION_CREATE_SHORTCUT_SMS: { |
| PhoneNumberPickerFragment fragment = getPhoneNumberPickerFragment(mRequest); |
| fragment.setShortcutAction(Intent.ACTION_SENDTO); |
| |
| mListFragment = fragment; |
| break; |
| } |
| |
| case ContactsRequest.ACTION_PICK_POSTAL: { |
| PostalAddressPickerFragment fragment = new PostalAddressPickerFragment(); |
| |
| mListFragment = fragment; |
| break; |
| } |
| |
| case ContactsRequest.ACTION_PICK_JOIN: { |
| JoinContactListFragment joinFragment = new JoinContactListFragment(); |
| joinFragment.setTargetContactId(getTargetContactId()); |
| mListFragment = joinFragment; |
| break; |
| } |
| |
| default: |
| throw new IllegalStateException("Invalid action code: " + mActionCode); |
| } |
| |
| // Setting compatibility is no longer needed for PhoneNumberPickerFragment since that logic |
| // has been separated into LegacyPhoneNumberPickerFragment. But we still need to set |
| // compatibility for other fragments. |
| mListFragment.setLegacyCompatibilityMode(mRequest.isLegacyCompatibilityMode()); |
| mListFragment.setDirectoryResultLimit(DEFAULT_DIRECTORY_RESULT_LIMIT); |
| |
| getFragmentManager().beginTransaction() |
| .replace(R.id.list_container, mListFragment) |
| .commitAllowingStateLoss(); |
| } |
| |
| private PhoneNumberPickerFragment getPhoneNumberPickerFragment(ContactsRequest request) { |
| if (mRequest.isLegacyCompatibilityMode()) { |
| return new LegacyPhoneNumberPickerFragment(); |
| } else { |
| return new PhoneNumberPickerFragment(); |
| } |
| } |
| |
| public void setupActionListener() { |
| if (mListFragment instanceof ContactPickerFragment) { |
| ((ContactPickerFragment) mListFragment).setOnContactPickerActionListener( |
| new ContactPickerActionListener()); |
| } else if (mListFragment instanceof PhoneNumberPickerFragment) { |
| ((PhoneNumberPickerFragment) mListFragment).setOnPhoneNumberPickerActionListener( |
| new PhoneNumberPickerActionListener()); |
| } else if (mListFragment instanceof PostalAddressPickerFragment) { |
| ((PostalAddressPickerFragment) mListFragment).setOnPostalAddressPickerActionListener( |
| new PostalAddressPickerActionListener()); |
| } else if (mListFragment instanceof EmailAddressPickerFragment) { |
| ((EmailAddressPickerFragment) mListFragment).setOnEmailAddressPickerActionListener( |
| new EmailAddressPickerActionListener()); |
| } else if (mListFragment instanceof JoinContactListFragment) { |
| ((JoinContactListFragment) mListFragment).setOnContactPickerActionListener( |
| new JoinContactActionListener()); |
| } else { |
| throw new IllegalStateException("Unsupported list fragment type: " + mListFragment); |
| } |
| } |
| |
| private final class ContactPickerActionListener implements OnContactPickerActionListener { |
| @Override |
| public void onCreateNewContactAction() { |
| startCreateNewContactActivity(); |
| } |
| |
| @Override |
| public void onEditContactAction(Uri contactLookupUri) { |
| startActivityAndForwardResult(EditorIntents.createEditContactIntent( |
| contactLookupUri, /* materialPalette =*/ null, /* photoId =*/ -1)); |
| } |
| |
| @Override |
| public void onPickContactAction(Uri contactUri) { |
| returnPickerResult(contactUri); |
| } |
| |
| @Override |
| public void onShortcutIntentCreated(Intent intent) { |
| returnPickerResult(intent); |
| } |
| } |
| |
| private final class PhoneNumberPickerActionListener implements |
| OnPhoneNumberPickerActionListener { |
| @Override |
| public void onPickDataUri(Uri dataUri, boolean isVideoCall, int callInitiationType) { |
| returnPickerResult(dataUri); |
| } |
| |
| @Override |
| public void onPickPhoneNumber(String phoneNumber, boolean isVideoCall, |
| int callInitiationType) { |
| Log.w(TAG, "Unsupported call."); |
| } |
| |
| @Override |
| public void onShortcutIntentCreated(Intent intent) { |
| returnPickerResult(intent); |
| } |
| |
| @Override |
| public void onHomeInActionBarSelected() { |
| ContactSelectionActivity.this.onBackPressed(); |
| } |
| } |
| |
| private final class JoinContactActionListener implements OnContactPickerActionListener { |
| @Override |
| public void onPickContactAction(Uri contactUri) { |
| Intent intent = new Intent(null, contactUri); |
| setResult(RESULT_OK, intent); |
| finish(); |
| } |
| |
| @Override |
| public void onShortcutIntentCreated(Intent intent) { |
| } |
| |
| @Override |
| public void onCreateNewContactAction() { |
| } |
| |
| @Override |
| public void onEditContactAction(Uri contactLookupUri) { |
| } |
| } |
| |
| private final class PostalAddressPickerActionListener implements |
| OnPostalAddressPickerActionListener { |
| @Override |
| public void onPickPostalAddressAction(Uri dataUri) { |
| returnPickerResult(dataUri); |
| } |
| } |
| |
| private final class EmailAddressPickerActionListener implements |
| OnEmailAddressPickerActionListener { |
| @Override |
| public void onPickEmailAddressAction(Uri dataUri) { |
| returnPickerResult(dataUri); |
| } |
| } |
| |
| public void startActivityAndForwardResult(final Intent intent) { |
| intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); |
| |
| // Forward extras to the new activity |
| Bundle extras = getIntent().getExtras(); |
| if (extras != null) { |
| intent.putExtras(extras); |
| } |
| try { |
| startActivity(intent); |
| } catch (ActivityNotFoundException e) { |
| Log.e(TAG, "startActivity() failed: " + e); |
| Toast.makeText(ContactSelectionActivity.this, R.string.missing_app, |
| Toast.LENGTH_SHORT).show(); |
| } |
| finish(); |
| } |
| |
| @Override |
| public boolean onQueryTextChange(String newText) { |
| mListFragment.setQueryString(newText, true); |
| return false; |
| } |
| |
| @Override |
| public boolean onQueryTextSubmit(String query) { |
| return false; |
| } |
| |
| @Override |
| public boolean onClose() { |
| if (!TextUtils.isEmpty(mSearchView.getQuery())) { |
| mSearchView.setQuery(null, true); |
| } |
| return true; |
| } |
| |
| @Override |
| public void onFocusChange(View view, boolean hasFocus) { |
| switch (view.getId()) { |
| case R.id.search_view: { |
| if (hasFocus) { |
| showInputMethod(mSearchView.findFocus()); |
| } |
| } |
| } |
| } |
| |
| public void returnPickerResult(Uri data) { |
| Intent intent = new Intent(); |
| intent.setData(data); |
| returnPickerResult(intent); |
| } |
| |
| public void returnPickerResult(Intent intent) { |
| intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); |
| setResult(RESULT_OK, intent); |
| finish(); |
| } |
| |
| @Override |
| public void onClick(View view) { |
| switch (view.getId()) { |
| case R.id.floating_action_button: { |
| startCreateNewContactActivity(); |
| break; |
| } |
| } |
| } |
| |
| private long getTargetContactId() { |
| Intent intent = getIntent(); |
| final long targetContactId = intent.getLongExtra( |
| UiIntentActions.TARGET_CONTACT_ID_EXTRA_KEY, -1); |
| if (targetContactId == -1) { |
| Log.e(TAG, "Intent " + intent.getAction() + " is missing required extra: " |
| + UiIntentActions.TARGET_CONTACT_ID_EXTRA_KEY); |
| setResult(RESULT_CANCELED); |
| finish(); |
| return -1; |
| } |
| return targetContactId; |
| } |
| |
| private void startCreateNewContactActivity() { |
| Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI); |
| intent.putExtra(ContactEditorActivity.INTENT_KEY_FINISH_ACTIVITY_ON_SAVE_COMPLETED, true); |
| startActivityAndForwardResult(intent); |
| } |
| |
| private void showInputMethod(View view) { |
| final InputMethodManager imm = (InputMethodManager) |
| getSystemService(Context.INPUT_METHOD_SERVICE); |
| if (imm != null) { |
| if (!imm.showSoftInput(view, 0)) { |
| Log.w(TAG, "Failed to show soft input method."); |
| } |
| } |
| } |
| |
| @Override |
| public boolean onCreateOptionsMenu(Menu menu) { |
| super.onCreateOptionsMenu(menu); |
| |
| final MenuInflater inflater = getMenuInflater(); |
| inflater.inflate(R.menu.search_menu, menu); |
| |
| final MenuItem searchItem = menu.findItem(R.id.menu_search); |
| searchItem.setVisible(!mIsSearchMode && mIsSearchSupported); |
| return true; |
| } |
| |
| @Override |
| public void onBackPressed() { |
| if (!isSafeToCommitTransactions()) { |
| return; |
| } |
| |
| if (mIsSearchMode) { |
| mIsSearchMode = false; |
| configureSearchMode(); |
| } else { |
| super.onBackPressed(); |
| } |
| } |
| } |