diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2017-09-11 20:05:36 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2017-09-11 20:05:36 +0000 |
commit | 03249752afd7d870ff708bf47d687a1643367ec5 (patch) | |
tree | f0b6004c5f2d035f0f4c177455bea79c5047d1a4 /java/com/android/dialer | |
parent | 3f14c296552a7af353094e420aa00dd0bf217268 (diff) | |
parent | 21e24b6d9355e5e64a594b3b32cb4c8301e3330b (diff) |
Merge changes I6af1fc7d,I0627d54a
* changes:
Automated g4 rollback of changelist 167310802.
Automated g4 rollback of changelist 167332236.
Diffstat (limited to 'java/com/android/dialer')
12 files changed, 1263 insertions, 55 deletions
diff --git a/java/com/android/dialer/app/AndroidManifest.xml b/java/com/android/dialer/app/AndroidManifest.xml index 1c04b764c..4200082a6 100644 --- a/java/com/android/dialer/app/AndroidManifest.xml +++ b/java/com/android/dialer/app/AndroidManifest.xml @@ -61,6 +61,18 @@ <application android:theme="@style/Theme.AppCompat"> <activity + android:exported="false" + android:label="@string/manage_blocked_numbers_label" + android:name="com.android.dialer.app.filterednumber.BlockedNumbersSettingsActivity" + android:parentActivityName="com.android.dialer.app.settings.DialerSettingsActivity" + android:theme="@style/ManageBlockedNumbersStyle"> + <intent-filter> + <action android:name="com.android.dialer.action.BLOCKED_NUMBERS_SETTINGS"/> + <category android:name="android.intent.category.DEFAULT"/> + </intent-filter> + </activity> + + <activity android:label="@string/call_log_activity_title" android:name="com.android.dialer.app.calllog.CallLogActivity" android:theme="@style/DialtactsThemeWithoutActionBarOverlay"> diff --git a/java/com/android/dialer/app/DialtactsActivity.java b/java/com/android/dialer/app/DialtactsActivity.java index 7f5a9b94a..13b6eb92c 100644 --- a/java/com/android/dialer/app/DialtactsActivity.java +++ b/java/com/android/dialer/app/DialtactsActivity.java @@ -49,6 +49,7 @@ import android.text.TextUtils; import android.text.TextWatcher; import android.view.DragEvent; import android.view.Gravity; +import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; @@ -168,7 +169,6 @@ public class DialtactsActivity extends TransactionSafeActivity private static final String TAG = "DialtactsActivity"; private static final String KEY_IN_REGULAR_SEARCH_UI = "in_regular_search_ui"; private static final String KEY_IN_DIALPAD_SEARCH_UI = "in_dialpad_search_ui"; - private static final String KEY_IN_NEW_SEARCH_UI = "in_new_search_ui"; private static final String KEY_SEARCH_QUERY = "search_query"; private static final String KEY_FIRST_LAUNCH = "first_launch"; private static final String KEY_WAS_CONFIGURATION_CHANGE = "was_configuration_change"; @@ -213,8 +213,6 @@ public class DialtactsActivity extends TransactionSafeActivity */ private boolean mStateSaved; - private boolean mIsKeyboardOpen; - private boolean mInNewSearch; private boolean mIsRestarting; private boolean mInDialpadSearch; private boolean mInRegularSearch; @@ -332,6 +330,27 @@ public class DialtactsActivity extends TransactionSafeActivity private int mActionBarHeight; private int mPreviouslySelectedTabIndex; + /** Handles the user closing the soft keyboard. */ + private final View.OnKeyListener mSearchEditTextLayoutListener = + new View.OnKeyListener() { + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) { + if (TextUtils.isEmpty(mSearchView.getText().toString())) { + // If the search term is empty, close the search UI. + PerformanceReport.recordClick(UiAction.Type.CLOSE_SEARCH_WITH_HIDE_BUTTON); + maybeExitSearchUi(); + } else { + // If the search term is not empty, show the dialpad fab. + if (!mFloatingActionButtonController.isVisible()) { + PerformanceReport.recordClick(UiAction.Type.HIDE_KEYBOARD_IN_SEARCH); + } + showFabInSearchUi(); + } + } + return false; + } + }; /** * The text returned from a voice search query. Set in {@link #onActivityResult} and used in @@ -391,20 +410,30 @@ public class DialtactsActivity extends TransactionSafeActivity SearchEditTextLayout searchEditTextLayout = actionBar.getCustomView().findViewById(R.id.search_view_container); + searchEditTextLayout.setPreImeKeyListener(mSearchEditTextLayoutListener); mActionBarController = new ActionBarController(this, searchEditTextLayout); mSearchView = searchEditTextLayout.findViewById(R.id.search_view); mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener); mSearchView.setHint(getSearchBoxHint()); - mVoiceSearchButton = searchEditTextLayout.findViewById(R.id.voice_search_button); searchEditTextLayout .findViewById(R.id.search_box_collapsed) .setOnClickListener(mSearchViewOnClickListener); - searchEditTextLayout - .findViewById(R.id.search_back_button) - .setOnClickListener(v -> exitSearchUi()); + searchEditTextLayout.setCallback( + new SearchEditTextLayout.Callback() { + @Override + public void onBackButtonClicked() { + onBackPressed(); + } + + @Override + public void onSearchViewClicked() { + // Hide FAB, as the keyboard is shown. + mFloatingActionButtonController.scaleOut(); + } + }); mIsLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; @@ -431,7 +460,6 @@ public class DialtactsActivity extends TransactionSafeActivity mSearchQuery = savedInstanceState.getString(KEY_SEARCH_QUERY); mInRegularSearch = savedInstanceState.getBoolean(KEY_IN_REGULAR_SEARCH_UI); mInDialpadSearch = savedInstanceState.getBoolean(KEY_IN_DIALPAD_SEARCH_UI); - mInNewSearch = savedInstanceState.getBoolean(KEY_IN_NEW_SEARCH_UI); mFirstLaunch = savedInstanceState.getBoolean(KEY_FIRST_LAUNCH); mWasConfigurationChange = savedInstanceState.getBoolean(KEY_WAS_CONFIGURATION_CHANGE); mShowDialpadOnResume = savedInstanceState.getBoolean(KEY_IS_DIALPAD_SHOWN); @@ -626,7 +654,6 @@ public class DialtactsActivity extends TransactionSafeActivity outState.putString(KEY_SEARCH_QUERY, mSearchQuery); outState.putBoolean(KEY_IN_REGULAR_SEARCH_UI, mInRegularSearch); outState.putBoolean(KEY_IN_DIALPAD_SEARCH_UI, mInDialpadSearch); - outState.putBoolean(KEY_IN_NEW_SEARCH_UI, mInNewSearch); outState.putBoolean(KEY_FIRST_LAUNCH, mFirstLaunch); outState.putBoolean(KEY_IS_DIALPAD_SHOWN, mIsDialpadShown); outState.putBoolean(KEY_WAS_CONFIGURATION_CHANGE, isChangingConfigurations()); @@ -866,19 +893,14 @@ public class DialtactsActivity extends TransactionSafeActivity updateSearchFragmentPosition(); } - @Override - public void onCallPlacedFromDialpad() { - hideDialpadFragment(false /* animate */, true /*clearDialpad */); - exitSearchUi(); - } - /** * Initiates animations and other visual updates to hide the dialpad. The fragment is hidden in a * callback after the hide animation ends. * * @see #commitDialpadFragmentHide */ - private void hideDialpadFragment(boolean animate, boolean clearDialpad) { + @Override + public void hideDialpadFragment(boolean animate, boolean clearDialpad) { LogUtil.enterBlock("DialtactsActivity.hideDialpadFragment"); if (mDialpadFragment == null || mDialpadFragment.getView() == null) { return; @@ -913,6 +935,11 @@ public class DialtactsActivity extends TransactionSafeActivity mActionBarController.onDialpadDown(); + if (isInSearchUi()) { + if (TextUtils.isEmpty(mSearchQuery)) { + exitSearchUi(); + } + } // reset the title to normal. setTitle(R.string.launcherActivityLabel); } @@ -960,7 +987,7 @@ public class DialtactsActivity extends TransactionSafeActivity @Override public boolean isInSearchUi() { - return mInDialpadSearch || mInRegularSearch || mInNewSearch; + return mInDialpadSearch || mInRegularSearch; } @Override @@ -971,7 +998,6 @@ public class DialtactsActivity extends TransactionSafeActivity private void setNotInSearchUi() { mInDialpadSearch = false; mInRegularSearch = false; - mInNewSearch = false; } private void hideDialpadAndSearchUi() { @@ -1145,21 +1171,17 @@ public class DialtactsActivity extends TransactionSafeActivity } final String tag; - mInDialpadSearch = false; - mInRegularSearch = false; - mInNewSearch = false; boolean useNewSearch = ConfigProviderBindings.get(this).getBoolean("enable_new_search_fragment", false); if (useNewSearch) { tag = TAG_NEW_SEARCH_FRAGMENT; - mInNewSearch = true; } else if (smartDialSearch) { tag = TAG_SMARTDIAL_SEARCH_FRAGMENT; - mInDialpadSearch = true; } else { tag = TAG_REGULAR_SEARCH_FRAGMENT; - mInRegularSearch = true; } + mInDialpadSearch = smartDialSearch; + mInRegularSearch = !smartDialSearch; mFloatingActionButtonController.scaleOut(); @@ -1282,36 +1304,45 @@ public class DialtactsActivity extends TransactionSafeActivity return; } if (mIsDialpadShown) { - hideDialpadFragment(true, false); - } else if (isInSearchUi()) { - if (mIsKeyboardOpen) { - DialerUtils.hideInputMethod(mParentLayout); - PerformanceReport.recordClick(UiAction.Type.HIDE_KEYBOARD_IN_SEARCH); - } else { + if (TextUtils.isEmpty(mSearchQuery) + || (mSmartDialSearchFragment != null + && mSmartDialSearchFragment.isVisible() + && mSmartDialSearchFragment.getAdapter().getCount() == 0)) { exitSearchUi(); } + hideDialpadFragment(true, false); + } else if (isInSearchUi()) { + exitSearchUi(); + DialerUtils.hideInputMethod(mParentLayout); } else { super.onBackPressed(); } } - @Override - public void onConfigurationChanged(Configuration configuration) { - super.onConfigurationChanged(configuration); - // Checks whether a hardware keyboard is available - if (configuration.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) { - mIsKeyboardOpen = true; - } else if (configuration.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) { - mIsKeyboardOpen = false; - } - } - private void maybeEnterSearchUi() { if (!isInSearchUi()) { enterSearchUi(true /* isSmartDial */, mSearchQuery, false); } } + /** @return True if the search UI was exited, false otherwise */ + private boolean maybeExitSearchUi() { + if (isInSearchUi() && TextUtils.isEmpty(mSearchQuery)) { + exitSearchUi(); + DialerUtils.hideInputMethod(mParentLayout); + return true; + } + return false; + } + + private void showFabInSearchUi() { + mFloatingActionButtonController.changeIcon( + getResources().getDrawable(R.drawable.quantum_ic_dialpad_white_24, null), + getResources().getString(R.string.action_menu_dialpad_button)); + mFloatingActionButtonController.align(getFabAlignment(), false /* animate */); + mFloatingActionButtonController.scaleIn(FAB_SCALE_IN_DELAY_MS); + } + @Override public void onDialpadQueryChanged(String query) { mDialpadQuery = query; diff --git a/java/com/android/dialer/app/filterednumber/BlockedNumbersAdapter.java b/java/com/android/dialer/app/filterednumber/BlockedNumbersAdapter.java new file mode 100644 index 000000000..4f8bc66a9 --- /dev/null +++ b/java/com/android/dialer/app/filterednumber/BlockedNumbersAdapter.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2015 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.dialer.app.filterednumber; + +import android.app.FragmentManager; +import android.content.Context; +import android.database.Cursor; +import android.telephony.PhoneNumberUtils; +import android.view.View; +import com.android.dialer.app.R; +import com.android.dialer.blocking.BlockNumberDialogFragment; +import com.android.dialer.contactphoto.ContactPhotoManager; +import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns; +import com.android.dialer.location.GeoUtil; +import com.android.dialer.logging.InteractionEvent; +import com.android.dialer.logging.Logger; +import com.android.dialer.phonenumbercache.ContactInfoHelper; + +/** TODO(calderwoodra): documentation */ +public class BlockedNumbersAdapter extends NumbersAdapter { + + private BlockedNumbersAdapter( + Context context, + FragmentManager fragmentManager, + ContactInfoHelper contactInfoHelper, + ContactPhotoManager contactPhotoManager) { + super(context, fragmentManager, contactInfoHelper, contactPhotoManager); + } + + public static BlockedNumbersAdapter newBlockedNumbersAdapter( + Context context, FragmentManager fragmentManager) { + return new BlockedNumbersAdapter( + context, + fragmentManager, + new ContactInfoHelper(context, GeoUtil.getCurrentCountryIso(context)), + ContactPhotoManager.getInstance(context)); + } + + @Override + public void bindView(View view, final Context context, Cursor cursor) { + super.bindView(view, context, cursor); + final Integer id = cursor.getInt(cursor.getColumnIndex(FilteredNumberColumns._ID)); + final String countryIso = + cursor.getString(cursor.getColumnIndex(FilteredNumberColumns.COUNTRY_ISO)); + final String number = cursor.getString(cursor.getColumnIndex(FilteredNumberColumns.NUMBER)); + + final View deleteButton = view.findViewById(R.id.delete_button); + deleteButton.setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View view) { + BlockNumberDialogFragment.show( + id, + number, + countryIso, + PhoneNumberUtils.formatNumber(number, countryIso), + R.id.blocked_numbers_activity_container, + getFragmentManager(), + new BlockNumberDialogFragment.Callback() { + @Override + public void onFilterNumberSuccess() {} + + @Override + public void onUnfilterNumberSuccess() { + Logger.get(context) + .logInteraction(InteractionEvent.Type.UNBLOCK_NUMBER_MANAGEMENT_SCREEN); + } + + @Override + public void onChangeFilteredNumberUndo() {} + }); + } + }); + + updateView(view, number, countryIso); + } + + @Override + public boolean isEmpty() { + // Always return false, so that the header with blocking-related options always shows. + return false; + } +} diff --git a/java/com/android/dialer/app/filterednumber/BlockedNumbersFragment.java b/java/com/android/dialer/app/filterednumber/BlockedNumbersFragment.java new file mode 100644 index 000000000..36afe5429 --- /dev/null +++ b/java/com/android/dialer/app/filterednumber/BlockedNumbersFragment.java @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2015 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.dialer.app.filterednumber; + +import android.app.ListFragment; +import android.app.LoaderManager; +import android.content.Context; +import android.content.CursorLoader; +import android.content.Loader; +import android.database.Cursor; +import android.graphics.drawable.ColorDrawable; +import android.os.Bundle; +import android.support.v4.app.ActivityCompat; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import com.android.dialer.app.R; +import com.android.dialer.blocking.BlockedNumbersMigrator; +import com.android.dialer.blocking.BlockedNumbersMigrator.Listener; +import com.android.dialer.blocking.FilteredNumberCompat; +import com.android.dialer.blocking.FilteredNumbersUtil; +import com.android.dialer.blocking.FilteredNumbersUtil.CheckForSendToVoicemailContactListener; +import com.android.dialer.blocking.FilteredNumbersUtil.ImportSendToVoicemailContactsListener; +import com.android.dialer.database.FilteredNumberContract; +import com.android.dialer.lettertile.LetterTileDrawable; +import com.android.dialer.voicemailstatus.VisualVoicemailEnabledChecker; + +/** TODO(calderwoodra): documentation */ +public class BlockedNumbersFragment extends ListFragment + implements LoaderManager.LoaderCallbacks<Cursor>, + View.OnClickListener, + VisualVoicemailEnabledChecker.Callback { + + private static final char ADD_BLOCKED_NUMBER_ICON_LETTER = '+'; + protected View migratePromoView; + private BlockedNumbersMigrator blockedNumbersMigratorForTest; + private TextView blockedNumbersText; + private TextView footerText; + private BlockedNumbersAdapter mAdapter; + private VisualVoicemailEnabledChecker mVoicemailEnabledChecker; + private View mImportSettings; + private View mBlockedNumbersDisabledForEmergency; + private View mBlockedNumberListDivider; + + @Override + public Context getContext() { + return getActivity(); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + LayoutInflater inflater = + (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + getListView().addHeaderView(inflater.inflate(R.layout.blocked_number_header, null)); + getListView().addFooterView(inflater.inflate(R.layout.blocked_number_footer, null)); + //replace the icon for add number with LetterTileDrawable(), so it will have identical style + ImageView addNumberIcon = (ImageView) getActivity().findViewById(R.id.add_number_icon); + LetterTileDrawable drawable = new LetterTileDrawable(getResources()); + drawable.setLetter(ADD_BLOCKED_NUMBER_ICON_LETTER); + drawable.setColor( + ActivityCompat.getColor(getActivity(), R.color.add_blocked_number_icon_color)); + drawable.setIsCircular(true); + addNumberIcon.setImageDrawable(drawable); + + if (mAdapter == null) { + mAdapter = + BlockedNumbersAdapter.newBlockedNumbersAdapter( + getContext(), getActivity().getFragmentManager()); + } + setListAdapter(mAdapter); + + blockedNumbersText = (TextView) getListView().findViewById(R.id.blocked_number_text_view); + migratePromoView = getListView().findViewById(R.id.migrate_promo); + getListView().findViewById(R.id.migrate_promo_allow_button).setOnClickListener(this); + mImportSettings = getListView().findViewById(R.id.import_settings); + mBlockedNumbersDisabledForEmergency = + getListView().findViewById(R.id.blocked_numbers_disabled_for_emergency); + mBlockedNumberListDivider = getActivity().findViewById(R.id.blocked_number_list_divider); + getListView().findViewById(R.id.import_button).setOnClickListener(this); + getListView().findViewById(R.id.view_numbers_button).setOnClickListener(this); + getListView().findViewById(R.id.add_number_linear_layout).setOnClickListener(this); + + footerText = (TextView) getActivity().findViewById(R.id.blocked_number_footer_textview); + mVoicemailEnabledChecker = new VisualVoicemailEnabledChecker(getContext(), this); + mVoicemailEnabledChecker.asyncUpdate(); + updateActiveVoicemailProvider(); + } + + @Override + public void onDestroy() { + setListAdapter(null); + super.onDestroy(); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getLoaderManager().initLoader(0, null, this); + } + + @Override + public void onResume() { + super.onResume(); + + ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); + ColorDrawable backgroundDrawable = + new ColorDrawable(ActivityCompat.getColor(getActivity(), R.color.dialer_theme_color)); + actionBar.setBackgroundDrawable(backgroundDrawable); + actionBar.setDisplayShowCustomEnabled(false); + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setDisplayShowHomeEnabled(true); + actionBar.setDisplayShowTitleEnabled(true); + actionBar.setTitle(R.string.manage_blocked_numbers_label); + + // If the device can use the framework blocking solution, users should not be able to add + // new blocked numbers from the Blocked Management UI. They will be shown a promo card + // asking them to migrate to new blocking instead. + if (FilteredNumberCompat.canUseNewFiltering()) { + migratePromoView.setVisibility(View.VISIBLE); + blockedNumbersText.setVisibility(View.GONE); + getListView().findViewById(R.id.add_number_linear_layout).setVisibility(View.GONE); + getListView().findViewById(R.id.add_number_linear_layout).setOnClickListener(null); + mBlockedNumberListDivider.setVisibility(View.GONE); + mImportSettings.setVisibility(View.GONE); + getListView().findViewById(R.id.import_button).setOnClickListener(null); + getListView().findViewById(R.id.view_numbers_button).setOnClickListener(null); + mBlockedNumbersDisabledForEmergency.setVisibility(View.GONE); + footerText.setVisibility(View.GONE); + } else { + FilteredNumbersUtil.checkForSendToVoicemailContact( + getActivity(), + new CheckForSendToVoicemailContactListener() { + @Override + public void onComplete(boolean hasSendToVoicemailContact) { + final int visibility = hasSendToVoicemailContact ? View.VISIBLE : View.GONE; + mImportSettings.setVisibility(visibility); + } + }); + } + + // All views except migrate and the block list are hidden when new filtering is available + if (!FilteredNumberCompat.canUseNewFiltering() + && FilteredNumbersUtil.hasRecentEmergencyCall(getContext())) { + mBlockedNumbersDisabledForEmergency.setVisibility(View.VISIBLE); + } else { + mBlockedNumbersDisabledForEmergency.setVisibility(View.GONE); + } + + mVoicemailEnabledChecker.asyncUpdate(); + } + + @Override + public View onCreateView( + LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.blocked_number_fragment, container, false); + } + + @Override + public Loader<Cursor> onCreateLoader(int id, Bundle args) { + final String[] projection = { + FilteredNumberContract.FilteredNumberColumns._ID, + FilteredNumberContract.FilteredNumberColumns.COUNTRY_ISO, + FilteredNumberContract.FilteredNumberColumns.NUMBER, + FilteredNumberContract.FilteredNumberColumns.NORMALIZED_NUMBER + }; + final String selection = + FilteredNumberContract.FilteredNumberColumns.TYPE + + "=" + + FilteredNumberContract.FilteredNumberTypes.BLOCKED_NUMBER; + return new CursorLoader( + getContext(), + FilteredNumberContract.FilteredNumber.CONTENT_URI, + projection, + selection, + null, + null); + } + + @Override + public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + mAdapter.swapCursor(data); + if (FilteredNumberCompat.canUseNewFiltering() || data.getCount() == 0) { + mBlockedNumberListDivider.setVisibility(View.INVISIBLE); + } else { + mBlockedNumberListDivider.setVisibility(View.VISIBLE); + } + } + + @Override + public void onLoaderReset(Loader<Cursor> loader) { + mAdapter.swapCursor(null); + } + + @Override + public void onClick(final View view) { + final BlockedNumbersSettingsActivity activity = (BlockedNumbersSettingsActivity) getActivity(); + if (activity == null) { + return; + } + + int resId = view.getId(); + if (resId == R.id.add_number_linear_layout) { + activity.showSearchUi(); + } else if (resId == R.id.view_numbers_button) { + activity.showNumbersToImportPreviewUi(); + } else if (resId == R.id.import_button) { + FilteredNumbersUtil.importSendToVoicemailContacts( + activity, + new ImportSendToVoicemailContactsListener() { + @Override + public void onImportComplete() { + mImportSettings.setVisibility(View.GONE); + } + }); + } else if (resId == R.id.migrate_promo_allow_button) { + view.setEnabled(false); + (blockedNumbersMigratorForTest != null + ? blockedNumbersMigratorForTest + : new BlockedNumbersMigrator(getContext())) + .migrate( + new Listener() { + @Override + public void onComplete() { + getContext() + .startActivity( + FilteredNumberCompat.createManageBlockedNumbersIntent(getContext())); + // Remove this activity from the backstack + activity.finish(); + } + }); + } + } + + @Override + public void onVisualVoicemailEnabledStatusChanged(boolean newStatus) { + updateActiveVoicemailProvider(); + } + + private void updateActiveVoicemailProvider() { + if (getActivity() == null || getActivity().isFinishing()) { + return; + } + if (mVoicemailEnabledChecker.isVisualVoicemailEnabled()) { + footerText.setText(R.string.block_number_footer_message_vvm); + } else { + footerText.setText(R.string.block_number_footer_message_no_vvm); + } + } + + void setBlockedNumbersMigratorForTest(BlockedNumbersMigrator blockedNumbersMigrator) { + blockedNumbersMigratorForTest = blockedNumbersMigrator; + } +} diff --git a/java/com/android/dialer/app/filterednumber/BlockedNumbersSettingsActivity.java b/java/com/android/dialer/app/filterednumber/BlockedNumbersSettingsActivity.java new file mode 100644 index 000000000..858d28355 --- /dev/null +++ b/java/com/android/dialer/app/filterednumber/BlockedNumbersSettingsActivity.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2015 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.dialer.app.filterednumber; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.MenuItem; +import com.android.dialer.app.R; +import com.android.dialer.app.list.BlockedListSearchFragment; +import com.android.dialer.app.list.SearchFragment; +import com.android.dialer.logging.Logger; +import com.android.dialer.logging.ScreenEvent; + +/** TODO(calderwoodra): documentation */ +public class BlockedNumbersSettingsActivity extends AppCompatActivity + implements SearchFragment.HostInterface { + + private static final String TAG_BLOCKED_MANAGEMENT_FRAGMENT = "blocked_management"; + private static final String TAG_BLOCKED_SEARCH_FRAGMENT = "blocked_search"; + private static final String TAG_VIEW_NUMBERS_TO_IMPORT_FRAGMENT = "view_numbers_to_import"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.blocked_numbers_activity); + + // If savedInstanceState != null, the Activity will automatically restore the last fragment. + if (savedInstanceState == null) { + showManagementUi(); + } + } + + /** Shows fragment with the list of currently blocked numbers and settings related to blocking. */ + public void showManagementUi() { + BlockedNumbersFragment fragment = + (BlockedNumbersFragment) + getFragmentManager().findFragmentByTag(TAG_BLOCKED_MANAGEMENT_FRAGMENT); + if (fragment == null) { + fragment = new BlockedNumbersFragment(); + } + + getFragmentManager() + .beginTransaction() + .replace(R.id.blocked_numbers_activity_container, fragment, TAG_BLOCKED_MANAGEMENT_FRAGMENT) + .commit(); + + Logger.get(this).logScreenView(ScreenEvent.Type.BLOCKED_NUMBER_MANAGEMENT, this); + } + + /** Shows fragment with search UI for browsing/finding numbers to block. */ + public void showSearchUi() { + BlockedListSearchFragment fragment = + (BlockedListSearchFragment) + getFragmentManager().findFragmentByTag(TAG_BLOCKED_SEARCH_FRAGMENT); + if (fragment == null) { + fragment = new BlockedListSearchFragment(); + fragment.setHasOptionsMenu(false); + fragment.setShowEmptyListForNullQuery(true); + fragment.setDirectorySearchEnabled(false); + } + + getFragmentManager() + .beginTransaction() + .replace(R.id.blocked_numbers_activity_container, fragment, TAG_BLOCKED_SEARCH_FRAGMENT) + .addToBackStack(null) + .commit(); + + Logger.get(this).logScreenView(ScreenEvent.Type.BLOCKED_NUMBER_ADD_NUMBER, this); + } + + /** + * Shows fragment with UI to preview the numbers of contacts currently marked as send-to-voicemail + * in Contacts. These numbers can be imported into Dialer's blocked number list. + */ + public void showNumbersToImportPreviewUi() { + ViewNumbersToImportFragment fragment = + (ViewNumbersToImportFragment) + getFragmentManager().findFragmentByTag(TAG_VIEW_NUMBERS_TO_IMPORT_FRAGMENT); + if (fragment == null) { + fragment = new ViewNumbersToImportFragment(); + } + + getFragmentManager() + .beginTransaction() + .replace( + R.id.blocked_numbers_activity_container, fragment, TAG_VIEW_NUMBERS_TO_IMPORT_FRAGMENT) + .addToBackStack(null) + .commit(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + onBackPressed(); + return true; + } + return false; + } + + @Override + public void onBackPressed() { + // TODO: Achieve back navigation without overriding onBackPressed. + if (getFragmentManager().getBackStackEntryCount() > 0) { + getFragmentManager().popBackStack(); + } else { + super.onBackPressed(); + } + } + + @Override + public boolean isActionBarShowing() { + return false; + } + + @Override + public boolean isDialpadShown() { + return false; + } + + @Override + public int getDialpadHeight() { + return 0; + } + + @Override + public int getActionBarHeight() { + return 0; + } +} diff --git a/java/com/android/dialer/app/filterednumber/NumbersAdapter.java b/java/com/android/dialer/app/filterednumber/NumbersAdapter.java new file mode 100644 index 000000000..938a78479 --- /dev/null +++ b/java/com/android/dialer/app/filterednumber/NumbersAdapter.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2015 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.dialer.app.filterednumber; + +import android.app.FragmentManager; +import android.content.Context; +import android.provider.ContactsContract; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.text.BidiFormatter; +import android.text.TextDirectionHeuristics; +import android.text.TextUtils; +import android.view.View; +import android.widget.QuickContactBadge; +import android.widget.SimpleCursorAdapter; +import android.widget.TextView; +import com.android.dialer.app.R; +import com.android.dialer.compat.CompatUtils; +import com.android.dialer.contactphoto.ContactPhotoManager; +import com.android.dialer.contactphoto.ContactPhotoManager.DefaultImageRequest; +import com.android.dialer.lettertile.LetterTileDrawable; +import com.android.dialer.phonenumbercache.ContactInfo; +import com.android.dialer.phonenumbercache.ContactInfoHelper; +import com.android.dialer.phonenumberutil.PhoneNumberHelper; +import com.android.dialer.util.UriUtils; + +/** TODO(calderwoodra): documentation */ +public class NumbersAdapter extends SimpleCursorAdapter { + + private final Context mContext; + private final FragmentManager mFragmentManager; + private final ContactInfoHelper mContactInfoHelper; + private final BidiFormatter mBidiFormatter = BidiFormatter.getInstance(); + private final ContactPhotoManager mContactPhotoManager; + + public NumbersAdapter( + Context context, + FragmentManager fragmentManager, + ContactInfoHelper contactInfoHelper, + ContactPhotoManager contactPhotoManager) { + super(context, R.layout.blocked_number_item, null, new String[] {}, new int[] {}, 0); + mContext = context; + mFragmentManager = fragmentManager; + mContactInfoHelper = contactInfoHelper; + mContactPhotoManager = contactPhotoManager; + } + + public void updateView(View view, String number, String countryIso) { + final TextView callerName = (TextView) view.findViewById(R.id.caller_name); + final TextView callerNumber = (TextView) view.findViewById(R.id.caller_number); + final QuickContactBadge quickContactBadge = + (QuickContactBadge) view.findViewById(R.id.quick_contact_photo); + quickContactBadge.setOverlay(null); + if (CompatUtils.hasPrioritizedMimeType()) { + quickContactBadge.setPrioritizedMimeType(Phone.CONTENT_ITEM_TYPE); + } + + ContactInfo info = mContactInfoHelper.lookupNumber(number, countryIso); + if (info == null) { + info = new ContactInfo(); + info.number = number; + } + final CharSequence locationOrType = getNumberTypeOrLocation(info); + final String displayNumber = getDisplayNumber(info); + final String displayNumberStr = + mBidiFormatter.unicodeWrap(displayNumber, TextDirectionHeuristics.LTR); + + String nameForDefaultImage; + if (!TextUtils.isEmpty(info.name)) { + nameForDefaultImage = info.name; + callerName.setText(info.name); + callerNumber.setText(locationOrType + " " + displayNumberStr); + } else { + nameForDefaultImage = displayNumber; + callerName.setText(displayNumberStr); + if (!TextUtils.isEmpty(locationOrType)) { + callerNumber.setText(locationOrType); + callerNumber.setVisibility(View.VISIBLE); + } else { + callerNumber.setVisibility(View.GONE); + } + } + loadContactPhoto(info, nameForDefaultImage, quickContactBadge); + } + + private void loadContactPhoto(ContactInfo info, String displayName, QuickContactBadge badge) { + final String lookupKey = + info.lookupUri == null ? null : UriUtils.getLookupKeyFromUri(info.lookupUri); + final int contactType = + mContactInfoHelper.isBusiness(info.sourceType) + ? LetterTileDrawable.TYPE_BUSINESS + : LetterTileDrawable.TYPE_DEFAULT; + final DefaultImageRequest request = + new DefaultImageRequest(displayName, lookupKey, contactType, true /* isCircular */); + badge.assignContactUri(info.lookupUri); + badge.setContentDescription( + mContext.getResources().getString(R.string.description_contact_details, displayName)); + mContactPhotoManager.loadDirectoryPhoto( + badge, info.photoUri, false /* darkTheme */, true /* isCircular */, request); + } + + private String getDisplayNumber(ContactInfo info) { + if (!TextUtils.isEmpty(info.formattedNumber)) { + return info.formattedNumber; + } else if (!TextUtils.isEmpty(info.number)) { + return info.number; + } else { + return ""; + } + } + + private CharSequence getNumberTypeOrLocation(ContactInfo info) { + if (!TextUtils.isEmpty(info.name)) { + return ContactsContract.CommonDataKinds.Phone.getTypeLabel( + mContext.getResources(), info.type, info.label); + } else { + return PhoneNumberHelper.getGeoDescription(mContext, info.number); + } + } + + protected Context getContext() { + return mContext; + } + + protected FragmentManager getFragmentManager() { + return mFragmentManager; + } +} diff --git a/java/com/android/dialer/app/filterednumber/ViewNumbersToImportAdapter.java b/java/com/android/dialer/app/filterednumber/ViewNumbersToImportAdapter.java new file mode 100644 index 000000000..106c4fb71 --- /dev/null +++ b/java/com/android/dialer/app/filterednumber/ViewNumbersToImportAdapter.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 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.dialer.app.filterednumber; + +import android.app.FragmentManager; +import android.content.Context; +import android.database.Cursor; +import android.view.View; +import com.android.dialer.app.R; +import com.android.dialer.blocking.FilteredNumbersUtil; +import com.android.dialer.contactphoto.ContactPhotoManager; +import com.android.dialer.location.GeoUtil; +import com.android.dialer.phonenumbercache.ContactInfoHelper; + +/** TODO(calderwoodra): documentation */ +public class ViewNumbersToImportAdapter extends NumbersAdapter { + + private ViewNumbersToImportAdapter( + Context context, + FragmentManager fragmentManager, + ContactInfoHelper contactInfoHelper, + ContactPhotoManager contactPhotoManager) { + super(context, fragmentManager, contactInfoHelper, contactPhotoManager); + } + + public static ViewNumbersToImportAdapter newViewNumbersToImportAdapter( + Context context, FragmentManager fragmentManager) { + return new ViewNumbersToImportAdapter( + context, + fragmentManager, + new ContactInfoHelper(context, GeoUtil.getCurrentCountryIso(context)), + ContactPhotoManager.getInstance(context)); + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + super.bindView(view, context, cursor); + + final String number = cursor.getString(FilteredNumbersUtil.PhoneQuery.NUMBER_COLUMN_INDEX); + + view.findViewById(R.id.delete_button).setVisibility(View.GONE); + updateView(view, number, null /* countryIso */); + } +} diff --git a/java/com/android/dialer/app/filterednumber/ViewNumbersToImportFragment.java b/java/com/android/dialer/app/filterednumber/ViewNumbersToImportFragment.java new file mode 100644 index 000000000..1de768219 --- /dev/null +++ b/java/com/android/dialer/app/filterednumber/ViewNumbersToImportFragment.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2015 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.dialer.app.filterednumber; + +import android.app.ListFragment; +import android.app.LoaderManager; +import android.content.Context; +import android.content.CursorLoader; +import android.content.Loader; +import android.database.Cursor; +import android.os.Bundle; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import com.android.dialer.app.R; +import com.android.dialer.blocking.FilteredNumbersUtil; +import com.android.dialer.blocking.FilteredNumbersUtil.ImportSendToVoicemailContactsListener; + +/** TODO(calderwoodra): documentation */ +public class ViewNumbersToImportFragment extends ListFragment + implements LoaderManager.LoaderCallbacks<Cursor>, View.OnClickListener { + + private ViewNumbersToImportAdapter mAdapter; + + @Override + public Context getContext() { + return getActivity(); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + if (mAdapter == null) { + mAdapter = + ViewNumbersToImportAdapter.newViewNumbersToImportAdapter( + getContext(), getActivity().getFragmentManager()); + } + setListAdapter(mAdapter); + } + + @Override + public void onDestroy() { + setListAdapter(null); + super.onDestroy(); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getLoaderManager().initLoader(0, null, this); + } + + @Override + public void onResume() { + super.onResume(); + + ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); + actionBar.setTitle(R.string.import_send_to_voicemail_numbers_label); + actionBar.setDisplayShowCustomEnabled(false); + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setDisplayShowHomeEnabled(true); + actionBar.setDisplayShowTitleEnabled(true); + + getActivity().findViewById(R.id.cancel_button).setOnClickListener(this); + getActivity().findViewById(R.id.import_button).setOnClickListener(this); + } + + @Override + public View onCreateView( + LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.view_numbers_to_import_fragment, container, false); + } + + @Override + public Loader<Cursor> onCreateLoader(int id, Bundle args) { + final CursorLoader cursorLoader = + new CursorLoader( + getContext(), + Phone.CONTENT_URI, + FilteredNumbersUtil.PhoneQuery.PROJECTION, + FilteredNumbersUtil.PhoneQuery.SELECT_SEND_TO_VOICEMAIL_TRUE, + null, + null); + return cursorLoader; + } + + @Override + public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + mAdapter.swapCursor(data); + } + + @Override + public void onLoaderReset(Loader<Cursor> loader) { + mAdapter.swapCursor(null); + } + + @Override + public void onClick(final View view) { + if (view.getId() == R.id.import_button) { + FilteredNumbersUtil.importSendToVoicemailContacts( + getContext(), + new ImportSendToVoicemailContactsListener() { + @Override + public void onImportComplete() { + if (getActivity() != null) { + getActivity().onBackPressed(); + } + } + }); + } else if (view.getId() == R.id.cancel_button) { + getActivity().onBackPressed(); + } + } +} diff --git a/java/com/android/dialer/app/list/BlockedListSearchFragment.java b/java/com/android/dialer/app/list/BlockedListSearchFragment.java new file mode 100644 index 000000000..bef5af713 --- /dev/null +++ b/java/com/android/dialer/app/list/BlockedListSearchFragment.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2015 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.dialer.app.list; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.telephony.PhoneNumberUtils; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.util.TypedValue; +import android.view.View; +import android.widget.AdapterView; +import android.widget.EditText; +import android.widget.Toast; +import com.android.contacts.common.list.ContactEntryListAdapter; +import com.android.contacts.common.util.ContactDisplayUtils; +import com.android.dialer.app.R; +import com.android.dialer.app.widget.SearchEditTextLayout; +import com.android.dialer.blocking.BlockNumberDialogFragment; +import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler; +import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler.OnCheckBlockedListener; +import com.android.dialer.common.LogUtil; +import com.android.dialer.location.GeoUtil; +import com.android.dialer.logging.InteractionEvent; +import com.android.dialer.logging.Logger; + +/** TODO(calderwoodra): documentation */ +public class BlockedListSearchFragment extends RegularSearchFragment + implements BlockNumberDialogFragment.Callback { + + private final TextWatcher mPhoneSearchQueryTextListener = + new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + setQueryString(s.toString()); + } + + @Override + public void afterTextChanged(Editable s) {} + }; + private final SearchEditTextLayout.Callback mSearchLayoutCallback = + new SearchEditTextLayout.Callback() { + @Override + public void onBackButtonClicked() { + getActivity().onBackPressed(); + } + + @Override + public void onSearchViewClicked() {} + }; + private FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler; + private EditText mSearchView; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setShowEmptyListForNullQuery(true); + /* + * Pass in the empty string here so ContactEntryListFragment#setQueryString interprets it as + * an empty search query, rather than as an uninitalized value. In the latter case, the + * adapter returned by #createListAdapter is used, which populates the view with contacts. + * Passing in the empty string forces ContactEntryListFragment to interpret it as an empty + * query, which results in showing an empty view + */ + setQueryString(getQueryString() == null ? "" : getQueryString()); + mFilteredNumberAsyncQueryHandler = new FilteredNumberAsyncQueryHandler(getContext()); + } + + @Override + public void onResume() { + super.onResume(); + + ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); + actionBar.setCustomView(R.layout.search_edittext); + actionBar.setDisplayShowCustomEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(false); + actionBar.setDisplayShowHomeEnabled(false); + + final SearchEditTextLayout searchEditTextLayout = + (SearchEditTextLayout) actionBar.getCustomView().findViewById(R.id.search_view_container); + searchEditTextLayout.expand(false, true); + searchEditTextLayout.setCallback(mSearchLayoutCallback); + searchEditTextLayout.setBackgroundDrawable(null); + + mSearchView = (EditText) searchEditTextLayout.findViewById(R.id.search_view); + mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener); + mSearchView.setHint(R.string.block_number_search_hint); + + searchEditTextLayout + .findViewById(R.id.search_box_expanded) + .setBackgroundColor(getContext().getResources().getColor(android.R.color.white)); + + if (!TextUtils.isEmpty(getQueryString())) { + mSearchView.setText(getQueryString()); + } + + // TODO: Don't set custom text size; use default search text size. + mSearchView.setTextSize( + TypedValue.COMPLEX_UNIT_PX, + getResources().getDimension(R.dimen.blocked_number_search_text_size)); + } + + @Override + protected ContactEntryListAdapter createListAdapter() { + BlockedListSearchAdapter adapter = new BlockedListSearchAdapter(getActivity()); + adapter.setDisplayPhotos(true); + // Don't show SIP addresses. + adapter.setUseCallableUri(false); + // Keep in sync with the queryString set in #onCreate + adapter.setQueryString(getQueryString() == null ? "" : getQueryString()); + return adapter; + } + + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + super.onItemClick(parent, view, position, id); + final int adapterPosition = position - getListView().getHeaderViewsCount(); + final BlockedListSearchAdapter adapter = (BlockedListSearchAdapter) getAdapter(); + final int shortcutType = adapter.getShortcutTypeFromPosition(adapterPosition); + final Integer blockId = (Integer) view.getTag(R.id.block_id); + final String number; + switch (shortcutType) { + case DialerPhoneNumberListAdapter.SHORTCUT_INVALID: + // Handles click on a search result, either contact or nearby places result. + number = adapter.getPhoneNumber(adapterPosition); + blockContactNumber(number, blockId); + break; + case DialerPhoneNumberListAdapter.SHORTCUT_BLOCK_NUMBER: + // Handles click on 'Block number' shortcut to add the user query as a number. + number = adapter.getQueryString(); + blockNumber(number); + break; + default: + LogUtil.w( + "BlockedListSearchFragment.onItemClick", + "ignoring unsupported shortcut type: " + shortcutType); + break; + } + } + + @Override + protected void onItemClick(int position, long id) { + // Prevent SearchFragment.onItemClicked from being called. + } + + private void blockNumber(final String number) { + final String countryIso = GeoUtil.getCurrentCountryIso(getContext()); + final OnCheckBlockedListener onCheckListener = + new OnCheckBlockedListener() { + @Override + public void onCheckComplete(Integer id) { + if (id == null) { + BlockNumberDialogFragment.show( + id, + number, + countryIso, + PhoneNumberUtils.formatNumber(number, countryIso), + R.id.blocked_numbers_activity_container, + getFragmentManager(), + BlockedListSearchFragment.this); + } else if (id == FilteredNumberAsyncQueryHandler.INVALID_ID) { + Toast.makeText( + getContext(), + ContactDisplayUtils.getTtsSpannedPhoneNumber( + getResources(), R.string.invalidNumber, number), + Toast.LENGTH_SHORT) + .show(); + } else { + Toast.makeText( + getContext(), + ContactDisplayUtils.getTtsSpannedPhoneNumber( + getResources(), R.string.alreadyBlocked, number), + Toast.LENGTH_SHORT) + .show(); + } + } + }; + mFilteredNumberAsyncQueryHandler.isBlockedNumber(onCheckListener, number, countryIso); + } + + @Override + public void onFilterNumberSuccess() { + Logger.get(getContext()).logInteraction(InteractionEvent.Type.BLOCK_NUMBER_MANAGEMENT_SCREEN); + goBack(); + } + + @Override + public void onUnfilterNumberSuccess() { + LogUtil.e( + "BlockedListSearchFragment.onUnfilterNumberSuccess", + "unblocked a number from the BlockedListSearchFragment"); + goBack(); + } + + private void goBack() { + Activity activity = getActivity(); + if (activity == null) { + return; + } + activity.onBackPressed(); + } + + @Override + public void onChangeFilteredNumberUndo() { + getAdapter().notifyDataSetChanged(); + } + + private void blockContactNumber(final String number, final Integer blockId) { + if (blockId != null) { + Toast.makeText( + getContext(), + ContactDisplayUtils.getTtsSpannedPhoneNumber( + getResources(), R.string.alreadyBlocked, number), + Toast.LENGTH_SHORT) + .show(); + return; + } + + BlockNumberDialogFragment.show( + blockId, + number, + GeoUtil.getCurrentCountryIso(getContext()), + number, + R.id.blocked_numbers_activity_container, + getFragmentManager(), + this); + } +} diff --git a/java/com/android/dialer/app/widget/ActionBarController.java b/java/com/android/dialer/app/widget/ActionBarController.java index 3daa0e2d4..c1b4cc2b4 100644 --- a/java/com/android/dialer/app/widget/ActionBarController.java +++ b/java/com/android/dialer/app/widget/ActionBarController.java @@ -49,6 +49,18 @@ public class ActionBarController { } }; + private final AnimationCallback mFadeInCallback = + new AnimationCallback() { + @Override + public void onAnimationEnd() { + slideActionBar(false /* slideUp */, false /* animate */); + } + + @Override + public void onAnimationCancel() { + slideActionBar(false /* slideUp */, false /* animate */); + } + }; private ValueAnimator mAnimator; public ActionBarController(ActivityUi activityUi, SearchEditTextLayout searchBox) { @@ -100,13 +112,17 @@ public class ActionBarController { mSearchBox.isFadedOut(), mSearchBox.isExpanded()); if (mActivityUi.isInSearchUi()) { - if (mSearchBox.isFadedOut()) { - mSearchBox.setVisible(true); - } - if (!mSearchBox.isExpanded()) { - mSearchBox.expand(false /* animate */, false /* requestFocus */); + if (mActivityUi.hasSearchQuery()) { + if (mSearchBox.isFadedOut()) { + mSearchBox.setVisible(true); + } + if (!mSearchBox.isExpanded()) { + mSearchBox.expand(false /* animate */, false /* requestFocus */); + } + slideActionBar(false /* slideUp */, true /* animate */); + } else { + mSearchBox.fadeIn(mFadeInCallback); } - slideActionBar(false /* slideUp */, true /* animate */); } } diff --git a/java/com/android/dialer/app/widget/SearchEditTextLayout.java b/java/com/android/dialer/app/widget/SearchEditTextLayout.java index 2051b65f2..95bd12aa1 100644 --- a/java/com/android/dialer/app/widget/SearchEditTextLayout.java +++ b/java/com/android/dialer/app/widget/SearchEditTextLayout.java @@ -23,6 +23,7 @@ import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.util.AttributeSet; +import android.view.KeyEvent; import android.view.View; import android.widget.EditText; import android.widget.FrameLayout; @@ -37,6 +38,7 @@ public class SearchEditTextLayout extends FrameLayout { /* Subclass-visible for testing */ protected boolean mIsExpanded = false; protected boolean mIsFadedOut = false; + private OnKeyListener mPreImeKeyListener; private int mTopMargin; private int mBottomMargin; private int mLeftMargin; @@ -54,10 +56,20 @@ public class SearchEditTextLayout extends FrameLayout { private ValueAnimator mAnimator; + private Callback mCallback; + public SearchEditTextLayout(Context context, AttributeSet attrs) { super(context, attrs); } + public void setPreImeKeyListener(OnKeyListener listener) { + mPreImeKeyListener = listener; + } + + public void setCallback(Callback listener) { + mCallback = listener; + } + @Override protected void onFinishInflate() { MarginLayoutParams params = (MarginLayoutParams) getLayoutParams(); @@ -70,7 +82,7 @@ public class SearchEditTextLayout extends FrameLayout { mCollapsed = findViewById(R.id.search_box_collapsed); mExpanded = findViewById(R.id.search_box_expanded); - mSearchView = mExpanded.findViewById(R.id.search_view); + mSearchView = (EditText) mExpanded.findViewById(R.id.search_view); mSearchIcon = findViewById(R.id.search_magnifying_glass); mCollapsedSearchBox = findViewById(R.id.search_box_start_search); @@ -111,6 +123,16 @@ public class SearchEditTextLayout extends FrameLayout { } }); + mSearchView.setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mCallback != null) { + mCallback.onSearchViewClicked(); + } + } + }); + mSearchView.addTextChangedListener( new TextWatcher() { @Override @@ -125,10 +147,43 @@ public class SearchEditTextLayout extends FrameLayout { public void afterTextChanged(Editable s) {} }); - mClearButtonView.setOnClickListener(v -> mSearchView.setText(null)); + findViewById(R.id.search_close_button) + .setOnClickListener( + new OnClickListener() { + @Override + public void onClick(View v) { + mSearchView.setText(null); + } + }); + + findViewById(R.id.search_back_button) + .setOnClickListener( + new OnClickListener() { + @Override + public void onClick(View v) { + if (mCallback != null) { + mCallback.onBackButtonClicked(); + } + } + }); + super.onFinishInflate(); } + @Override + public boolean dispatchKeyEventPreIme(KeyEvent event) { + if (mPreImeKeyListener != null) { + if (mPreImeKeyListener.onKey(this, event.getKeyCode(), event)) { + return true; + } + } + return super.dispatchKeyEventPreIme(event); + } + + public void fadeOut() { + fadeOut(null); + } + public void fadeOut(AnimUtils.AnimationCallback callback) { AnimUtils.fadeOut(this, ANIMATION_DURATION, callback); mIsFadedOut = true; @@ -269,4 +324,12 @@ public class SearchEditTextLayout extends FrameLayout { params.rightMargin = (int) (mRightMargin * fraction); requestLayout(); } + + /** Listener for the back button next to the search view being pressed */ + public interface Callback { + + void onBackButtonClicked(); + + void onSearchViewClicked(); + } } diff --git a/java/com/android/dialer/dialpadview/DialpadFragment.java b/java/com/android/dialer/dialpadview/DialpadFragment.java index 837c3af90..86a83796e 100644 --- a/java/com/android/dialer/dialpadview/DialpadFragment.java +++ b/java/com/android/dialer/dialpadview/DialpadFragment.java @@ -1001,12 +1001,12 @@ public class DialpadFragment extends Fragment DialerUtils.startActivityWithErrorToast( getActivity(), new CallIntentBuilder(CallUtil.getVoicemailUri(), CallInitiationType.Type.DIALPAD).build()); - hideAndClearDialpad(); + hideAndClearDialpad(false); } - private void hideAndClearDialpad() { + private void hideAndClearDialpad(boolean animate) { LogUtil.enterBlock("DialpadFragment.hideAndClearDialpad"); - FragmentUtils.getParentUnsafe(this, DialpadListener.class).onCallPlacedFromDialpad(); + FragmentUtils.getParentUnsafe(this, DialpadListener.class).hideDialpadFragment(animate, true); } /** @@ -1053,7 +1053,7 @@ public class DialpadFragment extends Fragment final Intent intent = new CallIntentBuilder(number, CallInitiationType.Type.DIALPAD).build(); DialerUtils.startActivityWithErrorToast(getActivity(), intent); - hideAndClearDialpad(); + hideAndClearDialpad(false); } } } @@ -1297,7 +1297,7 @@ public class DialpadFragment extends Fragment return true; } else if (resId == R.id.menu_call_with_note) { CallSubjectDialog.start(getActivity(), mDigits.getText().toString()); - hideAndClearDialpad(); + hideAndClearDialpad(false); return true; } else { return false; @@ -1710,7 +1710,7 @@ public class DialpadFragment extends Fragment void onDialpadShown(); - void onCallPlacedFromDialpad(); + void hideDialpadFragment(boolean animate, boolean value); } /** Callback for async lookup of the last number dialed. */ |