summaryrefslogtreecommitdiff
path: root/src/com
diff options
context:
space:
mode:
authorEric Erfanian <erfanian@google.com>2017-02-22 16:32:36 -0800
committerEric Erfanian <erfanian@google.com>2017-03-01 09:56:52 -0800
commitccca31529c07970e89419fb85a9e8153a5396838 (patch)
treea7034c0a01672b97728c13282a2672771cd28baa /src/com
parente7ae4624ba6f25cb8e648db74e0d64c0113a16ba (diff)
Update dialer sources.
Test: Built package and system image. This change clobbers the old source, and is an export from an internal Google repository. The internal repository was forked form Android in March, and this change includes modifications since then, to near the v8 release. Since the fork, we've moved code from monolithic to independent modules. In addition, we've switched to Blaze/Bazel as the build sysetm. This export, however, still uses make. New dependencies have been added: - Dagger - Auto-Value - Glide - Libshortcutbadger Going forward, development will still be in Google3, and the Gerrit release will become an automated export, with the next drop happening in ~ two weeks. Android.mk includes local modifications from ToT. Abridged changelog: Bug fixes ● Not able to mute, add a call when using Phone app in multiwindow mode ● Double tap on keypad triggering multiple key and tones ● Reported spam numbers not showing as spam in the call log ● Crash when user tries to block number while Phone app is not set as default ● Crash when user picks a number from search auto-complete list Visual Voicemail (VVM) improvements ● Share Voicemail audio via standard exporting mechanisms that support file attachment (email, MMS, etc.) ● Make phone number, email and web sites in VVM transcript clickable ● Set PIN before declining VVM Terms of Service {Carrier} ● Set client type for outbound visual voicemail SMS {Carrier} New incoming call and incall UI on older devices (Android M) ● Updated Phone app icon ● New incall UI (large buttons, button labels) ● New and animated Answer/Reject gestures Accessibility ● Add custom answer/decline call buttons on answer screen for touch exploration accessibility services ● Increase size of touch target ● Add verbal feedback when a Voicemail fails to load ● Fix pressing of Phone buttons while in a phone call using Switch Access ● Fix selecting and opening contacts in talkback mode ● Split focus for ‘Learn More’ link in caller id & spam to help distinguish similar text Other ● Backup & Restore for App Preferences ● Prompt user to enable Wi-Fi calling if the call ends due to out of service and Wi-Fi is connected ● Rename “Dialpad” to “Keypad” ● Show "Private number" for restricted calls ● Delete unused items (vcard, add contact, call history) from Phone menu Change-Id: I2a7e53532a24c21bf308bf0a6d178d7ddbca4958
Diffstat (limited to 'src/com')
-rw-r--r--src/com/android/dialer/CallDetailActivity.java507
-rw-r--r--src/com/android/dialer/DialerApplication.java58
-rw-r--r--src/com/android/dialer/DialerBackupAgent.java38
-rw-r--r--src/com/android/dialer/DialtactsActivity.java1413
-rw-r--r--src/com/android/dialer/FloatingActionButtonBehavior.java47
-rw-r--r--src/com/android/dialer/NeededForReflection.java30
-rw-r--r--src/com/android/dialer/PhoneCallDetails.java187
-rw-r--r--src/com/android/dialer/SpecialCharSequenceMgr.java495
-rw-r--r--src/com/android/dialer/TransactionSafeActivity.java65
-rw-r--r--src/com/android/dialer/calllog/BlockReportSpamListener.java124
-rw-r--r--src/com/android/dialer/calllog/CallDetailHistoryAdapter.java166
-rw-r--r--src/com/android/dialer/calllog/CallLogActivity.java235
-rw-r--r--src/com/android/dialer/calllog/CallLogAdapter.java959
-rw-r--r--src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java505
-rw-r--r--src/com/android/dialer/calllog/CallLogFragment.java530
-rw-r--r--src/com/android/dialer/calllog/CallLogGroupBuilder.java300
-rw-r--r--src/com/android/dialer/calllog/CallLogListItemHelper.java268
-rw-r--r--src/com/android/dialer/calllog/CallLogListItemViewHolder.java776
-rw-r--r--src/com/android/dialer/calllog/CallLogNotificationsHelper.java353
-rw-r--r--src/com/android/dialer/calllog/CallLogNotificationsService.java194
-rw-r--r--src/com/android/dialer/calllog/CallLogQuery.java115
-rw-r--r--src/com/android/dialer/calllog/CallLogQueryHandler.java354
-rw-r--r--src/com/android/dialer/calllog/CallLogReceiver.java44
-rw-r--r--src/com/android/dialer/calllog/CallTypeHelper.java134
-rw-r--r--src/com/android/dialer/calllog/CallTypeIconsView.java227
-rw-r--r--src/com/android/dialer/calllog/ClearCallLogDialog.java98
-rw-r--r--src/com/android/dialer/calllog/ContactInfo.java108
-rw-r--r--src/com/android/dialer/calllog/ContactInfoHelper.java499
-rw-r--r--src/com/android/dialer/calllog/DefaultVoicemailNotifier.java269
-rw-r--r--src/com/android/dialer/calllog/GroupingListAdapter.java171
-rw-r--r--src/com/android/dialer/calllog/IntentProvider.java206
-rw-r--r--src/com/android/dialer/calllog/MissedCallNotificationReceiver.java53
-rw-r--r--src/com/android/dialer/calllog/MissedCallNotifier.java292
-rw-r--r--src/com/android/dialer/calllog/PhoneAccountUtils.java117
-rw-r--r--src/com/android/dialer/calllog/PhoneCallDetailsHelper.java359
-rw-r--r--src/com/android/dialer/calllog/PhoneCallDetailsViews.java73
-rw-r--r--src/com/android/dialer/calllog/PhoneNumberDisplayUtil.java83
-rw-r--r--src/com/android/dialer/calllog/PhoneQuery.java96
-rw-r--r--src/com/android/dialer/calllog/PromoCardViewHolder.java83
-rw-r--r--src/com/android/dialer/calllog/VisualVoicemailCallLogFragment.java87
-rw-r--r--src/com/android/dialer/calllog/VoicemailQueryHandler.java70
-rw-r--r--src/com/android/dialer/calllog/calllogcache/CallLogCache.java96
-rw-r--r--src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipop.java73
-rw-r--r--src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipopMr1.java110
-rw-r--r--src/com/android/dialer/compat/DialerCompatUtils.java31
-rw-r--r--src/com/android/dialer/compat/FilteredNumberCompat.java393
-rw-r--r--src/com/android/dialer/compat/SettingsCompat.java47
-rw-r--r--src/com/android/dialer/compat/UserManagerCompat.java71
-rw-r--r--src/com/android/dialer/contact/ContactUpdateService.java51
-rw-r--r--src/com/android/dialer/contactinfo/ContactInfoCache.java333
-rw-r--r--src/com/android/dialer/contactinfo/ContactInfoRequest.java65
-rw-r--r--src/com/android/dialer/contactinfo/ContactPhotoLoader.java120
-rw-r--r--src/com/android/dialer/contactinfo/NumberWithCountryIso.java53
-rw-r--r--src/com/android/dialer/database/DialerDatabaseHelper.java1169
-rw-r--r--src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java273
-rw-r--r--src/com/android/dialer/database/FilteredNumberContract.java163
-rw-r--r--src/com/android/dialer/database/FilteredNumberProvider.java211
-rw-r--r--src/com/android/dialer/database/VoicemailArchiveContract.java203
-rw-r--r--src/com/android/dialer/database/VoicemailArchiveProvider.java218
-rw-r--r--src/com/android/dialer/dialpad/DialpadFragment.java1695
-rw-r--r--src/com/android/dialer/dialpad/LatinSmartDialMap.java413
-rw-r--r--src/com/android/dialer/dialpad/PseudoEmergencyAnimator.java160
-rw-r--r--src/com/android/dialer/dialpad/SmartDialCursorLoader.java193
-rw-r--r--src/com/android/dialer/dialpad/SmartDialMap.java43
-rw-r--r--src/com/android/dialer/dialpad/SmartDialMatchPosition.java70
-rw-r--r--src/com/android/dialer/dialpad/SmartDialNameMatcher.java439
-rw-r--r--src/com/android/dialer/dialpad/SmartDialPrefix.java608
-rw-r--r--src/com/android/dialer/dialpad/UnicodeDialerKeyListener.java54
-rw-r--r--src/com/android/dialer/filterednumber/BlockNumberDialogFragment.java320
-rw-r--r--src/com/android/dialer/filterednumber/BlockedNumbersAdapter.java96
-rw-r--r--src/com/android/dialer/filterednumber/BlockedNumbersAutoMigrator.java101
-rw-r--r--src/com/android/dialer/filterednumber/BlockedNumbersFragment.java264
-rw-r--r--src/com/android/dialer/filterednumber/BlockedNumbersMigrator.java135
-rw-r--r--src/com/android/dialer/filterednumber/BlockedNumbersSettingsActivity.java162
-rw-r--r--src/com/android/dialer/filterednumber/FilteredNumbersUtil.java369
-rw-r--r--src/com/android/dialer/filterednumber/MigrateBlockedNumbersDialogFragment.java110
-rw-r--r--src/com/android/dialer/filterednumber/NumbersAdapter.java137
-rw-r--r--src/com/android/dialer/filterednumber/ViewNumbersToImportAdapter.java57
-rw-r--r--src/com/android/dialer/filterednumber/ViewNumbersToImportFragment.java133
-rw-r--r--src/com/android/dialer/interactions/PhoneNumberInteraction.java516
-rw-r--r--src/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java109
-rw-r--r--src/com/android/dialer/list/AllContactsFragment.java198
-rw-r--r--src/com/android/dialer/list/BlockedListSearchAdapter.java90
-rw-r--r--src/com/android/dialer/list/BlockedListSearchFragment.java244
-rw-r--r--src/com/android/dialer/list/ContentChangedFilter.java40
-rw-r--r--src/com/android/dialer/list/DialerPhoneNumberListAdapter.java220
-rw-r--r--src/com/android/dialer/list/DragDropController.java95
-rw-r--r--src/com/android/dialer/list/ListsFragment.java487
-rw-r--r--src/com/android/dialer/list/OnDragDropListener.java41
-rw-r--r--src/com/android/dialer/list/OnListFragmentScrolledListener.java26
-rw-r--r--src/com/android/dialer/list/PhoneFavoriteListView.java326
-rw-r--r--src/com/android/dialer/list/PhoneFavoriteSquareTileView.java112
-rw-r--r--src/com/android/dialer/list/PhoneFavoriteTileView.java155
-rw-r--r--src/com/android/dialer/list/PhoneFavoritesTileAdapter.java696
-rw-r--r--src/com/android/dialer/list/RegularSearchFragment.java151
-rw-r--r--src/com/android/dialer/list/RegularSearchListAdapter.java130
-rw-r--r--src/com/android/dialer/list/RemoveView.java94
-rw-r--r--src/com/android/dialer/list/SearchFragment.java399
-rw-r--r--src/com/android/dialer/list/SmartDialNumberListAdapter.java130
-rw-r--r--src/com/android/dialer/list/SmartDialSearchFragment.java134
-rw-r--r--src/com/android/dialer/list/SpeedDialFragment.java504
-rw-r--r--src/com/android/dialer/logging/InteractionEvent.java76
-rw-r--r--src/com/android/dialer/logging/Logger.java85
-rw-r--r--src/com/android/dialer/logging/ScreenEvent.java172
-rw-r--r--src/com/android/dialer/service/CachedNumberLookupService.java61
-rw-r--r--src/com/android/dialer/service/ExtendedCallInfoService.java79
-rw-r--r--src/com/android/dialer/settings/AppCompatPreferenceActivity.java155
-rw-r--r--src/com/android/dialer/settings/DefaultRingtonePreference.java65
-rw-r--r--src/com/android/dialer/settings/DialerSettingsActivity.java190
-rw-r--r--src/com/android/dialer/settings/DisplayOptionsSettingsFragment.java31
-rw-r--r--src/com/android/dialer/settings/SoundSettingsFragment.java245
-rw-r--r--src/com/android/dialer/util/AppCompatConstants.java30
-rw-r--r--src/com/android/dialer/util/Assert.java36
-rw-r--r--src/com/android/dialer/util/AsyncTaskExecutor.java48
-rw-r--r--src/com/android/dialer/util/AsyncTaskExecutors.java100
-rw-r--r--src/com/android/dialer/util/BlockReportSpamDialogs.java293
-rw-r--r--src/com/android/dialer/util/DialerUtils.java195
-rw-r--r--src/com/android/dialer/util/EmptyLoader.java60
-rw-r--r--src/com/android/dialer/util/ExpirableCache.java266
-rw-r--r--src/com/android/dialer/util/IntentUtil.java162
-rw-r--r--src/com/android/dialer/util/MoreStrings.java43
-rw-r--r--src/com/android/dialer/util/OrientationUtil.java34
-rw-r--r--src/com/android/dialer/util/PhoneLookupUtil.java40
-rw-r--r--src/com/android/dialer/util/PhoneNumberUtil.java138
-rw-r--r--src/com/android/dialer/util/TelecomUtil.java229
-rw-r--r--src/com/android/dialer/voicemail/VisualVoicemailEnabledChecker.java103
-rw-r--r--src/com/android/dialer/voicemail/VoicemailArchiveActivity.java160
-rw-r--r--src/com/android/dialer/voicemail/VoicemailArchivePlaybackPresenter.java90
-rw-r--r--src/com/android/dialer/voicemail/VoicemailAsyncTaskUtil.java346
-rw-r--r--src/com/android/dialer/voicemail/VoicemailAudioManager.java200
-rw-r--r--src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java638
-rw-r--r--src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java1010
-rw-r--r--src/com/android/dialer/voicemail/VoicemailStatusHelper.java91
-rw-r--r--src/com/android/dialer/voicemail/VoicemailStatusHelperImpl.java272
-rw-r--r--src/com/android/dialer/voicemail/WiredHeadsetManager.java88
-rw-r--r--src/com/android/dialer/widget/ActionBarController.java243
-rw-r--r--src/com/android/dialer/widget/EmptyContentView.java118
-rw-r--r--src/com/android/dialer/widget/SearchEditTextLayout.java321
-rw-r--r--src/com/android/dialerbind/DatabaseHelperManager.java28
-rw-r--r--src/com/android/dialerbind/ObjectFactory.java96
140 files changed, 0 insertions, 31561 deletions
diff --git a/src/com/android/dialer/CallDetailActivity.java b/src/com/android/dialer/CallDetailActivity.java
deleted file mode 100644
index 94c2f0018..000000000
--- a/src/com/android/dialer/CallDetailActivity.java
+++ /dev/null
@@ -1,507 +0,0 @@
-/*
- * Copyright (C) 2009 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;
-
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.support.v7.app.AppCompatActivity;
-import android.text.BidiFormatter;
-import android.text.TextDirectionHeuristics;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.ListView;
-import android.widget.QuickContactBadge;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.contacts.common.CallUtil;
-import com.android.contacts.common.ClipboardUtils;
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.interactions.TouchPointManager;
-import com.android.contacts.common.preference.ContactsPreferences;
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.contacts.common.util.UriUtils;
-import com.android.dialer.calllog.CallDetailHistoryAdapter;
-import com.android.dialer.calllog.CallLogAsyncTaskUtil;
-import com.android.dialer.calllog.CallLogAsyncTaskUtil.CallLogAsyncTaskListener;
-import com.android.dialer.calllog.CallTypeHelper;
-import com.android.dialer.calllog.ContactInfoHelper;
-import com.android.dialer.calllog.PhoneAccountUtils;
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnCheckBlockedListener;
-import com.android.dialer.filterednumber.BlockNumberDialogFragment;
-import com.android.dialer.filterednumber.FilteredNumbersUtil;
-import com.android.dialer.logging.InteractionEvent;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.util.DialerUtils;
-import com.android.dialer.util.IntentUtil.CallIntentBuilder;
-import com.android.dialer.util.PhoneNumberUtil;
-import com.android.dialer.util.TelecomUtil;
-import com.android.incallui.Call.LogState;
-
-/**
- * Displays the details of a specific call log entry.
- * <p>
- * This activity can be either started with the URI of a single call log entry, or with the
- * {@link #EXTRA_CALL_LOG_IDS} extra to specify a group of call log entries.
- */
-public class CallDetailActivity extends AppCompatActivity
- implements MenuItem.OnMenuItemClickListener, View.OnClickListener,
- BlockNumberDialogFragment.Callback {
- private static final String TAG = CallDetailActivity.class.getSimpleName();
-
- /** A long array extra containing ids of call log entries to display. */
- public static final String EXTRA_CALL_LOG_IDS = "EXTRA_CALL_LOG_IDS";
- /** If we are started with a voicemail, we'll find the uri to play with this extra. */
- public static final String EXTRA_VOICEMAIL_URI = "EXTRA_VOICEMAIL_URI";
- /** If the activity was triggered from a notification. */
- public static final String EXTRA_FROM_NOTIFICATION = "EXTRA_FROM_NOTIFICATION";
-
- public static final String VOICEMAIL_FRAGMENT_TAG = "voicemail_fragment";
-
- private CallLogAsyncTaskListener mCallLogAsyncTaskListener = new CallLogAsyncTaskListener() {
- @Override
- public void onDeleteCall() {
- finish();
- }
-
- @Override
- public void onDeleteVoicemail() {
- finish();
- }
-
- @Override
- public void onGetCallDetails(PhoneCallDetails[] details) {
- if (details == null) {
- // Somewhere went wrong: we're going to bail out and show error to users.
- Toast.makeText(mContext, R.string.toast_call_detail_error,
- Toast.LENGTH_SHORT).show();
- finish();
- return;
- }
-
- // All calls are from the same number and same contact, so pick the first detail.
- mDetails = details[0];
- mNumber = TextUtils.isEmpty(mDetails.number) ? null : mDetails.number.toString();
- mPostDialDigits = TextUtils.isEmpty(mDetails.postDialDigits)
- ? "" : mDetails.postDialDigits;
- mDisplayNumber = mDetails.displayNumber;
-
- final CharSequence callLocationOrType = getNumberTypeOrLocation(mDetails);
-
- final CharSequence displayNumber;
- if (!TextUtils.isEmpty(mDetails.postDialDigits)) {
- displayNumber = mDetails.number + mDetails.postDialDigits;
- } else {
- displayNumber = mDetails.displayNumber;
- }
-
- final String displayNumberStr = mBidiFormatter.unicodeWrap(
- displayNumber.toString(), TextDirectionHeuristics.LTR);
-
- mDetails.nameDisplayOrder = mContactsPreferences.getDisplayOrder();
-
- if (!TextUtils.isEmpty(mDetails.getPreferredName())) {
- mCallerName.setText(mDetails.getPreferredName());
- mCallerNumber.setText(callLocationOrType + " " + displayNumberStr);
- } else {
- mCallerName.setText(displayNumberStr);
- if (!TextUtils.isEmpty(callLocationOrType)) {
- mCallerNumber.setText(callLocationOrType);
- mCallerNumber.setVisibility(View.VISIBLE);
- } else {
- mCallerNumber.setVisibility(View.GONE);
- }
- }
-
- CharSequence accountLabel = PhoneAccountUtils.getAccountLabel(mContext,
- mDetails.accountHandle);
- CharSequence accountContentDescription =
- PhoneCallDetails.createAccountLabelDescription(mResources, mDetails.viaNumber,
- accountLabel);
- if (!TextUtils.isEmpty(mDetails.viaNumber)) {
- if (!TextUtils.isEmpty(accountLabel)) {
- accountLabel = mResources.getString(R.string.call_log_via_number_phone_account,
- accountLabel, mDetails.viaNumber);
- } else {
- accountLabel = mResources.getString(R.string.call_log_via_number,
- mDetails.viaNumber);
- }
- }
- if (!TextUtils.isEmpty(accountLabel)) {
- mAccountLabel.setText(accountLabel);
- mAccountLabel.setContentDescription(accountContentDescription);
- mAccountLabel.setVisibility(View.VISIBLE);
- } else {
- mAccountLabel.setVisibility(View.GONE);
- }
-
- final boolean canPlaceCallsTo =
- PhoneNumberUtil.canPlaceCallsTo(mNumber, mDetails.numberPresentation);
- mCallButton.setVisibility(canPlaceCallsTo ? View.VISIBLE : View.GONE);
- mCopyNumberActionItem.setVisibility(canPlaceCallsTo ? View.VISIBLE : View.GONE);
-
- updateBlockActionItemVisibility(canPlaceCallsTo ? View.VISIBLE : View.GONE);
-
- final boolean isSipNumber = PhoneNumberUtil.isSipNumber(mNumber);
- final boolean isVoicemailNumber =
- PhoneNumberUtil.isVoicemailNumber(mContext, mDetails.accountHandle, mNumber);
- final boolean showEditNumberBeforeCallAction =
- canPlaceCallsTo && !isSipNumber && !isVoicemailNumber;
- mEditBeforeCallActionItem.setVisibility(
- showEditNumberBeforeCallAction ? View.VISIBLE : View.GONE);
-
- final boolean showReportAction = mContactInfoHelper.canReportAsInvalid(
- mDetails.sourceType, mDetails.objectId);
- mReportActionItem.setVisibility(
- showReportAction ? View.VISIBLE : View.GONE);
-
- invalidateOptionsMenu();
-
- mHistoryList.setAdapter(
- new CallDetailHistoryAdapter(mContext, mInflater, mCallTypeHelper, details));
-
- updateFilteredNumberChanges();
- updateContactPhoto();
-
- findViewById(R.id.call_detail).setVisibility(View.VISIBLE);
- }
-
- /**
- * Determines the location geocode text for a call, or the phone number type
- * (if available).
- *
- * @param details The call details.
- * @return The phone number type or location.
- */
- private CharSequence getNumberTypeOrLocation(PhoneCallDetails details) {
- if (!TextUtils.isEmpty(details.namePrimary)) {
- return Phone.getTypeLabel(mResources, details.numberType,
- details.numberLabel);
- } else {
- return details.geocode;
- }
- }
- };
-
- private Context mContext;
- private ContactInfoHelper mContactInfoHelper;
- private ContactsPreferences mContactsPreferences;
- private CallTypeHelper mCallTypeHelper;
- private ContactPhotoManager mContactPhotoManager;
- private FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
- private BidiFormatter mBidiFormatter = BidiFormatter.getInstance();
- private LayoutInflater mInflater;
- private Resources mResources;
-
- private PhoneCallDetails mDetails;
- protected String mNumber;
- private Uri mVoicemailUri;
- private String mPostDialDigits = "";
- private String mDisplayNumber;
-
- private ListView mHistoryList;
- private QuickContactBadge mQuickContactBadge;
- private TextView mCallerName;
- private TextView mCallerNumber;
- private TextView mAccountLabel;
- private View mCallButton;
-
- private TextView mBlockNumberActionItem;
- private View mEditBeforeCallActionItem;
- private View mReportActionItem;
- private View mCopyNumberActionItem;
-
- private Integer mBlockedNumberId;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- mContext = this;
- mResources = getResources();
- mContactInfoHelper = new ContactInfoHelper(this, GeoUtil.getCurrentCountryIso(this));
- mContactsPreferences = new ContactsPreferences(mContext);
- mCallTypeHelper = new CallTypeHelper(getResources());
- mFilteredNumberAsyncQueryHandler =
- new FilteredNumberAsyncQueryHandler(getContentResolver());
-
- mVoicemailUri = getIntent().getParcelableExtra(EXTRA_VOICEMAIL_URI);
-
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
-
- setContentView(R.layout.call_detail);
- mInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
-
- mHistoryList = (ListView) findViewById(R.id.history);
- mHistoryList.addHeaderView(mInflater.inflate(R.layout.call_detail_header, null));
- mHistoryList.addFooterView(
- mInflater.inflate(R.layout.call_detail_footer, null), null, false);
-
- mQuickContactBadge = (QuickContactBadge) findViewById(R.id.quick_contact_photo);
- mQuickContactBadge.setOverlay(null);
- if (CompatUtils.hasPrioritizedMimeType()) {
- mQuickContactBadge.setPrioritizedMimeType(Phone.CONTENT_ITEM_TYPE);
- }
- mCallerName = (TextView) findViewById(R.id.caller_name);
- mCallerNumber = (TextView) findViewById(R.id.caller_number);
- mAccountLabel = (TextView) findViewById(R.id.phone_account_label);
- mContactPhotoManager = ContactPhotoManager.getInstance(this);
-
- mCallButton = findViewById(R.id.call_back_button);
- mCallButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (TextUtils.isEmpty(mNumber)) {
- return;
- }
- mContext.startActivity(
- new CallIntentBuilder(getDialableNumber())
- .setCallInitiationType(LogState.INITIATION_CALL_DETAILS)
- .build());
- }
- });
-
-
- mBlockNumberActionItem = (TextView) findViewById(R.id.call_detail_action_block);
- updateBlockActionItemVisibility(View.VISIBLE);
- mBlockNumberActionItem.setOnClickListener(this);
- mEditBeforeCallActionItem = findViewById(R.id.call_detail_action_edit_before_call);
- mEditBeforeCallActionItem.setOnClickListener(this);
- mReportActionItem = findViewById(R.id.call_detail_action_report);
- mReportActionItem.setOnClickListener(this);
-
- mCopyNumberActionItem = findViewById(R.id.call_detail_action_copy);
- mCopyNumberActionItem.setOnClickListener(this);
-
- if (getIntent().getBooleanExtra(EXTRA_FROM_NOTIFICATION, false)) {
- closeSystemDialogs();
- }
- }
-
- private void updateBlockActionItemVisibility(int visibility) {
- if (!FilteredNumberCompat.canAttemptBlockOperations(mContext)) {
- visibility = View.GONE;
- }
- mBlockNumberActionItem.setVisibility(visibility);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mContactsPreferences.refreshValue(ContactsPreferences.DISPLAY_ORDER_KEY);
- getCallDetails();
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- TouchPointManager.getInstance().setPoint((int) ev.getRawX(), (int) ev.getRawY());
- }
- return super.dispatchTouchEvent(ev);
- }
-
- public void getCallDetails() {
- CallLogAsyncTaskUtil.getCallDetails(this, getCallLogEntryUris(), mCallLogAsyncTaskListener);
- }
-
- /**
- * Returns the list of URIs to show.
- * <p>
- * There are two ways the URIs can be provided to the activity: as the data on the intent, or as
- * a list of ids in the call log added as an extra on the URI.
- * <p>
- * If both are available, the data on the intent takes precedence.
- */
- private Uri[] getCallLogEntryUris() {
- final Uri uri = getIntent().getData();
- if (uri != null) {
- // If there is a data on the intent, it takes precedence over the extra.
- return new Uri[]{ uri };
- }
- final long[] ids = getIntent().getLongArrayExtra(EXTRA_CALL_LOG_IDS);
- final int numIds = ids == null ? 0 : ids.length;
- final Uri[] uris = new Uri[numIds];
- for (int index = 0; index < numIds; ++index) {
- uris[index] = ContentUris.withAppendedId(
- TelecomUtil.getCallLogUri(CallDetailActivity.this), ids[index]);
- }
- return uris;
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- final MenuItem deleteMenuItem = menu.add(
- Menu.NONE,
- R.id.call_detail_delete_menu_item,
- Menu.NONE,
- R.string.call_details_delete);
- deleteMenuItem.setIcon(R.drawable.ic_delete_24dp);
- deleteMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
- deleteMenuItem.setOnMenuItemClickListener(this);
-
- return super.onCreateOptionsMenu(menu);
- }
-
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- if (item.getItemId() == R.id.call_detail_delete_menu_item) {
- if (hasVoicemail()) {
- CallLogAsyncTaskUtil.deleteVoicemail(
- this, mVoicemailUri, mCallLogAsyncTaskListener);
- } else {
- final StringBuilder callIds = new StringBuilder();
- for (Uri callUri : getCallLogEntryUris()) {
- if (callIds.length() != 0) {
- callIds.append(",");
- }
- callIds.append(ContentUris.parseId(callUri));
- }
- CallLogAsyncTaskUtil.deleteCalls(
- this, callIds.toString(), mCallLogAsyncTaskListener);
- }
- }
- return true;
- }
-
- @Override
- public void onClick(View view) {
- int resId = view.getId();
- if (resId == R.id.call_detail_action_block) {
- FilteredNumberCompat
- .showBlockNumberDialogFlow(mContext.getContentResolver(), mBlockedNumberId,
- mNumber, mDetails.countryIso, mDisplayNumber, R.id.call_detail,
- getFragmentManager(), this);
- } else if (resId == R.id.call_detail_action_copy) {
- ClipboardUtils.copyText(mContext, null, mNumber, true);
- } else if (resId == R.id.call_detail_action_edit_before_call) {
- Intent dialIntent = new Intent(Intent.ACTION_DIAL,
- CallUtil.getCallUri(getDialableNumber()));
- DialerUtils.startActivityWithErrorToast(mContext, dialIntent);
- } else {
- Log.wtf(TAG, "Unexpected onClick event from " + view);
- }
- }
-
- @Override
- public void onFilterNumberSuccess() {
- Logger.logInteraction(InteractionEvent.BLOCK_NUMBER_CALL_DETAIL);
- updateFilteredNumberChanges();
- }
-
- @Override
- public void onUnfilterNumberSuccess() {
- Logger.logInteraction(InteractionEvent.UNBLOCK_NUMBER_CALL_DETAIL);
- updateFilteredNumberChanges();
- }
-
- @Override
- public void onChangeFilteredNumberUndo() {
- updateFilteredNumberChanges();
- }
-
- private void updateFilteredNumberChanges() {
- if (mDetails == null ||
- !FilteredNumbersUtil.canBlockNumber(this, mNumber, mDetails.countryIso)) {
- return;
- }
-
- final boolean success = mFilteredNumberAsyncQueryHandler.isBlockedNumber(
- new OnCheckBlockedListener() {
- @Override
- public void onCheckComplete(Integer id) {
- mBlockedNumberId = id;
- updateBlockActionItem();
- }
- }, mNumber, mDetails.countryIso);
-
- if (!success) {
- updateBlockActionItem();
- }
- }
-
- // Loads and displays the contact photo.
- private void updateContactPhoto() {
- if (mDetails == null) {
- return;
- }
-
- final boolean isVoicemailNumber =
- PhoneNumberUtil.isVoicemailNumber(mContext, mDetails.accountHandle, mNumber);
- final boolean isBusiness = mContactInfoHelper.isBusiness(mDetails.sourceType);
- int contactType = ContactPhotoManager.TYPE_DEFAULT;
- if (isVoicemailNumber) {
- contactType = ContactPhotoManager.TYPE_VOICEMAIL;
- } else if (isBusiness) {
- contactType = ContactPhotoManager.TYPE_BUSINESS;
- }
-
- final String displayName = TextUtils.isEmpty(mDetails.namePrimary)
- ? mDetails.displayNumber : mDetails.namePrimary.toString();
- final String lookupKey = mDetails.contactUri == null
- ? null : UriUtils.getLookupKeyFromUri(mDetails.contactUri);
-
- final DefaultImageRequest request =
- new DefaultImageRequest(displayName, lookupKey, contactType, true /* isCircular */);
-
- mQuickContactBadge.assignContactUri(mDetails.contactUri);
- mQuickContactBadge.setContentDescription(
- mResources.getString(R.string.description_contact_details, displayName));
-
- mContactPhotoManager.loadDirectoryPhoto(mQuickContactBadge, mDetails.photoUri,
- false /* darkTheme */, true /* isCircular */, request);
- }
-
- private void updateBlockActionItem() {
- if (mBlockedNumberId == null) {
- mBlockNumberActionItem.setText(R.string.action_block_number);
- mBlockNumberActionItem.setCompoundDrawablesRelativeWithIntrinsicBounds(
- R.drawable.ic_call_detail_block, 0, 0, 0);
- } else {
- mBlockNumberActionItem.setText(R.string.action_unblock_number);
- mBlockNumberActionItem.setCompoundDrawablesRelativeWithIntrinsicBounds(
- R.drawable.ic_call_detail_unblock, 0, 0, 0);
- }
- }
-
- private void closeSystemDialogs() {
- sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
- }
-
- private String getDialableNumber() {
- return mNumber + mPostDialDigits;
- }
-
- @NeededForTesting
- public boolean hasVoicemail() {
- return mVoicemailUri != null;
- }
-}
diff --git a/src/com/android/dialer/DialerApplication.java b/src/com/android/dialer/DialerApplication.java
deleted file mode 100644
index 1a0497bb9..000000000
--- a/src/com/android/dialer/DialerApplication.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2013 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;
-
-import android.app.Application;
-import android.content.Context;
-import android.os.Trace;
-import android.preference.PreferenceManager;
-import android.support.annotation.Nullable;
-
-import com.android.contacts.common.extensions.ExtensionsFactory;
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.filterednumber.BlockedNumbersAutoMigrator;
-
-public class DialerApplication extends Application {
-
- private static final String TAG = "DialerApplication";
-
- private static Context sContext;
-
- @Override
- public void onCreate() {
- sContext = this;
- Trace.beginSection(TAG + " onCreate");
- super.onCreate();
- Trace.beginSection(TAG + " ExtensionsFactory initialization");
- ExtensionsFactory.init(getApplicationContext());
- Trace.endSection();
- new BlockedNumbersAutoMigrator(PreferenceManager.getDefaultSharedPreferences(this),
- new FilteredNumberAsyncQueryHandler(getContentResolver())).autoMigrate();
- Trace.endSection();
- }
-
- @Nullable
- public static Context getContext() {
- return sContext;
- }
-
- @NeededForTesting
- public static void setContextForTest(Context context) {
- sContext = context;
- }
-}
diff --git a/src/com/android/dialer/DialerBackupAgent.java b/src/com/android/dialer/DialerBackupAgent.java
deleted file mode 100644
index 928be029c..000000000
--- a/src/com/android/dialer/DialerBackupAgent.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2013 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;
-
-import android.app.backup.BackupAgentHelper;
-import android.app.backup.BackupDataInput;
-import android.app.backup.SharedPreferencesBackupHelper;
-import android.content.Context;
-
-/**
- * The Dialer backup agent backs up the shared preferences settings of the
- * Dialer App. Right now it backs up the whole shared preference file. This
- * can be modified in the future to accommodate partical backup.
- */
-public class DialerBackupAgent extends BackupAgentHelper
-{
- private static final String SHARED_KEY = "shared_pref";
-
- @Override
- public void onCreate() {
- addHelper(SHARED_KEY, new SharedPreferencesBackupHelper(this,
- DialtactsActivity.SHARED_PREFS_NAME));
- }
-}
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
deleted file mode 100644
index 441501cfd..000000000
--- a/src/com/android/dialer/DialtactsActivity.java
+++ /dev/null
@@ -1,1413 +0,0 @@
-/*
- * Copyright (C) 2013 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;
-
-import android.app.Fragment;
-import android.app.FragmentTransaction;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Trace;
-import android.provider.CallLog.Calls;
-import android.speech.RecognizerIntent;
-import android.support.design.widget.CoordinatorLayout;
-import android.support.v4.view.ViewPager;
-import android.support.v7.app.ActionBar;
-import android.telecom.PhoneAccount;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.util.Log;
-import android.view.DragEvent;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnDragListener;
-import android.view.ViewTreeObserver;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.AbsListView.OnScrollListener;
-import android.widget.EditText;
-import android.widget.ImageButton;
-import android.widget.PopupMenu;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.contacts.common.dialog.ClearFrequentsDialog;
-import com.android.contacts.common.interactions.ImportExportDialogFragment;
-import com.android.contacts.common.interactions.TouchPointManager;
-import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.contacts.common.widget.FloatingActionButtonController;
-import com.android.dialer.calllog.CallLogActivity;
-import com.android.dialer.calllog.CallLogFragment;
-import com.android.dialer.database.DialerDatabaseHelper;
-import com.android.dialer.dialpad.DialpadFragment;
-import com.android.dialer.dialpad.SmartDialNameMatcher;
-import com.android.dialer.dialpad.SmartDialPrefix;
-import com.android.dialer.interactions.PhoneNumberInteraction;
-import com.android.dialer.list.DragDropController;
-import com.android.dialer.list.ListsFragment;
-import com.android.dialer.list.OnDragDropListener;
-import com.android.dialer.list.OnListFragmentScrolledListener;
-import com.android.dialer.list.PhoneFavoriteSquareTileView;
-import com.android.dialer.list.RegularSearchFragment;
-import com.android.dialer.list.SearchFragment;
-import com.android.dialer.list.SmartDialSearchFragment;
-import com.android.dialer.list.SpeedDialFragment;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.logging.ScreenEvent;
-import com.android.dialer.settings.DialerSettingsActivity;
-import com.android.dialer.util.Assert;
-import com.android.dialer.util.DialerUtils;
-import com.android.dialer.util.IntentUtil;
-import com.android.dialer.util.IntentUtil.CallIntentBuilder;
-import com.android.dialer.util.TelecomUtil;
-import com.android.dialer.voicemail.VoicemailArchiveActivity;
-import com.android.dialer.widget.ActionBarController;
-import com.android.dialer.widget.SearchEditTextLayout;
-import com.android.dialerbind.DatabaseHelperManager;
-import com.android.dialerbind.ObjectFactory;
-import com.android.phone.common.animation.AnimUtils;
-import com.android.phone.common.animation.AnimationListenerAdapter;
-import com.google.common.annotations.VisibleForTesting;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * The dialer tab's title is 'phone', a more common name (see strings.xml).
- */
-public class DialtactsActivity extends TransactionSafeActivity implements View.OnClickListener,
- DialpadFragment.OnDialpadQueryChangedListener,
- OnListFragmentScrolledListener,
- CallLogFragment.HostInterface,
- DialpadFragment.HostInterface,
- ListsFragment.HostInterface,
- SpeedDialFragment.HostInterface,
- SearchFragment.HostInterface,
- OnDragDropListener,
- OnPhoneNumberPickerActionListener,
- PopupMenu.OnMenuItemClickListener,
- ViewPager.OnPageChangeListener,
- ActionBarController.ActivityUi {
- private static final String TAG = "DialtactsActivity";
-
- public static final boolean DEBUG = false;
-
- public static final String SHARED_PREFS_NAME = "com.android.dialer_preferences";
-
- 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_SEARCH_QUERY = "search_query";
- private static final String KEY_FIRST_LAUNCH = "first_launch";
- private static final String KEY_IS_DIALPAD_SHOWN = "is_dialpad_shown";
-
- @VisibleForTesting
- public static final String TAG_DIALPAD_FRAGMENT = "dialpad";
- private static final String TAG_REGULAR_SEARCH_FRAGMENT = "search";
- private static final String TAG_SMARTDIAL_SEARCH_FRAGMENT = "smartdial";
- private static final String TAG_FAVORITES_FRAGMENT = "favorites";
-
- /**
- * Just for backward compatibility. Should behave as same as {@link Intent#ACTION_DIAL}.
- */
- private static final String ACTION_TOUCH_DIALER = "com.android.phone.action.TOUCH_DIALER";
- public static final String EXTRA_SHOW_TAB = "EXTRA_SHOW_TAB";
-
- private static final int ACTIVITY_REQUEST_CODE_VOICE_SEARCH = 1;
-
- private static final int FAB_SCALE_IN_DELAY_MS = 300;
-
- private CoordinatorLayout mParentLayout;
-
- /**
- * Fragment containing the dialpad that slides into view
- */
- protected DialpadFragment mDialpadFragment;
-
- /**
- * Fragment for searching phone numbers using the alphanumeric keyboard.
- */
- private RegularSearchFragment mRegularSearchFragment;
-
- /**
- * Fragment for searching phone numbers using the dialpad.
- */
- private SmartDialSearchFragment mSmartDialSearchFragment;
-
- /**
- * Animation that slides in.
- */
- private Animation mSlideIn;
-
- /**
- * Animation that slides out.
- */
- private Animation mSlideOut;
-
- AnimationListenerAdapter mSlideInListener = new AnimationListenerAdapter() {
- @Override
- public void onAnimationEnd(Animation animation) {
- maybeEnterSearchUi();
- }
- };
-
- /**
- * Listener for after slide out animation completes on dialer fragment.
- */
- AnimationListenerAdapter mSlideOutListener = new AnimationListenerAdapter() {
- @Override
- public void onAnimationEnd(Animation animation) {
- commitDialpadFragmentHide();
- }
- };
-
- /**
- * Fragment containing the speed dial list, call history list, and all contacts list.
- */
- private ListsFragment mListsFragment;
-
- /**
- * Tracks whether onSaveInstanceState has been called. If true, no fragment transactions can
- * be commited.
- */
- private boolean mStateSaved;
- private boolean mIsRestarting;
- private boolean mInDialpadSearch;
- private boolean mInRegularSearch;
- private boolean mClearSearchOnPause;
- private boolean mIsDialpadShown;
- private boolean mShowDialpadOnResume;
-
- /**
- * Whether or not the device is in landscape orientation.
- */
- private boolean mIsLandscape;
-
- /**
- * True if the dialpad is only temporarily showing due to being in call
- */
- private boolean mInCallDialpadUp;
-
- /**
- * True when this activity has been launched for the first time.
- */
- private boolean mFirstLaunch;
-
- /**
- * Search query to be applied to the SearchView in the ActionBar once
- * onCreateOptionsMenu has been called.
- */
- private String mPendingSearchViewQuery;
-
- private PopupMenu mOverflowMenu;
- private EditText mSearchView;
- private View mVoiceSearchButton;
-
- private String mSearchQuery;
- private String mDialpadQuery;
-
- private DialerDatabaseHelper mDialerDatabaseHelper;
- private DragDropController mDragDropController;
- private ActionBarController mActionBarController;
-
- private FloatingActionButtonController mFloatingActionButtonController;
-
- private int mActionBarHeight;
- private int mPreviouslySelectedTabIndex;
-
- /**
- * The text returned from a voice search query. Set in {@link #onActivityResult} and used in
- * {@link #onResume()} to populate the search box.
- */
- private String mVoiceSearchQuery;
-
- protected class OptionsPopupMenu extends PopupMenu {
- public OptionsPopupMenu(Context context, View anchor) {
- super(context, anchor, Gravity.END);
- }
-
- @Override
- public void show() {
- final boolean hasContactsPermission =
- PermissionsUtil.hasContactsPermissions(DialtactsActivity.this);
- final Menu menu = getMenu();
- final MenuItem clearFrequents = menu.findItem(R.id.menu_clear_frequents);
- clearFrequents.setVisible(mListsFragment != null &&
- mListsFragment.getSpeedDialFragment() != null &&
- mListsFragment.getSpeedDialFragment().hasFrequents() && hasContactsPermission);
-
- menu.findItem(R.id.menu_import_export).setVisible(hasContactsPermission);
- menu.findItem(R.id.menu_add_contact).setVisible(hasContactsPermission);
-
- menu.findItem(R.id.menu_history).setVisible(
- PermissionsUtil.hasPhonePermissions(DialtactsActivity.this));
- super.show();
- }
- }
-
- /**
- * Listener that listens to drag events and sends their x and y coordinates to a
- * {@link DragDropController}.
- */
- private class LayoutOnDragListener implements OnDragListener {
- @Override
- public boolean onDrag(View v, DragEvent event) {
- if (event.getAction() == DragEvent.ACTION_DRAG_LOCATION) {
- mDragDropController.handleDragHovered(v, (int) event.getX(), (int) event.getY());
- }
- return true;
- }
- }
-
- /**
- * Listener used to send search queries to the phone search fragment.
- */
- 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) {
- final String newText = s.toString();
- if (newText.equals(mSearchQuery)) {
- // If the query hasn't changed (perhaps due to activity being destroyed
- // and restored, or user launching the same DIAL intent twice), then there is
- // no need to do anything here.
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "onTextChange for mSearchView called with new query: " + newText);
- Log.d(TAG, "Previous Query: " + mSearchQuery);
- }
- mSearchQuery = newText;
-
- // Show search fragment only when the query string is changed to non-empty text.
- if (!TextUtils.isEmpty(newText)) {
- // Call enterSearchUi only if we are switching search modes, or showing a search
- // fragment for the first time.
- final boolean sameSearchMode = (mIsDialpadShown && mInDialpadSearch) ||
- (!mIsDialpadShown && mInRegularSearch);
- if (!sameSearchMode) {
- enterSearchUi(mIsDialpadShown, mSearchQuery, true /* animate */);
- }
- }
-
- if (mSmartDialSearchFragment != null && mSmartDialSearchFragment.isVisible()) {
- mSmartDialSearchFragment.setQueryString(mSearchQuery, false /* delaySelection */);
- } else if (mRegularSearchFragment != null && mRegularSearchFragment.isVisible()) {
- mRegularSearchFragment.setQueryString(mSearchQuery, false /* delaySelection */);
- }
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- }
- };
-
-
- /**
- * Open the search UI when the user clicks on the search box.
- */
- private final View.OnClickListener mSearchViewOnClickListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (!isInSearchUi()) {
- mActionBarController.onSearchBoxTapped();
- enterSearchUi(false /* smartDialSearch */, mSearchView.getText().toString(),
- true /* animate */);
- }
- }
- };
-
- /**
- * 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.
- maybeExitSearchUi();
- } else {
- // If the search term is not empty, show the dialpad fab.
- showFabInSearchUi();
- }
- }
- return false;
- }
- };
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- TouchPointManager.getInstance().setPoint((int) ev.getRawX(), (int) ev.getRawY());
- }
- return super.dispatchTouchEvent(ev);
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- Trace.beginSection(TAG + " onCreate");
- super.onCreate(savedInstanceState);
-
- mFirstLaunch = true;
-
- final Resources resources = getResources();
- mActionBarHeight = resources.getDimensionPixelSize(R.dimen.action_bar_height_large);
-
- Trace.beginSection(TAG + " setContentView");
- setContentView(R.layout.dialtacts_activity);
- Trace.endSection();
- getWindow().setBackgroundDrawable(null);
-
- Trace.beginSection(TAG + " setup Views");
- final ActionBar actionBar = getSupportActionBar();
- actionBar.setCustomView(R.layout.search_edittext);
- actionBar.setDisplayShowCustomEnabled(true);
- actionBar.setBackgroundDrawable(null);
-
- SearchEditTextLayout searchEditTextLayout = (SearchEditTextLayout) actionBar
- .getCustomView().findViewById(R.id.search_view_container);
- searchEditTextLayout.setPreImeKeyListener(mSearchEditTextLayoutListener);
-
- mActionBarController = new ActionBarController(this, searchEditTextLayout);
-
- mSearchView = (EditText) searchEditTextLayout.findViewById(R.id.search_view);
- mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener);
- mVoiceSearchButton = searchEditTextLayout.findViewById(R.id.voice_search_button);
- searchEditTextLayout.findViewById(R.id.search_magnifying_glass)
- .setOnClickListener(mSearchViewOnClickListener);
- searchEditTextLayout.findViewById(R.id.search_box_start_search)
- .setOnClickListener(mSearchViewOnClickListener);
- searchEditTextLayout.setOnClickListener(mSearchViewOnClickListener);
- 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;
- mPreviouslySelectedTabIndex = ListsFragment.TAB_INDEX_SPEED_DIAL;
- final View floatingActionButtonContainer = findViewById(
- R.id.floating_action_button_container);
- ImageButton floatingActionButton = (ImageButton) findViewById(R.id.floating_action_button);
- floatingActionButton.setOnClickListener(this);
- mFloatingActionButtonController = new FloatingActionButtonController(this,
- floatingActionButtonContainer, floatingActionButton);
-
- ImageButton optionsMenuButton =
- (ImageButton) searchEditTextLayout.findViewById(R.id.dialtacts_options_menu_button);
- optionsMenuButton.setOnClickListener(this);
- mOverflowMenu = buildOptionsMenu(searchEditTextLayout);
- optionsMenuButton.setOnTouchListener(mOverflowMenu.getDragToOpenListener());
-
- // Add the favorites fragment but only if savedInstanceState is null. Otherwise the
- // fragment manager is responsible for recreating it.
- if (savedInstanceState == null) {
- getFragmentManager().beginTransaction()
- .add(R.id.dialtacts_frame, new ListsFragment(), TAG_FAVORITES_FRAGMENT)
- .commit();
- } else {
- mSearchQuery = savedInstanceState.getString(KEY_SEARCH_QUERY);
- mInRegularSearch = savedInstanceState.getBoolean(KEY_IN_REGULAR_SEARCH_UI);
- mInDialpadSearch = savedInstanceState.getBoolean(KEY_IN_DIALPAD_SEARCH_UI);
- mFirstLaunch = savedInstanceState.getBoolean(KEY_FIRST_LAUNCH);
- mShowDialpadOnResume = savedInstanceState.getBoolean(KEY_IS_DIALPAD_SHOWN);
- mActionBarController.restoreInstanceState(savedInstanceState);
- }
-
- final boolean isLayoutRtl = DialerUtils.isRtl();
- if (mIsLandscape) {
- mSlideIn = AnimationUtils.loadAnimation(this,
- isLayoutRtl ? R.anim.dialpad_slide_in_left : R.anim.dialpad_slide_in_right);
- mSlideOut = AnimationUtils.loadAnimation(this,
- isLayoutRtl ? R.anim.dialpad_slide_out_left : R.anim.dialpad_slide_out_right);
- } else {
- mSlideIn = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_in_bottom);
- mSlideOut = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_out_bottom);
- }
-
- mSlideIn.setInterpolator(AnimUtils.EASE_IN);
- mSlideOut.setInterpolator(AnimUtils.EASE_OUT);
-
- mSlideIn.setAnimationListener(mSlideInListener);
- mSlideOut.setAnimationListener(mSlideOutListener);
-
- mParentLayout = (CoordinatorLayout) findViewById(R.id.dialtacts_mainlayout);
- mParentLayout.setOnDragListener(new LayoutOnDragListener());
- floatingActionButtonContainer.getViewTreeObserver().addOnGlobalLayoutListener(
- new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- final ViewTreeObserver observer =
- floatingActionButtonContainer.getViewTreeObserver();
- if (!observer.isAlive()) {
- return;
- }
- observer.removeOnGlobalLayoutListener(this);
- int screenWidth = mParentLayout.getWidth();
- mFloatingActionButtonController.setScreenWidth(screenWidth);
- mFloatingActionButtonController.align(
- getFabAlignment(), false /* animate */);
- }
- });
-
- Trace.endSection();
-
- Trace.beginSection(TAG + " initialize smart dialing");
- mDialerDatabaseHelper = DatabaseHelperManager.getDatabaseHelper(this);
- SmartDialPrefix.initializeNanpSettings(this);
- Trace.endSection();
- Trace.endSection();
- }
-
- @Override
- protected void onResume() {
- Trace.beginSection(TAG + " onResume");
- super.onResume();
-
- mStateSaved = false;
- if (mFirstLaunch) {
- displayFragment(getIntent());
- } else if (!phoneIsInUse() && mInCallDialpadUp) {
- hideDialpadFragment(false, true);
- mInCallDialpadUp = false;
- } else if (mShowDialpadOnResume) {
- showDialpadFragment(false);
- mShowDialpadOnResume = false;
- }
-
- // If there was a voice query result returned in the {@link #onActivityResult} callback, it
- // will have been stashed in mVoiceSearchQuery since the search results fragment cannot be
- // shown until onResume has completed. Active the search UI and set the search term now.
- if (!TextUtils.isEmpty(mVoiceSearchQuery)) {
- mActionBarController.onSearchBoxTapped();
- mSearchView.setText(mVoiceSearchQuery);
- mVoiceSearchQuery = null;
- }
-
- mFirstLaunch = false;
-
- if (mIsRestarting) {
- // This is only called when the activity goes from resumed -> paused -> resumed, so it
- // will not cause an extra view to be sent out on rotation
- if (mIsDialpadShown) {
- Logger.logScreenView(ScreenEvent.DIALPAD, this);
- }
- mIsRestarting = false;
- }
-
- prepareVoiceSearchButton();
- mDialerDatabaseHelper.startSmartDialUpdateThread();
- mFloatingActionButtonController.align(getFabAlignment(), false /* animate */);
-
- if (Calls.CONTENT_TYPE.equals(getIntent().getType())) {
- // Externally specified extras take precedence to EXTRA_SHOW_TAB, which is only
- // used internally.
- final Bundle extras = getIntent().getExtras();
- if (extras != null
- && extras.getInt(Calls.EXTRA_CALL_TYPE_FILTER) == Calls.VOICEMAIL_TYPE) {
- mListsFragment.showTab(ListsFragment.TAB_INDEX_VOICEMAIL);
- } else {
- mListsFragment.showTab(ListsFragment.TAB_INDEX_HISTORY);
- }
- } else if (getIntent().hasExtra(EXTRA_SHOW_TAB)) {
- int index = getIntent().getIntExtra(EXTRA_SHOW_TAB, ListsFragment.TAB_INDEX_SPEED_DIAL);
- if (index < mListsFragment.getTabCount()) {
- mListsFragment.showTab(index);
- }
- }
-
- setSearchBoxHint();
-
- Trace.endSection();
- }
-
- @Override
- protected void onRestart() {
- super.onRestart();
- mIsRestarting = true;
- }
-
- @Override
- protected void onPause() {
- // Only clear missed calls if the pause was not triggered by an orientation change
- // (or any other confirguration change)
- if (!isChangingConfigurations()) {
- updateMissedCalls();
- }
- if (mClearSearchOnPause) {
- hideDialpadAndSearchUi();
- mClearSearchOnPause = false;
- }
- if (mSlideOut.hasStarted() && !mSlideOut.hasEnded()) {
- commitDialpadFragmentHide();
- }
- super.onPause();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- 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_FIRST_LAUNCH, mFirstLaunch);
- outState.putBoolean(KEY_IS_DIALPAD_SHOWN, mIsDialpadShown);
- mActionBarController.saveInstanceState(outState);
- mStateSaved = true;
- }
-
- @Override
- public void onAttachFragment(Fragment fragment) {
- if (fragment instanceof DialpadFragment) {
- mDialpadFragment = (DialpadFragment) fragment;
- if (!mIsDialpadShown && !mShowDialpadOnResume) {
- final FragmentTransaction transaction = getFragmentManager().beginTransaction();
- transaction.hide(mDialpadFragment);
- transaction.commit();
- }
- } else if (fragment instanceof SmartDialSearchFragment) {
- mSmartDialSearchFragment = (SmartDialSearchFragment) fragment;
- mSmartDialSearchFragment.setOnPhoneNumberPickerActionListener(this);
- if (!TextUtils.isEmpty(mDialpadQuery)) {
- mSmartDialSearchFragment.setAddToContactNumber(mDialpadQuery);
- }
- } else if (fragment instanceof SearchFragment) {
- mRegularSearchFragment = (RegularSearchFragment) fragment;
- mRegularSearchFragment.setOnPhoneNumberPickerActionListener(this);
- } else if (fragment instanceof ListsFragment) {
- mListsFragment = (ListsFragment) fragment;
- mListsFragment.addOnPageChangeListener(this);
- }
- }
-
- protected void handleMenuSettings() {
- final Intent intent = new Intent(this, DialerSettingsActivity.class);
- startActivity(intent);
- }
-
- @Override
- public void onClick(View view) {
- int resId = view.getId();
- if (resId == R.id.floating_action_button) {
- if (mListsFragment.getCurrentTabIndex()
- == ListsFragment.TAB_INDEX_ALL_CONTACTS && !mInRegularSearch &&
- !mInDialpadSearch) {
- DialerUtils.startActivityWithErrorToast(
- this,
- IntentUtil.getNewContactIntent(),
- R.string.add_contact_not_available);
- } else if (!mIsDialpadShown) {
- mInCallDialpadUp = false;
- showDialpadFragment(true);
- }
- } else if (resId == R.id.voice_search_button) {
- try {
- startActivityForResult(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH),
- ACTIVITY_REQUEST_CODE_VOICE_SEARCH);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(DialtactsActivity.this, R.string.voice_search_not_available,
- Toast.LENGTH_SHORT).show();
- }
- } else if (resId == R.id.dialtacts_options_menu_button) {
- mOverflowMenu.show();
- } else {
- Log.wtf(TAG, "Unexpected onClick event from " + view);
- }
- }
-
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- if (!isSafeToCommitTransactions()) {
- return true;
- }
-
- int resId = item.getItemId();
- if (resId == R.id.menu_history) {// Use explicit CallLogActivity intent instead of ACTION_VIEW +
- // CONTENT_TYPE, so that we always open our call log from our dialer
- final Intent intent = new Intent(this, CallLogActivity.class);
- startActivity(intent);
- } else if (resId == R.id.menu_add_contact) {
- DialerUtils.startActivityWithErrorToast(
- this,
- IntentUtil.getNewContactIntent(),
- R.string.add_contact_not_available);
- } else if (resId == R.id.menu_import_export) {// We hard-code the "contactsAreAvailable" argument because doing it properly would
- // involve querying a {@link ProviderStatusLoader}, which we don't want to do right
- // now in Dialtacts for (potential) performance reasons. Compare with how it is
- // done in {@link PeopleActivity}.
- if (mListsFragment.getCurrentTabIndex() == ListsFragment.TAB_INDEX_SPEED_DIAL) {
- ImportExportDialogFragment.show(getFragmentManager(), true,
- DialtactsActivity.class, ImportExportDialogFragment.EXPORT_MODE_FAVORITES);
- } else {
- ImportExportDialogFragment.show(getFragmentManager(), true,
- DialtactsActivity.class, ImportExportDialogFragment.EXPORT_MODE_DEFAULT);
- }
- Logger.logScreenView(ScreenEvent.IMPORT_EXPORT_CONTACTS, this);
- return true;
- } else if (resId == R.id.menu_clear_frequents) {
- ClearFrequentsDialog.show(getFragmentManager());
- Logger.logScreenView(ScreenEvent.CLEAR_FREQUENTS, this);
- return true;
- } else if (resId == R.id.menu_call_settings) {
- handleMenuSettings();
- Logger.logScreenView(ScreenEvent.SETTINGS, this);
- return true;
- } else if (resId == R.id.menu_archive) {
- final Intent intent = new Intent(this, VoicemailArchiveActivity.class);
- startActivity(intent);
- return true;
- }
- return false;
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == ACTIVITY_REQUEST_CODE_VOICE_SEARCH) {
- if (resultCode == RESULT_OK) {
- final ArrayList<String> matches = data.getStringArrayListExtra(
- RecognizerIntent.EXTRA_RESULTS);
- if (matches.size() > 0) {
- final String match = matches.get(0);
- mVoiceSearchQuery = match;
- } else {
- Log.e(TAG, "Voice search - nothing heard");
- }
- } else {
- Log.e(TAG, "Voice search failed");
- }
- }
- super.onActivityResult(requestCode, resultCode, data);
- }
-
- /**
- * Update the number of unread voicemails (potentially other tabs) displayed next to the tab
- * icon.
- */
- public void updateTabUnreadCounts() {
- mListsFragment.updateTabUnreadCounts();
- }
-
- /**
- * Initiates a fragment transaction to show the dialpad fragment. Animations and other visual
- * updates are handled by a callback which is invoked after the dialpad fragment is shown.
- * @see #onDialpadShown
- */
- private void showDialpadFragment(boolean animate) {
- if (mIsDialpadShown || mStateSaved) {
- return;
- }
- mIsDialpadShown = true;
-
- mListsFragment.setUserVisibleHint(false);
-
- final FragmentTransaction ft = getFragmentManager().beginTransaction();
- if (mDialpadFragment == null) {
- mDialpadFragment = new DialpadFragment();
- ft.add(R.id.dialtacts_container, mDialpadFragment, TAG_DIALPAD_FRAGMENT);
- } else {
- ft.show(mDialpadFragment);
- }
-
- mDialpadFragment.setAnimate(animate);
- Logger.logScreenView(ScreenEvent.DIALPAD, this);
- ft.commit();
-
- if (animate) {
- mFloatingActionButtonController.scaleOut();
- } else {
- mFloatingActionButtonController.setVisible(false);
- maybeEnterSearchUi();
- }
- mActionBarController.onDialpadUp();
-
- mListsFragment.getView().animate().alpha(0).withLayer();
-
- //adjust the title, so the user will know where we're at when the activity start/resumes.
- setTitle(R.string.launcherDialpadActivityLabel);
- }
-
- /**
- * Callback from child DialpadFragment when the dialpad is shown.
- */
- public void onDialpadShown() {
- Assert.assertNotNull(mDialpadFragment);
- if (mDialpadFragment.getAnimate()) {
- mDialpadFragment.getView().startAnimation(mSlideIn);
- } else {
- mDialpadFragment.setYFraction(0);
- }
-
- updateSearchFragmentPosition();
- }
-
- /**
- * Initiates animations and other visual updates to hide the dialpad. The fragment is hidden in
- * a callback after the hide animation ends.
- * @see #commitDialpadFragmentHide
- */
- public void hideDialpadFragment(boolean animate, boolean clearDialpad) {
- if (mDialpadFragment == null || mDialpadFragment.getView() == null) {
- return;
- }
- if (clearDialpad) {
- // Temporarily disable accessibility when we clear the dialpad, since it should be
- // invisible and should not announce anything.
- mDialpadFragment.getDigitsWidget().setImportantForAccessibility(
- View.IMPORTANT_FOR_ACCESSIBILITY_NO);
- mDialpadFragment.clearDialpad();
- mDialpadFragment.getDigitsWidget().setImportantForAccessibility(
- View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
- }
- if (!mIsDialpadShown) {
- return;
- }
- mIsDialpadShown = false;
- mDialpadFragment.setAnimate(animate);
- mListsFragment.setUserVisibleHint(true);
- mListsFragment.sendScreenViewForCurrentPosition();
-
- updateSearchFragmentPosition();
-
- mFloatingActionButtonController.align(getFabAlignment(), animate);
- if (animate) {
- mDialpadFragment.getView().startAnimation(mSlideOut);
- } else {
- commitDialpadFragmentHide();
- }
-
- mActionBarController.onDialpadDown();
-
- if (isInSearchUi()) {
- if (TextUtils.isEmpty(mSearchQuery)) {
- exitSearchUi();
- }
- }
- //reset the title to normal.
- setTitle(R.string.launcherActivityLabel);
- }
-
- /**
- * Finishes hiding the dialpad fragment after any animations are completed.
- */
- private void commitDialpadFragmentHide() {
- if (!mStateSaved && mDialpadFragment != null && !mDialpadFragment.isHidden()) {
- final FragmentTransaction ft = getFragmentManager().beginTransaction();
- ft.hide(mDialpadFragment);
- ft.commit();
- }
- mFloatingActionButtonController.scaleIn(AnimUtils.NO_DELAY);
- }
-
- private void updateSearchFragmentPosition() {
- SearchFragment fragment = null;
- if (mSmartDialSearchFragment != null && mSmartDialSearchFragment.isVisible()) {
- fragment = mSmartDialSearchFragment;
- } else if (mRegularSearchFragment != null && mRegularSearchFragment.isVisible()) {
- fragment = mRegularSearchFragment;
- }
- if (fragment != null && fragment.isVisible()) {
- fragment.updatePosition(true /* animate */);
- }
- }
-
- @Override
- public boolean isInSearchUi() {
- return mInDialpadSearch || mInRegularSearch;
- }
-
- @Override
- public boolean hasSearchQuery() {
- return !TextUtils.isEmpty(mSearchQuery);
- }
-
- @Override
- public boolean shouldShowActionBar() {
- return mListsFragment.shouldShowActionBar();
- }
-
- private void setNotInSearchUi() {
- mInDialpadSearch = false;
- mInRegularSearch = false;
- }
-
- private void hideDialpadAndSearchUi() {
- if (mIsDialpadShown) {
- hideDialpadFragment(false, true);
- } else {
- exitSearchUi();
- }
- }
-
- private void prepareVoiceSearchButton() {
- final Intent voiceIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
- if (canIntentBeHandled(voiceIntent)) {
- mVoiceSearchButton.setVisibility(View.VISIBLE);
- mVoiceSearchButton.setOnClickListener(this);
- } else {
- mVoiceSearchButton.setVisibility(View.GONE);
- }
- }
-
- public boolean isNearbyPlacesSearchEnabled() {
- return false;
- }
-
- protected int getSearchBoxHint () {
- return R.string.dialer_hint_find_contact;
- }
-
- /**
- * Sets the hint text for the contacts search box
- */
- private void setSearchBoxHint() {
- SearchEditTextLayout searchEditTextLayout = (SearchEditTextLayout) getSupportActionBar()
- .getCustomView().findViewById(R.id.search_view_container);
- ((TextView) searchEditTextLayout.findViewById(R.id.search_box_start_search))
- .setHint(getSearchBoxHint());
- }
-
- protected OptionsPopupMenu buildOptionsMenu(View invoker) {
- final OptionsPopupMenu popupMenu = new OptionsPopupMenu(this, invoker);
- popupMenu.inflate(R.menu.dialtacts_options);
- if (ObjectFactory.isVoicemailArchiveEnabled(this)) {
- popupMenu.getMenu().findItem(R.id.menu_archive).setVisible(true);
- }
- popupMenu.setOnMenuItemClickListener(this);
- return popupMenu;
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- if (mPendingSearchViewQuery != null) {
- mSearchView.setText(mPendingSearchViewQuery);
- mPendingSearchViewQuery = null;
- }
- if (mActionBarController != null) {
- mActionBarController.restoreActionBarOffset();
- }
- return false;
- }
-
- /**
- * Returns true if the intent is due to hitting the green send key (hardware call button:
- * KEYCODE_CALL) while in a call.
- *
- * @param intent the intent that launched this activity
- * @return true if the intent is due to hitting the green send key while in a call
- */
- private boolean isSendKeyWhileInCall(Intent intent) {
- // If there is a call in progress and the user launched the dialer by hitting the call
- // button, go straight to the in-call screen.
- final boolean callKey = Intent.ACTION_CALL_BUTTON.equals(intent.getAction());
-
- if (callKey) {
- TelecomUtil.showInCallScreen(this, false);
- return true;
- }
-
- return false;
- }
-
- /**
- * Sets the current tab based on the intent's request type
- *
- * @param intent Intent that contains information about which tab should be selected
- */
- private void displayFragment(Intent intent) {
- // If we got here by hitting send and we're in call forward along to the in-call activity
- if (isSendKeyWhileInCall(intent)) {
- finish();
- return;
- }
-
- final boolean showDialpadChooser = phoneIsInUse() && !DialpadFragment.isAddCallMode(intent);
- if (showDialpadChooser || (intent.getData() != null && isDialIntent(intent))) {
- showDialpadFragment(false);
- mDialpadFragment.setStartedFromNewIntent(true);
- if (showDialpadChooser && !mDialpadFragment.isVisible()) {
- mInCallDialpadUp = true;
- }
- }
- }
-
- @Override
- public void onNewIntent(Intent newIntent) {
- setIntent(newIntent);
-
- mStateSaved = false;
- displayFragment(newIntent);
-
- invalidateOptionsMenu();
- }
-
- /** Returns true if the given intent contains a phone number to populate the dialer with */
- private boolean isDialIntent(Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_DIAL.equals(action) || ACTION_TOUCH_DIALER.equals(action)) {
- return true;
- }
- if (Intent.ACTION_VIEW.equals(action)) {
- final Uri data = intent.getData();
- if (data != null && PhoneAccount.SCHEME_TEL.equals(data.getScheme())) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Shows the search fragment
- */
- private void enterSearchUi(boolean smartDialSearch, String query, boolean animate) {
- if (mStateSaved || getFragmentManager().isDestroyed()) {
- // Weird race condition where fragment is doing work after the activity is destroyed
- // due to talkback being on (b/10209937). Just return since we can't do any
- // constructive here.
- return;
- }
-
- if (DEBUG) {
- Log.d(TAG, "Entering search UI - smart dial " + smartDialSearch);
- }
-
- final FragmentTransaction transaction = getFragmentManager().beginTransaction();
- if (mInDialpadSearch && mSmartDialSearchFragment != null) {
- transaction.remove(mSmartDialSearchFragment);
- } else if (mInRegularSearch && mRegularSearchFragment != null) {
- transaction.remove(mRegularSearchFragment);
- }
-
- final String tag;
- if (smartDialSearch) {
- tag = TAG_SMARTDIAL_SEARCH_FRAGMENT;
- } else {
- tag = TAG_REGULAR_SEARCH_FRAGMENT;
- }
- mInDialpadSearch = smartDialSearch;
- mInRegularSearch = !smartDialSearch;
-
- mFloatingActionButtonController.scaleOut();
-
- SearchFragment fragment = (SearchFragment) getFragmentManager().findFragmentByTag(tag);
- if (animate) {
- transaction.setCustomAnimations(android.R.animator.fade_in, 0);
- } else {
- transaction.setTransition(FragmentTransaction.TRANSIT_NONE);
- }
- if (fragment == null) {
- if (smartDialSearch) {
- fragment = new SmartDialSearchFragment();
- } else {
- fragment = ObjectFactory.newRegularSearchFragment();
- fragment.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- // Show the FAB when the user touches the lists fragment and the soft
- // keyboard is hidden.
- hideDialpadFragment(true, false);
- showFabInSearchUi();
- return false;
- }
- });
- }
- transaction.add(R.id.dialtacts_frame, fragment, tag);
- } else {
- transaction.show(fragment);
- }
- // DialtactsActivity will provide the options menu
- fragment.setHasOptionsMenu(false);
- fragment.setShowEmptyListForNullQuery(true);
- if (!smartDialSearch) {
- fragment.setQueryString(query, false /* delaySelection */);
- }
- transaction.commit();
-
- if (animate) {
- mListsFragment.getView().animate().alpha(0).withLayer();
- }
- mListsFragment.setUserVisibleHint(false);
-
- if (smartDialSearch) {
- Logger.logScreenView(ScreenEvent.SMART_DIAL_SEARCH, this);
- } else {
- Logger.logScreenView(ScreenEvent.REGULAR_SEARCH, this);
- }
- }
-
- /**
- * Hides the search fragment
- */
- private void exitSearchUi() {
- // See related bug in enterSearchUI();
- if (getFragmentManager().isDestroyed() || mStateSaved) {
- return;
- }
-
- mSearchView.setText(null);
-
- if (mDialpadFragment != null) {
- mDialpadFragment.clearDialpad();
- }
-
- setNotInSearchUi();
-
- // Restore the FAB for the lists fragment.
- if (getFabAlignment() != FloatingActionButtonController.ALIGN_END) {
- mFloatingActionButtonController.setVisible(false);
- }
- mFloatingActionButtonController.scaleIn(FAB_SCALE_IN_DELAY_MS);
- onPageScrolled(mListsFragment.getCurrentTabIndex(), 0 /* offset */, 0 /* pixelOffset */);
- onPageSelected(mListsFragment.getCurrentTabIndex());
-
- final FragmentTransaction transaction = getFragmentManager().beginTransaction();
- if (mSmartDialSearchFragment != null) {
- transaction.remove(mSmartDialSearchFragment);
- }
- if (mRegularSearchFragment != null) {
- transaction.remove(mRegularSearchFragment);
- }
- transaction.commit();
-
- mListsFragment.getView().animate().alpha(1).withLayer();
-
- if (mDialpadFragment == null || !mDialpadFragment.isVisible()) {
- // If the dialpad fragment wasn't previously visible, then send a screen view because
- // we are exiting regular search. Otherwise, the screen view will be sent by
- // {@link #hideDialpadFragment}.
- mListsFragment.sendScreenViewForCurrentPosition();
- mListsFragment.setUserVisibleHint(true);
- }
-
- mActionBarController.onSearchUiExited();
- }
-
- @Override
- public void onBackPressed() {
- if (mStateSaved) {
- return;
- }
- if (mIsDialpadShown) {
- 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();
- }
- }
-
- 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.fab_ic_dial),
- 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;
- if (mSmartDialSearchFragment != null) {
- mSmartDialSearchFragment.setAddToContactNumber(query);
- }
- final String normalizedQuery = SmartDialNameMatcher.normalizeNumber(query,
- SmartDialNameMatcher.LATIN_SMART_DIAL_MAP);
-
- if (!TextUtils.equals(mSearchView.getText(), normalizedQuery)) {
- if (DEBUG) {
- Log.d(TAG, "onDialpadQueryChanged - new query: " + query);
- }
- if (mDialpadFragment == null || !mDialpadFragment.isVisible()) {
- // This callback can happen if the dialpad fragment is recreated because of
- // activity destruction. In that case, don't update the search view because
- // that would bring the user back to the search fragment regardless of the
- // previous state of the application. Instead, just return here and let the
- // fragment manager correctly figure out whatever fragment was last displayed.
- if (!TextUtils.isEmpty(normalizedQuery)) {
- mPendingSearchViewQuery = normalizedQuery;
- }
- return;
- }
- mSearchView.setText(normalizedQuery);
- }
-
- try {
- if (mDialpadFragment != null && mDialpadFragment.isVisible()) {
- mDialpadFragment.process_quote_emergency_unquote(normalizedQuery);
- }
- } catch (Exception ignored) {
- // Skip any exceptions for this piece of code
- }
- }
-
- @Override
- public boolean onDialpadSpacerTouchWithEmptyQuery() {
- if (mInDialpadSearch && mSmartDialSearchFragment != null
- && !mSmartDialSearchFragment.isShowingPermissionRequest()) {
- hideDialpadFragment(true /* animate */, true /* clearDialpad */);
- return true;
- }
- return false;
- }
-
- @Override
- public void onListFragmentScrollStateChange(int scrollState) {
- if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
- hideDialpadFragment(true, false);
- DialerUtils.hideInputMethod(mParentLayout);
- }
- }
-
- @Override
- public void onListFragmentScroll(int firstVisibleItem, int visibleItemCount,
- int totalItemCount) {
- // TODO: No-op for now. This should eventually show/hide the actionBar based on
- // interactions with the ListsFragments.
- }
-
- private boolean phoneIsInUse() {
- return TelecomUtil.isInCall(this);
- }
-
- private boolean canIntentBeHandled(Intent intent) {
- final PackageManager packageManager = getPackageManager();
- final List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(intent,
- PackageManager.MATCH_DEFAULT_ONLY);
- return resolveInfo != null && resolveInfo.size() > 0;
- }
-
- /**
- * Called when the user has long-pressed a contact tile to start a drag operation.
- */
- @Override
- public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView view) {
- mListsFragment.showRemoveView(true);
- }
-
- @Override
- public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView view) {
- }
-
- /**
- * Called when the user has released a contact tile after long-pressing it.
- */
- @Override
- public void onDragFinished(int x, int y) {
- mListsFragment.showRemoveView(false);
- }
-
- @Override
- public void onDroppedOnRemove() {}
-
- /**
- * Allows the SpeedDialFragment to attach the drag controller to mRemoveViewContainer
- * once it has been attached to the activity.
- */
- @Override
- public void setDragDropController(DragDropController dragController) {
- mDragDropController = dragController;
- mListsFragment.getRemoveView().setDragDropController(dragController);
- }
-
- /**
- * Implemented to satisfy {@link SpeedDialFragment.HostInterface}
- */
- @Override
- public void showAllContactsTab() {
- if (mListsFragment != null) {
- mListsFragment.showTab(ListsFragment.TAB_INDEX_ALL_CONTACTS);
- }
- }
-
- /**
- * Implemented to satisfy {@link CallLogFragment.HostInterface}
- */
- @Override
- public void showDialpad() {
- showDialpadFragment(true);
- }
-
- @Override
- public void onPickDataUri(Uri dataUri, boolean isVideoCall, int callInitiationType) {
- mClearSearchOnPause = true;
- PhoneNumberInteraction.startInteractionForPhoneCall(
- DialtactsActivity.this, dataUri, isVideoCall, callInitiationType);
- }
-
- @Override
- public void onPickPhoneNumber(String phoneNumber, boolean isVideoCall, int callInitiationType) {
- if (phoneNumber == null) {
- // Invalid phone number, but let the call go through so that InCallUI can show
- // an error message.
- phoneNumber = "";
- }
-
- final Intent intent = new CallIntentBuilder(phoneNumber)
- .setIsVideoCall(isVideoCall)
- .setCallInitiationType(callInitiationType)
- .build();
-
- DialerUtils.startActivityWithErrorToast(this, intent);
- mClearSearchOnPause = true;
- }
-
- @Override
- public void onShortcutIntentCreated(Intent intent) {
- Log.w(TAG, "Unsupported intent has come (" + intent + "). Ignoring.");
- }
-
- @Override
- public void onHomeInActionBarSelected() {
- exitSearchUi();
- }
-
- @Override
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- int tabIndex = mListsFragment.getCurrentTabIndex();
-
- // Scroll the button from center to end when moving from the Speed Dial to Call History tab.
- // In RTL, scroll when the current tab is Call History instead, since the order of the tabs
- // is reversed and the ViewPager returns the left tab position during scroll.
- boolean isRtl = DialerUtils.isRtl();
- if (!isRtl && tabIndex == ListsFragment.TAB_INDEX_SPEED_DIAL && !mIsLandscape) {
- mFloatingActionButtonController.onPageScrolled(positionOffset);
- } else if (isRtl && tabIndex == ListsFragment.TAB_INDEX_HISTORY && !mIsLandscape) {
- mFloatingActionButtonController.onPageScrolled(1 - positionOffset);
- } else if (tabIndex != ListsFragment.TAB_INDEX_SPEED_DIAL) {
- mFloatingActionButtonController.onPageScrolled(1);
- }
- }
-
- @Override
- public void onPageSelected(int position) {
- updateMissedCalls();
- int tabIndex = mListsFragment.getCurrentTabIndex();
- mPreviouslySelectedTabIndex = tabIndex;
- if (tabIndex == ListsFragment.TAB_INDEX_ALL_CONTACTS &&
- !mInRegularSearch && !mInDialpadSearch) {
- mFloatingActionButtonController.changeIcon(
- getResources().getDrawable(R.drawable.ic_person_add_24dp),
- getResources().getString(R.string.search_shortcut_create_new_contact));
- } else {
- mFloatingActionButtonController.changeIcon(
- getResources().getDrawable(R.drawable.fab_ic_dial),
- getResources().getString(R.string.action_menu_dialpad_button));
- }
- }
-
- @Override
- public void onPageScrollStateChanged(int state) {
- }
-
- @Override
- public boolean isActionBarShowing() {
- return mActionBarController.isActionBarShowing();
- }
-
- @Override
- public ActionBarController getActionBarController() {
- return mActionBarController;
- }
-
- @Override
- public boolean isDialpadShown() {
- return mIsDialpadShown;
- }
-
- @Override
- public int getDialpadHeight() {
- if (mDialpadFragment != null) {
- return mDialpadFragment.getDialpadHeight();
- }
- return 0;
- }
-
- @Override
- public int getActionBarHideOffset() {
- return getSupportActionBar().getHideOffset();
- }
-
- @Override
- public void setActionBarHideOffset(int offset) {
- getSupportActionBar().setHideOffset(offset);
- }
-
- @Override
- public int getActionBarHeight() {
- return mActionBarHeight;
- }
-
- private int getFabAlignment() {
- if (!mIsLandscape && !isInSearchUi() &&
- mListsFragment.getCurrentTabIndex() == ListsFragment.TAB_INDEX_SPEED_DIAL) {
- return FloatingActionButtonController.ALIGN_MIDDLE;
- }
- return FloatingActionButtonController.ALIGN_END;
- }
-
- private void updateMissedCalls() {
- if (mPreviouslySelectedTabIndex == ListsFragment.TAB_INDEX_HISTORY) {
- mListsFragment.markMissedCallsAsReadAndRemoveNotifications();
- }
- }
-}
diff --git a/src/com/android/dialer/FloatingActionButtonBehavior.java b/src/com/android/dialer/FloatingActionButtonBehavior.java
deleted file mode 100644
index 679c9a7c1..000000000
--- a/src/com/android/dialer/FloatingActionButtonBehavior.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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;
-
-import android.content.Context;
-import android.support.design.widget.CoordinatorLayout;
-import android.support.design.widget.Snackbar.SnackbarLayout;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-
-/**
- * Implements custom behavior for the movement of the FAB in response to the Snackbar.
- * Because we are not using the design framework FloatingActionButton widget, we need to manually
- * implement the Material Design behavior of having the FAB translate upward and downward with
- * the appearance and disappearance of a Snackbar.
- */
-public class FloatingActionButtonBehavior extends CoordinatorLayout.Behavior<FrameLayout> {
- public FloatingActionButtonBehavior(Context context, AttributeSet attrs) {
- }
-
- @Override
- public boolean layoutDependsOn(CoordinatorLayout parent, FrameLayout child, View dependency) {
- return dependency instanceof SnackbarLayout;
- }
-
- @Override
- public boolean onDependentViewChanged(CoordinatorLayout parent, FrameLayout child,
- View dependency) {
- float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
- child.setTranslationY(translationY);
- return true;
- }
-}
diff --git a/src/com/android/dialer/NeededForReflection.java b/src/com/android/dialer/NeededForReflection.java
deleted file mode 100644
index e836908b1..000000000
--- a/src/com/android/dialer/NeededForReflection.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2012 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;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Denotes that the class, constructor, method or field is used for reflection and therefore cannot
- * be removed by tools like ProGuard.
- */
-@Retention(RetentionPolicy.CLASS)
-@Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD})
-public @interface NeededForReflection{}
diff --git a/src/com/android/dialer/PhoneCallDetails.java b/src/com/android/dialer/PhoneCallDetails.java
deleted file mode 100644
index 8a2e52090..000000000
--- a/src/com/android/dialer/PhoneCallDetails.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2011 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;
-
-import com.android.contacts.common.ContactsUtils.UserType;
-import com.android.contacts.common.preference.ContactsPreferences;
-import com.android.contacts.common.util.ContactDisplayUtils;
-import com.android.dialer.calllog.PhoneNumberDisplayUtil;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.provider.CallLog.Calls;
-import android.support.annotation.Nullable;
-import android.telecom.PhoneAccountHandle;
-import android.text.TextUtils;
-
-/**
- * The details of a phone call to be shown in the UI.
- */
-public class PhoneCallDetails {
- // The number of the other party involved in the call.
- public CharSequence number;
- // Post-dial digits associated with the outgoing call.
- public String postDialDigits;
- // The secondary line number the call was received via.
- public String viaNumber;
- // The number presenting rules set by the network, e.g., {@link Calls#PRESENTATION_ALLOWED}
- public int numberPresentation;
- // The formatted version of {@link #number}.
- public CharSequence formattedNumber;
- // The country corresponding with the phone number.
- public String countryIso;
- // The geocoded location for the phone number.
- public String geocode;
-
- /**
- * The type of calls, as defined in the call log table, e.g., {@link Calls#INCOMING_TYPE}.
- * <p>
- * There might be multiple types if this represents a set of entries grouped together.
- */
- public int[] callTypes;
-
- // The date of the call, in milliseconds since the epoch.
- public long date;
- // The duration of the call in milliseconds, or 0 for missed calls.
- public long duration;
- // The name of the contact, or the empty string.
- public CharSequence namePrimary;
- // The alternative name of the contact, e.g. last name first, or the empty string
- public CharSequence nameAlternative;
- /**
- * The user's preference on name display order, last name first or first time first.
- * {@see ContactsPreferences}
- */
- public int nameDisplayOrder;
- // The type of phone, e.g., {@link Phone#TYPE_HOME}, 0 if not available.
- public int numberType;
- // The custom label associated with the phone number in the contact, or the empty string.
- public CharSequence numberLabel;
- // The URI of the contact associated with this phone call.
- public Uri contactUri;
-
- /**
- * The photo URI of the picture of the contact that is associated with this phone call or
- * null if there is none.
- * <p>
- * This is meant to store the high-res photo only.
- */
- public Uri photoUri;
-
- // The source type of the contact associated with this call.
- public int sourceType;
-
- // The object id type of the contact associated with this call.
- public String objectId;
-
- // The unique identifier for the account associated with the call.
- public PhoneAccountHandle accountHandle;
-
- // Features applicable to this call.
- public int features;
-
- // Total data usage for this call.
- public Long dataUsage;
-
- // Voicemail transcription
- public String transcription;
-
- // The display string for the number.
- public String displayNumber;
-
- // Whether the contact number is a voicemail number.
- public boolean isVoicemail;
-
- /** The {@link UserType} of the contact */
- public @UserType long contactUserType;
-
- /**
- * If this is a voicemail, whether the message is read. For other types of calls, this defaults
- * to {@code true}.
- */
- public boolean isRead = true;
-
- // If this call is a spam number.
- public boolean isSpam = false;
-
- /**
- * Constructor with required fields for the details of a call with a number associated with a
- * contact.
- */
- public PhoneCallDetails(
- Context context,
- CharSequence number,
- int numberPresentation,
- CharSequence formattedNumber,
- CharSequence postDialDigits,
- boolean isVoicemail) {
- this.number = number;
- this.numberPresentation = numberPresentation;
- this.formattedNumber = formattedNumber;
- this.isVoicemail = isVoicemail;
- this.postDialDigits = postDialDigits.toString();
- this.displayNumber = PhoneNumberDisplayUtil.getDisplayNumber(
- context,
- this.number,
- this.numberPresentation,
- this.formattedNumber,
- this.postDialDigits,
- this.isVoicemail).toString();
- }
-
- /**
- * Returns the preferred name for the call details as specified by the
- * {@link #nameDisplayOrder}
- *
- * @return the preferred name
- */
- public CharSequence getPreferredName() {
- if (nameDisplayOrder == ContactsPreferences.DISPLAY_ORDER_PRIMARY
- || TextUtils.isEmpty(nameAlternative)) {
- return namePrimary;
- }
- return nameAlternative;
- }
-
- /**
- * Construct the "on {accountLabel} via {viaNumber}" accessibility description for the account
- * list item, depending on the existence of the accountLabel and viaNumber.
- * @param viaNumber The number that this call is being placed via.
- * @param accountLabel The {@link PhoneAccount} label that this call is being placed with.
- * @return The description of the account that this call has been placed on.
- */
- public static CharSequence createAccountLabelDescription(Resources resources,
- @Nullable String viaNumber, @Nullable CharSequence accountLabel) {
-
- if((!TextUtils.isEmpty(viaNumber)) && !TextUtils.isEmpty(accountLabel)) {
- String msg = resources.getString(R.string.description_via_number_phone_account,
- accountLabel, viaNumber);
- CharSequence accountNumberLabel = ContactDisplayUtils.getTelephoneTtsSpannable(msg,
- viaNumber);
- return (accountNumberLabel == null) ? msg : accountNumberLabel;
- } else if (!TextUtils.isEmpty(viaNumber)) {
- CharSequence viaNumberLabel = ContactDisplayUtils.getTtsSpannedPhoneNumber(resources,
- R.string.description_via_number, viaNumber);
- return (viaNumberLabel == null) ? viaNumber : viaNumberLabel;
- } else if (!TextUtils.isEmpty(accountLabel)) {
- return TextUtils.expandTemplate(
- resources.getString(R.string.description_phone_account), accountLabel);
- }
- return "";
- }
-}
diff --git a/src/com/android/dialer/SpecialCharSequenceMgr.java b/src/com/android/dialer/SpecialCharSequenceMgr.java
deleted file mode 100644
index fe2163f17..000000000
--- a/src/com/android/dialer/SpecialCharSequenceMgr.java
+++ /dev/null
@@ -1,495 +0,0 @@
-/*
- * Copyright (C) 2006 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;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.DialogFragment;
-import android.app.KeyguardManager;
-import android.app.ProgressDialog;
-import android.content.ActivityNotFoundException;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Looper;
-import android.provider.Settings;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.WindowManager;
-import android.widget.EditText;
-import android.widget.Toast;
-
-import com.android.common.io.MoreCloseables;
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.TelephonyManagerCompat;
-import com.android.contacts.common.database.NoNullCursorAsyncQueryHandler;
-import com.android.contacts.common.util.ContactDisplayUtils;
-import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment;
-import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment.SelectPhoneAccountListener;
-import com.android.dialer.calllog.PhoneAccountUtils;
-import com.android.dialer.util.TelecomUtil;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Helper class to listen for some magic character sequences
- * that are handled specially by the dialer.
- *
- * Note the Phone app also handles these sequences too (in a couple of
- * relatively obscure places in the UI), so there's a separate version of
- * this class under apps/Phone.
- *
- * TODO: there's lots of duplicated code between this class and the
- * corresponding class under apps/Phone. Let's figure out a way to
- * unify these two classes (in the framework? in a common shared library?)
- */
-public class SpecialCharSequenceMgr {
- private static final String TAG = "SpecialCharSequenceMgr";
-
- private static final String TAG_SELECT_ACCT_FRAGMENT = "tag_select_acct_fragment";
-
- private static final String SECRET_CODE_ACTION = "android.provider.Telephony.SECRET_CODE";
- private static final String MMI_IMEI_DISPLAY = "*#06#";
- private static final String MMI_REGULATORY_INFO_DISPLAY = "*#07#";
-
- /**
- * Remembers the previous {@link QueryHandler} and cancel the operation when needed, to
- * prevent possible crash.
- *
- * QueryHandler may call {@link ProgressDialog#dismiss()} when the screen is already gone,
- * which will cause the app crash. This variable enables the class to prevent the crash
- * on {@link #cleanup()}.
- *
- * TODO: Remove this and replace it (and {@link #cleanup()}) with better implementation.
- * One complication is that we have SpecialCharSequenceMgr in Phone package too, which has
- * *slightly* different implementation. Note that Phone package doesn't have this problem,
- * so the class on Phone side doesn't have this functionality.
- * Fundamental fix would be to have one shared implementation and resolve this corner case more
- * gracefully.
- */
- private static QueryHandler sPreviousAdnQueryHandler;
-
- public static class HandleAdnEntryAccountSelectedCallback extends SelectPhoneAccountListener{
- final private Context mContext;
- final private QueryHandler mQueryHandler;
- final private SimContactQueryCookie mCookie;
-
- public HandleAdnEntryAccountSelectedCallback(Context context,
- QueryHandler queryHandler, SimContactQueryCookie cookie) {
- mContext = context;
- mQueryHandler = queryHandler;
- mCookie = cookie;
- }
-
- @Override
- public void onPhoneAccountSelected(PhoneAccountHandle selectedAccountHandle,
- boolean setDefault) {
- Uri uri = TelecomUtil.getAdnUriForPhoneAccount(mContext, selectedAccountHandle);
- handleAdnQuery(mQueryHandler, mCookie, uri);
- // TODO: Show error dialog if result isn't valid.
- }
-
- }
-
- public static class HandleMmiAccountSelectedCallback extends SelectPhoneAccountListener{
- final private Context mContext;
- final private String mInput;
- public HandleMmiAccountSelectedCallback(Context context, String input) {
- mContext = context.getApplicationContext();
- mInput = input;
- }
-
- @Override
- public void onPhoneAccountSelected(PhoneAccountHandle selectedAccountHandle,
- boolean setDefault) {
- TelecomUtil.handleMmi(mContext, mInput, selectedAccountHandle);
- }
- }
-
- /** This class is never instantiated. */
- private SpecialCharSequenceMgr() {
- }
-
- public static boolean handleChars(Context context, String input, EditText textField) {
- //get rid of the separators so that the string gets parsed correctly
- String dialString = PhoneNumberUtils.stripSeparators(input);
-
- if (handleDeviceIdDisplay(context, dialString)
- || handleRegulatoryInfoDisplay(context, dialString)
- || handlePinEntry(context, dialString)
- || handleAdnEntry(context, dialString, textField)
- || handleSecretCode(context, dialString)) {
- return true;
- }
-
- return false;
- }
-
- /**
- * Cleanup everything around this class. Must be run inside the main thread.
- *
- * This should be called when the screen becomes background.
- */
- public static void cleanup() {
- if (Looper.myLooper() != Looper.getMainLooper()) {
- Log.wtf(TAG, "cleanup() is called outside the main thread");
- return;
- }
-
- if (sPreviousAdnQueryHandler != null) {
- sPreviousAdnQueryHandler.cancel();
- sPreviousAdnQueryHandler = null;
- }
- }
-
- /**
- * Handles secret codes to launch arbitrary activities in the form of *#*#<code>#*#*.
- * If a secret code is encountered, an Intent is started with the android_secret_code://<code>
- * URI.
- *
- * @param context the context to use
- * @param input the text to check for a secret code in
- * @return true if a secret code was encountered and intent is sent out
- */
- static boolean handleSecretCode(Context context, String input) {
- final TelephonyManager telephonyManager =
- (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
- if (telephonyManager != null) {
- return telephonyManager.sendDialerCode(input);
- }
- return false;
- }
-
- /**
- * Handle ADN requests by filling in the SIM contact number into the requested
- * EditText.
- *
- * This code works alongside the Asynchronous query handler {@link QueryHandler}
- * and query cancel handler implemented in {@link SimContactQueryCookie}.
- */
- static boolean handleAdnEntry(Context context, String input, EditText textField) {
- /* ADN entries are of the form "N(N)(N)#" */
- TelephonyManager telephonyManager =
- (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
- if (telephonyManager == null
- || telephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_GSM) {
- return false;
- }
-
- // if the phone is keyguard-restricted, then just ignore this
- // input. We want to make sure that sim card contacts are NOT
- // exposed unless the phone is unlocked, and this code can be
- // accessed from the emergency dialer.
- KeyguardManager keyguardManager =
- (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
- if (keyguardManager.inKeyguardRestrictedInputMode()) {
- return false;
- }
-
- int len = input.length();
- if ((len > 1) && (len < 5) && (input.endsWith("#"))) {
- try {
- // get the ordinal number of the sim contact
- final int index = Integer.parseInt(input.substring(0, len-1));
-
- // The original code that navigated to a SIM Contacts list view did not
- // highlight the requested contact correctly, a requirement for PTCRB
- // certification. This behaviour is consistent with the UI paradigm
- // for touch-enabled lists, so it does not make sense to try to work
- // around it. Instead we fill in the the requested phone number into
- // the dialer text field.
-
- // create the async query handler
- final QueryHandler handler = new QueryHandler(context.getContentResolver());
-
- // create the cookie object
- final SimContactQueryCookie sc = new SimContactQueryCookie(index - 1, handler,
- ADN_QUERY_TOKEN);
-
- // setup the cookie fields
- sc.contactNum = index - 1;
- sc.setTextField(textField);
-
- // create the progress dialog
- sc.progressDialog = new ProgressDialog(context);
- sc.progressDialog.setTitle(R.string.simContacts_title);
- sc.progressDialog.setMessage(context.getText(R.string.simContacts_emptyLoading));
- sc.progressDialog.setIndeterminate(true);
- sc.progressDialog.setCancelable(true);
- sc.progressDialog.setOnCancelListener(sc);
- sc.progressDialog.getWindow().addFlags(
- WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
-
- List<PhoneAccountHandle> subscriptionAccountHandles =
- PhoneAccountUtils.getSubscriptionPhoneAccounts(context);
- Context applicationContext = context.getApplicationContext();
- boolean hasUserSelectedDefault = subscriptionAccountHandles.contains(
- TelecomUtil.getDefaultOutgoingPhoneAccount(applicationContext,
- PhoneAccount.SCHEME_TEL));
-
- if (subscriptionAccountHandles.size() <= 1 || hasUserSelectedDefault) {
- Uri uri = TelecomUtil.getAdnUriForPhoneAccount(applicationContext, null);
- handleAdnQuery(handler, sc, uri);
- } else {
- SelectPhoneAccountListener callback = new HandleAdnEntryAccountSelectedCallback(
- applicationContext, handler, sc);
-
- DialogFragment dialogFragment = SelectPhoneAccountDialogFragment.newInstance(
- subscriptionAccountHandles, callback);
- dialogFragment.show(((Activity) context).getFragmentManager(),
- TAG_SELECT_ACCT_FRAGMENT);
- }
-
- return true;
- } catch (NumberFormatException ex) {
- // Ignore
- }
- }
- return false;
- }
-
- private static void handleAdnQuery(QueryHandler handler, SimContactQueryCookie cookie,
- Uri uri) {
- if (handler == null || cookie == null || uri == null) {
- Log.w(TAG, "queryAdn parameters incorrect");
- return;
- }
-
- // display the progress dialog
- cookie.progressDialog.show();
-
- // run the query.
- handler.startQuery(ADN_QUERY_TOKEN, cookie, uri, new String[]{ADN_PHONE_NUMBER_COLUMN_NAME},
- null, null, null);
-
- if (sPreviousAdnQueryHandler != null) {
- // It is harmless to call cancel() even after the handler's gone.
- sPreviousAdnQueryHandler.cancel();
- }
- sPreviousAdnQueryHandler = handler;
- }
-
- static boolean handlePinEntry(final Context context, final String input) {
- if ((input.startsWith("**04") || input.startsWith("**05")) && input.endsWith("#")) {
- List<PhoneAccountHandle> subscriptionAccountHandles =
- PhoneAccountUtils.getSubscriptionPhoneAccounts(context);
- boolean hasUserSelectedDefault = subscriptionAccountHandles.contains(
- TelecomUtil.getDefaultOutgoingPhoneAccount(context, PhoneAccount.SCHEME_TEL));
-
- if (subscriptionAccountHandles.size() <= 1 || hasUserSelectedDefault) {
- // Don't bring up the dialog for single-SIM or if the default outgoing account is
- // a subscription account.
- return TelecomUtil.handleMmi(context, input, null);
- } else {
- SelectPhoneAccountListener listener =
- new HandleMmiAccountSelectedCallback(context, input);
-
- DialogFragment dialogFragment = SelectPhoneAccountDialogFragment.newInstance(
- subscriptionAccountHandles, listener);
- dialogFragment.show(((Activity) context).getFragmentManager(),
- TAG_SELECT_ACCT_FRAGMENT);
- }
- return true;
- }
- return false;
- }
-
- // TODO: Use TelephonyCapabilities.getDeviceIdLabel() to get the device id label instead of a
- // hard-coded string.
- static boolean handleDeviceIdDisplay(Context context, String input) {
- TelephonyManager telephonyManager =
- (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
-
- if (telephonyManager != null && input.equals(MMI_IMEI_DISPLAY)) {
- int labelResId = (telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) ?
- R.string.imei : R.string.meid;
-
- List<String> deviceIds = new ArrayList<String>();
- if (TelephonyManagerCompat.getPhoneCount(telephonyManager) > 1 &&
- CompatUtils.isMethodAvailable(TelephonyManagerCompat.TELEPHONY_MANAGER_CLASS,
- "getDeviceId", Integer.TYPE)) {
- for (int slot = 0; slot < telephonyManager.getPhoneCount(); slot++) {
- String deviceId = telephonyManager.getDeviceId(slot);
- if (!TextUtils.isEmpty(deviceId)) {
- deviceIds.add(deviceId);
- }
- }
- } else {
- deviceIds.add(telephonyManager.getDeviceId());
- }
-
- AlertDialog alert = new AlertDialog.Builder(context)
- .setTitle(labelResId)
- .setItems(deviceIds.toArray(new String[deviceIds.size()]), null)
- .setPositiveButton(android.R.string.ok, null)
- .setCancelable(false)
- .show();
- return true;
- }
- return false;
- }
-
- private static boolean handleRegulatoryInfoDisplay(Context context, String input) {
- if (input.equals(MMI_REGULATORY_INFO_DISPLAY)) {
- Log.d(TAG, "handleRegulatoryInfoDisplay() sending intent to settings app");
- Intent showRegInfoIntent = new Intent(Settings.ACTION_SHOW_REGULATORY_INFO);
- try {
- context.startActivity(showRegInfoIntent);
- } catch (ActivityNotFoundException e) {
- Log.e(TAG, "startActivity() failed: " + e);
- }
- return true;
- }
- return false;
- }
-
- /*******
- * This code is used to handle SIM Contact queries
- *******/
- private static final String ADN_PHONE_NUMBER_COLUMN_NAME = "number";
- private static final String ADN_NAME_COLUMN_NAME = "name";
- private static final int ADN_QUERY_TOKEN = -1;
-
- /**
- * Cookie object that contains everything we need to communicate to the
- * handler's onQuery Complete, as well as what we need in order to cancel
- * the query (if requested).
- *
- * Note, access to the textField field is going to be synchronized, because
- * the user can request a cancel at any time through the UI.
- */
- private static class SimContactQueryCookie implements DialogInterface.OnCancelListener{
- public ProgressDialog progressDialog;
- public int contactNum;
-
- // Used to identify the query request.
- private int mToken;
- private QueryHandler mHandler;
-
- // The text field we're going to update
- private EditText textField;
-
- public SimContactQueryCookie(int number, QueryHandler handler, int token) {
- contactNum = number;
- mHandler = handler;
- mToken = token;
- }
-
- /**
- * Synchronized getter for the EditText.
- */
- public synchronized EditText getTextField() {
- return textField;
- }
-
- /**
- * Synchronized setter for the EditText.
- */
- public synchronized void setTextField(EditText text) {
- textField = text;
- }
-
- /**
- * Cancel the ADN query by stopping the operation and signaling
- * the cookie that a cancel request is made.
- */
- public synchronized void onCancel(DialogInterface dialog) {
- // close the progress dialog
- if (progressDialog != null) {
- progressDialog.dismiss();
- }
-
- // setting the textfield to null ensures that the UI does NOT get
- // updated.
- textField = null;
-
- // Cancel the operation if possible.
- mHandler.cancelOperation(mToken);
- }
- }
-
- /**
- * Asynchronous query handler that services requests to look up ADNs
- *
- * Queries originate from {@link #handleAdnEntry}.
- */
- private static class QueryHandler extends NoNullCursorAsyncQueryHandler {
-
- private boolean mCanceled;
-
- public QueryHandler(ContentResolver cr) {
- super(cr);
- }
-
- /**
- * Override basic onQueryComplete to fill in the textfield when
- * we're handed the ADN cursor.
- */
- @Override
- protected void onNotNullableQueryComplete(int token, Object cookie, Cursor c) {
- try {
- sPreviousAdnQueryHandler = null;
- if (mCanceled) {
- return;
- }
-
- SimContactQueryCookie sc = (SimContactQueryCookie) cookie;
-
- // close the progress dialog.
- sc.progressDialog.dismiss();
-
- // get the EditText to update or see if the request was cancelled.
- EditText text = sc.getTextField();
-
- // if the TextView is valid, and the cursor is valid and positionable on the
- // Nth number, then we update the text field and display a toast indicating the
- // caller name.
- if ((c != null) && (text != null) && (c.moveToPosition(sc.contactNum))) {
- String name = c.getString(c.getColumnIndexOrThrow(ADN_NAME_COLUMN_NAME));
- String number =
- c.getString(c.getColumnIndexOrThrow(ADN_PHONE_NUMBER_COLUMN_NAME));
-
- // fill the text in.
- text.getText().replace(0, 0, number);
-
- // display the name as a toast
- Context context = sc.progressDialog.getContext();
- CharSequence msg = ContactDisplayUtils.getTtsSpannedPhoneNumber(
- context.getResources(), R.string.menu_callNumber, name);
- Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
- }
- } finally {
- MoreCloseables.closeQuietly(c);
- }
- }
-
- public void cancel() {
- mCanceled = true;
- // Ask AsyncQueryHandler to cancel the whole request. This will fail when the query is
- // already started.
- cancelOperation(ADN_QUERY_TOKEN);
- }
- }
-}
diff --git a/src/com/android/dialer/TransactionSafeActivity.java b/src/com/android/dialer/TransactionSafeActivity.java
deleted file mode 100644
index 81e50128d..000000000
--- a/src/com/android/dialer/TransactionSafeActivity.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2011 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;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
-
-/**
- * A common superclass that keeps track of whether an {@link Activity} has saved its state yet or
- * not.
- */
-public abstract class TransactionSafeActivity extends AppCompatActivity {
-
- private boolean mIsSafeToCommitTransactions;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mIsSafeToCommitTransactions = true;
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- mIsSafeToCommitTransactions = true;
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mIsSafeToCommitTransactions = true;
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mIsSafeToCommitTransactions = false;
- }
-
- /**
- * Returns true if it is safe to commit {@link FragmentTransaction}s at this time, based on
- * whether {@link Activity#onSaveInstanceState} has been called or not.
- *
- * Make sure that the current activity calls into
- * {@link super.onSaveInstanceState(Bundle outState)} (if that method is overridden),
- * so the flag is properly set.
- */
- public boolean isSafeToCommitTransactions() {
- return mIsSafeToCommitTransactions;
- }
-}
diff --git a/src/com/android/dialer/calllog/BlockReportSpamListener.java b/src/com/android/dialer/calllog/BlockReportSpamListener.java
deleted file mode 100644
index 92cbc804b..000000000
--- a/src/com/android/dialer/calllog/BlockReportSpamListener.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package com.android.dialer.calllog;
-
-import android.app.Activity;
-import android.app.FragmentManager;
-import android.content.ContentValues;
-import android.content.DialogInterface;
-import android.net.Uri;
-import android.support.v7.widget.RecyclerView;
-
-import com.android.dialer.util.BlockReportSpamDialogs;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.service.ExtendedCallInfoService;
-
-/**
- * Listener to show dialogs for block and report spam actions.
- */
-public class BlockReportSpamListener implements CallLogListItemViewHolder.OnClickListener {
-
- private final FragmentManager mFragmentManager;
- private final RecyclerView.Adapter mAdapter;
- private final ExtendedCallInfoService mExtendedCallInfoService;
- private final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
-
- public BlockReportSpamListener(FragmentManager fragmentManager, RecyclerView.Adapter adapter,
- ExtendedCallInfoService extendedCallInfoService,
- FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler) {
- mFragmentManager = fragmentManager;
- mAdapter = adapter;
- mExtendedCallInfoService = extendedCallInfoService;
- mFilteredNumberAsyncQueryHandler = filteredNumberAsyncQueryHandler;
- }
-
- @Override
- public void onBlockReportSpam(String displayNumber, final String number,
- final String countryIso, final int callType) {
- BlockReportSpamDialogs.BlockReportSpamDialogFragment.newInstance(
- displayNumber,
- false,
- new BlockReportSpamDialogs.OnSpamDialogClickListener() {
- @Override
- public void onClick(boolean isSpamChecked) {
- if (isSpamChecked) {
- mExtendedCallInfoService.reportSpam(
- number, countryIso, callType,
- ExtendedCallInfoService.REPORTING_LOCATION_CALL_LOG_HISTORY);
- }
- mFilteredNumberAsyncQueryHandler.blockNumber(
- new FilteredNumberAsyncQueryHandler.OnBlockNumberListener() {
- @Override
- public void onBlockComplete(Uri uri) {
- mAdapter.notifyDataSetChanged();
- }
- },
- number,
- countryIso);
- }
- }, null)
- .show(mFragmentManager, BlockReportSpamDialogs.BLOCK_REPORT_SPAM_DIALOG_TAG);
- }
-
- @Override
- public void onBlock(String displayNumber, final String number, final String countryIso,
- final int callType) {
- BlockReportSpamDialogs.BlockDialogFragment.newInstance(displayNumber,
- new BlockReportSpamDialogs.OnConfirmListener() {
- @Override
- public void onClick() {
- mExtendedCallInfoService.reportSpam(number, countryIso, callType,
- ExtendedCallInfoService.REPORTING_LOCATION_CALL_LOG_HISTORY);
- mFilteredNumberAsyncQueryHandler.blockNumber(
- new FilteredNumberAsyncQueryHandler.OnBlockNumberListener() {
- @Override
- public void onBlockComplete(Uri uri) {
- mAdapter.notifyDataSetChanged();
- }
- },
- number,
- countryIso);
- }
- }, null)
- .show(mFragmentManager, BlockReportSpamDialogs.BLOCK_DIALOG_TAG);
- }
-
- @Override
- public void onUnblock(String displayNumber, final String number, final String countryIso,
- final Integer blockId, final boolean isSpam, final int callType) {
- BlockReportSpamDialogs.UnblockDialogFragment.newInstance(displayNumber, isSpam,
- new BlockReportSpamDialogs.OnConfirmListener() {
- @Override
- public void onClick() {
- if (isSpam) {
- mExtendedCallInfoService.reportNotSpam(
- number, countryIso, callType,
- ExtendedCallInfoService.REPORTING_LOCATION_CALL_LOG_HISTORY);
- }
- mFilteredNumberAsyncQueryHandler.unblock(
- new FilteredNumberAsyncQueryHandler.OnUnblockNumberListener() {
- @Override
- public void onUnblockComplete(int rows, ContentValues values) {
- mAdapter.notifyDataSetChanged();
- }
- },
- blockId);
- }
- }, null)
- .show(mFragmentManager, BlockReportSpamDialogs.UNBLOCK_DIALOG_TAG);
- }
-
- @Override
- public void onReportNotSpam(String displayNumber, final String number, final String countryIso,
- final int callType) {
- BlockReportSpamDialogs.ReportNotSpamDialogFragment.newInstance(displayNumber,
- new BlockReportSpamDialogs.OnConfirmListener() {
- @Override
- public void onClick() {
- mExtendedCallInfoService.reportNotSpam(
- number, countryIso, callType,
- ExtendedCallInfoService.REPORTING_LOCATION_CALL_LOG_HISTORY);
- mAdapter.notifyDataSetChanged();
- }
- }, null)
- .show(mFragmentManager, BlockReportSpamDialogs.NOT_SPAM_DIALOG_TAG);
- }
-}
diff --git a/src/com/android/dialer/calllog/CallDetailHistoryAdapter.java b/src/com/android/dialer/calllog/CallDetailHistoryAdapter.java
deleted file mode 100644
index ac56332ce..000000000
--- a/src/com/android/dialer/calllog/CallDetailHistoryAdapter.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import android.content.Context;
-import android.provider.CallLog.Calls;
-import android.text.format.DateUtils;
-import android.text.format.Formatter;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.TextView;
-
-import com.android.contacts.common.CallUtil;
-import com.android.dialer.PhoneCallDetails;
-import com.android.dialer.R;
-import com.android.dialer.util.DialerUtils;
-import com.google.common.collect.Lists;
-
-import java.util.ArrayList;
-
-/**
- * Adapter for a ListView containing history items from the details of a call.
- */
-public class CallDetailHistoryAdapter extends BaseAdapter {
- /** Each history item shows the detail of a call. */
- private static final int VIEW_TYPE_HISTORY_ITEM = 1;
-
- private final Context mContext;
- private final LayoutInflater mLayoutInflater;
- private final CallTypeHelper mCallTypeHelper;
- private final PhoneCallDetails[] mPhoneCallDetails;
-
- /**
- * List of items to be concatenated together for duration strings.
- */
- private ArrayList<CharSequence> mDurationItems = Lists.newArrayList();
-
- public CallDetailHistoryAdapter(Context context, LayoutInflater layoutInflater,
- CallTypeHelper callTypeHelper, PhoneCallDetails[] phoneCallDetails) {
- mContext = context;
- mLayoutInflater = layoutInflater;
- mCallTypeHelper = callTypeHelper;
- mPhoneCallDetails = phoneCallDetails;
- }
-
- @Override
- public boolean isEnabled(int position) {
- // None of history will be clickable.
- return false;
- }
-
- @Override
- public int getCount() {
- return mPhoneCallDetails.length;
- }
-
- @Override
- public Object getItem(int position) {
- return mPhoneCallDetails[position];
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public int getViewTypeCount() {
- return 1;
- }
-
- @Override
- public int getItemViewType(int position) {
- return VIEW_TYPE_HISTORY_ITEM;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- // Make sure we have a valid convertView to start with
- final View result = convertView == null
- ? mLayoutInflater.inflate(R.layout.call_detail_history_item, parent, false)
- : convertView;
-
- PhoneCallDetails details = mPhoneCallDetails[position];
- CallTypeIconsView callTypeIconView =
- (CallTypeIconsView) result.findViewById(R.id.call_type_icon);
- TextView callTypeTextView = (TextView) result.findViewById(R.id.call_type_text);
- TextView dateView = (TextView) result.findViewById(R.id.date);
- TextView durationView = (TextView) result.findViewById(R.id.duration);
-
- int callType = details.callTypes[0];
- boolean isVideoCall = (details.features & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO
- && CallUtil.isVideoEnabled(mContext);
-
- callTypeIconView.clear();
- callTypeIconView.add(callType);
- callTypeIconView.setShowVideo(isVideoCall);
- callTypeTextView.setText(mCallTypeHelper.getCallTypeText(callType, isVideoCall));
- // Set the date.
- CharSequence dateValue = DateUtils.formatDateRange(mContext, details.date, details.date,
- DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE |
- DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_YEAR);
- dateView.setText(dateValue);
- // Set the duration
- if (Calls.VOICEMAIL_TYPE == callType || CallTypeHelper.isMissedCallType(callType)) {
- durationView.setVisibility(View.GONE);
- } else {
- durationView.setVisibility(View.VISIBLE);
- durationView.setText(formatDurationAndDataUsage(details.duration, details.dataUsage));
- }
-
- return result;
- }
-
- private CharSequence formatDuration(long elapsedSeconds) {
- long minutes = 0;
- long seconds = 0;
-
- if (elapsedSeconds >= 60) {
- minutes = elapsedSeconds / 60;
- elapsedSeconds -= minutes * 60;
- seconds = elapsedSeconds;
- return mContext.getString(R.string.callDetailsDurationFormat, minutes, seconds);
- } else {
- seconds = elapsedSeconds;
- return mContext.getString(R.string.callDetailsShortDurationFormat, seconds);
- }
- }
-
- /**
- * Formats a string containing the call duration and the data usage (if specified).
- *
- * @param elapsedSeconds Total elapsed seconds.
- * @param dataUsage Data usage in bytes, or null if not specified.
- * @return String containing call duration and data usage.
- */
- private CharSequence formatDurationAndDataUsage(long elapsedSeconds, Long dataUsage) {
- CharSequence duration = formatDuration(elapsedSeconds);
-
- if (dataUsage != null) {
- mDurationItems.clear();
- mDurationItems.add(duration);
- mDurationItems.add(Formatter.formatShortFileSize(mContext, dataUsage));
-
- return DialerUtils.join(mContext.getResources(), mDurationItems);
- } else {
- return duration;
- }
- }
-}
diff --git a/src/com/android/dialer/calllog/CallLogActivity.java b/src/com/android/dialer/calllog/CallLogActivity.java
deleted file mode 100644
index 1823a5bd3..000000000
--- a/src/com/android/dialer/calllog/CallLogActivity.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (C) 2013 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.calllog;
-
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.content.Intent;
-import android.database.Cursor;
-import android.os.Bundle;
-import android.os.Handler;
-import android.provider.CallLog;
-import android.provider.CallLog.Calls;
-import android.support.v13.app.FragmentPagerAdapter;
-import android.support.v4.view.ViewPager;
-import android.support.v7.app.ActionBar;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.ViewGroup;
-
-import com.android.contacts.common.interactions.TouchPointManager;
-import com.android.contacts.common.list.ViewPagerTabs;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.contacts.commonbind.analytics.AnalyticsUtil;
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.R;
-import com.android.dialer.TransactionSafeActivity;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.logging.ScreenEvent;
-import com.android.dialer.util.DialerUtils;
-
-public class CallLogActivity extends TransactionSafeActivity implements ViewPager.OnPageChangeListener {
- private ViewPager mViewPager;
- private ViewPagerTabs mViewPagerTabs;
- private ViewPagerAdapter mViewPagerAdapter;
- private CallLogFragment mAllCallsFragment;
- private CallLogFragment mMissedCallsFragment;
-
- private String[] mTabTitles;
-
- private static final int TAB_INDEX_ALL = 0;
- private static final int TAB_INDEX_MISSED = 1;
-
- private static final int TAB_INDEX_COUNT = 2;
-
- private boolean mIsResumed;
-
- public class ViewPagerAdapter extends FragmentPagerAdapter {
- public ViewPagerAdapter(FragmentManager fm) {
- super(fm);
- }
-
- @Override
- public long getItemId(int position) {
- return getRtlPosition(position);
- }
-
- @Override
- public Fragment getItem(int position) {
- switch (getRtlPosition(position)) {
- case TAB_INDEX_ALL:
- return new CallLogFragment(
- CallLogQueryHandler.CALL_TYPE_ALL, true /* isCallLogActivity */);
- case TAB_INDEX_MISSED:
- return new CallLogFragment(Calls.MISSED_TYPE, true /* isCallLogActivity */);
- }
- throw new IllegalStateException("No fragment at position " + position);
- }
-
- @Override
- public Object instantiateItem(ViewGroup container, int position) {
- final CallLogFragment fragment =
- (CallLogFragment) super.instantiateItem(container, position);
- switch (position) {
- case TAB_INDEX_ALL:
- mAllCallsFragment = fragment;
- break;
- case TAB_INDEX_MISSED:
- mMissedCallsFragment = fragment;
- break;
- }
- return fragment;
- }
-
- @Override
- public CharSequence getPageTitle(int position) {
- return mTabTitles[position];
- }
-
- @Override
- public int getCount() {
- return TAB_INDEX_COUNT;
- }
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- TouchPointManager.getInstance().setPoint((int) ev.getRawX(), (int) ev.getRawY());
- }
- return super.dispatchTouchEvent(ev);
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.call_log_activity);
- getWindow().setBackgroundDrawable(null);
-
- final ActionBar actionBar = getSupportActionBar();
- actionBar.setDisplayShowHomeEnabled(true);
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowTitleEnabled(true);
- actionBar.setElevation(0);
-
- int startingTab = TAB_INDEX_ALL;
- final Intent intent = getIntent();
- if (intent != null) {
- final int callType = intent.getIntExtra(CallLog.Calls.EXTRA_CALL_TYPE_FILTER, -1);
- if (callType == CallLog.Calls.MISSED_TYPE) {
- startingTab = TAB_INDEX_MISSED;
- }
- }
-
- mTabTitles = new String[TAB_INDEX_COUNT];
- mTabTitles[0] = getString(R.string.call_log_all_title);
- mTabTitles[1] = getString(R.string.call_log_missed_title);
-
- mViewPager = (ViewPager) findViewById(R.id.call_log_pager);
-
- mViewPagerAdapter = new ViewPagerAdapter(getFragmentManager());
- mViewPager.setAdapter(mViewPagerAdapter);
- mViewPager.setOffscreenPageLimit(1);
- mViewPager.setOnPageChangeListener(this);
-
- mViewPagerTabs = (ViewPagerTabs) findViewById(R.id.viewpager_header);
-
- mViewPagerTabs.setViewPager(mViewPager);
- mViewPager.setCurrentItem(startingTab);
- }
-
- @Override
- protected void onResume() {
- mIsResumed = true;
- super.onResume();
- sendScreenViewForChildFragment(mViewPager.getCurrentItem());
- }
-
- @Override
- protected void onPause() {
- mIsResumed = false;
- super.onPause();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- final MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.call_log_options, menu);
- return true;
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- final MenuItem itemDeleteAll = menu.findItem(R.id.delete_all);
- if (mAllCallsFragment != null && itemDeleteAll != null) {
- // If onPrepareOptionsMenu is called before fragments are loaded, don't do anything.
- final CallLogAdapter adapter = mAllCallsFragment.getAdapter();
- itemDeleteAll.setVisible(adapter != null && !adapter.isEmpty());
- }
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (!isSafeToCommitTransactions()) {
- return true;
- }
-
- if (item.getItemId() == android.R.id.home) {
- final Intent intent = new Intent(this, DialtactsActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- return true;
- } else if (item.getItemId() == R.id.delete_all) {
- ClearCallLogDialog.show(getFragmentManager());
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- mViewPagerTabs.onPageScrolled(position, positionOffset, positionOffsetPixels);
- }
-
- @Override
- public void onPageSelected(int position) {
- if (mIsResumed) {
- sendScreenViewForChildFragment(position);
- }
- mViewPagerTabs.onPageSelected(position);
- }
-
- @Override
- public void onPageScrollStateChanged(int state) {
- mViewPagerTabs.onPageScrollStateChanged(state);
- }
-
- private void sendScreenViewForChildFragment(int position) {
- Logger.logScreenView(ScreenEvent.CALL_LOG_FILTER, this);
- }
-
- private int getRtlPosition(int position) {
- if (DialerUtils.isRtl()) {
- return mViewPagerAdapter.getCount() - 1 - position;
- }
- return position;
- }
-}
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java
deleted file mode 100644
index 9cde0b65d..000000000
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ /dev/null
@@ -1,959 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import com.android.dialer.filterednumber.BlockNumberDialogFragment;
-import com.android.dialer.service.ExtendedCallInfoService;
-import com.android.dialerbind.ObjectFactory;
-import com.google.common.annotations.VisibleForTesting;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Trace;
-import android.preference.PreferenceManager;
-import android.provider.CallLog;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerView.ViewHolder;
-import android.telecom.PhoneAccountHandle;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.AccessibilityDelegate;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-
-import com.android.contacts.common.ContactsUtils;
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
-import com.android.contacts.common.preference.ContactsPreferences;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.PhoneCallDetails;
-import com.android.dialer.R;
-import com.android.dialer.calllog.calllogcache.CallLogCache;
-import com.android.dialer.contactinfo.ContactInfoCache;
-import com.android.dialer.contactinfo.ContactInfoCache.OnContactInfoChangedListener;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.database.VoicemailArchiveContract;
-import com.android.dialer.logging.InteractionEvent;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.util.PhoneNumberUtil;
-import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
-
-import java.util.HashMap;
-
-/**
- * Adapter class to fill in data for the Call Log.
- */
-public class CallLogAdapter extends GroupingListAdapter
- implements CallLogGroupBuilder.GroupCreator,
- VoicemailPlaybackPresenter.OnVoicemailDeletedListener {
-
- // Types of activities the call log adapter is used for
- public static final int ACTIVITY_TYPE_CALL_LOG = 1;
- public static final int ACTIVITY_TYPE_ARCHIVE = 2;
- public static final int ACTIVITY_TYPE_DIALTACTS = 3;
-
- /** Interface used to initiate a refresh of the content. */
- public interface CallFetcher {
- public void fetchCalls();
- }
-
- private static final int NO_EXPANDED_LIST_ITEM = -1;
- // ConcurrentHashMap doesn't store null values. Use this value for numbers which aren't blocked.
- private static final int NOT_BLOCKED = -1;
-
- private static final int VOICEMAIL_PROMO_CARD_POSITION = 0;
-
- protected static final int VIEW_TYPE_NORMAL = 0;
- private static final int VIEW_TYPE_VOICEMAIL_PROMO_CARD = 1;
-
- /**
- * The key for the show voicemail promo card preference which will determine whether the promo
- * card was permanently dismissed or not.
- */
- private static final String SHOW_VOICEMAIL_PROMO_CARD = "show_voicemail_promo_card";
- private static final boolean SHOW_VOICEMAIL_PROMO_CARD_DEFAULT = true;
-
- protected final Context mContext;
- private final ContactInfoHelper mContactInfoHelper;
- protected final VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
- private final CallFetcher mCallFetcher;
- private final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
-
- protected ContactInfoCache mContactInfoCache;
-
- private final int mActivityType;
-
- private static final String KEY_EXPANDED_POSITION = "expanded_position";
- private static final String KEY_EXPANDED_ROW_ID = "expanded_row_id";
-
- // Tracks the position of the currently expanded list item.
- private int mCurrentlyExpandedPosition = RecyclerView.NO_POSITION;
- // Tracks the rowId of the currently expanded list item, so the position can be updated if there
- // are any changes to the call log entries, such as additions or removals.
- private long mCurrentlyExpandedRowId = NO_EXPANDED_LIST_ITEM;
- private int mHiddenPosition = RecyclerView.NO_POSITION;
- private Uri mHiddenItemUri = null;
- private boolean mPendingHide = false;
- private BlockNumberDialogFragment.Callback mBlockedNumberDialogCallback =
- new BlockNumberDialogFragment.Callback() {
- @Override
- public void onFilterNumberSuccess() {
- Logger.logInteraction(
- InteractionEvent.BLOCK_NUMBER_CALL_LOG);
- notifyDataSetChanged();
- }
-
- @Override
- public void onUnfilterNumberSuccess() {
- Logger.logInteraction(
- InteractionEvent.UNBLOCK_NUMBER_CALL_LOG);
- notifyDataSetChanged();
- }
-
- @Override
- public void onChangeFilteredNumberUndo() {
- }
- };
- private CallLogListItemViewHolder.OnClickListener mBlockReportSpamListener;
-
- /**
- * Hashmap, keyed by call Id, used to track the day group for a call. As call log entries are
- * put into the primary call groups in {@link com.android.dialer.calllog.CallLogGroupBuilder},
- * they are also assigned a secondary "day group". This hashmap tracks the day group assigned
- * to all calls in the call log. This information is used to trigger the display of a day
- * group header above the call log entry at the start of a day group.
- * Note: Multiple calls are grouped into a single primary "call group" in the call log, and
- * the cursor used to bind rows includes all of these calls. When determining if a day group
- * change has occurred it is necessary to look at the last entry in the call log to determine
- * its day group. This hashmap provides a means of determining the previous day group without
- * having to reverse the cursor to the start of the previous day call log entry.
- */
- private HashMap<Long, Integer> mDayGroups = new HashMap<>();
-
- private boolean mLoading = true;
-
- private SharedPreferences mPrefs;
-
- private ContactsPreferences mContactsPreferences;
-
- protected boolean mShowVoicemailPromoCard = false;
-
- /** Instance of helper class for managing views. */
- private final CallLogListItemHelper mCallLogListItemHelper;
-
- /** Cache for repeated requests to Telecom/Telephony. */
- protected final CallLogCache mCallLogCache;
-
- /** Helper to group call log entries. */
- private final CallLogGroupBuilder mCallLogGroupBuilder;
-
- private ExtendedCallInfoService mExtendedCallInfoService;
-
- /**
- * The OnClickListener used to expand or collapse the action buttons of a call log entry.
- */
- private final View.OnClickListener mExpandCollapseListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- CallLogListItemViewHolder viewHolder = (CallLogListItemViewHolder) v.getTag();
- if (viewHolder == null) {
- return;
- }
-
- if (mVoicemailPlaybackPresenter != null) {
- // Always reset the voicemail playback state on expand or collapse.
- mVoicemailPlaybackPresenter.resetAll();
- }
-
- if (viewHolder.getAdapterPosition() == mCurrentlyExpandedPosition) {
- // Hide actions, if the clicked item is the expanded item.
- viewHolder.showActions(false);
-
- mCurrentlyExpandedPosition = RecyclerView.NO_POSITION;
- mCurrentlyExpandedRowId = NO_EXPANDED_LIST_ITEM;
- } else {
- if (viewHolder.callType == CallLog.Calls.MISSED_TYPE) {
- CallLogAsyncTaskUtil.markCallAsRead(mContext, viewHolder.callIds);
- if (mActivityType == ACTIVITY_TYPE_DIALTACTS) {
- ((DialtactsActivity) v.getContext()).updateTabUnreadCounts();
- }
- }
- expandViewHolderActions(viewHolder);
- }
-
- }
- };
-
- /**
- * Click handler used to dismiss the promo card when the user taps the "ok" button.
- */
- private final View.OnClickListener mOkActionListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- dismissVoicemailPromoCard();
- }
- };
-
- /**
- * Click handler used to send the user to the voicemail settings screen and then dismiss the
- * promo card.
- */
- private final View.OnClickListener mVoicemailSettingsActionListener =
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Intent intent = new Intent(TelephonyManager.ACTION_CONFIGURE_VOICEMAIL);
- mContext.startActivity(intent);
- dismissVoicemailPromoCard();
- }
- };
-
- private void expandViewHolderActions(CallLogListItemViewHolder viewHolder) {
- // If another item is expanded, notify it that it has changed. Its actions will be
- // hidden when it is re-binded because we change mCurrentlyExpandedPosition below.
- if (mCurrentlyExpandedPosition != RecyclerView.NO_POSITION) {
- notifyItemChanged(mCurrentlyExpandedPosition);
- }
- // Show the actions for the clicked list item.
- viewHolder.showActions(true);
- mCurrentlyExpandedPosition = viewHolder.getAdapterPosition();
- mCurrentlyExpandedRowId = viewHolder.rowId;
- }
-
- /**
- * Expand the actions on a list item when focused in Talkback mode, to aid discoverability.
- */
- private AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() {
- @Override
- public boolean onRequestSendAccessibilityEvent(
- ViewGroup host, View child, AccessibilityEvent event) {
- if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) {
- // Only expand if actions are not already expanded, because triggering the expand
- // function on clicks causes the action views to lose the focus indicator.
- CallLogListItemViewHolder viewHolder = (CallLogListItemViewHolder) host.getTag();
- if (mCurrentlyExpandedPosition != viewHolder.getAdapterPosition()) {
- if (mVoicemailPlaybackPresenter != null) {
- // Always reset the voicemail playback state on expand.
- mVoicemailPlaybackPresenter.resetAll();
- }
-
- expandViewHolderActions((CallLogListItemViewHolder) host.getTag());
- }
- }
- return super.onRequestSendAccessibilityEvent(host, child, event);
- }
- };
-
- protected final OnContactInfoChangedListener mOnContactInfoChangedListener =
- new OnContactInfoChangedListener() {
- @Override
- public void onContactInfoChanged() {
- notifyDataSetChanged();
- }
- };
-
- public CallLogAdapter(
- Context context,
- CallFetcher callFetcher,
- ContactInfoHelper contactInfoHelper,
- VoicemailPlaybackPresenter voicemailPlaybackPresenter,
- int activityType) {
- super(context);
-
- mContext = context;
- mCallFetcher = callFetcher;
- mContactInfoHelper = contactInfoHelper;
- mVoicemailPlaybackPresenter = voicemailPlaybackPresenter;
- if (mVoicemailPlaybackPresenter != null) {
- mVoicemailPlaybackPresenter.setOnVoicemailDeletedListener(this);
- }
-
- mActivityType = activityType;
-
- mContactInfoCache = new ContactInfoCache(
- mContactInfoHelper, mOnContactInfoChangedListener);
- if (!PermissionsUtil.hasContactsPermissions(context)) {
- mContactInfoCache.disableRequestProcessing();
- }
-
- Resources resources = mContext.getResources();
- CallTypeHelper callTypeHelper = new CallTypeHelper(resources);
-
- mCallLogCache = CallLogCache.getCallLogCache(mContext);
-
- PhoneCallDetailsHelper phoneCallDetailsHelper =
- new PhoneCallDetailsHelper(mContext, resources, mCallLogCache);
- mCallLogListItemHelper =
- new CallLogListItemHelper(phoneCallDetailsHelper, resources, mCallLogCache);
- mCallLogGroupBuilder = new CallLogGroupBuilder(this);
- mFilteredNumberAsyncQueryHandler =
- new FilteredNumberAsyncQueryHandler(mContext.getContentResolver());
-
- mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
- mContactsPreferences = new ContactsPreferences(mContext);
- maybeShowVoicemailPromoCard();
-
- mExtendedCallInfoService = ObjectFactory.newExtendedCallInfoService(context);
- mBlockReportSpamListener = new BlockReportSpamListener(
- ((Activity) mContext).getFragmentManager(), this,
- mExtendedCallInfoService, mFilteredNumberAsyncQueryHandler);
- setHasStableIds(true);
- }
-
- public void onSaveInstanceState(Bundle outState) {
- outState.putInt(KEY_EXPANDED_POSITION, mCurrentlyExpandedPosition);
- outState.putLong(KEY_EXPANDED_ROW_ID, mCurrentlyExpandedRowId);
- }
-
- public void onRestoreInstanceState(Bundle savedInstanceState) {
- if (savedInstanceState != null) {
- mCurrentlyExpandedPosition =
- savedInstanceState.getInt(KEY_EXPANDED_POSITION, RecyclerView.NO_POSITION);
- mCurrentlyExpandedRowId =
- savedInstanceState.getLong(KEY_EXPANDED_ROW_ID, NO_EXPANDED_LIST_ITEM);
- }
- }
-
- /**
- * Requery on background thread when {@link Cursor} changes.
- */
- @Override
- protected void onContentChanged() {
- mCallFetcher.fetchCalls();
- }
-
- public void setLoading(boolean loading) {
- mLoading = loading;
- }
-
- public boolean isEmpty() {
- if (mLoading) {
- // We don't want the empty state to show when loading.
- return false;
- } else {
- return getItemCount() == 0;
- }
- }
-
- public void invalidateCache() {
- mContactInfoCache.invalidate();
- }
-
- public void onResume() {
- if (PermissionsUtil.hasPermission(mContext, android.Manifest.permission.READ_CONTACTS)) {
- mContactInfoCache.start();
- }
- mContactsPreferences.refreshValue(ContactsPreferences.DISPLAY_ORDER_KEY);
- }
-
- public void onPause() {
- pauseCache();
-
- if (mHiddenItemUri != null) {
- CallLogAsyncTaskUtil.deleteVoicemail(mContext, mHiddenItemUri, null);
- }
- }
-
- @VisibleForTesting
- /* package */ void pauseCache() {
- mContactInfoCache.stop();
- mCallLogCache.reset();
- }
-
- @Override
- protected void addGroups(Cursor cursor) {
- mCallLogGroupBuilder.addGroups(cursor);
- }
-
- @Override
- public void addVoicemailGroups(Cursor cursor) {
- mCallLogGroupBuilder.addVoicemailGroups(cursor);
- }
-
- @Override
- public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- if (viewType == VIEW_TYPE_VOICEMAIL_PROMO_CARD) {
- return createVoicemailPromoCardViewHolder(parent);
- }
- return createCallLogEntryViewHolder(parent);
- }
-
- /**
- * Creates a new call log entry {@link ViewHolder}.
- *
- * @param parent the parent view.
- * @return The {@link ViewHolder}.
- */
- private ViewHolder createCallLogEntryViewHolder(ViewGroup parent) {
- LayoutInflater inflater = LayoutInflater.from(mContext);
- View view = inflater.inflate(R.layout.call_log_list_item, parent, false);
- CallLogListItemViewHolder viewHolder = CallLogListItemViewHolder.create(
- view,
- mContext,
- mBlockReportSpamListener,
- mExpandCollapseListener,
- mCallLogCache,
- mCallLogListItemHelper,
- mVoicemailPlaybackPresenter,
- mFilteredNumberAsyncQueryHandler,
- mBlockedNumberDialogCallback,
- mActivityType == ACTIVITY_TYPE_ARCHIVE);
-
- viewHolder.callLogEntryView.setTag(viewHolder);
- viewHolder.callLogEntryView.setAccessibilityDelegate(mAccessibilityDelegate);
-
- viewHolder.primaryActionView.setTag(viewHolder);
-
- return viewHolder;
- }
-
- /**
- * Binds the views in the entry to the data in the call log.
- * TODO: This gets called 20-30 times when Dialer starts up for a single call log entry and
- * should not. It invokes cross-process methods and the repeat execution can get costly.
- *
- * @param viewHolder The view corresponding to this entry.
- * @param position The position of the entry.
- */
- @Override
- public void onBindViewHolder(ViewHolder viewHolder, int position) {
- Trace.beginSection("onBindViewHolder: " + position);
-
- switch (getItemViewType(position)) {
- case VIEW_TYPE_VOICEMAIL_PROMO_CARD:
- bindVoicemailPromoCardViewHolder(viewHolder);
- break;
- default:
- bindCallLogListViewHolder(viewHolder, position);
- break;
- }
-
- Trace.endSection();
- }
-
- /**
- * Binds the promo card view holder.
- *
- * @param viewHolder The promo card view holder.
- */
- protected void bindVoicemailPromoCardViewHolder(ViewHolder viewHolder) {
- PromoCardViewHolder promoCardViewHolder = (PromoCardViewHolder) viewHolder;
-
- promoCardViewHolder.getSecondaryActionView()
- .setOnClickListener(mVoicemailSettingsActionListener);
- promoCardViewHolder.getPrimaryActionView().setOnClickListener(mOkActionListener);
- }
-
- /**
- * Binds the view holder for the call log list item view.
- *
- * @param viewHolder The call log list item view holder.
- * @param position The position of the list item.
- */
-
- private void bindCallLogListViewHolder(final ViewHolder viewHolder, final int position) {
- Cursor c = (Cursor) getItem(position);
- if (c == null) {
- return;
- }
-
- final String number = c.getString(CallLogQuery.NUMBER);
- final String countryIso = c.getString(CallLogQuery.COUNTRY_ISO);
- final CallLogListItemViewHolder views = (CallLogListItemViewHolder) viewHolder;
- boolean success = mFilteredNumberAsyncQueryHandler.isBlockedNumber(
- new FilteredNumberAsyncQueryHandler.OnCheckBlockedListener() {
- @Override
- public void onCheckComplete(Integer id) {
- views.blockId = id;
- if (mExtendedCallInfoService == null) {
- loadDataAndRender(views);
- } else {
- views.isSpamFeatureEnabled = true;
- mExtendedCallInfoService.getExtendedCallInfo(number, countryIso,
- new ExtendedCallInfoService.Listener() {
- @Override
- public void onComplete(boolean isSpam) {
- views.isSpam = isSpam;
- loadDataAndRender(views);
- }
- });
- }
- }
- }, number, countryIso);
- if (!success) {
- loadDataAndRender(views);
- }
- }
-
- private void loadDataAndRender(CallLogListItemViewHolder views) {
- int position = views.getAdapterPosition();
- Cursor c = (Cursor) getItem(position);
- if (c == null) {
- return;
- }
-
- int count = getGroupSize(position);
-
- final String number = c.getString(CallLogQuery.NUMBER);
- final String countryIso = c.getString(CallLogQuery.COUNTRY_ISO);
- final String postDialDigits = CompatUtils.isNCompatible()
- && mActivityType != ACTIVITY_TYPE_ARCHIVE ?
- c.getString(CallLogQuery.POST_DIAL_DIGITS) : "";
- final String viaNumber = CompatUtils.isNCompatible()
- && mActivityType != ACTIVITY_TYPE_ARCHIVE ?
- c.getString(CallLogQuery.VIA_NUMBER) : "";
- final int numberPresentation = c.getInt(CallLogQuery.NUMBER_PRESENTATION);
- final PhoneAccountHandle accountHandle = PhoneAccountUtils.getAccount(
- c.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME),
- c.getString(CallLogQuery.ACCOUNT_ID));
- final ContactInfo cachedContactInfo = ContactInfoHelper.getContactInfo(c);
- final boolean isVoicemailNumber =
- mCallLogCache.isVoicemailNumber(accountHandle, number);
-
- // Note: Binding of the action buttons is done as required in configureActionViews when the
- // user expands the actions ViewStub.
-
- ContactInfo info = ContactInfo.EMPTY;
- if (PhoneNumberUtil.canPlaceCallsTo(number, numberPresentation) && !isVoicemailNumber) {
- // Lookup contacts with this number
- info = mContactInfoCache.getValue(number + postDialDigits,
- countryIso, cachedContactInfo);
- }
- CharSequence formattedNumber = info.formattedNumber == null
- ? null : PhoneNumberUtilsCompat.createTtsSpannable(info.formattedNumber);
-
- final PhoneCallDetails details = new PhoneCallDetails(
- mContext, number, numberPresentation, formattedNumber,
- postDialDigits, isVoicemailNumber);
- details.viaNumber = viaNumber;
- details.accountHandle = accountHandle;
- details.countryIso = countryIso;
- details.date = c.getLong(CallLogQuery.DATE);
- details.duration = c.getLong(CallLogQuery.DURATION);
- details.features = getCallFeatures(c, count);
- details.geocode = c.getString(CallLogQuery.GEOCODED_LOCATION);
- details.transcription = c.getString(CallLogQuery.TRANSCRIPTION);
- details.callTypes = getCallTypes(c, count);
-
- if (!c.isNull(CallLogQuery.DATA_USAGE)) {
- details.dataUsage = c.getLong(CallLogQuery.DATA_USAGE);
- }
-
- if (!TextUtils.isEmpty(info.name) || !TextUtils.isEmpty(info.nameAlternative)) {
- details.contactUri = info.lookupUri;
- details.namePrimary = info.name;
- details.nameAlternative = info.nameAlternative;
- details.nameDisplayOrder = mContactsPreferences.getDisplayOrder();
- details.numberType = info.type;
- details.numberLabel = info.label;
- details.photoUri = info.photoUri;
- details.sourceType = info.sourceType;
- details.objectId = info.objectId;
- details.contactUserType = info.userType;
- }
-
- views.info = info;
- views.rowId = c.getLong(CallLogQuery.ID);
- // Store values used when the actions ViewStub is inflated on expansion.
- views.number = number;
- views.postDialDigits = details.postDialDigits;
- views.displayNumber = details.displayNumber;
- views.numberPresentation = numberPresentation;
-
- views.accountHandle = accountHandle;
- // Stash away the Ids of the calls so that we can support deleting a row in the call log.
- views.callIds = getCallIds(c, count);
- views.isBusiness = mContactInfoHelper.isBusiness(info.sourceType);
- views.numberType = (String) Phone.getTypeLabel(mContext.getResources(), details.numberType,
- details.numberLabel);
- // Default case: an item in the call log.
- views.primaryActionView.setVisibility(View.VISIBLE);
- views.workIconView.setVisibility(
- details.contactUserType == ContactsUtils.USER_TYPE_WORK ? View.VISIBLE : View.GONE);
-
- // Check if the day group has changed and display a header if necessary.
- int currentGroup = getDayGroupForCall(views.rowId);
- int previousGroup = getPreviousDayGroup(c);
- if (currentGroup != previousGroup) {
- views.dayGroupHeader.setVisibility(View.VISIBLE);
- views.dayGroupHeader.setText(getGroupDescription(currentGroup));
- } else {
- views.dayGroupHeader.setVisibility(View.GONE);
- }
-
- if (mActivityType == ACTIVITY_TYPE_ARCHIVE) {
- views.callType = CallLog.Calls.VOICEMAIL_TYPE;
- views.voicemailUri = VoicemailArchiveContract.VoicemailArchive.buildWithId(c.getInt(
- c.getColumnIndex(VoicemailArchiveContract.VoicemailArchive._ID)))
- .toString();
-
- } else {
- if (details.callTypes[0] == CallLog.Calls.VOICEMAIL_TYPE ||
- details.callTypes[0] == CallLog.Calls.MISSED_TYPE) {
- details.isRead = c.getInt(CallLogQuery.IS_READ) == 1;
- }
- views.callType = c.getInt(CallLogQuery.CALL_TYPE);
- views.voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI);
- }
-
- // Reversely pass spam information from views since details is not constructed when spam
- // information comes back. This is used to render phone call details.
- details.isSpam = views.isSpam;
- render(views, details);
- }
-
- private void render(CallLogListItemViewHolder views, PhoneCallDetails details) {
- mCallLogListItemHelper.setPhoneCallDetails(views, details);
- if (mCurrentlyExpandedRowId == views.rowId) {
- // In case ViewHolders were added/removed, update the expanded position if the rowIds
- // match so that we can restore the correct expanded state on rebind.
- mCurrentlyExpandedPosition = views.getAdapterPosition();
- views.showActions(true);
- } else {
- views.showActions(false);
- }
- }
-
- private String getPreferredDisplayName(ContactInfo contactInfo) {
- if (mContactsPreferences.getDisplayOrder() == ContactsPreferences.DISPLAY_ORDER_PRIMARY ||
- TextUtils.isEmpty(contactInfo.nameAlternative)) {
- return contactInfo.name;
- }
- return contactInfo.nameAlternative;
- }
-
- @Override
- public int getItemCount() {
- return super.getItemCount() + (mShowVoicemailPromoCard ? 1 : 0)
- - (mHiddenPosition != RecyclerView.NO_POSITION ? 1 : 0);
- }
-
- @Override
- public int getItemViewType(int position) {
- if (position == VOICEMAIL_PROMO_CARD_POSITION && mShowVoicemailPromoCard) {
- return VIEW_TYPE_VOICEMAIL_PROMO_CARD;
- }
- return super.getItemViewType(position);
- }
-
- /**
- * Retrieves an item at the specified position, taking into account the presence of a promo
- * card.
- *
- * @param position The position to retrieve.
- * @return The item at that position.
- */
- @Override
- public Object getItem(int position) {
- return super.getItem(position - (mShowVoicemailPromoCard ? 1 : 0)
- + ((mHiddenPosition != RecyclerView.NO_POSITION && position >= mHiddenPosition)
- ? 1 : 0));
- }
-
- @Override
- public long getItemId(int position) {
- Cursor cursor = (Cursor) getItem(position);
- if (cursor != null) {
- return cursor.getLong(CallLogQuery.ID);
- } else {
- return 0;
- }
- }
-
- @Override
- public int getGroupSize(int position) {
- return super.getGroupSize(position - (mShowVoicemailPromoCard ? 1 : 0));
- }
-
- protected boolean isCallLogActivity() {
- return mActivityType == ACTIVITY_TYPE_CALL_LOG;
- }
-
- /**
- * In order to implement the "undo" function, when a voicemail is "deleted" i.e. when the user
- * clicks the delete button, the deleted item is temporarily hidden from the list. If a user
- * clicks delete on a second item before the first item's undo option has expired, the first
- * item is immediately deleted so that only one item can be "undoed" at a time.
- */
- @Override
- public void onVoicemailDeleted(Uri uri) {
- if (mHiddenItemUri == null) {
- // Immediately hide the currently expanded card.
- mHiddenPosition = mCurrentlyExpandedPosition;
- notifyDataSetChanged();
- } else {
- // This means that there was a previous item that was hidden in the UI but not
- // yet deleted from the database (call it a "pending delete"). Delete this previous item
- // now since it is only possible to do one "undo" at a time.
- CallLogAsyncTaskUtil.deleteVoicemail(mContext, mHiddenItemUri, null);
-
- // Set pending hide action so that the current item is hidden only after the previous
- // item is permanently deleted.
- mPendingHide = true;
- }
-
- collapseExpandedCard();
-
- // Save the new hidden item uri in case it needs to be deleted from the database when
- // a user attempts to delete another item.
- mHiddenItemUri = uri;
- }
-
- private void collapseExpandedCard() {
- mCurrentlyExpandedRowId = NO_EXPANDED_LIST_ITEM;
- mCurrentlyExpandedPosition = RecyclerView.NO_POSITION;
- }
-
- /**
- * When the list is changing all stored position is no longer valid.
- */
- public void invalidatePositions() {
- mCurrentlyExpandedPosition = RecyclerView.NO_POSITION;
- mHiddenPosition = RecyclerView.NO_POSITION;
- }
-
- /**
- * When the user clicks "undo", the hidden item is unhidden.
- */
- @Override
- public void onVoicemailDeleteUndo() {
- mHiddenPosition = RecyclerView.NO_POSITION;
- mHiddenItemUri = null;
-
- mPendingHide = false;
- notifyDataSetChanged();
- }
-
- /**
- * This callback signifies that a database deletion has completed. This means that if there is
- * an item pending deletion, it will be hidden because the previous item that was in "undo" mode
- * has been removed from the database. Otherwise it simply resets the hidden state because there
- * are no pending deletes and thus no hidden items.
- */
- @Override
- public void onVoicemailDeletedInDatabase() {
- if (mPendingHide) {
- mHiddenPosition = mCurrentlyExpandedPosition;
- mPendingHide = false;
- } else {
- // There should no longer be any hidden item because it has been deleted from the
- // database.
- mHiddenPosition = RecyclerView.NO_POSITION;
- mHiddenItemUri = null;
- }
- }
-
- /**
- * Retrieves the day group of the previous call in the call log. Used to determine if the day
- * group has changed and to trigger display of the day group text.
- *
- * @param cursor The call log cursor.
- * @return The previous day group, or DAY_GROUP_NONE if this is the first call.
- */
- private int getPreviousDayGroup(Cursor cursor) {
- // We want to restore the position in the cursor at the end.
- int startingPosition = cursor.getPosition();
- int dayGroup = CallLogGroupBuilder.DAY_GROUP_NONE;
- if (cursor.moveToPrevious()) {
- // If the previous entry is hidden (deleted in the UI but not in the database), skip it
- // and check the card above it. A list with the voicemail promo card at the top will be
- // 1-indexed because the 0th index is the promo card iteself.
- int previousViewPosition = mShowVoicemailPromoCard ? startingPosition :
- startingPosition - 1;
- if (previousViewPosition != mHiddenPosition ||
- (previousViewPosition == mHiddenPosition && cursor.moveToPrevious())) {
- long previousRowId = cursor.getLong(CallLogQuery.ID);
- dayGroup = getDayGroupForCall(previousRowId);
- }
- }
- cursor.moveToPosition(startingPosition);
- return dayGroup;
- }
-
- /**
- * Given a call Id, look up the day group that the call belongs to. The day group data is
- * populated in {@link com.android.dialer.calllog.CallLogGroupBuilder}.
- *
- * @param callId The call to retrieve the day group for.
- * @return The day group for the call.
- */
- private int getDayGroupForCall(long callId) {
- if (mDayGroups.containsKey(callId)) {
- return mDayGroups.get(callId);
- }
- return CallLogGroupBuilder.DAY_GROUP_NONE;
- }
-
- /**
- * Returns the call types for the given number of items in the cursor.
- * <p>
- * It uses the next {@code count} rows in the cursor to extract the types.
- * <p>
- * It position in the cursor is unchanged by this function.
- */
- private int[] getCallTypes(Cursor cursor, int count) {
- if (mActivityType == ACTIVITY_TYPE_ARCHIVE) {
- return new int[] {CallLog.Calls.VOICEMAIL_TYPE};
- }
- int position = cursor.getPosition();
- int[] callTypes = new int[count];
- for (int index = 0; index < count; ++index) {
- callTypes[index] = cursor.getInt(CallLogQuery.CALL_TYPE);
- cursor.moveToNext();
- }
- cursor.moveToPosition(position);
- return callTypes;
- }
-
- /**
- * Determine the features which were enabled for any of the calls that make up a call log
- * entry.
- *
- * @param cursor The cursor.
- * @param count The number of calls for the current call log entry.
- * @return The features.
- */
- private int getCallFeatures(Cursor cursor, int count) {
- int features = 0;
- int position = cursor.getPosition();
- for (int index = 0; index < count; ++index) {
- features |= cursor.getInt(CallLogQuery.FEATURES);
- cursor.moveToNext();
- }
- cursor.moveToPosition(position);
- return features;
- }
-
- /**
- * Sets whether processing of requests for contact details should be enabled.
- *
- * This method should be called in tests to disable such processing of requests when not
- * needed.
- */
- @VisibleForTesting
- void disableRequestProcessingForTest() {
- // TODO: Remove this and test the cache directly.
- mContactInfoCache.disableRequestProcessing();
- }
-
- @VisibleForTesting
- void injectContactInfoForTest(String number, String countryIso, ContactInfo contactInfo) {
- // TODO: Remove this and test the cache directly.
- mContactInfoCache.injectContactInfoForTest(number, countryIso, contactInfo);
- }
-
- /**
- * Stores the day group associated with a call in the call log.
- *
- * @param rowId The row Id of the current call.
- * @param dayGroup The day group the call belongs in.
- */
- @Override
- public void setDayGroup(long rowId, int dayGroup) {
- if (!mDayGroups.containsKey(rowId)) {
- mDayGroups.put(rowId, dayGroup);
- }
- }
-
- /**
- * Clears the day group associations on re-bind of the call log.
- */
- @Override
- public void clearDayGroups() {
- mDayGroups.clear();
- }
-
- /**
- * Retrieves the call Ids represented by the current call log row.
- *
- * @param cursor Call log cursor to retrieve call Ids from.
- * @param groupSize Number of calls associated with the current call log row.
- * @return Array of call Ids.
- */
- private long[] getCallIds(final Cursor cursor, final int groupSize) {
- // We want to restore the position in the cursor at the end.
- int startingPosition = cursor.getPosition();
- long[] ids = new long[groupSize];
- // Copy the ids of the rows in the group.
- for (int index = 0; index < groupSize; ++index) {
- ids[index] = cursor.getLong(CallLogQuery.ID);
- cursor.moveToNext();
- }
- cursor.moveToPosition(startingPosition);
- return ids;
- }
-
- /**
- * Determines the description for a day group.
- *
- * @param group The day group to retrieve the description for.
- * @return The day group description.
- */
- private CharSequence getGroupDescription(int group) {
- if (group == CallLogGroupBuilder.DAY_GROUP_TODAY) {
- return mContext.getResources().getString(R.string.call_log_header_today);
- } else if (group == CallLogGroupBuilder.DAY_GROUP_YESTERDAY) {
- return mContext.getResources().getString(R.string.call_log_header_yesterday);
- } else {
- return mContext.getResources().getString(R.string.call_log_header_other);
- }
- }
-
- /**
- * Determines if the voicemail promo card should be shown or not. The voicemail promo card will
- * be shown as the first item in the voicemail tab.
- */
- private void maybeShowVoicemailPromoCard() {
- boolean showPromoCard = mPrefs.getBoolean(SHOW_VOICEMAIL_PROMO_CARD,
- SHOW_VOICEMAIL_PROMO_CARD_DEFAULT);
- mShowVoicemailPromoCard = mActivityType != ACTIVITY_TYPE_ARCHIVE &&
- (mVoicemailPlaybackPresenter != null) && showPromoCard;
- }
-
- /**
- * Dismisses the voicemail promo card and refreshes the call log.
- */
- private void dismissVoicemailPromoCard() {
- mPrefs.edit().putBoolean(SHOW_VOICEMAIL_PROMO_CARD, false).apply();
- mShowVoicemailPromoCard = false;
- notifyItemRemoved(VOICEMAIL_PROMO_CARD_POSITION);
- }
-
- /**
- * Creates the view holder for the voicemail promo card.
- *
- * @param parent The parent view.
- * @return The {@link ViewHolder}.
- */
- protected ViewHolder createVoicemailPromoCardViewHolder(ViewGroup parent) {
- LayoutInflater inflater = LayoutInflater.from(mContext);
- View view = inflater.inflate(R.layout.voicemail_promo_card, parent, false);
-
- PromoCardViewHolder viewHolder = PromoCardViewHolder.create(view);
- return viewHolder;
- }
-}
diff --git a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java b/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
deleted file mode 100644
index b95d58e26..000000000
--- a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
+++ /dev/null
@@ -1,505 +0,0 @@
-/*
- * 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.calllog;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.provider.CallLog;
-import android.provider.VoicemailContract.Voicemails;
-import android.telecom.PhoneAccountHandle;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.dialer.PhoneCallDetails;
-import com.android.dialer.compat.CallsSdkCompat;
-import com.android.dialer.database.VoicemailArchiveContract;
-import com.android.dialer.util.AsyncTaskExecutor;
-import com.android.dialer.util.AsyncTaskExecutors;
-import com.android.dialer.util.PhoneNumberUtil;
-import com.android.dialer.util.TelecomUtil;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Locale;
-
-public class CallLogAsyncTaskUtil {
- private static String TAG = CallLogAsyncTaskUtil.class.getSimpleName();
-
- /** The enumeration of {@link AsyncTask} objects used in this class. */
- public enum Tasks {
- DELETE_VOICEMAIL,
- DELETE_CALL,
- DELETE_BLOCKED_CALL,
- MARK_VOICEMAIL_READ,
- MARK_CALL_READ,
- GET_CALL_DETAILS,
- UPDATE_DURATION,
- GET_NUMBER_IN_CALL_HISTORY
- }
-
- private static final class CallDetailQuery {
-
- private static final String[] CALL_LOG_PROJECTION_INTERNAL = new String[] {
- CallLog.Calls.DATE,
- CallLog.Calls.DURATION,
- CallLog.Calls.NUMBER,
- CallLog.Calls.TYPE,
- CallLog.Calls.COUNTRY_ISO,
- CallLog.Calls.GEOCODED_LOCATION,
- CallLog.Calls.NUMBER_PRESENTATION,
- CallLog.Calls.PHONE_ACCOUNT_COMPONENT_NAME,
- CallLog.Calls.PHONE_ACCOUNT_ID,
- CallLog.Calls.FEATURES,
- CallLog.Calls.DATA_USAGE,
- CallLog.Calls.TRANSCRIPTION
- };
- public static final String[] CALL_LOG_PROJECTION;
-
- static final int DATE_COLUMN_INDEX = 0;
- static final int DURATION_COLUMN_INDEX = 1;
- static final int NUMBER_COLUMN_INDEX = 2;
- static final int CALL_TYPE_COLUMN_INDEX = 3;
- static final int COUNTRY_ISO_COLUMN_INDEX = 4;
- static final int GEOCODED_LOCATION_COLUMN_INDEX = 5;
- static final int NUMBER_PRESENTATION_COLUMN_INDEX = 6;
- static final int ACCOUNT_COMPONENT_NAME = 7;
- static final int ACCOUNT_ID = 8;
- static final int FEATURES = 9;
- static final int DATA_USAGE = 10;
- static final int TRANSCRIPTION_COLUMN_INDEX = 11;
- static final int POST_DIAL_DIGITS = 12;
- static final int VIA_NUMBER = 13;
-
- static {
- ArrayList<String> projectionList = new ArrayList<>();
- projectionList.addAll(Arrays.asList(CALL_LOG_PROJECTION_INTERNAL));
- if (CompatUtils.isNCompatible()) {
- projectionList.add(CallsSdkCompat.POST_DIAL_DIGITS);
- projectionList.add(CallsSdkCompat.VIA_NUMBER);
- }
- projectionList.trimToSize();
- CALL_LOG_PROJECTION = projectionList.toArray(new String[projectionList.size()]);
- }
- }
-
- private static class CallLogDeleteBlockedCallQuery {
- static final String[] PROJECTION = new String[] {
- CallLog.Calls._ID,
- CallLog.Calls.DATE
- };
-
- static final int ID_COLUMN_INDEX = 0;
- static final int DATE_COLUMN_INDEX = 1;
- }
-
- public interface CallLogAsyncTaskListener {
- void onDeleteCall();
- void onDeleteVoicemail();
- void onGetCallDetails(PhoneCallDetails[] details);
- }
-
- public interface OnGetNumberInCallHistoryListener {
- void onComplete(boolean inCallHistory);
- }
-
- public interface OnCallLogQueryFinishedListener {
- void onQueryFinished(boolean hasEntry);
- }
-
- // Try to identify if a call log entry corresponds to a number which was blocked. We match by
- // by comparing its creation time to the time it was added in the InCallUi and seeing if they
- // fall within a certain threshold.
- private static final int MATCH_BLOCKED_CALL_THRESHOLD_MS = 3000;
-
- private static AsyncTaskExecutor sAsyncTaskExecutor;
-
- private static void initTaskExecutor() {
- sAsyncTaskExecutor = AsyncTaskExecutors.createThreadPoolExecutor();
- }
-
- public static void getCallDetails(
- final Context context,
- final Uri[] callUris,
- final CallLogAsyncTaskListener callLogAsyncTaskListener) {
- if (sAsyncTaskExecutor == null) {
- initTaskExecutor();
- }
-
- sAsyncTaskExecutor.submit(Tasks.GET_CALL_DETAILS,
- new AsyncTask<Void, Void, PhoneCallDetails[]>() {
- @Override
- public PhoneCallDetails[] doInBackground(Void... params) {
- // TODO: All calls correspond to the same person, so make a single lookup.
- final int numCalls = callUris.length;
- PhoneCallDetails[] details = new PhoneCallDetails[numCalls];
- try {
- for (int index = 0; index < numCalls; ++index) {
- details[index] =
- getPhoneCallDetailsForUri(context, callUris[index]);
- }
- return details;
- } catch (IllegalArgumentException e) {
- // Something went wrong reading in our primary data.
- Log.w(TAG, "Invalid URI starting call details", e);
- return null;
- }
- }
-
- @Override
- public void onPostExecute(PhoneCallDetails[] phoneCallDetails) {
- if (callLogAsyncTaskListener != null) {
- callLogAsyncTaskListener.onGetCallDetails(phoneCallDetails);
- }
- }
- });
- }
-
- /**
- * Return the phone call details for a given call log URI.
- */
- private static PhoneCallDetails getPhoneCallDetailsForUri(Context context, Uri callUri) {
- Cursor cursor = context.getContentResolver().query(
- callUri, CallDetailQuery.CALL_LOG_PROJECTION, null, null, null);
-
- try {
- if (cursor == null || !cursor.moveToFirst()) {
- throw new IllegalArgumentException("Cannot find content: " + callUri);
- }
-
- // Read call log.
- final String countryIso = cursor.getString(CallDetailQuery.COUNTRY_ISO_COLUMN_INDEX);
- final String number = cursor.getString(CallDetailQuery.NUMBER_COLUMN_INDEX);
- final String postDialDigits = CompatUtils.isNCompatible()
- ? cursor.getString(CallDetailQuery.POST_DIAL_DIGITS) : "";
- final String viaNumber = CompatUtils.isNCompatible() ?
- cursor.getString(CallDetailQuery.VIA_NUMBER) : "";
- final int numberPresentation =
- cursor.getInt(CallDetailQuery.NUMBER_PRESENTATION_COLUMN_INDEX);
-
- final PhoneAccountHandle accountHandle = PhoneAccountUtils.getAccount(
- cursor.getString(CallDetailQuery.ACCOUNT_COMPONENT_NAME),
- cursor.getString(CallDetailQuery.ACCOUNT_ID));
-
- // If this is not a regular number, there is no point in looking it up in the contacts.
- ContactInfoHelper contactInfoHelper =
- new ContactInfoHelper(context, GeoUtil.getCurrentCountryIso(context));
- boolean isVoicemail = PhoneNumberUtil.isVoicemailNumber(context, accountHandle, number);
- boolean shouldLookupNumber =
- PhoneNumberUtil.canPlaceCallsTo(number, numberPresentation) && !isVoicemail;
- ContactInfo info = ContactInfo.EMPTY;
-
- if (shouldLookupNumber) {
- ContactInfo lookupInfo = contactInfoHelper.lookupNumber(number, countryIso);
- info = lookupInfo != null ? lookupInfo : ContactInfo.EMPTY;
- }
-
- PhoneCallDetails details = new PhoneCallDetails(
- context, number, numberPresentation, info.formattedNumber,
- postDialDigits, isVoicemail);
-
- details.viaNumber = viaNumber;
- details.accountHandle = accountHandle;
- details.contactUri = info.lookupUri;
- details.namePrimary = info.name;
- details.nameAlternative = info.nameAlternative;
- details.numberType = info.type;
- details.numberLabel = info.label;
- details.photoUri = info.photoUri;
- details.sourceType = info.sourceType;
- details.objectId = info.objectId;
-
- details.callTypes = new int[] {
- cursor.getInt(CallDetailQuery.CALL_TYPE_COLUMN_INDEX)
- };
- details.date = cursor.getLong(CallDetailQuery.DATE_COLUMN_INDEX);
- details.duration = cursor.getLong(CallDetailQuery.DURATION_COLUMN_INDEX);
- details.features = cursor.getInt(CallDetailQuery.FEATURES);
- details.geocode = cursor.getString(CallDetailQuery.GEOCODED_LOCATION_COLUMN_INDEX);
- details.transcription = cursor.getString(CallDetailQuery.TRANSCRIPTION_COLUMN_INDEX);
-
- details.countryIso = !TextUtils.isEmpty(countryIso) ? countryIso
- : GeoUtil.getCurrentCountryIso(context);
-
- if (!cursor.isNull(CallDetailQuery.DATA_USAGE)) {
- details.dataUsage = cursor.getLong(CallDetailQuery.DATA_USAGE);
- }
-
- return details;
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-
-
- /**
- * Delete specified calls from the call log.
- *
- * @param context The context.
- * @param callIds String of the callIds to delete from the call log, delimited by commas (",").
- * @param callLogAsyncTaskListener The listener to invoke after the entries have been deleted.
- */
- public static void deleteCalls(
- final Context context,
- final String callIds,
- final CallLogAsyncTaskListener callLogAsyncTaskListener) {
- if (sAsyncTaskExecutor == null) {
- initTaskExecutor();
- }
-
- sAsyncTaskExecutor.submit(Tasks.DELETE_CALL, new AsyncTask<Void, Void, Void>() {
- @Override
- public Void doInBackground(Void... params) {
- context.getContentResolver().delete(
- TelecomUtil.getCallLogUri(context),
- CallLog.Calls._ID + " IN (" + callIds + ")", null);
- return null;
- }
-
- @Override
- public void onPostExecute(Void result) {
- if (callLogAsyncTaskListener != null) {
- callLogAsyncTaskListener.onDeleteCall();
- }
- }
- });
- }
-
- /**
- * Deletes the last call made by the number within a threshold of the call time added in the
- * call log, assuming it is a blocked call for which no entry should be shown.
- *
- * @param context The context.
- * @param number Number of blocked call, for which to delete the call log entry.
- * @param timeAddedMs The time the number was added to InCall, in milliseconds.
- * @param listener The listener to invoke after looking up for a call log entry matching the
- * number and time added.
- */
- public static void deleteBlockedCall(
- final Context context,
- final String number,
- final long timeAddedMs,
- final OnCallLogQueryFinishedListener listener) {
- if (sAsyncTaskExecutor == null) {
- initTaskExecutor();
- }
-
- sAsyncTaskExecutor.submit(Tasks.DELETE_BLOCKED_CALL, new AsyncTask<Void, Void, Long>() {
- @Override
- public Long doInBackground(Void... params) {
- // First, lookup the call log entry of the most recent call with this number.
- Cursor cursor = context.getContentResolver().query(
- TelecomUtil.getCallLogUri(context),
- CallLogDeleteBlockedCallQuery.PROJECTION,
- CallLog.Calls.NUMBER + "= ?",
- new String[] { number },
- CallLog.Calls.DATE + " DESC LIMIT 1");
-
- // If match is found, delete this call log entry and return the call log entry id.
- if (cursor.moveToFirst()) {
- long creationTime =
- cursor.getLong(CallLogDeleteBlockedCallQuery.DATE_COLUMN_INDEX);
- if (timeAddedMs > creationTime
- && timeAddedMs - creationTime < MATCH_BLOCKED_CALL_THRESHOLD_MS) {
- long callLogEntryId =
- cursor.getLong(CallLogDeleteBlockedCallQuery.ID_COLUMN_INDEX);
- context.getContentResolver().delete(
- TelecomUtil.getCallLogUri(context),
- CallLog.Calls._ID + " IN (" + callLogEntryId + ")",
- null);
- return callLogEntryId;
- }
- }
- return (long) -1;
- }
-
- @Override
- public void onPostExecute(Long callLogEntryId) {
- if (listener != null) {
- listener.onQueryFinished(callLogEntryId >= 0);
- }
- }
- });
- }
-
-
- public static void markVoicemailAsRead(final Context context, final Uri voicemailUri) {
- if (sAsyncTaskExecutor == null) {
- initTaskExecutor();
- }
-
- sAsyncTaskExecutor.submit(Tasks.MARK_VOICEMAIL_READ, new AsyncTask<Void, Void, Void>() {
- @Override
- public Void doInBackground(Void... params) {
- ContentValues values = new ContentValues();
- values.put(Voicemails.IS_READ, true);
- context.getContentResolver().update(
- voicemailUri, values, Voicemails.IS_READ + " = 0", null);
-
- Intent intent = new Intent(context, CallLogNotificationsService.class);
- intent.setAction(CallLogNotificationsService.ACTION_MARK_NEW_VOICEMAILS_AS_OLD);
- context.startService(intent);
- return null;
- }
- });
- }
-
- public static void deleteVoicemail(
- final Context context,
- final Uri voicemailUri,
- final CallLogAsyncTaskListener callLogAsyncTaskListener) {
- if (sAsyncTaskExecutor == null) {
- initTaskExecutor();
- }
-
- sAsyncTaskExecutor.submit(Tasks.DELETE_VOICEMAIL, new AsyncTask<Void, Void, Void>() {
- @Override
- public Void doInBackground(Void... params) {
- context.getContentResolver().delete(voicemailUri, null, null);
- return null;
- }
-
- @Override
- public void onPostExecute(Void result) {
- if (callLogAsyncTaskListener != null) {
- callLogAsyncTaskListener.onDeleteVoicemail();
- }
- }
- });
- }
-
- public static void markCallAsRead(final Context context, final long[] callIds) {
- if (!PermissionsUtil.hasPhonePermissions(context)) {
- return;
- }
- if (sAsyncTaskExecutor == null) {
- initTaskExecutor();
- }
-
- sAsyncTaskExecutor.submit(Tasks.MARK_CALL_READ, new AsyncTask<Void, Void, Void>() {
- @Override
- public Void doInBackground(Void... params) {
-
- StringBuilder where = new StringBuilder();
- where.append(CallLog.Calls.TYPE).append(" = ").append(CallLog.Calls.MISSED_TYPE);
- where.append(" AND ");
-
- Long[] callIdLongs = new Long[callIds.length];
- for (int i = 0; i < callIds.length; i++) {
- callIdLongs[i] = callIds[i];
- }
- where.append(CallLog.Calls._ID).append(
- " IN (" + TextUtils.join(",", callIdLongs) + ")");
-
- ContentValues values = new ContentValues(1);
- values.put(CallLog.Calls.IS_READ, "1");
- context.getContentResolver().update(
- CallLog.Calls.CONTENT_URI, values, where.toString(), null);
- return null;
- }
- });
- }
-
- /**
- * Updates the duration of a voicemail call log entry if the duration given is greater than 0,
- * and if if the duration currently in the database is less than or equal to 0 (non-existent).
- */
- public static void updateVoicemailDuration(
- final Context context,
- final Uri voicemailUri,
- final long duration) {
- if (duration <= 0 || !PermissionsUtil.hasPhonePermissions(context)) {
- return;
- }
- if (sAsyncTaskExecutor == null) {
- initTaskExecutor();
- }
-
- sAsyncTaskExecutor.submit(Tasks.UPDATE_DURATION, new AsyncTask<Void, Void, Void>() {
- @Override
- public Void doInBackground(Void... params) {
- ContentResolver contentResolver = context.getContentResolver();
- Cursor cursor = contentResolver.query(
- voicemailUri,
- new String[] { VoicemailArchiveContract.VoicemailArchive.DURATION },
- null, null, null);
- if (cursor != null && cursor.moveToFirst() && cursor.getInt(
- cursor.getColumnIndex(
- VoicemailArchiveContract.VoicemailArchive.DURATION)) <= 0) {
- ContentValues values = new ContentValues(1);
- values.put(CallLog.Calls.DURATION, duration);
- context.getContentResolver().update(voicemailUri, values, null, null);
- }
- return null;
- }
- });
- }
-
- /**
- * Checks if the number is in the call history.
- */
- public static void getNumberInCallHistory(
- final Context context,
- final String number,
- final OnGetNumberInCallHistoryListener listener) {
- Preconditions.checkNotNull(listener);
- if (!PermissionsUtil.hasPhonePermissions(context)) {
- return;
- }
- if (sAsyncTaskExecutor == null) {
- initTaskExecutor();
- }
-
- sAsyncTaskExecutor.submit(Tasks.GET_NUMBER_IN_CALL_HISTORY,
- new AsyncTask<Void, Void, Boolean>() {
- @Override
- public Boolean doInBackground(Void... params) {
- try (Cursor cursor = context.getContentResolver().query(
- TelecomUtil.getCallLogUri(context),
- new String[] {CallLog.Calls._ID},
- CallLog.Calls.NUMBER + " = ?",
- new String[] {number},
- null)) {
- return cursor != null && cursor.getCount() > 0;
- }
- }
-
- @Override
- public void onPostExecute(Boolean inCallHistory) {
- listener.onComplete(inCallHistory);
- }
- });
- }
-
- @VisibleForTesting
- public static void resetForTest() {
- sAsyncTaskExecutor = null;
- }
-}
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java
deleted file mode 100644
index 67b72a5a3..000000000
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ /dev/null
@@ -1,530 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.KeyguardManager;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.provider.CallLog;
-import android.provider.CallLog.Calls;
-import android.provider.ContactsContract;
-import android.support.annotation.Nullable;
-import android.support.v13.app.FragmentCompat;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.dialer.R;
-import com.android.dialer.list.ListsFragment;
-import com.android.dialer.util.EmptyLoader;
-import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
-import com.android.dialer.widget.EmptyContentView;
-import com.android.dialer.widget.EmptyContentView.OnEmptyViewActionButtonClickedListener;
-import com.android.dialerbind.ObjectFactory;
-
-import static android.Manifest.permission.READ_CALL_LOG;
-
-/**
- * Displays a list of call log entries. To filter for a particular kind of call
- * (all, missed or voicemails), specify it in the constructor.
- */
-public class CallLogFragment extends Fragment implements CallLogQueryHandler.Listener,
- CallLogAdapter.CallFetcher, OnEmptyViewActionButtonClickedListener,
- FragmentCompat.OnRequestPermissionsResultCallback {
- private static final String TAG = "CallLogFragment";
-
- /**
- * ID of the empty loader to defer other fragments.
- */
- private static final int EMPTY_LOADER_ID = 0;
-
- private static final String KEY_FILTER_TYPE = "filter_type";
- private static final String KEY_LOG_LIMIT = "log_limit";
- private static final String KEY_DATE_LIMIT = "date_limit";
- private static final String KEY_IS_CALL_LOG_ACTIVITY = "is_call_log_activity";
-
- // No limit specified for the number of logs to show; use the CallLogQueryHandler's default.
- private static final int NO_LOG_LIMIT = -1;
- // No date-based filtering.
- private static final int NO_DATE_LIMIT = 0;
-
- private static final int READ_CALL_LOG_PERMISSION_REQUEST_CODE = 1;
-
- private static final int EVENT_UPDATE_DISPLAY = 1;
-
- private static final long MILLIS_IN_MINUTE = 60 * 1000;
-
- private RecyclerView mRecyclerView;
- private LinearLayoutManager mLayoutManager;
- private CallLogAdapter mAdapter;
- private CallLogQueryHandler mCallLogQueryHandler;
- private boolean mScrollToTop;
-
-
- private EmptyContentView mEmptyListView;
- private KeyguardManager mKeyguardManager;
-
- private boolean mEmptyLoaderRunning;
- private boolean mCallLogFetched;
- private boolean mVoicemailStatusFetched;
-
- private final Handler mDisplayUpdateHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case EVENT_UPDATE_DISPLAY:
- refreshData();
- rescheduleDisplayUpdate();
- break;
- }
- }
- };
-
- private final Handler mHandler = new Handler();
-
- protected class CustomContentObserver extends ContentObserver {
- public CustomContentObserver() {
- super(mHandler);
- }
- @Override
- public void onChange(boolean selfChange) {
- mRefreshDataRequired = true;
- }
- }
-
- // See issue 6363009
- private final ContentObserver mCallLogObserver = new CustomContentObserver();
- private final ContentObserver mContactsObserver = new CustomContentObserver();
- private boolean mRefreshDataRequired = true;
-
- private boolean mHasReadCallLogPermission = false;
-
- // Exactly same variable is in Fragment as a package private.
- private boolean mMenuVisible = true;
-
- // Default to all calls.
- private int mCallTypeFilter = CallLogQueryHandler.CALL_TYPE_ALL;
-
- // Log limit - if no limit is specified, then the default in {@link CallLogQueryHandler}
- // will be used.
- private int mLogLimit = NO_LOG_LIMIT;
-
- // Date limit (in millis since epoch) - when non-zero, only calls which occurred on or after
- // the date filter are included. If zero, no date-based filtering occurs.
- private long mDateLimit = NO_DATE_LIMIT;
-
- /*
- * True if this instance of the CallLogFragment shown in the CallLogActivity.
- */
- private boolean mIsCallLogActivity = false;
-
- public interface HostInterface {
- public void showDialpad();
- }
-
- public CallLogFragment() {
- this(CallLogQueryHandler.CALL_TYPE_ALL, NO_LOG_LIMIT);
- }
-
- public CallLogFragment(int filterType) {
- this(filterType, NO_LOG_LIMIT);
- }
-
- public CallLogFragment(int filterType, boolean isCallLogActivity) {
- this(filterType, NO_LOG_LIMIT);
- mIsCallLogActivity = isCallLogActivity;
- }
-
- public CallLogFragment(int filterType, int logLimit) {
- this(filterType, logLimit, NO_DATE_LIMIT);
- }
-
- /**
- * Creates a call log fragment, filtering to include only calls of the desired type, occurring
- * after the specified date.
- * @param filterType type of calls to include.
- * @param dateLimit limits results to calls occurring on or after the specified date.
- */
- public CallLogFragment(int filterType, long dateLimit) {
- this(filterType, NO_LOG_LIMIT, dateLimit);
- }
-
- /**
- * Creates a call log fragment, filtering to include only calls of the desired type, occurring
- * after the specified date. Also provides a means to limit the number of results returned.
- * @param filterType type of calls to include.
- * @param logLimit limits the number of results to return.
- * @param dateLimit limits results to calls occurring on or after the specified date.
- */
- public CallLogFragment(int filterType, int logLimit, long dateLimit) {
- mCallTypeFilter = filterType;
- mLogLimit = logLimit;
- mDateLimit = dateLimit;
- }
-
- @Override
- public void onCreate(Bundle state) {
- super.onCreate(state);
- if (state != null) {
- mCallTypeFilter = state.getInt(KEY_FILTER_TYPE, mCallTypeFilter);
- mLogLimit = state.getInt(KEY_LOG_LIMIT, mLogLimit);
- mDateLimit = state.getLong(KEY_DATE_LIMIT, mDateLimit);
- mIsCallLogActivity = state.getBoolean(KEY_IS_CALL_LOG_ACTIVITY, mIsCallLogActivity);
- }
-
- final Activity activity = getActivity();
- final ContentResolver resolver = activity.getContentResolver();
- String currentCountryIso = GeoUtil.getCurrentCountryIso(activity);
- mCallLogQueryHandler = new CallLogQueryHandler(activity, resolver, this, mLogLimit);
- mKeyguardManager =
- (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE);
- resolver.registerContentObserver(CallLog.CONTENT_URI, true, mCallLogObserver);
- resolver.registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true,
- mContactsObserver);
- setHasOptionsMenu(true);
- }
-
- /** Called by the CallLogQueryHandler when the list of calls has been fetched or updated. */
- @Override
- public boolean onCallsFetched(Cursor cursor) {
- if (getActivity() == null || getActivity().isFinishing()) {
- // Return false; we did not take ownership of the cursor
- return false;
- }
- mAdapter.invalidatePositions();
- mAdapter.setLoading(false);
- mAdapter.changeCursor(cursor);
- // This will update the state of the "Clear call log" menu item.
- getActivity().invalidateOptionsMenu();
-
- boolean showListView = cursor != null && cursor.getCount() > 0;
- mRecyclerView.setVisibility(showListView ? View.VISIBLE : View.GONE);
- mEmptyListView.setVisibility(!showListView ? View.VISIBLE : View.GONE);
-
- if (mScrollToTop) {
- // The smooth-scroll animation happens over a fixed time period.
- // As a result, if it scrolls through a large portion of the list,
- // each frame will jump so far from the previous one that the user
- // will not experience the illusion of downward motion. Instead,
- // if we're not already near the top of the list, we instantly jump
- // near the top, and animate from there.
- if (mLayoutManager.findFirstVisibleItemPosition() > 5) {
- // TODO: Jump to near the top, then begin smooth scroll.
- mRecyclerView.smoothScrollToPosition(0);
- }
- // Workaround for framework issue: the smooth-scroll doesn't
- // occur if setSelection() is called immediately before.
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (getActivity() == null || getActivity().isFinishing()) {
- return;
- }
- mRecyclerView.smoothScrollToPosition(0);
- }
- });
-
- mScrollToTop = false;
- }
- mCallLogFetched = true;
- destroyEmptyLoaderIfAllDataFetched();
- return true;
- }
-
- /**
- * Called by {@link CallLogQueryHandler} after a successful query to voicemail status provider.
- */
- @Override
- public void onVoicemailStatusFetched(Cursor statusCursor) {
- Activity activity = getActivity();
- if (activity == null || activity.isFinishing()) {
- return;
- }
-
- mVoicemailStatusFetched = true;
- destroyEmptyLoaderIfAllDataFetched();
- }
-
- private void destroyEmptyLoaderIfAllDataFetched() {
- if (mCallLogFetched && mVoicemailStatusFetched && mEmptyLoaderRunning) {
- mEmptyLoaderRunning = false;
- getLoaderManager().destroyLoader(EMPTY_LOADER_ID);
- }
- }
-
- @Override
- public void onVoicemailUnreadCountFetched(Cursor cursor) {}
-
- @Override
- public void onMissedCallsUnreadCountFetched(Cursor cursor) {}
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
- View view = inflater.inflate(R.layout.call_log_fragment, container, false);
- setupView(view, null);
- return view;
- }
-
- protected void setupView(
- View view, @Nullable VoicemailPlaybackPresenter voicemailPlaybackPresenter) {
- mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
- mRecyclerView.setHasFixedSize(true);
- mLayoutManager = new LinearLayoutManager(getActivity());
- mRecyclerView.setLayoutManager(mLayoutManager);
- mEmptyListView = (EmptyContentView) view.findViewById(R.id.empty_list_view);
- mEmptyListView.setImage(R.drawable.empty_call_log);
- mEmptyListView.setActionClickedListener(this);
-
- int activityType = mIsCallLogActivity ? CallLogAdapter.ACTIVITY_TYPE_CALL_LOG :
- CallLogAdapter.ACTIVITY_TYPE_DIALTACTS;
- String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
- mAdapter = ObjectFactory.newCallLogAdapter(
- getActivity(),
- this,
- new ContactInfoHelper(getActivity(), currentCountryIso),
- voicemailPlaybackPresenter,
- activityType);
- mRecyclerView.setAdapter(mAdapter);
- fetchCalls();
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- updateEmptyMessage(mCallTypeFilter);
- mAdapter.onRestoreInstanceState(savedInstanceState);
- }
-
- @Override
- public void onStart() {
- // Start the empty loader now to defer other fragments. We destroy it when both calllog
- // and the voicemail status are fetched.
- getLoaderManager().initLoader(EMPTY_LOADER_ID, null,
- new EmptyLoader.Callback(getActivity()));
- mEmptyLoaderRunning = true;
- super.onStart();
- }
-
- @Override
- public void onResume() {
- super.onResume();
- final boolean hasReadCallLogPermission =
- PermissionsUtil.hasPermission(getActivity(), READ_CALL_LOG);
- if (!mHasReadCallLogPermission && hasReadCallLogPermission) {
- // We didn't have the permission before, and now we do. Force a refresh of the call log.
- // Note that this code path always happens on a fresh start, but mRefreshDataRequired
- // is already true in that case anyway.
- mRefreshDataRequired = true;
- updateEmptyMessage(mCallTypeFilter);
- }
-
- mHasReadCallLogPermission = hasReadCallLogPermission;
- refreshData();
- mAdapter.onResume();
-
- rescheduleDisplayUpdate();
- }
-
- @Override
- public void onPause() {
- cancelDisplayUpdate();
- mAdapter.onPause();
- super.onPause();
- }
-
- @Override
- public void onStop() {
- updateOnTransition();
-
- super.onStop();
- }
-
- @Override
- public void onDestroy() {
- mAdapter.changeCursor(null);
-
- getActivity().getContentResolver().unregisterContentObserver(mCallLogObserver);
- getActivity().getContentResolver().unregisterContentObserver(mContactsObserver);
- super.onDestroy();
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putInt(KEY_FILTER_TYPE, mCallTypeFilter);
- outState.putInt(KEY_LOG_LIMIT, mLogLimit);
- outState.putLong(KEY_DATE_LIMIT, mDateLimit);
- outState.putBoolean(KEY_IS_CALL_LOG_ACTIVITY, mIsCallLogActivity);
-
- mAdapter.onSaveInstanceState(outState);
- }
-
- @Override
- public void fetchCalls() {
- mCallLogQueryHandler.fetchCalls(mCallTypeFilter, mDateLimit);
- if (!mIsCallLogActivity) {
- ((ListsFragment) getParentFragment()).updateTabUnreadCounts();
- }
- }
-
- private void updateEmptyMessage(int filterType) {
- final Context context = getActivity();
- if (context == null) {
- return;
- }
-
- if (!PermissionsUtil.hasPermission(context, READ_CALL_LOG)) {
- mEmptyListView.setDescription(R.string.permission_no_calllog);
- mEmptyListView.setActionLabel(R.string.permission_single_turn_on);
- return;
- }
-
- final int messageId;
- switch (filterType) {
- case Calls.MISSED_TYPE:
- messageId = R.string.call_log_missed_empty;
- break;
- case Calls.VOICEMAIL_TYPE:
- messageId = R.string.call_log_voicemail_empty;
- break;
- case CallLogQueryHandler.CALL_TYPE_ALL:
- messageId = R.string.call_log_all_empty;
- break;
- default:
- throw new IllegalArgumentException("Unexpected filter type in CallLogFragment: "
- + filterType);
- }
- mEmptyListView.setDescription(messageId);
- if (mIsCallLogActivity) {
- mEmptyListView.setActionLabel(EmptyContentView.NO_LABEL);
- } else if (filterType == CallLogQueryHandler.CALL_TYPE_ALL) {
- mEmptyListView.setActionLabel(R.string.call_log_all_empty_action);
- }
- }
-
- CallLogAdapter getAdapter() {
- return mAdapter;
- }
-
- @Override
- public void setMenuVisibility(boolean menuVisible) {
- super.setMenuVisibility(menuVisible);
- if (mMenuVisible != menuVisible) {
- mMenuVisible = menuVisible;
- if (!menuVisible) {
- updateOnTransition();
- } else if (isResumed()) {
- refreshData();
- }
- }
- }
-
- /** Requests updates to the data to be shown. */
- private void refreshData() {
- // Prevent unnecessary refresh.
- if (mRefreshDataRequired) {
- // Mark all entries in the contact info cache as out of date, so they will be looked up
- // again once being shown.
- mAdapter.invalidateCache();
- mAdapter.setLoading(true);
-
- fetchCalls();
- mCallLogQueryHandler.fetchVoicemailStatus();
- mCallLogQueryHandler.fetchMissedCallsUnreadCount();
- updateOnTransition();
- mRefreshDataRequired = false;
- } else {
- // Refresh the display of the existing data to update the timestamp text descriptions.
- mAdapter.notifyDataSetChanged();
- }
- }
-
- /**
- * Updates the voicemail notification state.
- *
- * TODO: Move to CallLogActivity
- */
- private void updateOnTransition() {
- // We don't want to update any call data when keyguard is on because the user has likely not
- // seen the new calls yet.
- // This might be called before onCreate() and thus we need to check null explicitly.
- if (mKeyguardManager != null && !mKeyguardManager.inKeyguardRestrictedInputMode()
- && mCallTypeFilter == Calls.VOICEMAIL_TYPE) {
- CallLogNotificationsHelper.updateVoicemailNotifications(getActivity());
- }
- }
-
- @Override
- public void onEmptyViewActionButtonClicked() {
- final Activity activity = getActivity();
- if (activity == null) {
- return;
- }
-
- if (!PermissionsUtil.hasPermission(activity, READ_CALL_LOG)) {
- FragmentCompat.requestPermissions(this, new String[] {READ_CALL_LOG},
- READ_CALL_LOG_PERMISSION_REQUEST_CODE);
- } else if (!mIsCallLogActivity) {
- // Show dialpad if we are not in the call log activity.
- ((HostInterface) activity).showDialpad();
- }
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, String[] permissions,
- int[] grantResults) {
- if (requestCode == READ_CALL_LOG_PERMISSION_REQUEST_CODE) {
- if (grantResults.length >= 1 && PackageManager.PERMISSION_GRANTED == grantResults[0]) {
- // Force a refresh of the data since we were missing the permission before this.
- mRefreshDataRequired = true;
- }
- }
- }
-
- /**
- * Schedules an update to the relative call times (X mins ago).
- */
- private void rescheduleDisplayUpdate() {
- if (!mDisplayUpdateHandler.hasMessages(EVENT_UPDATE_DISPLAY)) {
- long time = System.currentTimeMillis();
- // This value allows us to change the display relatively close to when the time changes
- // from one minute to the next.
- long millisUtilNextMinute = MILLIS_IN_MINUTE - (time % MILLIS_IN_MINUTE);
- mDisplayUpdateHandler.sendEmptyMessageDelayed(
- EVENT_UPDATE_DISPLAY, millisUtilNextMinute);
- }
- }
-
- /**
- * Cancels any pending update requests to update the relative call times (X mins ago).
- */
- private void cancelDisplayUpdate() {
- mDisplayUpdateHandler.removeMessages(EVENT_UPDATE_DISPLAY);
- }
-}
diff --git a/src/com/android/dialer/calllog/CallLogGroupBuilder.java b/src/com/android/dialer/calllog/CallLogGroupBuilder.java
deleted file mode 100644
index aa45029c0..000000000
--- a/src/com/android/dialer/calllog/CallLogGroupBuilder.java
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import android.database.Cursor;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.text.format.Time;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.util.DateUtils;
-import com.android.contacts.common.util.PhoneNumberHelper;
-import com.android.dialer.util.AppCompatConstants;
-
-/**
- * Groups together calls in the call log. The primary grouping attempts to group together calls
- * to and from the same number into a single row on the call log.
- * A secondary grouping assigns calls, grouped via the primary grouping, to "day groups". The day
- * groups provide a means of identifying the calls which occurred "Today", "Yesterday", "Last week",
- * or "Other".
- * <p>
- * This class is meant to be used in conjunction with {@link GroupingListAdapter}.
- */
-public class CallLogGroupBuilder {
- public interface GroupCreator {
-
- /**
- * Defines the interface for adding a group to the call log.
- * The primary group for a call log groups the calls together based on the number which was
- * dialed.
- * @param cursorPosition The starting position of the group in the cursor.
- * @param size The size of the group.
- */
- public void addGroup(int cursorPosition, int size);
-
- /**
- * Defines the interface for tracking the day group each call belongs to. Calls in a call
- * group are assigned the same day group as the first call in the group. The day group
- * assigns calls to the buckets: Today, Yesterday, Last week, and Other
- *
- * @param rowId The row Id of the current call.
- * @param dayGroup The day group the call belongs in.
- */
- public void setDayGroup(long rowId, int dayGroup);
-
- /**
- * Defines the interface for clearing the day groupings information on rebind/regroup.
- */
- public void clearDayGroups();
- }
-
- /**
- * Day grouping for call log entries used to represent no associated day group. Used primarily
- * when retrieving the previous day group, but there is no previous day group (i.e. we are at
- * the start of the list).
- */
- public static final int DAY_GROUP_NONE = -1;
-
- /** Day grouping for calls which occurred today. */
- public static final int DAY_GROUP_TODAY = 0;
-
- /** Day grouping for calls which occurred yesterday. */
- public static final int DAY_GROUP_YESTERDAY = 1;
-
- /** Day grouping for calls which occurred before last week. */
- public static final int DAY_GROUP_OTHER = 2;
-
- /** Instance of the time object used for time calculations. */
- private static final Time TIME = new Time();
-
- /** The object on which the groups are created. */
- private final GroupCreator mGroupCreator;
-
- public CallLogGroupBuilder(GroupCreator groupCreator) {
- mGroupCreator = groupCreator;
- }
-
- /**
- * Finds all groups of adjacent entries in the call log which should be grouped together and
- * calls {@link GroupCreator#addGroup(int, int)} on {@link #mGroupCreator} for each of
- * them.
- * <p>
- * For entries that are not grouped with others, we do not need to create a group of size one.
- * <p>
- * It assumes that the cursor will not change during its execution.
- *
- * @see GroupingListAdapter#addGroups(Cursor)
- */
- public void addGroups(Cursor cursor) {
- final int count = cursor.getCount();
- if (count == 0) {
- return;
- }
-
- // Clear any previous day grouping information.
- mGroupCreator.clearDayGroups();
-
- // Get current system time, used for calculating which day group calls belong to.
- long currentTime = System.currentTimeMillis();
- cursor.moveToFirst();
-
- // Determine the day group for the first call in the cursor.
- final long firstDate = cursor.getLong(CallLogQuery.DATE);
- final long firstRowId = cursor.getLong(CallLogQuery.ID);
- int groupDayGroup = getDayGroup(firstDate, currentTime);
- mGroupCreator.setDayGroup(firstRowId, groupDayGroup);
-
- // Instantiate the group values to those of the first call in the cursor.
- String groupNumber = cursor.getString(CallLogQuery.NUMBER);
- String groupPostDialDigits = CompatUtils.isNCompatible()
- ? cursor.getString(CallLogQuery.POST_DIAL_DIGITS) : "";
- String groupViaNumbers = CompatUtils.isNCompatible()
- ? cursor.getString(CallLogQuery.VIA_NUMBER) : "";
- int groupCallType = cursor.getInt(CallLogQuery.CALL_TYPE);
- String groupAccountComponentName = cursor.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME);
- String groupAccountId = cursor.getString(CallLogQuery.ACCOUNT_ID);
- int groupSize = 1;
-
- String number;
- String numberPostDialDigits;
- String numberViaNumbers;
- int callType;
- String accountComponentName;
- String accountId;
-
- while (cursor.moveToNext()) {
- // Obtain the values for the current call to group.
- number = cursor.getString(CallLogQuery.NUMBER);
- numberPostDialDigits = CompatUtils.isNCompatible()
- ? cursor.getString(CallLogQuery.POST_DIAL_DIGITS) : "";
- numberViaNumbers = CompatUtils.isNCompatible()
- ? cursor.getString(CallLogQuery.VIA_NUMBER) : "";
- callType = cursor.getInt(CallLogQuery.CALL_TYPE);
- accountComponentName = cursor.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME);
- accountId = cursor.getString(CallLogQuery.ACCOUNT_ID);
-
- final boolean isSameNumber = equalNumbers(groupNumber, number);
- final boolean isSamePostDialDigits = groupPostDialDigits.equals(numberPostDialDigits);
- final boolean isSameViaNumbers = groupViaNumbers.equals(numberViaNumbers);
- final boolean isSameAccount = isSameAccount(
- groupAccountComponentName, accountComponentName, groupAccountId, accountId);
-
- // Group with the same number and account. Never group voicemails. Only group blocked
- // calls with other blocked calls.
- if (isSameNumber && isSameAccount && isSamePostDialDigits && isSameViaNumbers
- && areBothNotVoicemail(callType, groupCallType)
- && (areBothNotBlocked(callType, groupCallType)
- || areBothBlocked(callType, groupCallType))) {
- // Increment the size of the group to include the current call, but do not create
- // the group until finding a call that does not match.
- groupSize++;
- } else {
- // The call group has changed. Determine the day group for the new call group.
- final long date = cursor.getLong(CallLogQuery.DATE);
- groupDayGroup = getDayGroup(date, currentTime);
-
- // Create a group for the previous group of calls, which does not include the
- // current call.
- mGroupCreator.addGroup(cursor.getPosition() - groupSize, groupSize);
-
- // Start a new group; it will include at least the current call.
- groupSize = 1;
-
- // Update the group values to those of the current call.
- groupNumber = number;
- groupPostDialDigits = numberPostDialDigits;
- groupViaNumbers = numberViaNumbers;
- groupCallType = callType;
- groupAccountComponentName = accountComponentName;
- groupAccountId = accountId;
- }
-
- // Save the day group associated with the current call.
- final long currentCallId = cursor.getLong(CallLogQuery.ID);
- mGroupCreator.setDayGroup(currentCallId, groupDayGroup);
- }
-
- // Create a group for the last set of calls.
- mGroupCreator.addGroup(count - groupSize, groupSize);
- }
-
- /**
- * Group cursor entries by date, with only one entry per group. This is used for listing
- * voicemails in the archive tab.
- */
- public void addVoicemailGroups(Cursor cursor) {
- if (cursor.getCount() == 0) {
- return;
- }
-
- // Clear any previous day grouping information.
- mGroupCreator.clearDayGroups();
-
- // Get current system time, used for calculating which day group calls belong to.
- long currentTime = System.currentTimeMillis();
-
- // Reset cursor to start before the first row
- cursor.moveToPosition(-1);
-
- // Create an individual group for each voicemail
- while (cursor.moveToNext()) {
- mGroupCreator.addGroup(cursor.getPosition(), 1);
- mGroupCreator.setDayGroup(cursor.getLong(CallLogQuery.ID),
- getDayGroup(cursor.getLong(CallLogQuery.DATE), currentTime));
-
- }
- }
-
- @VisibleForTesting
- boolean equalNumbers(String number1, String number2) {
- if (PhoneNumberHelper.isUriNumber(number1) || PhoneNumberHelper.isUriNumber(number2)) {
- return compareSipAddresses(number1, number2);
- } else {
- return PhoneNumberUtils.compare(number1, number2);
- }
- }
-
- private boolean isSameAccount(String name1, String name2, String id1, String id2) {
- return TextUtils.equals(name1, name2) && TextUtils.equals(id1, id2);
- }
-
- @VisibleForTesting
- boolean compareSipAddresses(String number1, String number2) {
- if (number1 == null || number2 == null) return number1 == number2;
-
- int index1 = number1.indexOf('@');
- final String userinfo1;
- final String rest1;
- if (index1 != -1) {
- userinfo1 = number1.substring(0, index1);
- rest1 = number1.substring(index1);
- } else {
- userinfo1 = number1;
- rest1 = "";
- }
-
- int index2 = number2.indexOf('@');
- final String userinfo2;
- final String rest2;
- if (index2 != -1) {
- userinfo2 = number2.substring(0, index2);
- rest2 = number2.substring(index2);
- } else {
- userinfo2 = number2;
- rest2 = "";
- }
-
- return userinfo1.equals(userinfo2) && rest1.equalsIgnoreCase(rest2);
- }
-
- /**
- * Given a call date and the current date, determine which date group the call belongs in.
- *
- * @param date The call date.
- * @param now The current date.
- * @return The date group the call belongs in.
- */
- private int getDayGroup(long date, long now) {
- int days = DateUtils.getDayDifference(TIME, date, now);
-
- if (days == 0) {
- return DAY_GROUP_TODAY;
- } else if (days == 1) {
- return DAY_GROUP_YESTERDAY;
- } else {
- return DAY_GROUP_OTHER;
- }
- }
-
- private boolean areBothNotVoicemail(int callType, int groupCallType) {
- return callType != AppCompatConstants.CALLS_VOICEMAIL_TYPE
- && groupCallType != AppCompatConstants.CALLS_VOICEMAIL_TYPE;
- }
-
- private boolean areBothNotBlocked(int callType, int groupCallType) {
- return callType != AppCompatConstants.CALLS_BLOCKED_TYPE
- && groupCallType != AppCompatConstants.CALLS_BLOCKED_TYPE;
- }
-
- private boolean areBothBlocked(int callType, int groupCallType) {
- return callType == AppCompatConstants.CALLS_BLOCKED_TYPE
- && groupCallType == AppCompatConstants.CALLS_BLOCKED_TYPE;
- }
-}
diff --git a/src/com/android/dialer/calllog/CallLogListItemHelper.java b/src/com/android/dialer/calllog/CallLogListItemHelper.java
deleted file mode 100644
index 18b6ff5d3..000000000
--- a/src/com/android/dialer/calllog/CallLogListItemHelper.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import android.content.res.Resources;
-import android.provider.CallLog.Calls;
-import android.text.SpannableStringBuilder;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.dialer.PhoneCallDetails;
-import com.android.dialer.util.AppCompatConstants;
-import com.android.dialer.R;
-import com.android.dialer.calllog.calllogcache.CallLogCache;
-
-/**
- * Helper class to fill in the views of a call log entry.
- */
-/* package */class CallLogListItemHelper {
- private static final String TAG = "CallLogListItemHelper";
-
- /** Helper for populating the details of a phone call. */
- private final PhoneCallDetailsHelper mPhoneCallDetailsHelper;
- /** Resources to look up strings. */
- private final Resources mResources;
- private final CallLogCache mCallLogCache;
-
- /**
- * Creates a new helper instance.
- *
- * @param phoneCallDetailsHelper used to set the details of a phone call
- * @param resources The object from which resources can be retrieved
- * @param callLogCache A cache for values retrieved from telecom/telephony
- */
- public CallLogListItemHelper(
- PhoneCallDetailsHelper phoneCallDetailsHelper,
- Resources resources,
- CallLogCache callLogCache) {
- mPhoneCallDetailsHelper = phoneCallDetailsHelper;
- mResources = resources;
- mCallLogCache = callLogCache;
- }
-
- /**
- * Sets the name, label, and number for a contact.
- *
- * @param views the views to populate
- * @param details the details of a phone call needed to fill in the data
- */
- public void setPhoneCallDetails(
- CallLogListItemViewHolder views,
- PhoneCallDetails details) {
- mPhoneCallDetailsHelper.setPhoneCallDetails(views.phoneCallDetailsViews, details);
-
- // Set the accessibility text for the contact badge
- views.quickContactView.setContentDescription(getContactBadgeDescription(details));
-
- // Set the primary action accessibility description
- views.primaryActionView.setContentDescription(getCallDescription(details));
-
- // Cache name or number of caller. Used when setting the content descriptions of buttons
- // when the actions ViewStub is inflated.
- views.nameOrNumber = getNameOrNumber(details);
-
- // The call type or Location associated with the call. Use when setting text for a
- // voicemail log's call button
- views.callTypeOrLocation = mPhoneCallDetailsHelper.getCallTypeOrLocation(details);
-
- // Cache country iso. Used for number filtering.
- views.countryIso = details.countryIso;
-
- views.updatePhoto();
- }
-
- /**
- * Sets the accessibility descriptions for the action buttons in the action button ViewStub.
- *
- * @param views The views associated with the current call log entry.
- */
- public void setActionContentDescriptions(CallLogListItemViewHolder views) {
- if (views.nameOrNumber == null) {
- Log.e(TAG, "setActionContentDescriptions; name or number is null.");
- }
-
- // Calling expandTemplate with a null parameter will cause a NullPointerException.
- // Although we don't expect a null name or number, it is best to protect against it.
- CharSequence nameOrNumber = views.nameOrNumber == null ? "" : views.nameOrNumber;
-
- views.videoCallButtonView.setContentDescription(
- TextUtils.expandTemplate(
- mResources.getString(R.string.description_video_call_action),
- nameOrNumber));
-
- views.createNewContactButtonView.setContentDescription(
- TextUtils.expandTemplate(
- mResources.getString(R.string.description_create_new_contact_action),
- nameOrNumber));
-
- views.addToExistingContactButtonView.setContentDescription(
- TextUtils.expandTemplate(
- mResources.getString(R.string.description_add_to_existing_contact_action),
- nameOrNumber));
-
- views.detailsButtonView.setContentDescription(
- TextUtils.expandTemplate(
- mResources.getString(R.string.description_details_action), nameOrNumber));
- }
-
- /**
- * Returns the accessibility description for the contact badge for a call log entry.
- *
- * @param details Details of call.
- * @return Accessibility description.
- */
- private CharSequence getContactBadgeDescription(PhoneCallDetails details) {
- return mResources.getString(R.string.description_contact_details, getNameOrNumber(details));
- }
-
- /**
- * Returns the accessibility description of the "return call/call" action for a call log
- * entry.
- * Accessibility text is a combination of:
- * {Voicemail Prefix}. {Number of Calls}. {Caller information} {Phone Account}.
- * If most recent call is a voicemail, {Voicemail Prefix} is "New Voicemail.", otherwise "".
- *
- * If more than one call for the caller, {Number of Calls} is:
- * "{number of calls} calls.", otherwise "".
- *
- * The {Caller Information} references the most recent call associated with the caller.
- * For incoming calls:
- * If missed call: Missed call from {Name/Number} {Call Type} {Call Time}.
- * If answered call: Answered call from {Name/Number} {Call Type} {Call Time}.
- *
- * For outgoing calls:
- * If outgoing: Call to {Name/Number] {Call Type} {Call Time}.
- *
- * Where:
- * {Name/Number} is the name or number of the caller (as shown in call log).
- * {Call type} is the contact phone number type (eg mobile) or location.
- * {Call Time} is the time since the last call for the contact occurred.
- *
- * The {Phone Account} refers to the account/SIM through which the call was placed or received
- * in multi-SIM devices.
- *
- * Examples:
- * 3 calls. New Voicemail. Missed call from Joe Smith mobile 2 hours ago on SIM 1.
- *
- * 2 calls. Answered call from John Doe mobile 1 hour ago.
- *
- * @param context The application context.
- * @param details Details of call.
- * @return Return call action description.
- */
- public CharSequence getCallDescription(PhoneCallDetails details) {
- int lastCallType = getLastCallType(details.callTypes);
-
- // Get the name or number of the caller.
- final CharSequence nameOrNumber = getNameOrNumber(details);
-
- // Get the call type or location of the caller; null if not applicable
- final CharSequence typeOrLocation = mPhoneCallDetailsHelper.getCallTypeOrLocation(details);
-
- // Get the time/date of the call
- final CharSequence timeOfCall = mPhoneCallDetailsHelper.getCallDate(details);
-
- SpannableStringBuilder callDescription = new SpannableStringBuilder();
-
- // Add number of calls if more than one.
- if (details.callTypes.length > 1) {
- callDescription.append(mResources.getString(R.string.description_num_calls,
- details.callTypes.length));
- }
-
- // If call had video capabilities, add the "Video Call" string.
- if ((details.features & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO) {
- callDescription.append(mResources.getString(R.string.description_video_call));
- }
-
- String accountLabel = mCallLogCache.getAccountLabel(details.accountHandle);
- CharSequence onAccountLabel = PhoneCallDetails.createAccountLabelDescription(mResources,
- details.viaNumber, accountLabel);
-
- int stringID = getCallDescriptionStringID(details.callTypes, details.isRead);
- callDescription.append(
- TextUtils.expandTemplate(
- mResources.getString(stringID),
- nameOrNumber,
- typeOrLocation == null ? "" : typeOrLocation,
- timeOfCall,
- onAccountLabel));
-
- return callDescription;
- }
-
- /**
- * Determine the appropriate string ID to describe a call for accessibility purposes.
- *
- * @param callTypes The type of call corresponding to this entry or multiple if this entry
- * represents multiple calls grouped together.
- * @param isRead If the entry is a voicemail, {@code true} if the voicemail is read.
- * @return String resource ID to use.
- */
- public int getCallDescriptionStringID(int[] callTypes, boolean isRead) {
- int lastCallType = getLastCallType(callTypes);
- int stringID;
-
- if (lastCallType == AppCompatConstants.CALLS_MISSED_TYPE) {
- //Message: Missed call from <NameOrNumber>, <TypeOrLocation>, <TimeOfCall>,
- //<PhoneAccount>.
- stringID = R.string.description_incoming_missed_call;
- } else if (lastCallType == AppCompatConstants.CALLS_INCOMING_TYPE) {
- //Message: Answered call from <NameOrNumber>, <TypeOrLocation>, <TimeOfCall>,
- //<PhoneAccount>.
- stringID = R.string.description_incoming_answered_call;
- } else if (lastCallType == AppCompatConstants.CALLS_VOICEMAIL_TYPE) {
- //Message: (Unread) [V/v]oicemail from <NameOrNumber>, <TypeOrLocation>, <TimeOfCall>,
- //<PhoneAccount>.
- stringID = isRead ? R.string.description_read_voicemail
- : R.string.description_unread_voicemail;
- } else {
- //Message: Call to <NameOrNumber>, <TypeOrLocation>, <TimeOfCall>, <PhoneAccount>.
- stringID = R.string.description_outgoing_call;
- }
- return stringID;
- }
-
- /**
- * Determine the call type for the most recent call.
- * @param callTypes Call types to check.
- * @return Call type.
- */
- private int getLastCallType(int[] callTypes) {
- if (callTypes.length > 0) {
- return callTypes[0];
- } else {
- return Calls.MISSED_TYPE;
- }
- }
-
- /**
- * Return the name or number of the caller specified by the details.
- * @param details Call details
- * @return the name (if known) of the caller, otherwise the formatted number.
- */
- private CharSequence getNameOrNumber(PhoneCallDetails details) {
- final CharSequence recipient;
- if (!TextUtils.isEmpty(details.getPreferredName())) {
- recipient = details.getPreferredName();
- } else {
- recipient = details.displayNumber + details.postDialDigits;
- }
- return recipient;
- }
-}
diff --git a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
deleted file mode 100644
index 858cc2102..000000000
--- a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
+++ /dev/null
@@ -1,776 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.provider.CallLog;
-import android.provider.CallLog.Calls;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.support.v7.widget.CardView;
-import android.support.v7.widget.RecyclerView;
-import android.telecom.PhoneAccountHandle;
-import android.text.BidiFormatter;
-import android.text.TextDirectionHeuristics;
-import android.text.TextUtils;
-import android.view.ContextMenu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewStub;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.QuickContactBadge;
-import android.widget.TextView;
-
-import com.android.contacts.common.CallUtil;
-import com.android.contacts.common.ClipboardUtils;
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
-import com.android.contacts.common.dialog.CallSubjectDialog;
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.contacts.common.util.UriUtils;
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.R;
-import com.android.dialer.calllog.calllogcache.CallLogCache;
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.filterednumber.BlockNumberDialogFragment;
-import com.android.dialer.filterednumber.BlockedNumbersMigrator;
-import com.android.dialer.filterednumber.FilteredNumbersUtil;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.logging.ScreenEvent;
-import com.android.dialer.util.DialerUtils;
-import com.android.dialer.util.PhoneNumberUtil;
-import com.android.dialer.voicemail.VoicemailPlaybackLayout;
-import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
-
-/**
- * This is an object containing references to views contained by the call log list item. This
- * improves performance by reducing the frequency with which we need to find views by IDs.
- *
- * This object also contains UI logic pertaining to the view, to isolate it from the CallLogAdapter.
- */
-public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
- implements View.OnClickListener, MenuItem.OnMenuItemClickListener,
- View.OnCreateContextMenuListener {
-
- public interface OnClickListener {
- void onBlockReportSpam(
- String displayNumber, String number, String countryIso, int callType);
- void onBlock(String displayNumber, String number, String countryIso, int callType);
- void onUnblock(String displayNumber, String number, String countryIso, Integer blockId,
- boolean isSpam, int callType);
- void onReportNotSpam(String displayNumber, String number, String countryIso, int callType);
- }
-
- /** The root view of the call log list item */
- public final View rootView;
- /** The quick contact badge for the contact. */
- public final QuickContactBadge quickContactView;
- /** The primary action view of the entry. */
- public final View primaryActionView;
- /** The details of the phone call. */
- public final PhoneCallDetailsViews phoneCallDetailsViews;
- /** The text of the header for a day grouping. */
- public final TextView dayGroupHeader;
- /** The view containing the details for the call log row, including the action buttons. */
- public final CardView callLogEntryView;
- /** The actionable view which places a call to the number corresponding to the call log row. */
- public final ImageView primaryActionButtonView;
-
- /** The view containing call log item actions. Null until the ViewStub is inflated. */
- public View actionsView;
- /** The button views below are assigned only when the action section is expanded. */
- public VoicemailPlaybackLayout voicemailPlaybackView;
- public View callButtonView;
- public View videoCallButtonView;
- public View createNewContactButtonView;
- public View addToExistingContactButtonView;
- public View sendMessageView;
- public View blockReportView;
- public View blockView;
- public View unblockView;
- public View reportNotSpamView;
- public View detailsButtonView;
- public View callWithNoteButtonView;
- public ImageView workIconView;
-
- /**
- * The row Id for the first call associated with the call log entry. Used as a key for the
- * map used to track which call log entries have the action button section expanded.
- */
- public long rowId;
-
- /**
- * The call Ids for the calls represented by the current call log entry. Used when the user
- * deletes a call log entry.
- */
- public long[] callIds;
-
- /**
- * The callable phone number for the current call log entry. Cached here as the call back
- * intent is set only when the actions ViewStub is inflated.
- */
- public String number;
-
- /**
- * The post-dial numbers that are dialed following the phone number.
- */
- public String postDialDigits;
-
- /**
- * The formatted phone number to display.
- */
- public String displayNumber;
-
- /**
- * The phone number presentation for the current call log entry. Cached here as the call back
- * intent is set only when the actions ViewStub is inflated.
- */
- public int numberPresentation;
-
- /**
- * The type of the phone number (e.g. main, work, etc).
- */
- public String numberType;
-
- /**
- * The country iso for the call. Cached here as the call back
- * intent is set only when the actions ViewStub is inflated.
- */
- public String countryIso;
-
- /**
- * The type of call for the current call log entry. Cached here as the call back
- * intent is set only when the actions ViewStub is inflated.
- */
- public int callType;
-
- /**
- * ID for blocked numbers database.
- * Set when context menu is created, if the number is blocked.
- */
- public Integer blockId;
-
- /**
- * The account for the current call log entry. Cached here as the call back
- * intent is set only when the actions ViewStub is inflated.
- */
- public PhoneAccountHandle accountHandle;
-
- /**
- * If the call has an associated voicemail message, the URI of the voicemail message for
- * playback. Cached here as the voicemail intent is only set when the actions ViewStub is
- * inflated.
- */
- public String voicemailUri;
-
- /**
- * The name or number associated with the call. Cached here for use when setting content
- * descriptions on buttons in the actions ViewStub when it is inflated.
- */
- public CharSequence nameOrNumber;
-
- /**
- * The call type or Location associated with the call. Cached here for use when setting text
- * for a voicemail log's call button
- */
- public CharSequence callTypeOrLocation;
-
- /**
- * Whether this row is for a business or not.
- */
- public boolean isBusiness;
-
- /**
- * The contact info for the contact displayed in this list item.
- */
- public ContactInfo info;
-
- /**
- * Whether spam feature is enabled, which affects UI.
- */
- public boolean isSpamFeatureEnabled;
-
- /**
- * Whether the current log entry is a spam number or not.
- */
- public boolean isSpam;
-
- /**
- * Whether this is the archive tab or not.
- */
- public final boolean isArchiveTab;
-
- private final Context mContext;
- private final CallLogCache mCallLogCache;
- private final CallLogListItemHelper mCallLogListItemHelper;
- private final VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
- private final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
- private final OnClickListener mBlockReportListener;
-
- private final BlockNumberDialogFragment.Callback mFilteredNumberDialogCallback;
-
- private final int mPhotoSize;
-
- private View.OnClickListener mExpandCollapseListener;
- private boolean mVoicemailPrimaryActionButtonClicked;
-
- private CallLogListItemViewHolder(
- Context context,
- OnClickListener blockReportListener,
- View.OnClickListener expandCollapseListener,
- CallLogCache callLogCache,
- CallLogListItemHelper callLogListItemHelper,
- VoicemailPlaybackPresenter voicemailPlaybackPresenter,
- FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler,
- BlockNumberDialogFragment.Callback filteredNumberDialogCallback,
- View rootView,
- QuickContactBadge quickContactView,
- View primaryActionView,
- PhoneCallDetailsViews phoneCallDetailsViews,
- CardView callLogEntryView,
- TextView dayGroupHeader,
- ImageView primaryActionButtonView,
- boolean isArchiveTab) {
- super(rootView);
-
- mContext = context;
- mExpandCollapseListener = expandCollapseListener;
- mCallLogCache = callLogCache;
- mCallLogListItemHelper = callLogListItemHelper;
- mVoicemailPlaybackPresenter = voicemailPlaybackPresenter;
- mFilteredNumberAsyncQueryHandler = filteredNumberAsyncQueryHandler;
- mFilteredNumberDialogCallback = filteredNumberDialogCallback;
- mBlockReportListener = blockReportListener;
-
- this.rootView = rootView;
- this.quickContactView = quickContactView;
- this.primaryActionView = primaryActionView;
- this.phoneCallDetailsViews = phoneCallDetailsViews;
- this.callLogEntryView = callLogEntryView;
- this.dayGroupHeader = dayGroupHeader;
- this.primaryActionButtonView = primaryActionButtonView;
- this.workIconView = (ImageView) rootView.findViewById(R.id.work_profile_icon);
- this.isArchiveTab = isArchiveTab;
- Resources resources = mContext.getResources();
- mPhotoSize = mContext.getResources().getDimensionPixelSize(R.dimen.contact_photo_size);
-
- // Set text height to false on the TextViews so they don't have extra padding.
- phoneCallDetailsViews.nameView.setElegantTextHeight(false);
- phoneCallDetailsViews.callLocationAndDate.setElegantTextHeight(false);
-
- quickContactView.setOverlay(null);
- if (CompatUtils.hasPrioritizedMimeType()) {
- quickContactView.setPrioritizedMimeType(Phone.CONTENT_ITEM_TYPE);
- }
- primaryActionButtonView.setOnClickListener(this);
- primaryActionView.setOnClickListener(mExpandCollapseListener);
- primaryActionView.setOnCreateContextMenuListener(this);
- }
-
- public static CallLogListItemViewHolder create(
- View view,
- Context context,
- OnClickListener blockReportListener,
- View.OnClickListener expandCollapseListener,
- CallLogCache callLogCache,
- CallLogListItemHelper callLogListItemHelper,
- VoicemailPlaybackPresenter voicemailPlaybackPresenter,
- FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler,
- BlockNumberDialogFragment.Callback filteredNumberDialogCallback,
- boolean isArchiveTab) {
-
- return new CallLogListItemViewHolder(
- context,
- blockReportListener,
- expandCollapseListener,
- callLogCache,
- callLogListItemHelper,
- voicemailPlaybackPresenter,
- filteredNumberAsyncQueryHandler,
- filteredNumberDialogCallback,
- view,
- (QuickContactBadge) view.findViewById(R.id.quick_contact_photo),
- view.findViewById(R.id.primary_action_view),
- PhoneCallDetailsViews.fromView(view),
- (CardView) view.findViewById(R.id.call_log_row),
- (TextView) view.findViewById(R.id.call_log_day_group_label),
- (ImageView) view.findViewById(R.id.primary_action_button),
- isArchiveTab);
- }
-
- @Override
- public void onCreateContextMenu(
- final ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
- if (TextUtils.isEmpty(number)) {
- return;
- }
-
- if (callType == CallLog.Calls.VOICEMAIL_TYPE) {
- menu.setHeaderTitle(mContext.getResources().getText(R.string.voicemail));
- } else {
- menu.setHeaderTitle(PhoneNumberUtilsCompat.createTtsSpannable(
- BidiFormatter.getInstance().unicodeWrap(number, TextDirectionHeuristics.LTR)));
- }
-
- menu.add(ContextMenu.NONE, R.id.context_menu_copy_to_clipboard, ContextMenu.NONE,
- R.string.action_copy_number_text)
- .setOnMenuItemClickListener(this);
-
- // The edit number before call does not show up if any of the conditions apply:
- // 1) Number cannot be called
- // 2) Number is the voicemail number
- // 3) Number is a SIP address
-
- if (PhoneNumberUtil.canPlaceCallsTo(number, numberPresentation)
- && !mCallLogCache.isVoicemailNumber(accountHandle, number)
- && !PhoneNumberUtil.isSipNumber(number)) {
- menu.add(ContextMenu.NONE, R.id.context_menu_edit_before_call, ContextMenu.NONE,
- R.string.action_edit_number_before_call)
- .setOnMenuItemClickListener(this);
- }
-
- if (callType == CallLog.Calls.VOICEMAIL_TYPE
- && phoneCallDetailsViews.voicemailTranscriptionView.length() > 0) {
- menu.add(ContextMenu.NONE, R.id.context_menu_copy_transcript_to_clipboard,
- ContextMenu.NONE, R.string.copy_transcript_text)
- .setOnMenuItemClickListener(this);
- }
-
- if (FilteredNumberCompat.canAttemptBlockOperations(mContext)
- && FilteredNumbersUtil.canBlockNumber(mContext, number, countryIso)) {
- mFilteredNumberAsyncQueryHandler.isBlockedNumber(
- new FilteredNumberAsyncQueryHandler.OnCheckBlockedListener() {
- @Override
- public void onCheckComplete(Integer id) {
- blockId = id;
- int blockTitleId = blockId == null ? R.string.action_block_number
- : R.string.action_unblock_number;
- final MenuItem blockItem = menu.add(
- ContextMenu.NONE,
- R.id.context_menu_block_number,
- ContextMenu.NONE,
- blockTitleId);
- blockItem.setOnMenuItemClickListener(
- CallLogListItemViewHolder.this);
- }
- }, number, countryIso);
- }
-
- Logger.logScreenView(ScreenEvent.CALL_LOG_CONTEXT_MENU, (Activity) mContext);
- }
-
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- int resId = item.getItemId();
- if (resId == R.id.context_menu_block_number) {
- FilteredNumberCompat
- .showBlockNumberDialogFlow(mContext.getContentResolver(), blockId, number,
- countryIso, displayNumber, R.id.floating_action_button_container,
- ((Activity) mContext).getFragmentManager(),
- mFilteredNumberDialogCallback);
- return true;
- } else if (resId == R.id.context_menu_copy_to_clipboard) {
- ClipboardUtils.copyText(mContext, null, number, true);
- return true;
- } else if (resId == R.id.context_menu_copy_transcript_to_clipboard) {
- ClipboardUtils.copyText(mContext, null,
- phoneCallDetailsViews.voicemailTranscriptionView.getText(), true);
- return true;
- } else if (resId == R.id.context_menu_edit_before_call) {
- final Intent intent = new Intent(
- Intent.ACTION_DIAL, CallUtil.getCallUri(number));
- intent.setClass(mContext, DialtactsActivity.class);
- DialerUtils.startActivityWithErrorToast(mContext, intent);
- return true;
- }
- return false;
- }
-
- /**
- * Configures the action buttons in the expandable actions ViewStub. The ViewStub is not
- * inflated during initial binding, so click handlers, tags and accessibility text must be set
- * here, if necessary.
- */
- public void inflateActionViewStub() {
- ViewStub stub = (ViewStub) rootView.findViewById(R.id.call_log_entry_actions_stub);
- if (stub != null) {
- actionsView = stub.inflate();
-
- voicemailPlaybackView = (VoicemailPlaybackLayout) actionsView
- .findViewById(R.id.voicemail_playback_layout);
- if (isArchiveTab) {
- voicemailPlaybackView.hideArchiveButton();
- }
-
-
- callButtonView = actionsView.findViewById(R.id.call_action);
- callButtonView.setOnClickListener(this);
-
- videoCallButtonView = actionsView.findViewById(R.id.video_call_action);
- videoCallButtonView.setOnClickListener(this);
-
- createNewContactButtonView = actionsView.findViewById(R.id.create_new_contact_action);
- createNewContactButtonView.setOnClickListener(this);
-
- addToExistingContactButtonView =
- actionsView.findViewById(R.id.add_to_existing_contact_action);
- addToExistingContactButtonView.setOnClickListener(this);
-
- sendMessageView = actionsView.findViewById(R.id.send_message_action);
- sendMessageView.setOnClickListener(this);
-
- blockReportView = actionsView.findViewById(R.id.block_report_action);
- blockReportView.setOnClickListener(this);
-
- blockView = actionsView.findViewById(R.id.block_action);
- blockView.setOnClickListener(this);
-
- unblockView = actionsView.findViewById(R.id.unblock_action);
- unblockView.setOnClickListener(this);
-
- reportNotSpamView = actionsView.findViewById(R.id.report_not_spam_action);
- reportNotSpamView.setOnClickListener(this);
-
- detailsButtonView = actionsView.findViewById(R.id.details_action);
- detailsButtonView.setOnClickListener(this);
-
- callWithNoteButtonView = actionsView.findViewById(R.id.call_with_note_action);
- callWithNoteButtonView.setOnClickListener(this);
- }
-
- bindActionButtons();
- }
-
- private void updatePrimaryActionButton(boolean isExpanded) {
- if (!TextUtils.isEmpty(voicemailUri)) {
- // Treat as voicemail list item; show play button if not expanded.
- if (!isExpanded) {
- primaryActionButtonView.setImageResource(R.drawable.ic_play_arrow_24dp);
- primaryActionButtonView.setContentDescription(TextUtils.expandTemplate(
- mContext.getString(R.string.description_voicemail_action),
- nameOrNumber));
- primaryActionButtonView.setVisibility(View.VISIBLE);
- } else {
- primaryActionButtonView.setVisibility(View.GONE);
- }
- } else {
- // Treat as normal list item; show call button, if possible.
- if (PhoneNumberUtil.canPlaceCallsTo(number, numberPresentation)) {
- boolean isVoicemailNumber =
- mCallLogCache.isVoicemailNumber(accountHandle, number);
- if (isVoicemailNumber) {
- // Call to generic voicemail number, in case there are multiple accounts.
- primaryActionButtonView.setTag(
- IntentProvider.getReturnVoicemailCallIntentProvider());
- } else {
- primaryActionButtonView.setTag(
- IntentProvider.getReturnCallIntentProvider(number + postDialDigits));
- }
-
- primaryActionButtonView.setContentDescription(TextUtils.expandTemplate(
- mContext.getString(R.string.description_call_action),
- nameOrNumber));
- primaryActionButtonView.setImageResource(R.drawable.ic_call_24dp);
- primaryActionButtonView.setVisibility(View.VISIBLE);
- } else {
- primaryActionButtonView.setTag(null);
- primaryActionButtonView.setVisibility(View.GONE);
- }
- }
- }
-
- /**
- * Binds text titles, click handlers and intents to the voicemail, details and callback action
- * buttons.
- */
- private void bindActionButtons() {
- boolean canPlaceCallToNumber = PhoneNumberUtil.canPlaceCallsTo(number, numberPresentation);
-
- if (!TextUtils.isEmpty(voicemailUri) && canPlaceCallToNumber) {
- callButtonView.setTag(IntentProvider.getReturnCallIntentProvider(number));
- ((TextView) callButtonView.findViewById(R.id.call_action_text))
- .setText(TextUtils.expandTemplate(
- mContext.getString(R.string.call_log_action_call),
- nameOrNumber));
- TextView callTypeOrLocationView = ((TextView) callButtonView.findViewById(
- R.id.call_type_or_location_text));
- if (callType == Calls.VOICEMAIL_TYPE && !TextUtils.isEmpty(callTypeOrLocation)) {
- callTypeOrLocationView.setText(callTypeOrLocation);
- callTypeOrLocationView.setVisibility(View.VISIBLE);
- } else {
- callTypeOrLocationView.setVisibility(View.GONE);
- }
- callButtonView.setVisibility(View.VISIBLE);
- } else {
- callButtonView.setVisibility(View.GONE);
- }
-
- // If one of the calls had video capabilities, show the video call button.
- if (mCallLogCache.isVideoEnabled() && canPlaceCallToNumber &&
- phoneCallDetailsViews.callTypeIcons.isVideoShown()) {
- videoCallButtonView.setTag(IntentProvider.getReturnVideoCallIntentProvider(number));
- videoCallButtonView.setVisibility(View.VISIBLE);
- } else {
- videoCallButtonView.setVisibility(View.GONE);
- }
-
- // For voicemail calls, show the voicemail playback layout; hide otherwise.
- if (callType == Calls.VOICEMAIL_TYPE && mVoicemailPlaybackPresenter != null
- && !TextUtils.isEmpty(voicemailUri)) {
- voicemailPlaybackView.setVisibility(View.VISIBLE);
-
- Uri uri = Uri.parse(voicemailUri);
- mVoicemailPlaybackPresenter.setPlaybackView(
- voicemailPlaybackView, uri, mVoicemailPrimaryActionButtonClicked);
- mVoicemailPrimaryActionButtonClicked = false;
- // Only mark voicemail as read when not in archive tab
- if (!isArchiveTab) {
- CallLogAsyncTaskUtil.markVoicemailAsRead(mContext, uri);
- }
- } else {
- voicemailPlaybackView.setVisibility(View.GONE);
- }
-
- if (callType == Calls.VOICEMAIL_TYPE) {
- detailsButtonView.setVisibility(View.GONE);
- } else {
- detailsButtonView.setVisibility(View.VISIBLE);
- detailsButtonView.setTag(
- IntentProvider.getCallDetailIntentProvider(rowId, callIds, null));
- }
-
- if (info != null && UriUtils.isEncodedContactUri(info.lookupUri)) {
- createNewContactButtonView.setTag(IntentProvider.getAddContactIntentProvider(
- info.lookupUri, info.name, info.number, info.type, true /* isNewContact */));
- createNewContactButtonView.setVisibility(View.VISIBLE);
-
- addToExistingContactButtonView.setTag(IntentProvider.getAddContactIntentProvider(
- info.lookupUri, info.name, info.number, info.type, false /* isNewContact */));
- addToExistingContactButtonView.setVisibility(View.VISIBLE);
- } else {
- createNewContactButtonView.setVisibility(View.GONE);
- addToExistingContactButtonView.setVisibility(View.GONE);
- }
-
- if (canPlaceCallToNumber) {
- sendMessageView.setTag(IntentProvider.getSendSmsIntentProvider(number));
- sendMessageView.setVisibility(View.VISIBLE);
- } else {
- sendMessageView.setVisibility(View.GONE);
- }
-
- mCallLogListItemHelper.setActionContentDescriptions(this);
-
- boolean supportsCallSubject =
- mCallLogCache.doesAccountSupportCallSubject(accountHandle);
- boolean isVoicemailNumber =
- mCallLogCache.isVoicemailNumber(accountHandle, number);
- callWithNoteButtonView.setVisibility(
- supportsCallSubject && !isVoicemailNumber ? View.VISIBLE : View.GONE);
-
- updateBlockReportActions();
- }
-
- /**
- * Show or hide the action views, such as voicemail, details, and add contact.
- *
- * If the action views have never been shown yet for this view, inflate the view stub.
- */
- public void showActions(boolean show) {
- showOrHideVoicemailTranscriptionView(show);
-
- if (show) {
- // Inflate the view stub if necessary, and wire up the event handlers.
- inflateActionViewStub();
-
- actionsView.setVisibility(View.VISIBLE);
- actionsView.setAlpha(1.0f);
- } else {
- // When recycling a view, it is possible the actionsView ViewStub was previously
- // inflated so we should hide it in this case.
- if (actionsView != null) {
- actionsView.setVisibility(View.GONE);
- }
- }
-
- updatePrimaryActionButton(show);
- }
-
- public void showOrHideVoicemailTranscriptionView(boolean isExpanded) {
- if (callType != Calls.VOICEMAIL_TYPE) {
- return;
- }
-
- final TextView view = phoneCallDetailsViews.voicemailTranscriptionView;
- if (!isExpanded || TextUtils.isEmpty(view.getText())) {
- view.setVisibility(View.GONE);
- return;
- }
- view.setVisibility(View.VISIBLE);
- }
-
- public void updatePhoto() {
- if (isSpamFeatureEnabled && isSpam) {
- quickContactView.setImageDrawable(
- mContext.getDrawable(R.drawable.blocked_contact));
- return;
- }
- quickContactView.assignContactUri(info.lookupUri);
-
- final boolean isVoicemail = mCallLogCache.isVoicemailNumber(accountHandle, number);
- int contactType = ContactPhotoManager.TYPE_DEFAULT;
- if (isVoicemail) {
- contactType = ContactPhotoManager.TYPE_VOICEMAIL;
- } else if (isBusiness) {
- contactType = ContactPhotoManager.TYPE_BUSINESS;
- }
-
- final String lookupKey = info.lookupUri != null
- ? UriUtils.getLookupKeyFromUri(info.lookupUri) : null;
- final String displayName = TextUtils.isEmpty(info.name) ? displayNumber : info.name;
- final DefaultImageRequest request = new DefaultImageRequest(
- displayName, lookupKey, contactType, true /* isCircular */);
-
- if (info.photoId == 0 && info.photoUri != null) {
- ContactPhotoManager.getInstance(mContext).loadPhoto(quickContactView, info.photoUri,
- mPhotoSize, false /* darkTheme */, true /* isCircular */, request);
- } else {
- ContactPhotoManager.getInstance(mContext).loadThumbnail(quickContactView, info.photoId,
- false /* darkTheme */, true /* isCircular */, request);
- }
- }
-
- @Override
- public void onClick(View view) {
- if (view.getId() == R.id.primary_action_button && !TextUtils.isEmpty(voicemailUri)) {
- mVoicemailPrimaryActionButtonClicked = true;
- mExpandCollapseListener.onClick(primaryActionView);
- } else if (view.getId() == R.id.call_with_note_action) {
- CallSubjectDialog.start(
- (Activity) mContext,
- info.photoId,
- info.photoUri,
- info.lookupUri,
- (String) nameOrNumber /* top line of contact view in call subject dialog */,
- isBusiness,
- number,
- TextUtils.isEmpty(info.name) ? null : displayNumber, /* second line of contact
- view in dialog. */
- numberType, /* phone number type (e.g. mobile) in second line of contact view */
- accountHandle);
- } else if (view.getId() == R.id.block_report_action) {
- maybeShowBlockNumberMigrationDialog(new BlockedNumbersMigrator.Listener() {
- @Override
- public void onComplete() {
- mBlockReportListener.onBlockReportSpam(
- displayNumber, number, countryIso, callType);
- }
- });
- } else if (view.getId() == R.id.block_action) {
- maybeShowBlockNumberMigrationDialog(new BlockedNumbersMigrator.Listener() {
- @Override
- public void onComplete() {
- mBlockReportListener.onBlock(displayNumber, number, countryIso, callType);
- }
- });
- } else if (view.getId() == R.id.unblock_action) {
- mBlockReportListener.onUnblock(
- displayNumber, number, countryIso, blockId, isSpam, callType);
- } else if (view.getId() == R.id.report_not_spam_action) {
- mBlockReportListener.onReportNotSpam(displayNumber, number, countryIso, callType);
- } else {
- final IntentProvider intentProvider = (IntentProvider) view.getTag();
- if (intentProvider != null) {
- final Intent intent = intentProvider.getIntent(mContext);
- // See IntentProvider.getCallDetailIntentProvider() for why this may be null.
- if (intent != null) {
- DialerUtils.startActivityWithErrorToast(mContext, intent);
- }
- }
- }
- }
-
- private void maybeShowBlockNumberMigrationDialog(BlockedNumbersMigrator.Listener listener) {
- if (!FilteredNumberCompat.maybeShowBlockNumberMigrationDialog(
- mContext.getContentResolver(),
- ((Activity) mContext).getFragmentManager(), listener)) {
- listener.onComplete();
- }
- }
-
- @NeededForTesting
- public static CallLogListItemViewHolder createForTest(Context context) {
- Resources resources = context.getResources();
- CallLogCache callLogCache =
- CallLogCache.getCallLogCache(context);
- PhoneCallDetailsHelper phoneCallDetailsHelper = new PhoneCallDetailsHelper(
- context, resources, callLogCache);
-
- CallLogListItemViewHolder viewHolder = new CallLogListItemViewHolder(
- context,
- null,
- null /* expandCollapseListener */,
- callLogCache,
- new CallLogListItemHelper(phoneCallDetailsHelper, resources, callLogCache),
- null /* voicemailPlaybackPresenter */,
- null /* filteredNumberAsyncQueryHandler */,
- null /* filteredNumberDialogCallback */,
- new View(context),
- new QuickContactBadge(context),
- new View(context),
- PhoneCallDetailsViews.createForTest(context),
- new CardView(context),
- new TextView(context),
- new ImageView(context),
- false);
- viewHolder.detailsButtonView = new TextView(context);
- viewHolder.actionsView = new View(context);
- viewHolder.voicemailPlaybackView = new VoicemailPlaybackLayout(context);
- viewHolder.workIconView = new ImageButton(context);
- return viewHolder;
- }
-
- private void updateBlockReportActions() {
- if (!isSpamFeatureEnabled) {
- return;
- }
- // Set block/spam actions.
- blockReportView.setVisibility(View.GONE);
- blockView.setVisibility(View.GONE);
- unblockView.setVisibility(View.GONE);
- reportNotSpamView.setVisibility(View.GONE);
- boolean isBlocked = blockId != null;
- if (isBlocked) {
- unblockView.setVisibility(View.VISIBLE);
- } else {
- if (isSpam) {
- blockView.setVisibility(View.VISIBLE);
- reportNotSpamView.setVisibility(View.VISIBLE);
- } else {
- blockReportView.setVisibility(View.VISIBLE);
- }
- }
-
- }
-} \ No newline at end of file
diff --git a/src/com/android/dialer/calllog/CallLogNotificationsHelper.java b/src/com/android/dialer/calllog/CallLogNotificationsHelper.java
deleted file mode 100644
index 9a5028460..000000000
--- a/src/com/android/dialer/calllog/CallLogNotificationsHelper.java
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * Copyright (C) 2013 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.calllog;
-
-import com.google.common.base.Strings;
-
-import android.Manifest;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.CallLog.Calls;
-import android.provider.ContactsContract.PhoneLookup;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.dialer.R;
-import com.android.dialer.util.TelecomUtil;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Helper class operating on call log notifications.
- */
-public class CallLogNotificationsHelper {
- private static final String TAG = "CallLogNotifHelper";
- private static CallLogNotificationsHelper sInstance;
-
- /** Returns the singleton instance of the {@link CallLogNotificationsHelper}. */
- public static CallLogNotificationsHelper getInstance(Context context) {
- if (sInstance == null) {
- ContentResolver contentResolver = context.getContentResolver();
- String countryIso = GeoUtil.getCurrentCountryIso(context);
- sInstance = new CallLogNotificationsHelper(context,
- createNewCallsQuery(context, contentResolver),
- createNameLookupQuery(context, contentResolver),
- new ContactInfoHelper(context, countryIso),
- countryIso);
- }
- return sInstance;
- }
-
- private final Context mContext;
- private final NewCallsQuery mNewCallsQuery;
- private final NameLookupQuery mNameLookupQuery;
- private final ContactInfoHelper mContactInfoHelper;
- private final String mCurrentCountryIso;
-
- CallLogNotificationsHelper(Context context, NewCallsQuery newCallsQuery,
- NameLookupQuery nameLookupQuery, ContactInfoHelper contactInfoHelper,
- String countryIso) {
- mContext = context;
- mNewCallsQuery = newCallsQuery;
- mNameLookupQuery = nameLookupQuery;
- mContactInfoHelper = contactInfoHelper;
- mCurrentCountryIso = countryIso;
- }
-
- /**
- * Get all voicemails with the "new" flag set to 1.
- *
- * @return A list of NewCall objects where each object represents a new voicemail.
- */
- @Nullable
- public List<NewCall> getNewVoicemails() {
- return mNewCallsQuery.query(Calls.VOICEMAIL_TYPE);
- }
-
- /**
- * Get all missed calls with the "new" flag set to 1.
- *
- * @return A list of NewCall objects where each object represents a new missed call.
- */
- @Nullable
- public List<NewCall> getNewMissedCalls() {
- return mNewCallsQuery.query(Calls.MISSED_TYPE);
- }
-
- /**
- * Given a number and number information (presentation and country ISO), get the best name
- * for display. If the name is empty but we have a special presentation, display that.
- * Otherwise attempt to look it up in the database or the cache.
- * If that fails, fall back to displaying the number.
- */
- public String getName(@Nullable String number, int numberPresentation,
- @Nullable String countryIso) {
- return getContactInfo(number, numberPresentation, countryIso).name;
- }
-
- /**
- * Given a number and number information (presentation and country ISO), get
- * {@link ContactInfo}. If the name is empty but we have a special presentation, display that.
- * Otherwise attempt to look it up in the cache.
- * If that fails, fall back to displaying the number.
- */
- public ContactInfo getContactInfo(@Nullable String number, int numberPresentation,
- @Nullable String countryIso) {
- if (countryIso == null) {
- countryIso = mCurrentCountryIso;
- }
-
- number = Strings.nullToEmpty(number);
- ContactInfo contactInfo = new ContactInfo();
- contactInfo.number = number;
- contactInfo.formattedNumber = PhoneNumberUtils.formatNumber(number, countryIso);
- // contactInfo.normalizedNumber is not PhoneNumberUtils.normalizeNumber. Read ContactInfo.
- contactInfo.normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
-
- // 1. Special number representation.
- contactInfo.name = PhoneNumberDisplayUtil.getDisplayName(
- mContext,
- number,
- numberPresentation,
- false).toString();
- if (!TextUtils.isEmpty(contactInfo.name)) {
- return contactInfo;
- }
-
- // 2. Look it up in the cache.
- ContactInfo cachedContactInfo = mContactInfoHelper.lookupNumber(number, countryIso);
-
- if (cachedContactInfo != null && !TextUtils.isEmpty(cachedContactInfo.name)) {
- return cachedContactInfo;
- }
-
- if (!TextUtils.isEmpty(contactInfo.formattedNumber)) {
- // 3. If we cannot lookup the contact, use the formatted number instead.
- contactInfo.name = contactInfo.formattedNumber;
- } else if (!TextUtils.isEmpty(number)) {
- // 4. If number can't be formatted, use number.
- contactInfo.name = number;
- } else {
- // 5. Otherwise, it's unknown number.
- contactInfo.name = mContext.getResources().getString(R.string.unknown);
- }
- return contactInfo;
- }
-
- /** Removes the missed call notifications. */
- public static void removeMissedCallNotifications(Context context) {
- TelecomUtil.cancelMissedCallsNotification(context);
- }
-
- /** Update the voice mail notifications. */
- public static void updateVoicemailNotifications(Context context) {
- CallLogNotificationsService.updateVoicemailNotifications(context, null);
- }
-
- /** Information about a new voicemail. */
- public static final class NewCall {
- public final Uri callsUri;
- public final Uri voicemailUri;
- public final String number;
- public final int numberPresentation;
- public final String accountComponentName;
- public final String accountId;
- public final String transcription;
- public final String countryIso;
- public final long dateMs;
-
- public NewCall(
- Uri callsUri,
- Uri voicemailUri,
- String number,
- int numberPresentation,
- String accountComponentName,
- String accountId,
- String transcription,
- String countryIso,
- long dateMs) {
- this.callsUri = callsUri;
- this.voicemailUri = voicemailUri;
- this.number = number;
- this.numberPresentation = numberPresentation;
- this.accountComponentName = accountComponentName;
- this.accountId = accountId;
- this.transcription = transcription;
- this.countryIso = countryIso;
- this.dateMs = dateMs;
- }
- }
-
- /** Allows determining the new calls for which a notification should be generated. */
- public interface NewCallsQuery {
- /**
- * Returns the new calls of a certain type for which a notification should be generated.
- */
- @Nullable
- public List<NewCall> query(int type);
- }
-
- /** Create a new instance of {@link NewCallsQuery}. */
- public static NewCallsQuery createNewCallsQuery(Context context,
- ContentResolver contentResolver) {
-
- return new DefaultNewCallsQuery(context.getApplicationContext(), contentResolver);
- }
-
- /**
- * Default implementation of {@link NewCallsQuery} that looks up the list of new calls to
- * notify about in the call log.
- */
- private static final class DefaultNewCallsQuery implements NewCallsQuery {
- private static final String[] PROJECTION = {
- Calls._ID,
- Calls.NUMBER,
- Calls.VOICEMAIL_URI,
- Calls.NUMBER_PRESENTATION,
- Calls.PHONE_ACCOUNT_COMPONENT_NAME,
- Calls.PHONE_ACCOUNT_ID,
- Calls.TRANSCRIPTION,
- Calls.COUNTRY_ISO,
- Calls.DATE
- };
- private static final int ID_COLUMN_INDEX = 0;
- private static final int NUMBER_COLUMN_INDEX = 1;
- private static final int VOICEMAIL_URI_COLUMN_INDEX = 2;
- private static final int NUMBER_PRESENTATION_COLUMN_INDEX = 3;
- private static final int PHONE_ACCOUNT_COMPONENT_NAME_COLUMN_INDEX = 4;
- private static final int PHONE_ACCOUNT_ID_COLUMN_INDEX = 5;
- private static final int TRANSCRIPTION_COLUMN_INDEX = 6;
- private static final int COUNTRY_ISO_COLUMN_INDEX = 7;
- private static final int DATE_COLUMN_INDEX = 8;
-
- private final ContentResolver mContentResolver;
- private final Context mContext;
-
- private DefaultNewCallsQuery(Context context, ContentResolver contentResolver) {
- mContext = context;
- mContentResolver = contentResolver;
- }
-
- @Override
- @Nullable
- public List<NewCall> query(int type) {
- if (!PermissionsUtil.hasPermission(mContext, Manifest.permission.READ_CALL_LOG)) {
- Log.w(TAG, "No READ_CALL_LOG permission, returning null for calls lookup.");
- return null;
- }
- final String selection = String.format("%s = 1 AND %s = ?", Calls.NEW, Calls.TYPE);
- final String[] selectionArgs = new String[]{ Integer.toString(type) };
- try (Cursor cursor = mContentResolver.query(Calls.CONTENT_URI_WITH_VOICEMAIL,
- PROJECTION, selection, selectionArgs, Calls.DEFAULT_SORT_ORDER)) {
- if (cursor == null) {
- return null;
- }
- List<NewCall> newCalls = new ArrayList<>();
- while (cursor.moveToNext()) {
- newCalls.add(createNewCallsFromCursor(cursor));
- }
- return newCalls;
- } catch (RuntimeException e) {
- Log.w(TAG, "Exception when querying Contacts Provider for calls lookup");
- return null;
- }
- }
-
- /** Returns an instance of {@link NewCall} created by using the values of the cursor. */
- private NewCall createNewCallsFromCursor(Cursor cursor) {
- String voicemailUriString = cursor.getString(VOICEMAIL_URI_COLUMN_INDEX);
- Uri callsUri = ContentUris.withAppendedId(
- Calls.CONTENT_URI_WITH_VOICEMAIL, cursor.getLong(ID_COLUMN_INDEX));
- Uri voicemailUri = voicemailUriString == null ? null : Uri.parse(voicemailUriString);
- return new NewCall(
- callsUri,
- voicemailUri,
- cursor.getString(NUMBER_COLUMN_INDEX),
- cursor.getInt(NUMBER_PRESENTATION_COLUMN_INDEX),
- cursor.getString(PHONE_ACCOUNT_COMPONENT_NAME_COLUMN_INDEX),
- cursor.getString(PHONE_ACCOUNT_ID_COLUMN_INDEX),
- cursor.getString(TRANSCRIPTION_COLUMN_INDEX),
- cursor.getString(COUNTRY_ISO_COLUMN_INDEX),
- cursor.getLong(DATE_COLUMN_INDEX));
- }
- }
-
- /** Allows determining the name associated with a given phone number. */
- public interface NameLookupQuery {
- /**
- * Returns the name associated with the given number in the contacts database, or null if
- * the number does not correspond to any of the contacts.
- * <p>
- * If there are multiple contacts with the same phone number, it will return the name of one
- * of the matching contacts.
- */
- @Nullable
- public String query(@Nullable String number);
- }
-
- /** Create a new instance of {@link NameLookupQuery}. */
- public static NameLookupQuery createNameLookupQuery(Context context,
- ContentResolver contentResolver) {
- return new DefaultNameLookupQuery(context.getApplicationContext(), contentResolver);
- }
-
- /**
- * Default implementation of {@link NameLookupQuery} that looks up the name of a contact in the
- * contacts database.
- */
- private static final class DefaultNameLookupQuery implements NameLookupQuery {
- private static final String[] PROJECTION = { PhoneLookup.DISPLAY_NAME };
- private static final int DISPLAY_NAME_COLUMN_INDEX = 0;
-
- private final ContentResolver mContentResolver;
- private final Context mContext;
-
- private DefaultNameLookupQuery(Context context, ContentResolver contentResolver) {
- mContext = context;
- mContentResolver = contentResolver;
- }
-
- @Override
- @Nullable
- public String query(@Nullable String number) {
- if (!PermissionsUtil.hasPermission(mContext, Manifest.permission.READ_CONTACTS)) {
- Log.w(TAG, "No READ_CONTACTS permission, returning null for name lookup.");
- return null;
- }
- try (Cursor cursor = mContentResolver.query(
- Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)),
- PROJECTION, null, null, null)) {
- if (cursor == null || !cursor.moveToFirst()) {
- return null;
- }
- return cursor.getString(DISPLAY_NAME_COLUMN_INDEX);
- } catch (RuntimeException e) {
- Log.w(TAG, "Exception when querying Contacts Provider for name lookup");
- return null;
- }
- }
- }
-}
diff --git a/src/com/android/dialer/calllog/CallLogNotificationsService.java b/src/com/android/dialer/calllog/CallLogNotificationsService.java
deleted file mode 100644
index 4ff9576ca..000000000
--- a/src/com/android/dialer/calllog/CallLogNotificationsService.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import android.app.IntentService;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.util.Log;
-
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.dialer.util.TelecomUtil;
-
-/**
- * Provides operations for managing call-related notifications.
- * <p>
- * It handles the following actions:
- * <ul>
- * <li>Updating voicemail notifications</li>
- * <li>Marking new voicemails as old</li>
- * <li>Updating missed call notifications</li>
- * <li>Marking new missed calls as old</li>
- * <li>Calling back from a missed call</li>
- * <li>Sending an SMS from a missed call</li>
- * </ul>
- */
-public class CallLogNotificationsService extends IntentService {
- private static final String TAG = "CallLogNotificationsService";
-
- /** Action to mark all the new voicemails as old. */
- public static final String ACTION_MARK_NEW_VOICEMAILS_AS_OLD =
- "com.android.dialer.calllog.ACTION_MARK_NEW_VOICEMAILS_AS_OLD";
-
- /**
- * Action to update voicemail notifications.
- * <p>
- * May include an optional extra {@link #EXTRA_NEW_VOICEMAIL_URI}.
- */
- public static final String ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS =
- "com.android.dialer.calllog.UPDATE_VOICEMAIL_NOTIFICATIONS";
-
- /**
- * Extra to included with {@link #ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS} to identify the new
- * voicemail that triggered an update.
- * <p>
- * It must be a {@link Uri}.
- */
- public static final String EXTRA_NEW_VOICEMAIL_URI = "NEW_VOICEMAIL_URI";
-
- /**
- * Action to update the missed call notifications.
- * <p>
- * Includes optional extras {@link #EXTRA_MISSED_CALL_NUMBER} and
- * {@link #EXTRA_MISSED_CALL_COUNT}.
- */
- public static final String ACTION_UPDATE_MISSED_CALL_NOTIFICATIONS =
- "com.android.dialer.calllog.UPDATE_MISSED_CALL_NOTIFICATIONS";
-
- /** Action to mark all the new missed calls as old. */
- public static final String ACTION_MARK_NEW_MISSED_CALLS_AS_OLD =
- "com.android.dialer.calllog.ACTION_MARK_NEW_MISSED_CALLS_AS_OLD";
-
- /** Action to call back a missed call. */
- public static final String ACTION_CALL_BACK_FROM_MISSED_CALL_NOTIFICATION =
- "com.android.dialer.calllog.CALL_BACK_FROM_MISSED_CALL_NOTIFICATION";
-
- public static final String ACTION_SEND_SMS_FROM_MISSED_CALL_NOTIFICATION =
- "com.android.dialer.calllog.SEND_SMS_FROM_MISSED_CALL_NOTIFICATION";
-
- /**
- * Extra to be included with {@link #ACTION_UPDATE_MISSED_CALL_NOTIFICATIONS},
- * {@link #ACTION_SEND_SMS_FROM_MISSED_CALL_NOTIFICATION} and
- * {@link #ACTION_CALL_BACK_FROM_MISSED_CALL_NOTIFICATION} to identify the number to display,
- * call or text back.
- * <p>
- * It must be a {@link String}.
- */
- public static final String EXTRA_MISSED_CALL_NUMBER = "MISSED_CALL_NUMBER";
-
- /**
- * Extra to be included with {@link #ACTION_UPDATE_MISSED_CALL_NOTIFICATIONS} to represent the
- * number of missed calls.
- * <p>
- * It must be a {@link Integer}
- */
- public static final String EXTRA_MISSED_CALL_COUNT =
- "MISSED_CALL_COUNT";
-
- public static final int UNKNOWN_MISSED_CALL_COUNT = -1;
-
- private VoicemailQueryHandler mVoicemailQueryHandler;
-
- public CallLogNotificationsService() {
- super("CallLogNotificationsService");
- }
-
- @Override
- protected void onHandleIntent(Intent intent) {
- if (intent == null) {
- Log.d(TAG, "onHandleIntent: could not handle null intent");
- return;
- }
-
- if (!PermissionsUtil.hasPermission(this, android.Manifest.permission.READ_CALL_LOG)) {
- return;
- }
-
- String action = intent.getAction();
- switch (action) {
- case ACTION_MARK_NEW_VOICEMAILS_AS_OLD:
- if (mVoicemailQueryHandler == null) {
- mVoicemailQueryHandler = new VoicemailQueryHandler(this, getContentResolver());
- }
- mVoicemailQueryHandler.markNewVoicemailsAsOld();
- break;
- case ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS:
- Uri voicemailUri = (Uri) intent.getParcelableExtra(EXTRA_NEW_VOICEMAIL_URI);
- DefaultVoicemailNotifier.getInstance(this).updateNotification(voicemailUri);
- break;
- case ACTION_UPDATE_MISSED_CALL_NOTIFICATIONS:
- int count = intent.getIntExtra(EXTRA_MISSED_CALL_COUNT,
- UNKNOWN_MISSED_CALL_COUNT);
- String number = intent.getStringExtra(EXTRA_MISSED_CALL_NUMBER);
- MissedCallNotifier.getInstance(this).updateMissedCallNotification(count, number);
- break;
- case ACTION_MARK_NEW_MISSED_CALLS_AS_OLD:
- CallLogNotificationsHelper.removeMissedCallNotifications(this);
- break;
- case ACTION_CALL_BACK_FROM_MISSED_CALL_NOTIFICATION:
- MissedCallNotifier.getInstance(this).callBackFromMissedCall(
- intent.getStringExtra(EXTRA_MISSED_CALL_NUMBER));
- break;
- case ACTION_SEND_SMS_FROM_MISSED_CALL_NOTIFICATION:
- MissedCallNotifier.getInstance(this).sendSmsFromMissedCall(
- intent.getStringExtra(EXTRA_MISSED_CALL_NUMBER));
- break;
- default:
- Log.d(TAG, "onHandleIntent: could not handle: " + intent);
- break;
- }
- }
-
- /**
- * Updates notifications for any new voicemails.
- *
- * @param context a valid context.
- * @param voicemailUri The uri pointing to the voicemail to update the notification for. If
- * {@code null}, then notifications for all new voicemails will be updated.
- */
- public static void updateVoicemailNotifications(Context context, Uri voicemailUri) {
- if (TelecomUtil.hasReadWriteVoicemailPermissions(context)) {
- Intent serviceIntent = new Intent(context, CallLogNotificationsService.class);
- serviceIntent.setAction(
- CallLogNotificationsService.ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS);
- // If voicemailUri is null, then notifications for all voicemails will be updated.
- if (voicemailUri != null) {
- serviceIntent.putExtra(
- CallLogNotificationsService.EXTRA_NEW_VOICEMAIL_URI, voicemailUri);
- }
- context.startService(serviceIntent);
- }
- }
-
- /**
- * Updates notifications for any new missed calls.
- *
- * @param context A valid context.
- * @param count The number of new missed calls.
- * @param number The phone number of the newest missed call.
- */
- public static void updateMissedCallNotifications(Context context, int count,
- String number) {
- Intent serviceIntent = new Intent(context, CallLogNotificationsService.class);
- serviceIntent.setAction(
- CallLogNotificationsService.ACTION_UPDATE_MISSED_CALL_NOTIFICATIONS);
- serviceIntent.putExtra(EXTRA_MISSED_CALL_COUNT, count);
- serviceIntent.putExtra(EXTRA_MISSED_CALL_NUMBER, number);
- context.startService(serviceIntent);
- }
-}
diff --git a/src/com/android/dialer/calllog/CallLogQuery.java b/src/com/android/dialer/calllog/CallLogQuery.java
deleted file mode 100644
index e1a41199a..000000000
--- a/src/com/android/dialer/calllog/CallLogQuery.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import com.google.common.collect.Lists;
-
-import android.provider.CallLog.Calls;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.dialer.compat.CallsSdkCompat;
-import com.android.dialer.compat.DialerCompatUtils;
-
-import java.util.List;
-
-/**
- * The query for the call log table.
- */
-public final class CallLogQuery {
-
- private static final String[] _PROJECTION_INTERNAL = new String[] {
- Calls._ID, // 0
- Calls.NUMBER, // 1
- Calls.DATE, // 2
- Calls.DURATION, // 3
- Calls.TYPE, // 4
- Calls.COUNTRY_ISO, // 5
- Calls.VOICEMAIL_URI, // 6
- Calls.GEOCODED_LOCATION, // 7
- Calls.CACHED_NAME, // 8
- Calls.CACHED_NUMBER_TYPE, // 9
- Calls.CACHED_NUMBER_LABEL, // 10
- Calls.CACHED_LOOKUP_URI, // 11
- Calls.CACHED_MATCHED_NUMBER, // 12
- Calls.CACHED_NORMALIZED_NUMBER, // 13
- Calls.CACHED_PHOTO_ID, // 14
- Calls.CACHED_FORMATTED_NUMBER, // 15
- Calls.IS_READ, // 16
- Calls.NUMBER_PRESENTATION, // 17
- Calls.PHONE_ACCOUNT_COMPONENT_NAME, // 18
- Calls.PHONE_ACCOUNT_ID, // 19
- Calls.FEATURES, // 20
- Calls.DATA_USAGE, // 21
- Calls.TRANSCRIPTION, // 22
- };
-
- public static final int ID = 0;
- public static final int NUMBER = 1;
- public static final int DATE = 2;
- public static final int DURATION = 3;
- public static final int CALL_TYPE = 4;
- public static final int COUNTRY_ISO = 5;
- public static final int VOICEMAIL_URI = 6;
- public static final int GEOCODED_LOCATION = 7;
- public static final int CACHED_NAME = 8;
- public static final int CACHED_NUMBER_TYPE = 9;
- public static final int CACHED_NUMBER_LABEL = 10;
- public static final int CACHED_LOOKUP_URI = 11;
- public static final int CACHED_MATCHED_NUMBER = 12;
- public static final int CACHED_NORMALIZED_NUMBER = 13;
- public static final int CACHED_PHOTO_ID = 14;
- public static final int CACHED_FORMATTED_NUMBER = 15;
- public static final int IS_READ = 16;
- public static final int NUMBER_PRESENTATION = 17;
- public static final int ACCOUNT_COMPONENT_NAME = 18;
- public static final int ACCOUNT_ID = 19;
- public static final int FEATURES = 20;
- public static final int DATA_USAGE = 21;
- public static final int TRANSCRIPTION = 22;
-
- // Indices for columns that may not be available, depending on the Sdk Version
- /**
- * Only available in versions >= M
- * Call {@link DialerCompatUtils#isCallsCachedPhotoUriCompatible()} prior to use
- */
- public static int CACHED_PHOTO_URI = -1;
-
- /**
- * Only available in versions > M
- * Call {@link CompatUtils#isNCompatible()} prior to use
- */
- public static int POST_DIAL_DIGITS = -1;
- public static int VIA_NUMBER = -1;
-
- public static final String[] _PROJECTION;
-
- static {
- List<String> projectionList = Lists.newArrayList(_PROJECTION_INTERNAL);
- if (DialerCompatUtils.isCallsCachedPhotoUriCompatible()) {
- projectionList.add(Calls.CACHED_PHOTO_URI);
- CACHED_PHOTO_URI = projectionList.size() - 1;
- }
- if (CompatUtils.isNCompatible()) {
- projectionList.add(CallsSdkCompat.POST_DIAL_DIGITS);
- POST_DIAL_DIGITS = projectionList.size() - 1;
- projectionList.add(CallsSdkCompat.VIA_NUMBER);
- VIA_NUMBER = projectionList.size() - 1;
- }
- _PROJECTION = projectionList.toArray(new String[projectionList.size()]);
- }
-
-}
diff --git a/src/com/android/dialer/calllog/CallLogQueryHandler.java b/src/com/android/dialer/calllog/CallLogQueryHandler.java
deleted file mode 100644
index cf86bad7f..000000000
--- a/src/com/android/dialer/calllog/CallLogQueryHandler.java
+++ /dev/null
@@ -1,354 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import android.content.AsyncQueryHandler;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabaseCorruptException;
-import android.database.sqlite.SQLiteDiskIOException;
-import android.database.sqlite.SQLiteException;
-import android.database.sqlite.SQLiteFullException;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.CallLog.Calls;
-import android.provider.VoicemailContract.Status;
-import android.provider.VoicemailContract.Voicemails;
-import android.util.Log;
-
-import com.android.contacts.common.compat.SdkVersionOverride;
-import com.android.contacts.common.database.NoNullCursorAsyncQueryHandler;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.dialer.database.VoicemailArchiveContract;
-import com.android.dialer.util.AppCompatConstants;
-import com.android.dialer.util.TelecomUtil;
-import com.android.dialer.voicemail.VoicemailStatusHelperImpl;
-
-import com.google.common.collect.Lists;
-
-import java.lang.ref.WeakReference;
-import java.util.List;
-
-/** Handles asynchronous queries to the call log. */
-public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler {
- private static final String TAG = "CallLogQueryHandler";
- private static final int NUM_LOGS_TO_DISPLAY = 1000;
-
- /** The token for the query to fetch the old entries from the call log. */
- private static final int QUERY_CALLLOG_TOKEN = 54;
- /** The token for the query to mark all missed calls as old after seeing the call log. */
- private static final int UPDATE_MARK_AS_OLD_TOKEN = 55;
- /** The token for the query to mark all missed calls as read after seeing the call log. */
- private static final int UPDATE_MARK_MISSED_CALL_AS_READ_TOKEN = 56;
- /** The token for the query to fetch voicemail status messages. */
- private static final int QUERY_VOICEMAIL_STATUS_TOKEN = 57;
- /** The token for the query to fetch the number of unread voicemails. */
- private static final int QUERY_VOICEMAIL_UNREAD_COUNT_TOKEN = 58;
- /** The token for the query to fetch the number of missed calls. */
- private static final int QUERY_MISSED_CALLS_UNREAD_COUNT_TOKEN = 59;
- /** The oken for the query to fetch the archived voicemails. */
- private static final int QUERY_VOICEMAIL_ARCHIVE = 60;
-
- private final int mLogLimit;
-
- /**
- * Call type similar to Calls.INCOMING_TYPE used to specify all types instead of one particular
- * type. Exception: excludes Calls.VOICEMAIL_TYPE.
- */
- public static final int CALL_TYPE_ALL = -1;
-
- private final WeakReference<Listener> mListener;
-
- private final Context mContext;
-
- /**
- * Simple handler that wraps background calls to catch
- * {@link SQLiteException}, such as when the disk is full.
- */
- protected class CatchingWorkerHandler extends AsyncQueryHandler.WorkerHandler {
- public CatchingWorkerHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- try {
- // Perform same query while catching any exceptions
- super.handleMessage(msg);
- } catch (SQLiteDiskIOException e) {
- Log.w(TAG, "Exception on background worker thread", e);
- } catch (SQLiteFullException e) {
- Log.w(TAG, "Exception on background worker thread", e);
- } catch (SQLiteDatabaseCorruptException e) {
- Log.w(TAG, "Exception on background worker thread", e);
- } catch (IllegalArgumentException e) {
- Log.w(TAG, "ContactsProvider not present on device", e);
- } catch (SecurityException e) {
- // Shouldn't happen if we are protecting the entry points correctly,
- // but just in case.
- Log.w(TAG, "No permission to access ContactsProvider.", e);
- }
- }
- }
-
- @Override
- protected Handler createHandler(Looper looper) {
- // Provide our special handler that catches exceptions
- return new CatchingWorkerHandler(looper);
- }
-
- public CallLogQueryHandler(Context context, ContentResolver contentResolver,
- Listener listener) {
- this(context, contentResolver, listener, -1);
- }
-
- public CallLogQueryHandler(Context context, ContentResolver contentResolver, Listener listener,
- int limit) {
- super(contentResolver);
- mContext = context.getApplicationContext();
- mListener = new WeakReference<Listener>(listener);
- mLogLimit = limit;
- }
-
- /**
- * Fetch all the voicemails in the voicemail archive.
- */
- public void fetchVoicemailArchive() {
- startQuery(QUERY_VOICEMAIL_ARCHIVE, null,
- VoicemailArchiveContract.VoicemailArchive.CONTENT_URI,
- null, VoicemailArchiveContract.VoicemailArchive.ARCHIVED + " = 1", null,
- VoicemailArchiveContract.VoicemailArchive.DATE + " DESC");
- }
-
-
- /**
- * Fetches the list of calls from the call log for a given type.
- * This call ignores the new or old state.
- * <p>
- * It will asynchronously update the content of the list view when the fetch completes.
- */
- public void fetchCalls(int callType, long newerThan) {
- cancelFetch();
- if (PermissionsUtil.hasPhonePermissions(mContext)) {
- fetchCalls(QUERY_CALLLOG_TOKEN, callType, false /* newOnly */, newerThan);
- } else {
- updateAdapterData(null);
- }
- }
-
- public void fetchCalls(int callType) {
- fetchCalls(callType, 0);
- }
-
- public void fetchVoicemailStatus() {
- if (TelecomUtil.hasReadWriteVoicemailPermissions(mContext)) {
- startQuery(QUERY_VOICEMAIL_STATUS_TOKEN, null, Status.CONTENT_URI,
- VoicemailStatusHelperImpl.PROJECTION, null, null, null);
- }
- }
-
- public void fetchVoicemailUnreadCount() {
- if (TelecomUtil.hasReadWriteVoicemailPermissions(mContext)) {
- // Only count voicemails that have not been read and have not been deleted.
- startQuery(QUERY_VOICEMAIL_UNREAD_COUNT_TOKEN, null, Voicemails.CONTENT_URI,
- new String[] { Voicemails._ID },
- Voicemails.IS_READ + "=0" + " AND " + Voicemails.DELETED + "=0", null, null);
- }
- }
-
- /** Fetches the list of calls in the call log. */
- private void fetchCalls(int token, int callType, boolean newOnly, long newerThan) {
- StringBuilder where = new StringBuilder();
- List<String> selectionArgs = Lists.newArrayList();
-
- // Always hide blocked calls.
- where.append("(").append(Calls.TYPE).append(" != ?)");
- selectionArgs.add(Integer.toString(AppCompatConstants.CALLS_BLOCKED_TYPE));
-
- // Ignore voicemails marked as deleted
- if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M)
- >= Build.VERSION_CODES.M) {
- where.append(" AND (").append(Voicemails.DELETED).append(" = 0)");
- }
-
- if (newOnly) {
- where.append(" AND (").append(Calls.NEW).append(" = 1)");
- }
-
- if (callType > CALL_TYPE_ALL) {
- where.append(" AND (").append(Calls.TYPE).append(" = ?)");
- selectionArgs.add(Integer.toString(callType));
- } else {
- where.append(" AND NOT ");
- where.append("(" + Calls.TYPE + " = " + AppCompatConstants.CALLS_VOICEMAIL_TYPE + ")");
- }
-
- if (newerThan > 0) {
- where.append(" AND (").append(Calls.DATE).append(" > ?)");
- selectionArgs.add(Long.toString(newerThan));
- }
-
- final int limit = (mLogLimit == -1) ? NUM_LOGS_TO_DISPLAY : mLogLimit;
- final String selection = where.length() > 0 ? where.toString() : null;
- Uri uri = TelecomUtil.getCallLogUri(mContext).buildUpon()
- .appendQueryParameter(Calls.LIMIT_PARAM_KEY, Integer.toString(limit))
- .build();
- startQuery(token, null, uri, CallLogQuery._PROJECTION, selection, selectionArgs.toArray(
- new String[selectionArgs.size()]), Calls.DEFAULT_SORT_ORDER);
- }
-
- /** Cancel any pending fetch request. */
- private void cancelFetch() {
- cancelOperation(QUERY_CALLLOG_TOKEN);
- }
-
- /** Updates all new calls to mark them as old. */
- public void markNewCallsAsOld() {
- if (!PermissionsUtil.hasPhonePermissions(mContext)) {
- return;
- }
- // Mark all "new" calls as not new anymore.
- StringBuilder where = new StringBuilder();
- where.append(Calls.NEW);
- where.append(" = 1");
-
- ContentValues values = new ContentValues(1);
- values.put(Calls.NEW, "0");
-
- startUpdate(UPDATE_MARK_AS_OLD_TOKEN, null, TelecomUtil.getCallLogUri(mContext),
- values, where.toString(), null);
- }
-
- /** Updates all missed calls to mark them as read. */
- public void markMissedCallsAsRead() {
- if (!PermissionsUtil.hasPhonePermissions(mContext)) {
- return;
- }
-
- ContentValues values = new ContentValues(1);
- values.put(Calls.IS_READ, "1");
-
- startUpdate(UPDATE_MARK_MISSED_CALL_AS_READ_TOKEN, null, Calls.CONTENT_URI, values,
- getUnreadMissedCallsQuery(), null);
- }
-
- /** Fetch all missed calls received since last time the tab was opened. */
- public void fetchMissedCallsUnreadCount() {
- if (!PermissionsUtil.hasPhonePermissions(mContext)) {
- return;
- }
-
- startQuery(QUERY_MISSED_CALLS_UNREAD_COUNT_TOKEN, null, Calls.CONTENT_URI,
- new String[]{Calls._ID}, getUnreadMissedCallsQuery(), null, null);
- }
-
-
- @Override
- protected synchronized void onNotNullableQueryComplete(int token, Object cookie,
- Cursor cursor) {
- if (cursor == null) {
- return;
- }
- try {
- if (token == QUERY_CALLLOG_TOKEN || token == QUERY_VOICEMAIL_ARCHIVE) {
- if (updateAdapterData(cursor)) {
- cursor = null;
- }
- } else if (token == QUERY_VOICEMAIL_STATUS_TOKEN) {
- updateVoicemailStatus(cursor);
- } else if (token == QUERY_VOICEMAIL_UNREAD_COUNT_TOKEN) {
- updateVoicemailUnreadCount(cursor);
- } else if (token == QUERY_MISSED_CALLS_UNREAD_COUNT_TOKEN) {
- updateMissedCallsUnreadCount(cursor);
- } else {
- Log.w(TAG, "Unknown query completed: ignoring: " + token);
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-
- /**
- * Updates the adapter in the call log fragment to show the new cursor data.
- * Returns true if the listener took ownership of the cursor.
- */
- private boolean updateAdapterData(Cursor cursor) {
- final Listener listener = mListener.get();
- if (listener != null) {
- return listener.onCallsFetched(cursor);
- }
- return false;
-
- }
-
- /**
- * @return Query string to get all unread missed calls.
- */
- private String getUnreadMissedCallsQuery() {
- StringBuilder where = new StringBuilder();
- where.append(Calls.IS_READ).append(" = 0 OR ").append(Calls.IS_READ).append(" IS NULL");
- where.append(" AND ");
- where.append(Calls.TYPE).append(" = ").append(Calls.MISSED_TYPE);
- return where.toString();
- }
-
- private void updateVoicemailStatus(Cursor statusCursor) {
- final Listener listener = mListener.get();
- if (listener != null) {
- listener.onVoicemailStatusFetched(statusCursor);
- }
- }
-
- private void updateVoicemailUnreadCount(Cursor statusCursor) {
- final Listener listener = mListener.get();
- if (listener != null) {
- listener.onVoicemailUnreadCountFetched(statusCursor);
- }
- }
-
- private void updateMissedCallsUnreadCount(Cursor statusCursor) {
- final Listener listener = mListener.get();
- if (listener != null) {
- listener.onMissedCallsUnreadCountFetched(statusCursor);
- }
- }
-
- /** Listener to completion of various queries. */
- public interface Listener {
- /** Called when {@link CallLogQueryHandler#fetchVoicemailStatus()} completes. */
- void onVoicemailStatusFetched(Cursor statusCursor);
-
- /** Called when {@link CallLogQueryHandler#fetchVoicemailUnreadCount()} completes. */
- void onVoicemailUnreadCountFetched(Cursor cursor);
-
- /** Called when {@link CallLogQueryHandler#fetchMissedCallsUnreadCount()} completes. */
- void onMissedCallsUnreadCountFetched(Cursor cursor);
-
- /**
- * Called when {@link CallLogQueryHandler#fetchCalls(int)} complete.
- * Returns true if takes ownership of cursor.
- */
- boolean onCallsFetched(Cursor combinedCursor);
- }
-}
diff --git a/src/com/android/dialer/calllog/CallLogReceiver.java b/src/com/android/dialer/calllog/CallLogReceiver.java
deleted file mode 100644
index fef76086c..000000000
--- a/src/com/android/dialer/calllog/CallLogReceiver.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.provider.VoicemailContract;
-import android.util.Log;
-
-/**
- * Receiver for call log events.
- * <p>
- * It is currently used to handle {@link VoicemailContract#ACTION_NEW_VOICEMAIL} and
- * {@link Intent#ACTION_BOOT_COMPLETED}.
- */
-public class CallLogReceiver extends BroadcastReceiver {
- private static final String TAG = "CallLogReceiver";
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (VoicemailContract.ACTION_NEW_VOICEMAIL.equals(intent.getAction())) {
- CallLogNotificationsService.updateVoicemailNotifications(context, intent.getData());
- } else if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
- CallLogNotificationsService.updateVoicemailNotifications(context, null);
- } else {
- Log.w(TAG, "onReceive: could not handle: " + intent);
- }
- }
-}
diff --git a/src/com/android/dialer/calllog/CallTypeHelper.java b/src/com/android/dialer/calllog/CallTypeHelper.java
deleted file mode 100644
index acc114c5c..000000000
--- a/src/com/android/dialer/calllog/CallTypeHelper.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import android.content.res.Resources;
-
-import com.android.dialer.R;
-import com.android.dialer.util.AppCompatConstants;
-
-/**
- * Helper class to perform operations related to call types.
- */
-public class CallTypeHelper {
- /** Name used to identify incoming calls. */
- private final CharSequence mIncomingName;
- /** Name used to identify outgoing calls. */
- private final CharSequence mOutgoingName;
- /** Name used to identify missed calls. */
- private final CharSequence mMissedName;
- /** Name used to identify incoming video calls. */
- private final CharSequence mIncomingVideoName;
- /** Name used to identify outgoing video calls. */
- private final CharSequence mOutgoingVideoName;
- /** Name used to identify missed video calls. */
- private final CharSequence mMissedVideoName;
- /** Name used to identify voicemail calls. */
- private final CharSequence mVoicemailName;
- /** Name used to identify rejected calls. */
- private final CharSequence mRejectedName;
- /** Name used to identify blocked calls. */
- private final CharSequence mBlockedName;
- /** Color used to identify new missed calls. */
- private final int mNewMissedColor;
- /** Color used to identify new voicemail calls. */
- private final int mNewVoicemailColor;
-
- public CallTypeHelper(Resources resources) {
- // Cache these values so that we do not need to look them up each time.
- mIncomingName = resources.getString(R.string.type_incoming);
- mOutgoingName = resources.getString(R.string.type_outgoing);
- mMissedName = resources.getString(R.string.type_missed);
- mIncomingVideoName = resources.getString(R.string.type_incoming_video);
- mOutgoingVideoName = resources.getString(R.string.type_outgoing_video);
- mMissedVideoName = resources.getString(R.string.type_missed_video);
- mVoicemailName = resources.getString(R.string.type_voicemail);
- mRejectedName = resources.getString(R.string.type_rejected);
- mBlockedName = resources.getString(R.string.type_blocked);
- mNewMissedColor = resources.getColor(R.color.call_log_missed_call_highlight_color);
- mNewVoicemailColor = resources.getColor(R.color.call_log_voicemail_highlight_color);
- }
-
- /** Returns the text used to represent the given call type. */
- public CharSequence getCallTypeText(int callType, boolean isVideoCall) {
- switch (callType) {
- case AppCompatConstants.CALLS_INCOMING_TYPE:
- if (isVideoCall) {
- return mIncomingVideoName;
- } else {
- return mIncomingName;
- }
-
- case AppCompatConstants.CALLS_OUTGOING_TYPE:
- if (isVideoCall) {
- return mOutgoingVideoName;
- } else {
- return mOutgoingName;
- }
-
- case AppCompatConstants.CALLS_MISSED_TYPE:
- if (isVideoCall) {
- return mMissedVideoName;
- } else {
- return mMissedName;
- }
-
- case AppCompatConstants.CALLS_VOICEMAIL_TYPE:
- return mVoicemailName;
-
- case AppCompatConstants.CALLS_REJECTED_TYPE:
- return mRejectedName;
-
- case AppCompatConstants.CALLS_BLOCKED_TYPE:
- return mBlockedName;
-
- default:
- return mMissedName;
- }
- }
-
- /** Returns the color used to highlight the given call type, null if not highlight is needed. */
- public Integer getHighlightedColor(int callType) {
- switch (callType) {
- case AppCompatConstants.CALLS_INCOMING_TYPE:
- // New incoming calls are not highlighted.
- return null;
-
- case AppCompatConstants.CALLS_OUTGOING_TYPE:
- // New outgoing calls are not highlighted.
- return null;
-
- case AppCompatConstants.CALLS_MISSED_TYPE:
- return mNewMissedColor;
-
- case AppCompatConstants.CALLS_VOICEMAIL_TYPE:
- return mNewVoicemailColor;
-
- default:
- // Don't highlight calls of unknown types. They are treated as missed calls by
- // the rest of the UI, but since they will never be marked as read by
- // {@link CallLogQueryHandler}, just don't ever highlight them anyway.
- return null;
- }
- }
-
- public static boolean isMissedCallType(int callType) {
- return (callType != AppCompatConstants.CALLS_INCOMING_TYPE
- && callType != AppCompatConstants.CALLS_OUTGOING_TYPE
- && callType != AppCompatConstants.CALLS_VOICEMAIL_TYPE);
- }
-}
diff --git a/src/com/android/dialer/calllog/CallTypeIconsView.java b/src/com/android/dialer/calllog/CallTypeIconsView.java
deleted file mode 100644
index 14748433c..000000000
--- a/src/com/android/dialer/calllog/CallTypeIconsView.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.view.View;
-
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.contacts.common.util.BitmapUtil;
-import com.android.dialer.R;
-import com.android.dialer.util.AppCompatConstants;
-import com.google.common.collect.Lists;
-
-import java.util.List;
-
-/**
- * View that draws one or more symbols for different types of calls (missed calls, outgoing etc).
- * The symbols are set up horizontally. As this view doesn't create subviews, it is better suited
- * for ListView-recycling that a regular LinearLayout using ImageViews.
- */
-public class CallTypeIconsView extends View {
- private List<Integer> mCallTypes = Lists.newArrayListWithCapacity(3);
- private boolean mShowVideo = false;
- private int mWidth;
- private int mHeight;
-
- private static Resources sResources;
-
- public CallTypeIconsView(Context context) {
- this(context, null);
- }
-
- public CallTypeIconsView(Context context, AttributeSet attrs) {
- super(context, attrs);
- if (sResources == null) {
- sResources = new Resources(context);
- }
- }
-
- public void clear() {
- mCallTypes.clear();
- mWidth = 0;
- mHeight = 0;
- invalidate();
- }
-
- public void add(int callType) {
- mCallTypes.add(callType);
-
- final Drawable drawable = getCallTypeDrawable(callType);
- mWidth += drawable.getIntrinsicWidth() + sResources.iconMargin;
- mHeight = Math.max(mHeight, drawable.getIntrinsicHeight());
- invalidate();
- }
-
- /**
- * Determines whether the video call icon will be shown.
- *
- * @param showVideo True where the video icon should be shown.
- */
- public void setShowVideo(boolean showVideo) {
- mShowVideo = showVideo;
- if (showVideo) {
- mWidth += sResources.videoCall.getIntrinsicWidth();
- mHeight = Math.max(mHeight, sResources.videoCall.getIntrinsicHeight());
- invalidate();
- }
- }
-
- /**
- * Determines if the video icon should be shown.
- *
- * @return True if the video icon should be shown.
- */
- public boolean isVideoShown() {
- return mShowVideo;
- }
-
- @NeededForTesting
- public int getCount() {
- return mCallTypes.size();
- }
-
- @NeededForTesting
- public int getCallType(int index) {
- return mCallTypes.get(index);
- }
-
- private Drawable getCallTypeDrawable(int callType) {
- switch (callType) {
- case AppCompatConstants.CALLS_INCOMING_TYPE:
- return sResources.incoming;
- case AppCompatConstants.CALLS_OUTGOING_TYPE:
- return sResources.outgoing;
- case AppCompatConstants.CALLS_MISSED_TYPE:
- return sResources.missed;
- case AppCompatConstants.CALLS_VOICEMAIL_TYPE:
- return sResources.voicemail;
- case AppCompatConstants.CALLS_BLOCKED_TYPE:
- return sResources.blocked;
- default:
- // It is possible for users to end up with calls with unknown call types in their
- // call history, possibly due to 3rd party call log implementations (e.g. to
- // distinguish between rejected and missed calls). Instead of crashing, just
- // assume that all unknown call types are missed calls.
- return sResources.missed;
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- setMeasuredDimension(mWidth, mHeight);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- int left = 0;
- for (Integer callType : mCallTypes) {
- final Drawable drawable = getCallTypeDrawable(callType);
- final int right = left + drawable.getIntrinsicWidth();
- drawable.setBounds(left, 0, right, drawable.getIntrinsicHeight());
- drawable.draw(canvas);
- left = right + sResources.iconMargin;
- }
-
- // If showing the video call icon, draw it scaled appropriately.
- if (mShowVideo) {
- final Drawable drawable = sResources.videoCall;
- final int right = left + sResources.videoCall.getIntrinsicWidth();
- drawable.setBounds(left, 0, right, sResources.videoCall.getIntrinsicHeight());
- drawable.draw(canvas);
- }
- }
-
- private static class Resources {
-
- // Drawable representing an incoming answered call.
- public final Drawable incoming;
-
- // Drawable respresenting an outgoing call.
- public final Drawable outgoing;
-
- // Drawable representing an incoming missed call.
- public final Drawable missed;
-
- // Drawable representing a voicemail.
- public final Drawable voicemail;
-
- // Drawable representing a blocked call.
- public final Drawable blocked;
-
- // Drawable repesenting a video call.
- public final Drawable videoCall;
-
- /**
- * The margin to use for icons.
- */
- public final int iconMargin;
-
- /**
- * Configures the call icon drawables.
- * A single white call arrow which points down and left is used as a basis for all of the
- * call arrow icons, applying rotation and colors as needed.
- *
- * @param context The current context.
- */
- public Resources(Context context) {
- final android.content.res.Resources r = context.getResources();
-
- incoming = r.getDrawable(R.drawable.ic_call_arrow);
- incoming.setColorFilter(r.getColor(R.color.answered_call), PorterDuff.Mode.MULTIPLY);
-
- // Create a rotated instance of the call arrow for outgoing calls.
- outgoing = BitmapUtil.getRotatedDrawable(r, R.drawable.ic_call_arrow, 180f);
- outgoing.setColorFilter(r.getColor(R.color.answered_call), PorterDuff.Mode.MULTIPLY);
-
- // Need to make a copy of the arrow drawable, otherwise the same instance colored
- // above will be recolored here.
- missed = r.getDrawable(R.drawable.ic_call_arrow).mutate();
- missed.setColorFilter(r.getColor(R.color.missed_call), PorterDuff.Mode.MULTIPLY);
-
- voicemail = r.getDrawable(R.drawable.ic_call_voicemail_holo_dark);
-
- blocked = getScaledBitmap(context, R.drawable.ic_block_24dp);
- blocked.setColorFilter(r.getColor(R.color.blocked_call), PorterDuff.Mode.MULTIPLY);
-
- videoCall = getScaledBitmap(context, R.drawable.ic_videocam_24dp);
- videoCall.setColorFilter(r.getColor(R.color.dialtacts_secondary_text_color),
- PorterDuff.Mode.MULTIPLY);
-
- iconMargin = r.getDimensionPixelSize(R.dimen.call_log_icon_margin);
- }
-
- // Gets the icon, scaled to the height of the call type icons. This helps display all the
- // icons to be the same height, while preserving their width aspect ratio.
- private Drawable getScaledBitmap(Context context, int resourceId) {
- Bitmap icon = BitmapFactory.decodeResource(context.getResources(), resourceId);
- int scaledHeight =
- context.getResources().getDimensionPixelSize(R.dimen.call_type_icon_size);
- int scaledWidth = (int) ((float) icon.getWidth()
- * ((float) scaledHeight / (float) icon.getHeight()));
- Bitmap scaledIcon = Bitmap.createScaledBitmap(icon, scaledWidth, scaledHeight, false);
- return new BitmapDrawable(context.getResources(), scaledIcon);
- }
- }
-}
diff --git a/src/com/android/dialer/calllog/ClearCallLogDialog.java b/src/com/android/dialer/calllog/ClearCallLogDialog.java
deleted file mode 100644
index bef5010ec..000000000
--- a/src/com/android/dialer/calllog/ClearCallLogDialog.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.FragmentManager;
-import android.app.ProgressDialog;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.provider.CallLog.Calls;
-
-import com.android.dialer.R;
-import com.android.dialer.service.CachedNumberLookupService;
-import com.android.dialerbind.ObjectFactory;
-
-/**
- * Dialog that clears the call log after confirming with the user
- */
-public class ClearCallLogDialog extends DialogFragment {
- private static final CachedNumberLookupService mCachedNumberLookupService =
- ObjectFactory.newCachedNumberLookupService();
-
- /** Preferred way to show this dialog */
- public static void show(FragmentManager fragmentManager) {
- ClearCallLogDialog dialog = new ClearCallLogDialog();
- dialog.show(fragmentManager, "deleteCallLog");
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- final ContentResolver resolver = getActivity().getContentResolver();
- final Context context = getActivity().getApplicationContext();
- final OnClickListener okListener = new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- final ProgressDialog progressDialog = ProgressDialog.show(getActivity(),
- getString(R.string.clearCallLogProgress_title),
- "", true, false);
- progressDialog.setOwnerActivity(getActivity());
- final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- resolver.delete(Calls.CONTENT_URI, null, null);
- if (mCachedNumberLookupService != null) {
- mCachedNumberLookupService.clearAllCacheEntries(context);
- }
- return null;
- }
- @Override
- protected void onPostExecute(Void result) {
- final Activity activity = progressDialog.getOwnerActivity();
-
- if (activity == null || activity.isDestroyed() || activity.isFinishing()) {
- return;
- }
-
- if (progressDialog != null && progressDialog.isShowing()) {
- progressDialog.dismiss();
- }
- }
- };
- // TODO: Once we have the API, we should configure this ProgressDialog
- // to only show up after a certain time (e.g. 150ms)
- progressDialog.show();
- task.execute();
- }
- };
- return new AlertDialog.Builder(getActivity())
- .setTitle(R.string.clearCallLogConfirmation_title)
- .setIconAttribute(android.R.attr.alertDialogIcon)
- .setMessage(R.string.clearCallLogConfirmation)
- .setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(android.R.string.ok, okListener)
- .setCancelable(true)
- .create();
- }
-}
diff --git a/src/com/android/dialer/calllog/ContactInfo.java b/src/com/android/dialer/calllog/ContactInfo.java
deleted file mode 100644
index 8fe4964bc..000000000
--- a/src/com/android/dialer/calllog/ContactInfo.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import android.net.Uri;
-import android.text.TextUtils;
-
-import com.android.contacts.common.ContactsUtils.UserType;
-import com.android.contacts.common.util.UriUtils;
-import com.google.common.base.Objects;
-
-/**
- * Information for a contact as needed by the Call Log.
- */
-public class ContactInfo {
- public Uri lookupUri;
-
- /**
- * Contact lookup key. Note this may be a lookup key for a corp contact, in which case
- * "lookup by lookup key" doesn't work on the personal profile.
- */
- public String lookupKey;
- public String name;
- public String nameAlternative;
- public int type;
- public String label;
- public String number;
- public String formattedNumber;
- /*
- * ContactInfo.normalizedNumber is a column value returned by PhoneLookup query. By definition,
- * it's E164 representation.
- * http://developer.android.com/reference/android/provider/ContactsContract.PhoneLookupColumns.
- * html#NORMALIZED_NUMBER.
- *
- * The fallback value, when PhoneLookup fails or else, should be either null or
- * PhoneNumberUtils.formatNumberToE164.
- */
- public String normalizedNumber;
- /** The photo for the contact, if available. */
- public long photoId;
- /** The high-res photo for the contact, if available. */
- public Uri photoUri;
- public boolean isBadData;
- public String objectId;
- public @UserType long userType;
-
- public static ContactInfo EMPTY = new ContactInfo();
-
- public int sourceType = 0;
-
- @Override
- public int hashCode() {
- // Uses only name and contactUri to determine hashcode.
- // This should be sufficient to have a reasonable distribution of hash codes.
- // Moreover, there should be no two people with the same lookupUri.
- final int prime = 31;
- int result = 1;
- result = prime * result + ((lookupUri == null) ? 0 : lookupUri.hashCode());
- result = prime * result + ((name == null) ? 0 : name.hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (getClass() != obj.getClass()) return false;
- ContactInfo other = (ContactInfo) obj;
- if (!UriUtils.areEqual(lookupUri, other.lookupUri)) return false;
- if (!TextUtils.equals(name, other.name)) return false;
- if (!TextUtils.equals(nameAlternative, other.nameAlternative)) return false;
- if (type != other.type) return false;
- if (!TextUtils.equals(label, other.label)) return false;
- if (!TextUtils.equals(number, other.number)) return false;
- if (!TextUtils.equals(formattedNumber, other.formattedNumber)) return false;
- if (!TextUtils.equals(normalizedNumber, other.normalizedNumber)) return false;
- if (photoId != other.photoId) return false;
- if (!UriUtils.areEqual(photoUri, other.photoUri)) return false;
- if (!TextUtils.equals(objectId, other.objectId)) return false;
- if (userType != other.userType) return false;
- return true;
- }
-
- @Override
- public String toString() {
- return Objects.toStringHelper(this).add("lookupUri", lookupUri).add("name", name)
- .add("nameAlternative", nameAlternative)
- .add("type", type).add("label", label)
- .add("number", number).add("formattedNumber",formattedNumber)
- .add("normalizedNumber", normalizedNumber).add("photoId", photoId)
- .add("photoUri", photoUri).add("objectId", objectId)
- .add("userType",userType).toString();
- }
-}
diff --git a/src/com/android/dialer/calllog/ContactInfoHelper.java b/src/com/android/dialer/calllog/ContactInfoHelper.java
deleted file mode 100644
index d9898ab94..000000000
--- a/src/com/android/dialer/calllog/ContactInfoHelper.java
+++ /dev/null
@@ -1,499 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import com.google.common.primitives.Longs;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteFullException;
-import android.net.Uri;
-import android.provider.CallLog.Calls;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.DisplayNameSources;
-import android.provider.ContactsContract.PhoneLookup;
-import android.support.annotation.Nullable;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.common.ContactsUtils;
-import com.android.contacts.common.ContactsUtils.UserType;
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.DirectoryCompat;
-import com.android.contacts.common.util.Constants;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.contacts.common.util.PhoneNumberHelper;
-import com.android.contacts.common.util.UriUtils;
-import com.android.dialer.compat.DialerCompatUtils;
-import com.android.dialer.service.CachedNumberLookupService;
-import com.android.dialer.service.CachedNumberLookupService.CachedContactInfo;
-import com.android.dialer.util.TelecomUtil;
-import com.android.dialerbind.ObjectFactory;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-/**
- * Utility class to look up the contact information for a given number.
- */
-public class ContactInfoHelper {
- private static final String TAG = ContactInfoHelper.class.getSimpleName();
-
- private final Context mContext;
- private final String mCurrentCountryIso;
-
- private static final CachedNumberLookupService mCachedNumberLookupService =
- ObjectFactory.newCachedNumberLookupService();
-
- public ContactInfoHelper(Context context, String currentCountryIso) {
- mContext = context;
- mCurrentCountryIso = currentCountryIso;
- }
-
- /**
- * Returns the contact information for the given number.
- * <p>
- * If the number does not match any contact, returns a contact info containing only the number
- * and the formatted number.
- * <p>
- * If an error occurs during the lookup, it returns null.
- *
- * @param number the number to look up
- * @param countryIso the country associated with this number
- */
- @Nullable
- public ContactInfo lookupNumber(String number, String countryIso) {
- if (TextUtils.isEmpty(number)) {
- return null;
- }
-
- ContactInfo info;
-
- if (PhoneNumberHelper.isUriNumber(number)) {
- // The number is a SIP address..
- info = lookupContactFromUri(getContactInfoLookupUri(number), true);
- if (info == null || info == ContactInfo.EMPTY) {
- // If lookup failed, check if the "username" of the SIP address is a phone number.
- String username = PhoneNumberHelper.getUsernameFromUriNumber(number);
- if (PhoneNumberUtils.isGlobalPhoneNumber(username)) {
- info = queryContactInfoForPhoneNumber(username, countryIso, true);
- }
- }
- } else {
- // Look for a contact that has the given phone number.
- info = queryContactInfoForPhoneNumber(number, countryIso, false);
- }
-
- final ContactInfo updatedInfo;
- if (info == null) {
- // The lookup failed.
- updatedInfo = null;
- } else {
- // If we did not find a matching contact, generate an empty contact info for the number.
- if (info == ContactInfo.EMPTY) {
- // Did not find a matching contact.
- updatedInfo = new ContactInfo();
- updatedInfo.number = number;
- updatedInfo.formattedNumber = formatPhoneNumber(number, null, countryIso);
- updatedInfo.normalizedNumber = PhoneNumberUtils.formatNumberToE164(
- number, countryIso);
- updatedInfo.lookupUri = createTemporaryContactUri(updatedInfo.formattedNumber);
- } else {
- updatedInfo = info;
- }
- }
- return updatedInfo;
- }
-
- /**
- * Creates a JSON-encoded lookup uri for a unknown number without an associated contact
- *
- * @param number - Unknown phone number
- * @return JSON-encoded URI that can be used to perform a lookup when clicking on the quick
- * contact card.
- */
- private static Uri createTemporaryContactUri(String number) {
- try {
- final JSONObject contactRows = new JSONObject().put(Phone.CONTENT_ITEM_TYPE,
- new JSONObject().put(Phone.NUMBER, number).put(Phone.TYPE, Phone.TYPE_CUSTOM));
-
- final String jsonString = new JSONObject().put(Contacts.DISPLAY_NAME, number)
- .put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.PHONE)
- .put(Contacts.CONTENT_ITEM_TYPE, contactRows).toString();
-
- return Contacts.CONTENT_LOOKUP_URI
- .buildUpon()
- .appendPath(Constants.LOOKUP_URI_ENCODED)
- .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
- String.valueOf(Long.MAX_VALUE))
- .encodedFragment(jsonString)
- .build();
- } catch (JSONException e) {
- return null;
- }
- }
-
- /**
- * Looks up a contact using the given URI.
- * <p>
- * It returns null if an error occurs, {@link ContactInfo#EMPTY} if no matching contact is
- * found, or the {@link ContactInfo} for the given contact.
- * <p>
- * The {@link ContactInfo#formattedNumber} field is always set to {@code null} in the returned
- * value.
- */
- ContactInfo lookupContactFromUri(Uri uri, boolean isSip) {
- if (uri == null) {
- return null;
- }
- if (!PermissionsUtil.hasContactsPermissions(mContext)) {
- return ContactInfo.EMPTY;
- }
-
- final String directory = uri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
- final Long directoryId = directory == null ? null : Longs.tryParse(directory);
-
- Cursor phoneLookupCursor = null;
- try {
- String[] projection = PhoneQuery.getPhoneLookupProjection(uri);
- phoneLookupCursor = mContext.getContentResolver().query(uri, projection, null, null,
- null);
- } catch (NullPointerException e) {
- // Trap NPE from pre-N CP2
- return null;
- }
- if (phoneLookupCursor == null) {
- return null;
- }
-
- try {
- if (!phoneLookupCursor.moveToFirst()) {
- return ContactInfo.EMPTY;
- }
- String lookupKey = phoneLookupCursor.getString(PhoneQuery.LOOKUP_KEY);
- ContactInfo contactInfo = createPhoneLookupContactInfo(phoneLookupCursor, lookupKey);
- contactInfo.nameAlternative = lookUpDisplayNameAlternative(mContext, lookupKey,
- contactInfo.userType, directoryId);
- return contactInfo;
- } finally {
- phoneLookupCursor.close();
- }
- }
-
- private ContactInfo createPhoneLookupContactInfo(Cursor phoneLookupCursor, String lookupKey) {
- ContactInfo info = new ContactInfo();
- info.lookupKey = lookupKey;
- info.lookupUri = Contacts.getLookupUri(phoneLookupCursor.getLong(PhoneQuery.PERSON_ID),
- lookupKey);
- info.name = phoneLookupCursor.getString(PhoneQuery.NAME);
- info.type = phoneLookupCursor.getInt(PhoneQuery.PHONE_TYPE);
- info.label = phoneLookupCursor.getString(PhoneQuery.LABEL);
- info.number = phoneLookupCursor.getString(PhoneQuery.MATCHED_NUMBER);
- info.normalizedNumber = phoneLookupCursor.getString(PhoneQuery.NORMALIZED_NUMBER);
- info.photoId = phoneLookupCursor.getLong(PhoneQuery.PHOTO_ID);
- info.photoUri = UriUtils.parseUriOrNull(phoneLookupCursor.getString(PhoneQuery.PHOTO_URI));
- info.formattedNumber = null;
- info.userType = ContactsUtils.determineUserType(null,
- phoneLookupCursor.getLong(PhoneQuery.PERSON_ID));
-
- return info;
- }
-
- public static String lookUpDisplayNameAlternative(Context context, String lookupKey,
- @UserType long userType, @Nullable Long directoryId) {
- // Query {@link Contacts#CONTENT_LOOKUP_URI} directly with work lookup key is not allowed.
- if (lookupKey == null || userType == ContactsUtils.USER_TYPE_WORK) {
- return null;
- }
-
- if (directoryId != null) {
- // Query {@link Contacts#CONTENT_LOOKUP_URI} with work lookup key is not allowed.
- if (DirectoryCompat.isEnterpriseDirectoryId(directoryId)) {
- return null;
- }
-
- // Skip this to avoid an extra remote network call for alternative name
- if (DirectoryCompat.isRemoteDirectoryId(directoryId)) {
- return null;
- }
- }
-
- final Uri uri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey);
- Cursor cursor = null;
- try {
- cursor = context.getContentResolver().query(uri,
- PhoneQuery.DISPLAY_NAME_ALTERNATIVE_PROJECTION, null, null, null);
-
- if (cursor != null && cursor.moveToFirst()) {
- return cursor.getString(PhoneQuery.NAME_ALTERNATIVE);
- }
- } catch (IllegalArgumentException e) {
- // Avoid dialer crash when lookup key is not valid
- Log.e(TAG, "IllegalArgumentException in lookUpDisplayNameAlternative", e);
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- return null;
- }
-
- /**
- * Determines the contact information for the given phone number.
- * <p>
- * It returns the contact info if found.
- * <p>
- * If no contact corresponds to the given phone number, returns {@link ContactInfo#EMPTY}.
- * <p>
- * If the lookup fails for some other reason, it returns null.
- */
- private ContactInfo queryContactInfoForPhoneNumber(String number, String countryIso,
- boolean isSip) {
- if (TextUtils.isEmpty(number)) {
- return null;
- }
-
- ContactInfo info = lookupContactFromUri(getContactInfoLookupUri(number), isSip);
- if (info != null && info != ContactInfo.EMPTY) {
- info.formattedNumber = formatPhoneNumber(number, null, countryIso);
- } else if (mCachedNumberLookupService != null) {
- CachedContactInfo cacheInfo =
- mCachedNumberLookupService.lookupCachedContactFromNumber(mContext, number);
- if (cacheInfo != null) {
- info = cacheInfo.getContactInfo().isBadData ? null : cacheInfo.getContactInfo();
- } else {
- info = null;
- }
- }
- return info;
- }
-
- /**
- * Format the given phone number
- *
- * @param number the number to be formatted.
- * @param normalizedNumber the normalized number of the given number.
- * @param countryIso the ISO 3166-1 two letters country code, the country's convention will be
- * used to format the number if the normalized phone is null.
- *
- * @return the formatted number, or the given number if it was formatted.
- */
- private String formatPhoneNumber(String number, String normalizedNumber, String countryIso) {
- if (TextUtils.isEmpty(number)) {
- return "";
- }
- // If "number" is really a SIP address, don't try to do any formatting at all.
- if (PhoneNumberHelper.isUriNumber(number)) {
- return number;
- }
- if (TextUtils.isEmpty(countryIso)) {
- countryIso = mCurrentCountryIso;
- }
- return PhoneNumberUtils.formatNumber(number, normalizedNumber, countryIso);
- }
-
- /**
- * Stores differences between the updated contact info and the current call log contact info.
- *
- * @param number The number of the contact.
- * @param countryIso The country associated with this number.
- * @param updatedInfo The updated contact info.
- * @param callLogInfo The call log entry's current contact info.
- */
- public void updateCallLogContactInfo(String number, String countryIso, ContactInfo updatedInfo,
- ContactInfo callLogInfo) {
- if (!PermissionsUtil.hasPermission(mContext, android.Manifest.permission.WRITE_CALL_LOG)) {
- return;
- }
-
- final ContentValues values = new ContentValues();
- boolean needsUpdate = false;
-
- if (callLogInfo != null) {
- if (!TextUtils.equals(updatedInfo.name, callLogInfo.name)) {
- values.put(Calls.CACHED_NAME, updatedInfo.name);
- needsUpdate = true;
- }
-
- if (updatedInfo.type != callLogInfo.type) {
- values.put(Calls.CACHED_NUMBER_TYPE, updatedInfo.type);
- needsUpdate = true;
- }
-
- if (!TextUtils.equals(updatedInfo.label, callLogInfo.label)) {
- values.put(Calls.CACHED_NUMBER_LABEL, updatedInfo.label);
- needsUpdate = true;
- }
-
- if (!UriUtils.areEqual(updatedInfo.lookupUri, callLogInfo.lookupUri)) {
- values.put(Calls.CACHED_LOOKUP_URI, UriUtils.uriToString(updatedInfo.lookupUri));
- needsUpdate = true;
- }
-
- // Only replace the normalized number if the new updated normalized number isn't empty.
- if (!TextUtils.isEmpty(updatedInfo.normalizedNumber) &&
- !TextUtils.equals(updatedInfo.normalizedNumber, callLogInfo.normalizedNumber)) {
- values.put(Calls.CACHED_NORMALIZED_NUMBER, updatedInfo.normalizedNumber);
- needsUpdate = true;
- }
-
- if (!TextUtils.equals(updatedInfo.number, callLogInfo.number)) {
- values.put(Calls.CACHED_MATCHED_NUMBER, updatedInfo.number);
- needsUpdate = true;
- }
-
- if (updatedInfo.photoId != callLogInfo.photoId) {
- values.put(Calls.CACHED_PHOTO_ID, updatedInfo.photoId);
- needsUpdate = true;
- }
-
- final Uri updatedPhotoUriContactsOnly =
- UriUtils.nullForNonContactsUri(updatedInfo.photoUri);
- if (DialerCompatUtils.isCallsCachedPhotoUriCompatible() &&
- !UriUtils.areEqual(updatedPhotoUriContactsOnly, callLogInfo.photoUri)) {
- values.put(Calls.CACHED_PHOTO_URI,
- UriUtils.uriToString(updatedPhotoUriContactsOnly));
- needsUpdate = true;
- }
-
- if (!TextUtils.equals(updatedInfo.formattedNumber, callLogInfo.formattedNumber)) {
- values.put(Calls.CACHED_FORMATTED_NUMBER, updatedInfo.formattedNumber);
- needsUpdate = true;
- }
- } else {
- // No previous values, store all of them.
- values.put(Calls.CACHED_NAME, updatedInfo.name);
- values.put(Calls.CACHED_NUMBER_TYPE, updatedInfo.type);
- values.put(Calls.CACHED_NUMBER_LABEL, updatedInfo.label);
- values.put(Calls.CACHED_LOOKUP_URI, UriUtils.uriToString(updatedInfo.lookupUri));
- values.put(Calls.CACHED_MATCHED_NUMBER, updatedInfo.number);
- values.put(Calls.CACHED_NORMALIZED_NUMBER, updatedInfo.normalizedNumber);
- values.put(Calls.CACHED_PHOTO_ID, updatedInfo.photoId);
- if (DialerCompatUtils.isCallsCachedPhotoUriCompatible()) {
- values.put(Calls.CACHED_PHOTO_URI, UriUtils.uriToString(
- UriUtils.nullForNonContactsUri(updatedInfo.photoUri)));
- }
- values.put(Calls.CACHED_FORMATTED_NUMBER, updatedInfo.formattedNumber);
- needsUpdate = true;
- }
-
- if (!needsUpdate) {
- return;
- }
-
- try {
- if (countryIso == null) {
- mContext.getContentResolver().update(
- TelecomUtil.getCallLogUri(mContext),
- values,
- Calls.NUMBER + " = ? AND " + Calls.COUNTRY_ISO + " IS NULL",
- new String[]{ number });
- } else {
- mContext.getContentResolver().update(
- TelecomUtil.getCallLogUri(mContext),
- values,
- Calls.NUMBER + " = ? AND " + Calls.COUNTRY_ISO + " = ?",
- new String[]{ number, countryIso });
- }
- } catch (SQLiteFullException e) {
- Log.e(TAG, "Unable to update contact info in call log db", e);
- }
- }
-
- public static Uri getContactInfoLookupUri(String number) {
- return getContactInfoLookupUri(number, -1);
- }
-
- public static Uri getContactInfoLookupUri(String number, long directoryId) {
- // Get URI for the number in the PhoneLookup table, with a parameter to indicate whether
- // the number is a SIP number.
- Uri uri = PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI;
- if (!ContactsUtils.FLAG_N_FEATURE) {
- if (directoryId != -1) {
- // ENTERPRISE_CONTENT_FILTER_URI in M doesn't support directory lookup
- uri = PhoneLookup.CONTENT_FILTER_URI;
- } else {
- // b/25900607 in M. PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, encodes twice.
- number = Uri.encode(number);
- }
- }
- Uri.Builder builder = uri.buildUpon()
- .appendPath(number)
- .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS,
- String.valueOf(PhoneNumberHelper.isUriNumber(number)));
- if (directoryId != -1) {
- builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
- String.valueOf(directoryId));
- }
- return builder.build();
- }
-
- /**
- * Returns the contact information stored in an entry of the call log.
- *
- * @param c A cursor pointing to an entry in the call log.
- */
- public static ContactInfo getContactInfo(Cursor c) {
- ContactInfo info = new ContactInfo();
- info.lookupUri = UriUtils.parseUriOrNull(c.getString(CallLogQuery.CACHED_LOOKUP_URI));
- info.name = c.getString(CallLogQuery.CACHED_NAME);
- info.type = c.getInt(CallLogQuery.CACHED_NUMBER_TYPE);
- info.label = c.getString(CallLogQuery.CACHED_NUMBER_LABEL);
- String matchedNumber = c.getString(CallLogQuery.CACHED_MATCHED_NUMBER);
- String postDialDigits = CompatUtils.isNCompatible()
- ? c.getString(CallLogQuery.POST_DIAL_DIGITS) : "";
- info.number = (matchedNumber == null) ?
- c.getString(CallLogQuery.NUMBER) + postDialDigits : matchedNumber;
-
- info.normalizedNumber = c.getString(CallLogQuery.CACHED_NORMALIZED_NUMBER);
- info.photoId = c.getLong(CallLogQuery.CACHED_PHOTO_ID);
- info.photoUri = DialerCompatUtils.isCallsCachedPhotoUriCompatible() ?
- UriUtils.nullForNonContactsUri(
- UriUtils.parseUriOrNull(c.getString(CallLogQuery.CACHED_PHOTO_URI)))
- : null;
- info.formattedNumber = c.getString(CallLogQuery.CACHED_FORMATTED_NUMBER);
-
- return info;
- }
-
- /**
- * Given a contact's sourceType, return true if the contact is a business
- *
- * @param sourceType sourceType of the contact. This is usually populated by
- * {@link #mCachedNumberLookupService}.
- */
- public boolean isBusiness(int sourceType) {
- return mCachedNumberLookupService != null
- && mCachedNumberLookupService.isBusiness(sourceType);
- }
-
- /**
- * This function looks at a contact's source and determines if the user can
- * mark caller ids from this source as invalid.
- *
- * @param sourceType The source type to be checked
- * @param objectId The ID of the Contact object.
- * @return true if contacts from this source can be marked with an invalid caller id
- */
- public boolean canReportAsInvalid(int sourceType, String objectId) {
- return mCachedNumberLookupService != null
- && mCachedNumberLookupService.canReportAsInvalid(sourceType, objectId);
- }
-}
diff --git a/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java b/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
deleted file mode 100644
index de6fc6a3d..000000000
--- a/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import com.google.common.collect.Maps;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.support.annotation.Nullable;
-import android.support.v4.util.Pair;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.common.ContactsUtils;
-import com.android.contacts.common.compat.TelephonyManagerCompat;
-import com.android.contacts.common.util.ContactDisplayUtils;
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.R;
-import com.android.dialer.calllog.CallLogNotificationsHelper.NewCall;
-import com.android.dialer.filterednumber.FilteredNumbersUtil;
-import com.android.dialer.list.ListsFragment;
-import com.android.dialer.util.TelecomUtil;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Shows a voicemail notification in the status bar.
- */
-public class DefaultVoicemailNotifier {
- public static final String TAG = "VoicemailNotifier";
-
- /** The tag used to identify notifications from this class. */
- private static final String NOTIFICATION_TAG = "DefaultVoicemailNotifier";
- /** The identifier of the notification of new voicemails. */
- private static final int NOTIFICATION_ID = 1;
-
- /** The singleton instance of {@link DefaultVoicemailNotifier}. */
- private static DefaultVoicemailNotifier sInstance;
-
- private final Context mContext;
-
- /** Returns the singleton instance of the {@link DefaultVoicemailNotifier}. */
- public static DefaultVoicemailNotifier getInstance(Context context) {
- if (sInstance == null) {
- ContentResolver contentResolver = context.getContentResolver();
- sInstance = new DefaultVoicemailNotifier(context);
- }
- return sInstance;
- }
-
- private DefaultVoicemailNotifier(Context context) {
- mContext = context;
- }
-
- /**
- * Updates the notification and notifies of the call with the given URI.
- *
- * Clears the notification if there are no new voicemails, and notifies if the given URI
- * corresponds to a new voicemail.
- *
- * It is not safe to call this method from the main thread.
- */
- public void updateNotification(Uri newCallUri) {
- // Lookup the list of new voicemails to include in the notification.
- // TODO: Move this into a service, to avoid holding the receiver up.
- final List<NewCall> newCalls =
- CallLogNotificationsHelper.getInstance(mContext).getNewVoicemails();
-
- if (newCalls == null) {
- // Query failed, just return.
- return;
- }
-
- if (newCalls.isEmpty()) {
- // No voicemails to notify about: clear the notification.
- getNotificationManager().cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
- return;
- }
-
- Resources resources = mContext.getResources();
-
- // This represents a list of names to include in the notification.
- String callers = null;
-
- // Maps each number into a name: if a number is in the map, it has already left a more
- // recent voicemail.
- final Map<String, String> names = Maps.newHashMap();
-
- // Determine the call corresponding to the new voicemail we have to notify about.
- NewCall callToNotify = null;
-
- // Iterate over the new voicemails to determine all the information above.
- Iterator<NewCall> itr = newCalls.iterator();
- while (itr.hasNext()) {
- NewCall newCall = itr.next();
-
- // Skip notifying for numbers which are blocked.
- if (FilteredNumbersUtil.shouldBlockVoicemail(
- mContext, newCall.number, newCall.countryIso, newCall.dateMs)) {
- itr.remove();
-
- // Delete the voicemail.
- mContext.getContentResolver().delete(newCall.voicemailUri, null, null);
- continue;
- }
-
- // Check if we already know the name associated with this number.
- String name = names.get(newCall.number);
- if (name == null) {
- name = CallLogNotificationsHelper.getInstance(mContext).getName(newCall.number,
- newCall.numberPresentation, newCall.countryIso);
- names.put(newCall.number, name);
- // This is a new caller. Add it to the back of the list of callers.
- if (TextUtils.isEmpty(callers)) {
- callers = name;
- } else {
- callers = resources.getString(
- R.string.notification_voicemail_callers_list, callers, name);
- }
- }
- // Check if this is the new call we need to notify about.
- if (newCallUri != null && newCall.voicemailUri != null &&
- ContentUris.parseId(newCallUri) == ContentUris.parseId(newCall.voicemailUri)) {
- callToNotify = newCall;
- }
- }
-
- // All the potential new voicemails have been removed, e.g. if they were spam.
- if (newCalls.isEmpty()) {
- return;
- }
-
- // If there is only one voicemail, set its transcription as the "long text".
- String transcription = null;
- if (newCalls.size() == 1) {
- transcription = newCalls.get(0).transcription;
- }
-
- if (newCallUri != null && callToNotify == null) {
- Log.e(TAG, "The new call could not be found in the call log: " + newCallUri);
- }
-
- // Determine the title of the notification and the icon for it.
- final String title = resources.getQuantityString(
- R.plurals.notification_voicemail_title, newCalls.size(), newCalls.size());
- // TODO: Use the photo of contact if all calls are from the same person.
- final int icon = android.R.drawable.stat_notify_voicemail;
-
- Pair<Uri, Integer> info = getNotificationInfo(callToNotify);
-
- Notification.Builder notificationBuilder = new Notification.Builder(mContext)
- .setSmallIcon(icon)
- .setContentTitle(title)
- .setContentText(callers)
- .setStyle(new Notification.BigTextStyle().bigText(transcription))
- .setColor(resources.getColor(R.color.dialer_theme_color))
- .setSound(info.first)
- .setDefaults(info.second)
- .setDeleteIntent(createMarkNewVoicemailsAsOldIntent())
- .setAutoCancel(true);
-
- // Determine the intent to fire when the notification is clicked on.
- final Intent contentIntent;
- // Open the call log.
- contentIntent = new Intent(mContext, DialtactsActivity.class);
- contentIntent.putExtra(DialtactsActivity.EXTRA_SHOW_TAB, ListsFragment.TAB_INDEX_VOICEMAIL);
- notificationBuilder.setContentIntent(PendingIntent.getActivity(
- mContext, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT));
-
- // The text to show in the ticker, describing the new event.
- if (callToNotify != null) {
- CharSequence msg = ContactDisplayUtils.getTtsSpannedPhoneNumber(
- resources,
- R.string.notification_new_voicemail_ticker,
- names.get(callToNotify.number));
- notificationBuilder.setTicker(msg);
- }
- Log.i(TAG, "Creating voicemail notification");
- getNotificationManager().notify(NOTIFICATION_TAG, NOTIFICATION_ID,
- notificationBuilder.build());
- }
-
- /**
- * Determines which ringtone Uri and Notification defaults to use when updating the notification
- * for the given call.
- */
- private Pair<Uri, Integer> getNotificationInfo(@Nullable NewCall callToNotify) {
- Log.v(TAG, "getNotificationInfo");
- if (callToNotify == null) {
- Log.i(TAG, "callToNotify == null");
- return new Pair<>(null, 0);
- }
- PhoneAccountHandle accountHandle = null;
- if (callToNotify.accountComponentName == null || callToNotify.accountId == null) {
- Log.v(TAG, "accountComponentName == null || callToNotify.accountId == null");
- accountHandle = TelecomUtil
- .getDefaultOutgoingPhoneAccount(mContext, PhoneAccount.SCHEME_TEL);
- if (accountHandle == null) {
- Log.i(TAG, "No default phone account found, using default notification ringtone");
- return new Pair<>(null, Notification.DEFAULT_ALL);
- }
-
- } else {
- accountHandle = new PhoneAccountHandle(
- ComponentName.unflattenFromString(callToNotify.accountComponentName),
- callToNotify.accountId);
- }
- if (accountHandle.getComponentName() != null) {
- Log.v(TAG, "PhoneAccountHandle.ComponentInfo:" + accountHandle.getComponentName());
- } else {
- Log.i(TAG, "PhoneAccountHandle.ComponentInfo: null");
- }
- return new Pair<>(
- TelephonyManagerCompat.getVoicemailRingtoneUri(
- getTelephonyManager(), accountHandle),
- getNotificationDefaults(accountHandle));
- }
-
- private int getNotificationDefaults(PhoneAccountHandle accountHandle) {
- if (ContactsUtils.FLAG_N_FEATURE) {
- return TelephonyManagerCompat.isVoicemailVibrationEnabled(getTelephonyManager(),
- accountHandle) ? Notification.DEFAULT_VIBRATE : 0;
- }
- return Notification.DEFAULT_ALL;
- }
-
- /** Creates a pending intent that marks all new voicemails as old. */
- private PendingIntent createMarkNewVoicemailsAsOldIntent() {
- Intent intent = new Intent(mContext, CallLogNotificationsService.class);
- intent.setAction(CallLogNotificationsService.ACTION_MARK_NEW_VOICEMAILS_AS_OLD);
- return PendingIntent.getService(mContext, 0, intent, 0);
- }
-
- private NotificationManager getNotificationManager() {
- return (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- }
-
- private TelephonyManager getTelephonyManager() {
- return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
- }
-
-}
diff --git a/src/com/android/dialer/calllog/GroupingListAdapter.java b/src/com/android/dialer/calllog/GroupingListAdapter.java
deleted file mode 100644
index 0d06298e7..000000000
--- a/src/com/android/dialer/calllog/GroupingListAdapter.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * 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.calllog;
-
-import android.content.Context;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.database.DataSetObserver;
-import android.os.Handler;
-import android.support.v7.widget.RecyclerView;
-import android.util.SparseIntArray;
-
-/**
- * Maintains a list that groups items into groups of consecutive elements which are disjoint,
- * that is, an item can only belong to one group. This is leveraged for grouping calls in the
- * call log received from or made to the same phone number.
- *
- * There are two integers stored as metadata for every list item in the adapter.
- */
-abstract class GroupingListAdapter extends RecyclerView.Adapter {
-
- private Context mContext;
- private Cursor mCursor;
-
- /**
- * SparseIntArray, which maps the cursor position of the first element of a group to the size
- * of the group. The index of a key in this map corresponds to the list position of that group.
- */
- private SparseIntArray mGroupMetadata;
- private int mItemCount;
-
- protected ContentObserver mChangeObserver = new ContentObserver(new Handler()) {
- @Override
- public boolean deliverSelfNotifications() {
- return true;
- }
-
- @Override
- public void onChange(boolean selfChange) {
- onContentChanged();
- }
- };
-
- protected DataSetObserver mDataSetObserver = new DataSetObserver() {
- @Override
- public void onChanged() {
- notifyDataSetChanged();
- }
- };
-
- public GroupingListAdapter(Context context) {
- mContext = context;
- reset();
- }
-
- /**
- * Finds all groups of adjacent items in the cursor and calls {@link #addGroup} for
- * each of them.
- */
- protected abstract void addGroups(Cursor cursor);
-
- protected abstract void addVoicemailGroups(Cursor cursor);
-
- protected abstract void onContentChanged();
-
- public void changeCursor(Cursor cursor) {
- changeCursor(cursor, false);
- }
-
- public void changeCursorVoicemail(Cursor cursor) {
- changeCursor(cursor, true);
- }
-
- public void changeCursor(Cursor cursor, boolean voicemail) {
- if (cursor == mCursor) {
- return;
- }
-
- if (mCursor != null) {
- mCursor.unregisterContentObserver(mChangeObserver);
- mCursor.unregisterDataSetObserver(mDataSetObserver);
- mCursor.close();
- }
-
- // Reset whenever the cursor is changed.
- reset();
- mCursor = cursor;
-
- if (cursor != null) {
- if (voicemail) {
- addVoicemailGroups(mCursor);
- } else {
- addGroups(mCursor);
- }
-
- // Calculate the item count by subtracting group child counts from the cursor count.
- mItemCount = mGroupMetadata.size();
-
- cursor.registerContentObserver(mChangeObserver);
- cursor.registerDataSetObserver(mDataSetObserver);
- notifyDataSetChanged();
- }
- }
-
- /**
- * Records information about grouping in the list.
- * Should be called by the overridden {@link #addGroups} method.
- */
- public void addGroup(int cursorPosition, int groupSize) {
- int lastIndex = mGroupMetadata.size() - 1;
- if (lastIndex < 0 || cursorPosition <= mGroupMetadata.keyAt(lastIndex)) {
- mGroupMetadata.put(cursorPosition, groupSize);
- } else {
- // Optimization to avoid binary search if adding groups in ascending cursor position.
- mGroupMetadata.append(cursorPosition, groupSize);
- }
- }
-
- @Override
- public int getItemCount() {
- return mItemCount;
- }
-
- /**
- * Given the position of a list item, returns the size of the group of items corresponding to
- * that position.
- */
- public int getGroupSize(int listPosition) {
- if (listPosition < 0 || listPosition >= mGroupMetadata.size()) {
- return 0;
- }
-
- return mGroupMetadata.valueAt(listPosition);
- }
-
- /**
- * Given the position of a list item, returns the the first item in the group of items
- * corresponding to that position.
- */
- public Object getItem(int listPosition) {
- if (mCursor == null || listPosition < 0 || listPosition >= mGroupMetadata.size()) {
- return null;
- }
-
- int cursorPosition = mGroupMetadata.keyAt(listPosition);
- if (mCursor.moveToPosition(cursorPosition)) {
- return mCursor;
- } else {
- return null;
- }
- }
-
- private void reset() {
- mItemCount = 0;
- mGroupMetadata = new SparseIntArray();
- }
-}
diff --git a/src/com/android/dialer/calllog/IntentProvider.java b/src/com/android/dialer/calllog/IntentProvider.java
deleted file mode 100644
index 773436be4..000000000
--- a/src/com/android/dialer/calllog/IntentProvider.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import android.content.ContentValues;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.provider.ContactsContract;
-import android.telecom.PhoneAccountHandle;
-
-import com.android.contacts.common.CallUtil;
-import com.android.contacts.common.model.Contact;
-import com.android.contacts.common.model.ContactLoader;
-import com.android.dialer.CallDetailActivity;
-import com.android.dialer.util.IntentUtil;
-import com.android.dialer.util.IntentUtil.CallIntentBuilder;
-import com.android.dialer.util.TelecomUtil;
-import com.android.incallui.Call.LogState;
-
-import java.util.ArrayList;
-
-/**
- * Used to create an intent to attach to an action in the call log.
- * <p>
- * The intent is constructed lazily with the given information.
- */
-public abstract class IntentProvider {
-
- private static final String TAG = IntentProvider.class.getSimpleName();
-
- public abstract Intent getIntent(Context context);
-
- public static IntentProvider getReturnCallIntentProvider(final String number) {
- return getReturnCallIntentProvider(number, null);
- }
-
- public static IntentProvider getReturnCallIntentProvider(final String number,
- final PhoneAccountHandle accountHandle) {
- return new IntentProvider() {
- @Override
- public Intent getIntent(Context context) {
- return new CallIntentBuilder(number)
- .setPhoneAccountHandle(accountHandle)
- .setCallInitiationType(LogState.INITIATION_CALL_LOG)
- .build();
- }
- };
- }
-
- public static IntentProvider getReturnVideoCallIntentProvider(final String number) {
- return getReturnVideoCallIntentProvider(number, null);
- }
-
- public static IntentProvider getReturnVideoCallIntentProvider(final String number,
- final PhoneAccountHandle accountHandle) {
- return new IntentProvider() {
- @Override
- public Intent getIntent(Context context) {
- return new CallIntentBuilder(number)
- .setPhoneAccountHandle(accountHandle)
- .setCallInitiationType(LogState.INITIATION_CALL_LOG)
- .setIsVideoCall(true)
- .build();
- }
- };
- }
-
- public static IntentProvider getReturnVoicemailCallIntentProvider() {
- return new IntentProvider() {
- @Override
- public Intent getIntent(Context context) {
- return new CallIntentBuilder(CallUtil.getVoicemailUri())
- .setCallInitiationType(LogState.INITIATION_CALL_LOG)
- .build();
- }
- };
- }
-
- public static IntentProvider getSendSmsIntentProvider(final String number) {
- return new IntentProvider() {
- @Override
- public Intent getIntent(Context context) {
- return IntentUtil.getSendSmsIntent(number);
- }
- };
- }
-
- /**
- * Retrieves the call details intent provider for an entry in the call log.
- *
- * @param id The call ID of the first call in the call group.
- * @param extraIds The call ID of the other calls grouped together with the call.
- * @param voicemailUri If call log entry is for a voicemail, the voicemail URI.
- * @return The call details intent provider.
- */
- public static IntentProvider getCallDetailIntentProvider(
- final long id, final long[] extraIds, final String voicemailUri) {
- return new IntentProvider() {
- @Override
- public Intent getIntent(Context context) {
- Intent intent = new Intent(context, CallDetailActivity.class);
- // Check if the first item is a voicemail.
- if (voicemailUri != null) {
- intent.putExtra(CallDetailActivity.EXTRA_VOICEMAIL_URI,
- Uri.parse(voicemailUri));
- }
-
- if (extraIds != null && extraIds.length > 0) {
- intent.putExtra(CallDetailActivity.EXTRA_CALL_LOG_IDS, extraIds);
- } else {
- // If there is a single item, use the direct URI for it.
- intent.setData(ContentUris.withAppendedId(TelecomUtil.getCallLogUri(context),
- id));
- }
- return intent;
- }
- };
- }
-
- /**
- * Retrieves an add contact intent for the given contact and phone call details.
- */
- public static IntentProvider getAddContactIntentProvider(
- final Uri lookupUri,
- final CharSequence name,
- final CharSequence number,
- final int numberType,
- final boolean isNewContact) {
- return new IntentProvider() {
- @Override
- public Intent getIntent(Context context) {
- Contact contactToSave = null;
-
- if (lookupUri != null) {
- contactToSave = ContactLoader.parseEncodedContactEntity(lookupUri);
- }
-
- if (contactToSave != null) {
- // Populate the intent with contact information stored in the lookup URI.
- // Note: This code mirrors code in Contacts/QuickContactsActivity.
- final Intent intent;
- if (isNewContact) {
- intent = IntentUtil.getNewContactIntent();
- } else {
- intent = IntentUtil.getAddToExistingContactIntent();
- }
-
- ArrayList<ContentValues> values = contactToSave.getContentValues();
- // Only pre-fill the name field if the provided display name is an nickname
- // or better (e.g. structured name, nickname)
- if (contactToSave.getDisplayNameSource()
- >= ContactsContract.DisplayNameSources.NICKNAME) {
- intent.putExtra(ContactsContract.Intents.Insert.NAME,
- contactToSave.getDisplayName());
- } else if (contactToSave.getDisplayNameSource()
- == ContactsContract.DisplayNameSources.ORGANIZATION) {
- // This is probably an organization. Instead of copying the organization
- // name into a name entry, copy it into the organization entry. This
- // way we will still consider the contact an organization.
- final ContentValues organization = new ContentValues();
- organization.put(ContactsContract.CommonDataKinds.Organization.COMPANY,
- contactToSave.getDisplayName());
- organization.put(ContactsContract.Data.MIMETYPE,
- ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE);
- values.add(organization);
- }
-
- // Last time used and times used are aggregated values from the usage stat
- // table. They need to be removed from data values so the SQL table can insert
- // properly
- for (ContentValues value : values) {
- value.remove(ContactsContract.Data.LAST_TIME_USED);
- value.remove(ContactsContract.Data.TIMES_USED);
- }
-
- intent.putExtra(ContactsContract.Intents.Insert.DATA, values);
-
- return intent;
- } else {
- // If no lookup uri is provided, rely on the available phone number and name.
- if (isNewContact) {
- return IntentUtil.getNewContactIntent(name, number, numberType);
- } else {
- return IntentUtil.getAddToExistingContactIntent(name, number, numberType);
- }
- }
- }
- };
- }
-}
diff --git a/src/com/android/dialer/calllog/MissedCallNotificationReceiver.java b/src/com/android/dialer/calllog/MissedCallNotificationReceiver.java
deleted file mode 100644
index 86d6cb9fb..000000000
--- a/src/com/android/dialer/calllog/MissedCallNotificationReceiver.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2016 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.calllog;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.telecom.TelecomManager;
-import android.util.Log;
-
-import com.android.dialer.calllog.CallLogNotificationsService;
-
-/**
- * Receives broadcasts that should trigger a refresh of the missed call notification. This includes
- * both an explicit broadcast from Telecom and a reboot.
- */
-public class MissedCallNotificationReceiver extends BroadcastReceiver {
- //TODO: Use compat class for these methods.
- public static final String ACTION_SHOW_MISSED_CALLS_NOTIFICATION =
- "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION";
-
- public static final String EXTRA_NOTIFICATION_COUNT =
- "android.telecom.extra.NOTIFICATION_COUNT";
-
- public static final String EXTRA_NOTIFICATION_PHONE_NUMBER =
- "android.telecom.extra.NOTIFICATION_PHONE_NUMBER";
-
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (!ACTION_SHOW_MISSED_CALLS_NOTIFICATION.equals(action)) {
- return;
- }
-
- int count = intent.getIntExtra(EXTRA_NOTIFICATION_COUNT,
- CallLogNotificationsService.UNKNOWN_MISSED_CALL_COUNT);
- String number = intent.getStringExtra(EXTRA_NOTIFICATION_PHONE_NUMBER);
- CallLogNotificationsService.updateMissedCallNotifications(context, count, number);
- }
-}
diff --git a/src/com/android/dialer/calllog/MissedCallNotifier.java b/src/com/android/dialer/calllog/MissedCallNotifier.java
deleted file mode 100644
index 47f9ea770..000000000
--- a/src/com/android/dialer/calllog/MissedCallNotifier.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Copyright (C) 2016 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.calllog;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.os.AsyncTask;
-import android.provider.CallLog.Calls;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.common.ContactsUtils;
-import com.android.contacts.common.util.PhoneNumberHelper;
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.R;
-import com.android.dialer.calllog.CallLogNotificationsHelper.NewCall;
-import com.android.dialer.contactinfo.ContactPhotoLoader;
-import com.android.dialer.compat.UserManagerCompat;
-import com.android.dialer.list.ListsFragment;
-import com.android.dialer.util.DialerUtils;
-import com.android.dialer.util.IntentUtil;
-import com.android.dialer.util.IntentUtil.CallIntentBuilder;
-
-import java.util.List;
-
-/**
- * Creates a notification for calls that the user missed (neither answered nor rejected).
- *
- */
-public class MissedCallNotifier {
- public static final String TAG = "MissedCallNotifier";
-
- /** The tag used to identify notifications from this class. */
- private static final String NOTIFICATION_TAG = "MissedCallNotifier";
- /** The identifier of the notification of new missed calls. */
- private static final int NOTIFICATION_ID = 1;
- /** Preference file key for number of missed calls. */
- private static final String MISSED_CALL_COUNT = "missed_call_count";
-
- private static MissedCallNotifier sInstance;
- private Context mContext;
-
- /** Returns the singleton instance of the {@link MissedCallNotifier}. */
- public static MissedCallNotifier getInstance(Context context) {
- if (sInstance == null) {
- sInstance = new MissedCallNotifier(context);
- }
- return sInstance;
- }
-
- private MissedCallNotifier(Context context) {
- mContext = context;
- }
-
- public void updateMissedCallNotification(int count, String number) {
- final int titleResId;
- final String expandedText; // The text in the notification's line 1 and 2.
-
- final List<NewCall> newCalls =
- CallLogNotificationsHelper.getInstance(mContext).getNewMissedCalls();
-
- if (count == CallLogNotificationsService.UNKNOWN_MISSED_CALL_COUNT) {
- if (newCalls == null) {
- // If the intent did not contain a count, and we are unable to get a count from the
- // call log, then no notification can be shown.
- return;
- }
- count = newCalls.size();
- }
-
- if (count == 0) {
- // No voicemails to notify about: clear the notification.
- clearMissedCalls();
- return;
- }
-
- // The call log has been updated, use that information preferentially.
- boolean useCallLog = newCalls != null && newCalls.size() == count;
- NewCall newestCall = useCallLog ? newCalls.get(0) : null;
- long timeMs = useCallLog ? newestCall.dateMs : System.currentTimeMillis();
- String missedNumber = useCallLog ? newestCall.number : number;
-
- Notification.Builder builder = new Notification.Builder(mContext);
- // Display the first line of the notification:
- // 1 missed call: <caller name || handle>
- // More than 1 missed call: <number of calls> + "missed calls"
- if (count == 1) {
- //TODO: look up caller ID that is not in contacts.
- ContactInfo contactInfo = CallLogNotificationsHelper.getInstance(mContext)
- .getContactInfo(missedNumber,
- useCallLog ? newestCall.numberPresentation
- : Calls.PRESENTATION_ALLOWED,
- useCallLog ? newestCall.countryIso : null);
-
- titleResId = contactInfo.userType == ContactsUtils.USER_TYPE_WORK
- ? R.string.notification_missedWorkCallTitle
- : R.string.notification_missedCallTitle;
-
- expandedText = contactInfo.name;
- ContactPhotoLoader loader = new ContactPhotoLoader(mContext, contactInfo);
- Bitmap photoIcon = loader.loadPhotoIcon();
- if (photoIcon != null) {
- builder.setLargeIcon(photoIcon);
- }
- } else {
- titleResId = R.string.notification_missedCallsTitle;
- expandedText =
- mContext.getString(R.string.notification_missedCallsMsg, count);
- }
-
- // Create a public viewable version of the notification, suitable for display when sensitive
- // notification content is hidden.
- Notification.Builder publicBuilder = new Notification.Builder(mContext);
- publicBuilder.setSmallIcon(android.R.drawable.stat_notify_missed_call)
- .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
- // Show "Phone" for notification title.
- .setContentTitle(mContext.getText(R.string.userCallActivityLabel))
- // Notification details shows that there are missed call(s), but does not reveal
- // the missed caller information.
- .setContentText(mContext.getText(titleResId))
- .setContentIntent(createCallLogPendingIntent())
- .setAutoCancel(true)
- .setWhen(timeMs)
- .setDeleteIntent(createClearMissedCallsPendingIntent());
-
- // Create the notification suitable for display when sensitive information is showing.
- builder.setSmallIcon(android.R.drawable.stat_notify_missed_call)
- .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
- .setContentTitle(mContext.getText(titleResId))
- .setContentText(expandedText)
- .setContentIntent(createCallLogPendingIntent())
- .setAutoCancel(true)
- .setWhen(timeMs)
- .setDefaults(Notification.DEFAULT_VIBRATE)
- .setDeleteIntent(createClearMissedCallsPendingIntent())
- // Include a public version of the notification to be shown when the missed call
- // notification is shown on the user's lock screen and they have chosen to hide
- // sensitive notification information.
- .setPublicVersion(publicBuilder.build());
-
- // Add additional actions when there is only 1 missed call and the user isn't locked
- if (UserManagerCompat.isUserUnlocked(mContext) && count == 1) {
- if (!TextUtils.isEmpty(missedNumber)
- && !TextUtils.equals(
- missedNumber, mContext.getString(R.string.handle_restricted))) {
- builder.addAction(R.drawable.ic_phone_24dp,
- mContext.getString(R.string.notification_missedCall_call_back),
- createCallBackPendingIntent(missedNumber));
-
- if (!PhoneNumberHelper.isUriNumber(missedNumber)) {
- builder.addAction(R.drawable.ic_message_24dp,
- mContext.getString(R.string.notification_missedCall_message),
- createSendSmsFromNotificationPendingIntent(missedNumber));
- }
- }
- }
-
- Notification notification = builder.build();
- configureLedOnNotification(notification);
-
- Log.i(TAG, "Adding missed call notification.");
- getNotificationMgr().notify(NOTIFICATION_TAG, NOTIFICATION_ID, notification);
- }
-
- private void clearMissedCalls() {
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- // Call log is only accessible when unlocked. If that's the case, clear the list of
- // new missed calls from the call log.
- if (UserManagerCompat.isUserUnlocked(mContext)) {
- ContentValues values = new ContentValues();
- values.put(Calls.NEW, 0);
- values.put(Calls.IS_READ, 1);
- StringBuilder where = new StringBuilder();
- where.append(Calls.NEW);
- where.append(" = 1 AND ");
- where.append(Calls.TYPE);
- where.append(" = ?");
- try {
- mContext.getContentResolver().update(Calls.CONTENT_URI, values,
- where.toString(), new String[]{Integer.toString(Calls.
- MISSED_TYPE)});
- } catch (IllegalArgumentException e) {
- Log.w(TAG, "ContactsProvider update command failed", e);
- }
- }
- getNotificationMgr().cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
- }
- });
- }
-
- /**
- * Trigger an intent to make a call from a missed call number.
- */
- public void callBackFromMissedCall(String number) {
- closeSystemDialogs(mContext);
- CallLogNotificationsHelper.removeMissedCallNotifications(mContext);
- DialerUtils.startActivityWithErrorToast(
- mContext,
- new CallIntentBuilder(number)
- .build()
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- }
-
- /**
- * Trigger an intent to send an sms from a missed call number.
- */
- public void sendSmsFromMissedCall(String number) {
- closeSystemDialogs(mContext);
- CallLogNotificationsHelper.removeMissedCallNotifications(mContext);
- DialerUtils.startActivityWithErrorToast(
- mContext,
- IntentUtil.getSendSmsIntent(number).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- }
-
- /**
- * Creates a new pending intent that sends the user to the call log.
- *
- * @return The pending intent.
- */
- private PendingIntent createCallLogPendingIntent() {
- Intent contentIntent = new Intent(mContext, DialtactsActivity.class);
- contentIntent.putExtra(DialtactsActivity.EXTRA_SHOW_TAB, ListsFragment.TAB_INDEX_HISTORY);
- return PendingIntent.getActivity(
- mContext, 0, contentIntent,PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- /** Creates a pending intent that marks all new missed calls as old. */
- private PendingIntent createClearMissedCallsPendingIntent() {
- Intent intent = new Intent(mContext, CallLogNotificationsService.class);
- intent.setAction(CallLogNotificationsService.ACTION_MARK_NEW_MISSED_CALLS_AS_OLD);
- return PendingIntent.getService(mContext, 0, intent, 0);
- }
-
- private PendingIntent createCallBackPendingIntent(String number) {
- Intent intent = new Intent(mContext, CallLogNotificationsService.class);
- intent.setAction(
- CallLogNotificationsService.ACTION_CALL_BACK_FROM_MISSED_CALL_NOTIFICATION);
- intent.putExtra(CallLogNotificationsService.EXTRA_MISSED_CALL_NUMBER, number);
- // Use FLAG_UPDATE_CURRENT to make sure any previous pending intent is updated with the new
- // extra.
- return PendingIntent.getService(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- private PendingIntent createSendSmsFromNotificationPendingIntent(String number) {
- Intent intent = new Intent(mContext, CallLogNotificationsService.class);
- intent.setAction(
- CallLogNotificationsService.ACTION_SEND_SMS_FROM_MISSED_CALL_NOTIFICATION);
- intent.putExtra(CallLogNotificationsService.EXTRA_MISSED_CALL_NUMBER, number);
- // Use FLAG_UPDATE_CURRENT to make sure any previous pending intent is updated with the new
- // extra.
- return PendingIntent.getService(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- /**
- * Configures a notification to emit the blinky notification light.
- */
- private void configureLedOnNotification(Notification notification) {
- notification.flags |= Notification.FLAG_SHOW_LIGHTS;
- notification.defaults |= Notification.DEFAULT_LIGHTS;
- }
-
- /**
- * Closes open system dialogs and the notification shade.
- */
- private void closeSystemDialogs(Context context) {
- context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
- }
-
- private NotificationManager getNotificationMgr() {
- return (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- }
-}
diff --git a/src/com/android/dialer/calllog/PhoneAccountUtils.java b/src/com/android/dialer/calllog/PhoneAccountUtils.java
deleted file mode 100644
index b3ce18b3c..000000000
--- a/src/com/android/dialer/calllog/PhoneAccountUtils.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2013 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.calllog;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.support.annotation.Nullable;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.text.TextUtils;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.dialer.util.TelecomUtil;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Methods to help extract {@code PhoneAccount} information from database and Telecomm sources.
- */
-public class PhoneAccountUtils {
- /**
- * Return a list of phone accounts that are subscription/SIM accounts.
- */
- public static List<PhoneAccountHandle> getSubscriptionPhoneAccounts(Context context) {
- List<PhoneAccountHandle> subscriptionAccountHandles = new ArrayList<PhoneAccountHandle>();
- final List<PhoneAccountHandle> accountHandles =
- TelecomUtil.getCallCapablePhoneAccounts(context);
- for (PhoneAccountHandle accountHandle : accountHandles) {
- PhoneAccount account = TelecomUtil.getPhoneAccount(context, accountHandle);
- if (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
- subscriptionAccountHandles.add(accountHandle);
- }
- }
- return subscriptionAccountHandles;
- }
-
- /**
- * Compose PhoneAccount object from component name and account id.
- */
- @Nullable
- public static PhoneAccountHandle getAccount(@Nullable String componentString,
- @Nullable String accountId) {
- if (TextUtils.isEmpty(componentString) || TextUtils.isEmpty(accountId)) {
- return null;
- }
- final ComponentName componentName = ComponentName.unflattenFromString(componentString);
- if (componentName == null) {
- return null;
- }
- return new PhoneAccountHandle(componentName, accountId);
- }
-
- /**
- * Extract account label from PhoneAccount object.
- */
- @Nullable
- public static String getAccountLabel(Context context,
- @Nullable PhoneAccountHandle accountHandle) {
- PhoneAccount account = getAccountOrNull(context, accountHandle);
- if (account != null && account.getLabel() != null) {
- return account.getLabel().toString();
- }
- return null;
- }
-
- /**
- * Extract account color from PhoneAccount object.
- */
- public static int getAccountColor(Context context, @Nullable PhoneAccountHandle accountHandle) {
- final PhoneAccount account = TelecomUtil.getPhoneAccount(context, accountHandle);
-
- // For single-sim devices the PhoneAccount will be NO_HIGHLIGHT_COLOR by default, so it is
- // safe to always use the account highlight color.
- return account == null ? PhoneAccount.NO_HIGHLIGHT_COLOR : account.getHighlightColor();
- }
-
- /**
- * Determine whether a phone account supports call subjects.
- *
- * @return {@code true} if call subjects are supported, {@code false} otherwise.
- */
- public static boolean getAccountSupportsCallSubject(Context context,
- @Nullable PhoneAccountHandle accountHandle) {
- final PhoneAccount account = TelecomUtil.getPhoneAccount(context, accountHandle);
-
- return account == null ? false :
- account.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT);
- }
-
- /**
- * Retrieve the account metadata, but if the account does not exist or the device has only a
- * single registered and enabled account, return null.
- */
- @Nullable
- private static PhoneAccount getAccountOrNull(Context context,
- @Nullable PhoneAccountHandle accountHandle) {
- if (TelecomUtil.getCallCapablePhoneAccounts(context).size() <= 1) {
- return null;
- }
- return TelecomUtil.getPhoneAccount(context, accountHandle);
- }
-}
diff --git a/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java b/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
deleted file mode 100644
index 53121614c..000000000
--- a/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import com.google.common.base.MoreObjects;
-import com.google.common.collect.Lists;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Typeface;
-import android.provider.CallLog.Calls;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.support.v4.content.ContextCompat;
-import android.telecom.PhoneAccount;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.view.View;
-import android.widget.TextView;
-
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.contacts.common.util.PhoneNumberHelper;
-import com.android.dialer.PhoneCallDetails;
-import com.android.dialer.R;
-import com.android.dialer.calllog.calllogcache.CallLogCache;
-import com.android.dialer.util.DialerUtils;
-
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Helper class to fill in the views in {@link PhoneCallDetailsViews}.
- */
-public class PhoneCallDetailsHelper {
-
- /** The maximum number of icons will be shown to represent the call types in a group. */
- private static final int MAX_CALL_TYPE_ICONS = 3;
-
- private final Context mContext;
- private final Resources mResources;
- /** The injected current time in milliseconds since the epoch. Used only by tests. */
- private Long mCurrentTimeMillisForTest;
-
- private CharSequence mPhoneTypeLabelForTest;
-
- private final CallLogCache mCallLogCache;
-
- /** Calendar used to construct dates */
- private final Calendar mCalendar;
-
- /**
- * List of items to be concatenated together for accessibility descriptions
- */
- private ArrayList<CharSequence> mDescriptionItems = Lists.newArrayList();
-
- /**
- * Creates a new instance of the helper.
- * <p>
- * Generally you should have a single instance of this helper in any context.
- *
- * @param resources used to look up strings
- */
- public PhoneCallDetailsHelper(
- Context context,
- Resources resources,
- CallLogCache callLogCache) {
- mContext = context;
- mResources = resources;
- mCallLogCache = callLogCache;
- mCalendar = Calendar.getInstance();
- }
-
- /** Fills the call details views with content. */
- public void setPhoneCallDetails(PhoneCallDetailsViews views, PhoneCallDetails details) {
- // Display up to a given number of icons.
- views.callTypeIcons.clear();
- int count = details.callTypes.length;
- boolean isVoicemail = false;
- for (int index = 0; index < count && index < MAX_CALL_TYPE_ICONS; ++index) {
- views.callTypeIcons.add(details.callTypes[index]);
- if (index == 0) {
- isVoicemail = details.callTypes[index] == Calls.VOICEMAIL_TYPE;
- }
- }
-
- // Show the video icon if the call had video enabled.
- views.callTypeIcons.setShowVideo(
- (details.features & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO);
- views.callTypeIcons.requestLayout();
- views.callTypeIcons.setVisibility(View.VISIBLE);
-
- // Show the total call count only if there are more than the maximum number of icons.
- final Integer callCount;
- if (count > MAX_CALL_TYPE_ICONS) {
- callCount = count;
- } else {
- callCount = null;
- }
-
- // Set the call count, location, date and if voicemail, set the duration.
- setDetailText(views, callCount, details);
-
- // Set the account label if it exists.
- String accountLabel = mCallLogCache.getAccountLabel(details.accountHandle);
- if (!TextUtils.isEmpty(details.viaNumber)) {
- if (!TextUtils.isEmpty(accountLabel)) {
- accountLabel = mResources.getString(R.string.call_log_via_number_phone_account,
- accountLabel, details.viaNumber);
- } else {
- accountLabel = mResources.getString(R.string.call_log_via_number,
- details.viaNumber);
- }
- }
- if (!TextUtils.isEmpty(accountLabel)) {
- views.callAccountLabel.setVisibility(View.VISIBLE);
- views.callAccountLabel.setText(accountLabel);
- int color = mCallLogCache.getAccountColor(details.accountHandle);
- if (color == PhoneAccount.NO_HIGHLIGHT_COLOR) {
- int defaultColor = R.color.dialtacts_secondary_text_color;
- views.callAccountLabel.setTextColor(mContext.getResources().getColor(defaultColor));
- } else {
- views.callAccountLabel.setTextColor(color);
- }
- } else {
- views.callAccountLabel.setVisibility(View.GONE);
- }
-
- final CharSequence nameText;
- final CharSequence displayNumber = details.displayNumber;
- if (TextUtils.isEmpty(details.getPreferredName())) {
- nameText = displayNumber;
- // We have a real phone number as "nameView" so make it always LTR
- views.nameView.setTextDirection(View.TEXT_DIRECTION_LTR);
- } else {
- nameText = details.getPreferredName();
- }
-
- views.nameView.setText(nameText);
-
- if (isVoicemail) {
- views.voicemailTranscriptionView.setText(TextUtils.isEmpty(details.transcription) ? null
- : details.transcription);
- }
-
- // Bold if not read
- Typeface typeface = details.isRead ? Typeface.SANS_SERIF : Typeface.DEFAULT_BOLD;
- views.nameView.setTypeface(typeface);
- views.voicemailTranscriptionView.setTypeface(typeface);
- views.callLocationAndDate.setTypeface(typeface);
- views.callLocationAndDate.setTextColor(ContextCompat.getColor(mContext, details.isRead ?
- R.color.call_log_detail_color : R.color.call_log_unread_text_color));
- }
-
- /**
- * Builds a string containing the call location and date. For voicemail logs only the call date
- * is returned because location information is displayed in the call action button
- *
- * @param details The call details.
- * @return The call location and date string.
- */
- private CharSequence getCallLocationAndDate(PhoneCallDetails details) {
- mDescriptionItems.clear();
-
- if (details.callTypes[0] != Calls.VOICEMAIL_TYPE) {
- // Get type of call (ie mobile, home, etc) if known, or the caller's location.
- CharSequence callTypeOrLocation = getCallTypeOrLocation(details);
-
- // Only add the call type or location if its not empty. It will be empty for unknown
- // callers.
- if (!TextUtils.isEmpty(callTypeOrLocation)) {
- mDescriptionItems.add(callTypeOrLocation);
- }
- }
-
- // The date of this call
- mDescriptionItems.add(getCallDate(details));
-
- // Create a comma separated list from the call type or location, and call date.
- return DialerUtils.join(mResources, mDescriptionItems);
- }
-
- /**
- * For a call, if there is an associated contact for the caller, return the known call type
- * (e.g. mobile, home, work). If there is no associated contact, attempt to use the caller's
- * location if known.
- *
- * @param details Call details to use.
- * @return Type of call (mobile/home) if known, or the location of the caller (if known).
- */
- public CharSequence getCallTypeOrLocation(PhoneCallDetails details) {
- CharSequence numberFormattedLabel = null;
- // Only show a label if the number is shown and it is not a SIP address.
- if (!TextUtils.isEmpty(details.number)
- && !PhoneNumberHelper.isUriNumber(details.number.toString())
- && !mCallLogCache.isVoicemailNumber(details.accountHandle, details.number)) {
-
- if (TextUtils.isEmpty(details.namePrimary) && !TextUtils.isEmpty(details.geocode)) {
- numberFormattedLabel = details.geocode;
- } else if (!(details.numberType == Phone.TYPE_CUSTOM
- && TextUtils.isEmpty(details.numberLabel))) {
- // Get type label only if it will not be "Custom" because of an empty number label.
- numberFormattedLabel = MoreObjects.firstNonNull(mPhoneTypeLabelForTest,
- Phone.getTypeLabel(mResources, details.numberType, details.numberLabel));
- }
- }
-
- if (!TextUtils.isEmpty(details.namePrimary) && TextUtils.isEmpty(numberFormattedLabel)) {
- numberFormattedLabel = details.displayNumber;
- }
- return numberFormattedLabel;
- }
-
- @NeededForTesting
- public void setPhoneTypeLabelForTest(CharSequence phoneTypeLabel) {
- this.mPhoneTypeLabelForTest = phoneTypeLabel;
- }
-
- /**
- * Get the call date/time of the call. For the call log this is relative to the current time.
- * e.g. 3 minutes ago. For voicemail, see {@link #getGranularDateTime(PhoneCallDetails)}
- *
- * @param details Call details to use.
- * @return String representing when the call occurred.
- */
- public CharSequence getCallDate(PhoneCallDetails details) {
- if (details.callTypes[0] == Calls.VOICEMAIL_TYPE) {
- return getGranularDateTime(details);
- }
-
- return DateUtils.getRelativeTimeSpanString(details.date, getCurrentTimeMillis(),
- DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE);
- }
-
- /**
- * Get the granular version of the call date/time of the call. The result is always in the form
- * 'DATE at TIME'. The date value changes based on when the call was created.
- *
- * If created today, DATE is 'Today'
- * If created this year, DATE is 'MMM dd'
- * Otherwise, DATE is 'MMM dd, yyyy'
- *
- * TIME is the localized time format, e.g. 'hh:mm a' or 'HH:mm'
- *
- * @param details Call details to use
- * @return String representing when the call occurred
- */
- public CharSequence getGranularDateTime(PhoneCallDetails details) {
- return mResources.getString(R.string.voicemailCallLogDateTimeFormat,
- getGranularDate(details.date),
- DateUtils.formatDateTime(mContext, details.date, DateUtils.FORMAT_SHOW_TIME));
- }
-
- /**
- * Get the granular version of the call date. See {@link #getGranularDateTime(PhoneCallDetails)}
- */
- private String getGranularDate(long date) {
- if (DateUtils.isToday(date)) {
- return mResources.getString(R.string.voicemailCallLogToday);
- }
- return DateUtils.formatDateTime(mContext, date, DateUtils.FORMAT_SHOW_DATE
- | DateUtils.FORMAT_ABBREV_MONTH
- | (shouldShowYear(date) ? DateUtils.FORMAT_SHOW_YEAR : DateUtils.FORMAT_NO_YEAR));
- }
-
- /**
- * Determines whether the year should be shown for the given date
- *
- * @return {@code true} if date is within the current year, {@code false} otherwise
- */
- private boolean shouldShowYear(long date) {
- mCalendar.setTimeInMillis(getCurrentTimeMillis());
- int currentYear = mCalendar.get(Calendar.YEAR);
- mCalendar.setTimeInMillis(date);
- return currentYear != mCalendar.get(Calendar.YEAR);
- }
-
- /** Sets the text of the header view for the details page of a phone call. */
- @NeededForTesting
- public void setCallDetailsHeader(TextView nameView, PhoneCallDetails details) {
- final CharSequence nameText;
- if (!TextUtils.isEmpty(details.namePrimary)) {
- nameText = details.namePrimary;
- } else if (!TextUtils.isEmpty(details.displayNumber)) {
- nameText = details.displayNumber;
- } else {
- nameText = mResources.getString(R.string.unknown);
- }
-
- nameView.setText(nameText);
- }
-
- @NeededForTesting
- public void setCurrentTimeForTest(long currentTimeMillis) {
- mCurrentTimeMillisForTest = currentTimeMillis;
- }
-
- /**
- * Returns the current time in milliseconds since the epoch.
- * <p>
- * It can be injected in tests using {@link #setCurrentTimeForTest(long)}.
- */
- private long getCurrentTimeMillis() {
- if (mCurrentTimeMillisForTest == null) {
- return System.currentTimeMillis();
- } else {
- return mCurrentTimeMillisForTest;
- }
- }
-
- /** Sets the call count, date, and if it is a voicemail, sets the duration. */
- private void setDetailText(PhoneCallDetailsViews views, Integer callCount,
- PhoneCallDetails details) {
- if (details.isSpam) {
- views.callLocationAndDate.setText(
- mContext.getString(R.string.spam_number_call_log_label));
- return;
- }
- // Combine the count (if present) and the date.
- CharSequence dateText = getCallLocationAndDate(details);
- final CharSequence text;
- if (callCount != null) {
- text = mResources.getString(
- R.string.call_log_item_count_and_date, callCount.intValue(), dateText);
- } else {
- text = dateText;
- }
-
- if (details.callTypes[0] == Calls.VOICEMAIL_TYPE && details.duration > 0) {
- views.callLocationAndDate.setText(mResources.getString(
- R.string.voicemailCallLogDateTimeFormatWithDuration, text,
- getVoicemailDuration(details)));
- } else {
- views.callLocationAndDate.setText(text);
- }
- }
-
- private String getVoicemailDuration(PhoneCallDetails details) {
- long minutes = TimeUnit.SECONDS.toMinutes(details.duration);
- long seconds = details.duration - TimeUnit.MINUTES.toSeconds(minutes);
- if (minutes > 99) {
- minutes = 99;
- }
- return mResources.getString(R.string.voicemailDurationFormat, minutes, seconds);
- }
-}
diff --git a/src/com/android/dialer/calllog/PhoneCallDetailsViews.java b/src/com/android/dialer/calllog/PhoneCallDetailsViews.java
deleted file mode 100644
index 94f4411b0..000000000
--- a/src/com/android/dialer/calllog/PhoneCallDetailsViews.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import android.content.Context;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.dialer.R;
-
-/**
- * Encapsulates the views that are used to display the details of a phone call in the call log.
- */
-public final class PhoneCallDetailsViews {
- public final TextView nameView;
- public final View callTypeView;
- public final CallTypeIconsView callTypeIcons;
- public final TextView callLocationAndDate;
- public final TextView voicemailTranscriptionView;
- public final TextView callAccountLabel;
-
- private PhoneCallDetailsViews(TextView nameView, View callTypeView,
- CallTypeIconsView callTypeIcons, TextView callLocationAndDate,
- TextView voicemailTranscriptionView, TextView callAccountLabel) {
- this.nameView = nameView;
- this.callTypeView = callTypeView;
- this.callTypeIcons = callTypeIcons;
- this.callLocationAndDate = callLocationAndDate;
- this.voicemailTranscriptionView = voicemailTranscriptionView;
- this.callAccountLabel = callAccountLabel;
- }
-
- /**
- * Create a new instance by extracting the elements from the given view.
- * <p>
- * The view should contain three text views with identifiers {@code R.id.name},
- * {@code R.id.date}, and {@code R.id.number}, and a linear layout with identifier
- * {@code R.id.call_types}.
- */
- public static PhoneCallDetailsViews fromView(View view) {
- return new PhoneCallDetailsViews((TextView) view.findViewById(R.id.name),
- view.findViewById(R.id.call_type),
- (CallTypeIconsView) view.findViewById(R.id.call_type_icons),
- (TextView) view.findViewById(R.id.call_location_and_date),
- (TextView) view.findViewById(R.id.voicemail_transcription),
- (TextView) view.findViewById(R.id.call_account_label));
- }
-
- public static PhoneCallDetailsViews createForTest(Context context) {
- return new PhoneCallDetailsViews(
- new TextView(context),
- new View(context),
- new CallTypeIconsView(context),
- new TextView(context),
- new TextView(context),
- new TextView(context));
- }
-}
diff --git a/src/com/android/dialer/calllog/PhoneNumberDisplayUtil.java b/src/com/android/dialer/calllog/PhoneNumberDisplayUtil.java
deleted file mode 100644
index 5b1fc9e3a..000000000
--- a/src/com/android/dialer/calllog/PhoneNumberDisplayUtil.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import android.content.Context;
-import android.provider.CallLog.Calls;
-import android.text.TextUtils;
-
-import com.android.dialer.R;
-import com.android.dialer.util.PhoneNumberUtil;
-
-/**
- * Helper for formatting and managing the display of phone numbers.
- */
-public class PhoneNumberDisplayUtil {
-
- /**
- * Returns the string to display for the given phone number if there is no matching contact.
- */
- /* package */ static CharSequence getDisplayName(
- Context context,
- CharSequence number,
- int presentation,
- boolean isVoicemail) {
- if (presentation == Calls.PRESENTATION_UNKNOWN) {
- return context.getResources().getString(R.string.unknown);
- }
- if (presentation == Calls.PRESENTATION_RESTRICTED) {
- return context.getResources().getString(R.string.private_num);
- }
- if (presentation == Calls.PRESENTATION_PAYPHONE) {
- return context.getResources().getString(R.string.payphone);
- }
- if (isVoicemail) {
- return context.getResources().getString(R.string.voicemail);
- }
- if (PhoneNumberUtil.isLegacyUnknownNumbers(number)) {
- return context.getResources().getString(R.string.unknown);
- }
- return "";
- }
-
- /**
- * Returns the string to display for the given phone number.
- *
- * @param number the number to display
- * @param formattedNumber the formatted number if available, may be null
- */
- public static CharSequence getDisplayNumber(
- Context context,
- CharSequence number,
- int presentation,
- CharSequence formattedNumber,
- CharSequence postDialDigits,
- boolean isVoicemail) {
- final CharSequence displayName = getDisplayName(context, number, presentation, isVoicemail);
- if (!TextUtils.isEmpty(displayName)) {
- return displayName;
- }
-
- if (!TextUtils.isEmpty(formattedNumber)) {
- return formattedNumber;
- } else if (!TextUtils.isEmpty(number)) {
- return number.toString() + postDialDigits;
- } else {
- return context.getResources().getString(R.string.unknown);
- }
- }
-}
diff --git a/src/com/android/dialer/calllog/PhoneQuery.java b/src/com/android/dialer/calllog/PhoneQuery.java
deleted file mode 100644
index f1f14c66e..000000000
--- a/src/com/android/dialer/calllog/PhoneQuery.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import android.net.Uri;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.PhoneLookup;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.PhoneLookupSdkCompat;
-import com.android.contacts.common.ContactsUtils;
-
-/**
- * The queries to look up the {@link ContactInfo} for a given number in the Call Log.
- */
-final class PhoneQuery {
-
- /**
- * Projection to look up the ContactInfo. Does not include DISPLAY_NAME_ALTERNATIVE as that
- * column isn't available in ContactsCommon.PhoneLookup.
- * We should always use this projection starting from NYC onward.
- */
- private static final String[] PHONE_LOOKUP_PROJECTION = new String[] {
- PhoneLookupSdkCompat.CONTACT_ID,
- PhoneLookup.DISPLAY_NAME,
- PhoneLookup.TYPE,
- PhoneLookup.LABEL,
- PhoneLookup.NUMBER,
- PhoneLookup.NORMALIZED_NUMBER,
- PhoneLookup.PHOTO_ID,
- PhoneLookup.LOOKUP_KEY,
- PhoneLookup.PHOTO_URI
- };
-
- /**
- * Similar to {@link PHONE_LOOKUP_PROJECTION}. In pre-N, contact id is stored in
- * {@link PhoneLookup#_ID} in non-sip query.
- */
- private static final String[] BACKWARD_COMPATIBLE_NON_SIP_PHONE_LOOKUP_PROJECTION =
- new String[] {
- PhoneLookup._ID,
- PhoneLookup.DISPLAY_NAME,
- PhoneLookup.TYPE,
- PhoneLookup.LABEL,
- PhoneLookup.NUMBER,
- PhoneLookup.NORMALIZED_NUMBER,
- PhoneLookup.PHOTO_ID,
- PhoneLookup.LOOKUP_KEY,
- PhoneLookup.PHOTO_URI
- };
-
- public static String[] getPhoneLookupProjection(Uri phoneLookupUri) {
- if (CompatUtils.isNCompatible()) {
- return PHONE_LOOKUP_PROJECTION;
- }
- // Pre-N
- boolean isSip = phoneLookupUri.getBooleanQueryParameter(
- ContactsContract.PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, false);
- return (isSip) ? PHONE_LOOKUP_PROJECTION
- : BACKWARD_COMPATIBLE_NON_SIP_PHONE_LOOKUP_PROJECTION;
- }
-
- public static final int PERSON_ID = 0;
- public static final int NAME = 1;
- public static final int PHONE_TYPE = 2;
- public static final int LABEL = 3;
- public static final int MATCHED_NUMBER = 4;
- public static final int NORMALIZED_NUMBER = 5;
- public static final int PHOTO_ID = 6;
- public static final int LOOKUP_KEY = 7;
- public static final int PHOTO_URI = 8;
-
- /**
- * Projection to look up a contact's DISPLAY_NAME_ALTERNATIVE
- */
- public static final String[] DISPLAY_NAME_ALTERNATIVE_PROJECTION = new String[] {
- Contacts.DISPLAY_NAME_ALTERNATIVE,
- };
-
- public static final int NAME_ALTERNATIVE = 0;
-}
diff --git a/src/com/android/dialer/calllog/PromoCardViewHolder.java b/src/com/android/dialer/calllog/PromoCardViewHolder.java
deleted file mode 100644
index f5a7501fc..000000000
--- a/src/com/android/dialer/calllog/PromoCardViewHolder.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.calllog;
-
-import android.content.Context;
-import android.support.v7.widget.CardView;
-import android.support.v7.widget.RecyclerView;
-import android.view.View;
-
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.dialer.R;
-
-/**
- * Generic ViewHolder class for a promo card with a primary and secondary action.
- * Example: the promo card which appears in the voicemail tab.
- */
-public class PromoCardViewHolder extends RecyclerView.ViewHolder {
- public static PromoCardViewHolder create(View rootView) {
- return new PromoCardViewHolder(rootView);
- }
-
- /**
- * The primary action button view.
- */
- private View mPrimaryActionView;
-
- /**
- * The secondary action button view.
- * The "Ok" button view.
- */
- private View mSecondaryActionView;
-
- /**
- * Creates an instance of the {@link ViewHolder}.
- *
- * @param rootView The root view.
- */
- private PromoCardViewHolder(View rootView) {
- super(rootView);
-
- mPrimaryActionView = rootView.findViewById(R.id.primary_action);
- mSecondaryActionView = rootView.findViewById(R.id.secondary_action);
- }
-
- /**
- * Retrieves the "primary" action button (eg. "OK").
- *
- * @return The view.
- */
- public View getPrimaryActionView() {
- return mPrimaryActionView;
- }
-
- /**
- * Retrieves the "secondary" action button (eg. "Cancel" or "More Info").
- *
- * @return The view.
- */
- public View getSecondaryActionView() {
- return mSecondaryActionView;
- }
-
- @NeededForTesting
- public static PromoCardViewHolder createForTest(Context context) {
- PromoCardViewHolder viewHolder = new PromoCardViewHolder(new View(context));
- viewHolder.mPrimaryActionView = new View(context);
- viewHolder.mSecondaryActionView = new View(context);
- return viewHolder;
- }
-}
diff --git a/src/com/android/dialer/calllog/VisualVoicemailCallLogFragment.java b/src/com/android/dialer/calllog/VisualVoicemailCallLogFragment.java
deleted file mode 100644
index 311ff7dc5..000000000
--- a/src/com/android/dialer/calllog/VisualVoicemailCallLogFragment.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2016 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.calllog;
-
-import android.database.ContentObserver;
-import android.os.Bundle;
-import android.provider.CallLog;
-import android.provider.VoicemailContract;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.dialer.R;
-import com.android.dialer.list.ListsFragment;
-import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
-
-public class VisualVoicemailCallLogFragment extends CallLogFragment {
-
- private VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
- private final ContentObserver mVoicemailStatusObserver = new CustomContentObserver();
-
- public VisualVoicemailCallLogFragment() {
- super(CallLog.Calls.VOICEMAIL_TYPE);
- }
-
- @Override
- public void onCreate(Bundle state) {
- super.onCreate(state);
- mVoicemailPlaybackPresenter = VoicemailPlaybackPresenter.getInstance(getActivity(), state);
- getActivity().getContentResolver().registerContentObserver(
- VoicemailContract.Status.CONTENT_URI, true, mVoicemailStatusObserver);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
- View view = inflater.inflate(R.layout.call_log_fragment, container, false);
- setupView(view, mVoicemailPlaybackPresenter);
- return view;
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mVoicemailPlaybackPresenter.onResume();
- }
-
- @Override
- public void onPause() {
- mVoicemailPlaybackPresenter.onPause();
- super.onPause();
- }
-
- @Override
- public void onDestroy() {
- mVoicemailPlaybackPresenter.onDestroy();
- getActivity().getContentResolver().unregisterContentObserver(mVoicemailStatusObserver);
- super.onDestroy();
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mVoicemailPlaybackPresenter.onSaveInstanceState(outState);
- }
-
- @Override
- public void fetchCalls() {
- super.fetchCalls();
- ((ListsFragment) getParentFragment()).updateTabUnreadCounts();
- }
-}
diff --git a/src/com/android/dialer/calllog/VoicemailQueryHandler.java b/src/com/android/dialer/calllog/VoicemailQueryHandler.java
deleted file mode 100644
index c6e644c32..000000000
--- a/src/com/android/dialer/calllog/VoicemailQueryHandler.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2011 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.calllog;
-
-import android.content.AsyncQueryHandler;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.provider.CallLog.Calls;
-import android.util.Log;
-
-/**
- * Handles asynchronous queries to the call log for voicemail.
- */
-public class VoicemailQueryHandler extends AsyncQueryHandler {
- private static final String TAG = "VoicemailQueryHandler";
-
- /** The token for the query to mark all new voicemails as old. */
- private static final int UPDATE_MARK_VOICEMAILS_AS_OLD_TOKEN = 50;
- private Context mContext;
-
- public VoicemailQueryHandler(Context context, ContentResolver contentResolver) {
- super(contentResolver);
- mContext = context;
- }
-
- /** Updates all new voicemails to mark them as old. */
- public void markNewVoicemailsAsOld() {
- // Mark all "new" voicemails as not new anymore.
- StringBuilder where = new StringBuilder();
- where.append(Calls.NEW);
- where.append(" = 1 AND ");
- where.append(Calls.TYPE);
- where.append(" = ?");
-
- ContentValues values = new ContentValues(1);
- values.put(Calls.NEW, "0");
-
- startUpdate(UPDATE_MARK_VOICEMAILS_AS_OLD_TOKEN, null, Calls.CONTENT_URI_WITH_VOICEMAIL,
- values, where.toString(), new String[]{ Integer.toString(Calls.VOICEMAIL_TYPE) });
- }
-
- @Override
- protected void onUpdateComplete(int token, Object cookie, int result) {
- if (token == UPDATE_MARK_VOICEMAILS_AS_OLD_TOKEN) {
- if (mContext != null) {
- Intent serviceIntent = new Intent(mContext, CallLogNotificationsService.class);
- serviceIntent.setAction(
- CallLogNotificationsService.ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS);
- mContext.startService(serviceIntent);
- } else {
- Log.w(TAG, "Unknown update completed: ignoring: " + token);
- }
- }
- }
-}
diff --git a/src/com/android/dialer/calllog/calllogcache/CallLogCache.java b/src/com/android/dialer/calllog/calllogcache/CallLogCache.java
deleted file mode 100644
index dc1217cf5..000000000
--- a/src/com/android/dialer/calllog/calllogcache/CallLogCache.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.calllog.calllogcache;
-
-import android.content.Context;
-import android.telecom.PhoneAccountHandle;
-
-import com.android.contacts.common.CallUtil;
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.dialer.calllog.CallLogAdapter;
-
-/**
- * This is the base class for the CallLogCaches.
- *
- * Keeps a cache of recently made queries to the Telecom/Telephony processes. The aim of this cache
- * is to reduce the number of cross-process requests to TelecomManager, which can negatively affect
- * performance.
- *
- * This is designed with the specific use case of the {@link CallLogAdapter} in mind.
- */
-public abstract class CallLogCache {
- // TODO: Dialer should be fixed so as not to check isVoicemail() so often but at the time of
- // this writing, that was a much larger undertaking than creating this cache.
-
- protected final Context mContext;
-
- private boolean mHasCheckedForVideoEnabled;
- private boolean mIsVideoEnabled;
-
- public CallLogCache(Context context) {
- mContext = context;
- }
-
- /**
- * Return the most compatible version of the TelecomCallLogCache.
- */
- public static CallLogCache getCallLogCache(Context context) {
- if (CompatUtils.isClassAvailable("android.telecom.PhoneAccountHandle")) {
- return new CallLogCacheLollipopMr1(context);
- }
- return new CallLogCacheLollipop(context);
- }
-
- public void reset() {
- mHasCheckedForVideoEnabled = false;
- mIsVideoEnabled = false;
- }
-
- /**
- * Returns true if the given number is the number of the configured voicemail. To be able to
- * mock-out this, it is not a static method.
- */
- public abstract boolean isVoicemailNumber(PhoneAccountHandle accountHandle,
- CharSequence number);
-
- public boolean isVideoEnabled() {
- if (!mHasCheckedForVideoEnabled) {
- mIsVideoEnabled = CallUtil.isVideoEnabled(mContext);
- mHasCheckedForVideoEnabled = true;
- }
- return mIsVideoEnabled;
- }
-
- /**
- * Extract account label from PhoneAccount object.
- */
- public abstract String getAccountLabel(PhoneAccountHandle accountHandle);
-
- /**
- * Extract account color from PhoneAccount object.
- */
- public abstract int getAccountColor(PhoneAccountHandle accountHandle);
-
- /**
- * Determines if the PhoneAccount supports specifying a call subject (i.e. calling with a note)
- * for outgoing calls.
- *
- * @param accountHandle The PhoneAccount handle.
- * @return {@code true} if calling with a note is supported, {@code false} otherwise.
- */
- public abstract boolean doesAccountSupportCallSubject(PhoneAccountHandle accountHandle);
-}
diff --git a/src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipop.java b/src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipop.java
deleted file mode 100644
index 770cc9d3e..000000000
--- a/src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipop.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.calllog.calllogcache;
-
-import android.content.Context;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-
-/**
- * This is a compatibility class for the CallLogCache for versions of dialer before Lollipop Mr1
- * (the introduction of phone accounts).
- *
- * This class should not be initialized directly and instead be acquired from
- * {@link CallLogCache#getCallLogCache}.
- */
-class CallLogCacheLollipop extends CallLogCache {
- private String mVoicemailNumber;
-
- /* package */ CallLogCacheLollipop(Context context) {
- super(context);
- }
-
- @Override
- public boolean isVoicemailNumber(PhoneAccountHandle accountHandle, CharSequence number) {
- if (TextUtils.isEmpty(number)) {
- return false;
- }
-
- String numberString = number.toString();
-
- if (!TextUtils.isEmpty(mVoicemailNumber)) {
- return PhoneNumberUtils.compare(numberString, mVoicemailNumber);
- }
-
- if (PhoneNumberUtils.isVoiceMailNumber(numberString)) {
- mVoicemailNumber = numberString;
- return true;
- }
-
- return false;
- }
-
- @Override
- public String getAccountLabel(PhoneAccountHandle accountHandle) {
- return null;
- }
-
- @Override
- public int getAccountColor(PhoneAccountHandle accountHandle) {
- return PhoneAccount.NO_HIGHLIGHT_COLOR;
- }
-
- @Override
- public boolean doesAccountSupportCallSubject(PhoneAccountHandle accountHandle) {
- return false;
- }
-}
diff --git a/src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipopMr1.java b/src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipopMr1.java
deleted file mode 100644
index d1e3f7bcf..000000000
--- a/src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipopMr1.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2013 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.calllog.calllogcache;
-
-import android.content.Context;
-import android.telecom.PhoneAccountHandle;
-import android.text.TextUtils;
-import android.util.Pair;
-
-import com.android.dialer.calllog.PhoneAccountUtils;
-import com.android.dialer.util.PhoneNumberUtil;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * This is the CallLogCache for versions of dialer Lollipop Mr1 and above with support for
- * multi-SIM devices.
- *
- * This class should not be initialized directly and instead be acquired from
- * {@link CallLogCache#getCallLogCache}.
- */
-class CallLogCacheLollipopMr1 extends CallLogCache {
- // Maps from a phone-account/number pair to a boolean because multiple numbers could return true
- // for the voicemail number if those numbers are not pre-normalized.
- private final Map<Pair<PhoneAccountHandle, CharSequence>, Boolean> mVoicemailQueryCache =
- new HashMap<>();
- private final Map<PhoneAccountHandle, String> mPhoneAccountLabelCache = new HashMap<>();
- private final Map<PhoneAccountHandle, Integer> mPhoneAccountColorCache = new HashMap<>();
- private final Map<PhoneAccountHandle, Boolean> mPhoneAccountCallWithNoteCache = new HashMap<>();
-
- /* package */ CallLogCacheLollipopMr1(Context context) {
- super(context);
- }
-
- @Override
- public void reset() {
- mVoicemailQueryCache.clear();
- mPhoneAccountLabelCache.clear();
- mPhoneAccountColorCache.clear();
- mPhoneAccountCallWithNoteCache.clear();
-
- super.reset();
- }
-
- @Override
- public boolean isVoicemailNumber(PhoneAccountHandle accountHandle, CharSequence number) {
- if (TextUtils.isEmpty(number)) {
- return false;
- }
-
- Pair<PhoneAccountHandle, CharSequence> key = new Pair<>(accountHandle, number);
- if (mVoicemailQueryCache.containsKey(key)) {
- return mVoicemailQueryCache.get(key);
- } else {
- Boolean isVoicemail =
- PhoneNumberUtil.isVoicemailNumber(mContext, accountHandle, number.toString());
- mVoicemailQueryCache.put(key, isVoicemail);
- return isVoicemail;
- }
- }
-
- @Override
- public String getAccountLabel(PhoneAccountHandle accountHandle) {
- if (mPhoneAccountLabelCache.containsKey(accountHandle)) {
- return mPhoneAccountLabelCache.get(accountHandle);
- } else {
- String label = PhoneAccountUtils.getAccountLabel(mContext, accountHandle);
- mPhoneAccountLabelCache.put(accountHandle, label);
- return label;
- }
- }
-
- @Override
- public int getAccountColor(PhoneAccountHandle accountHandle) {
- if (mPhoneAccountColorCache.containsKey(accountHandle)) {
- return mPhoneAccountColorCache.get(accountHandle);
- } else {
- Integer color = PhoneAccountUtils.getAccountColor(mContext, accountHandle);
- mPhoneAccountColorCache.put(accountHandle, color);
- return color;
- }
- }
-
- @Override
- public boolean doesAccountSupportCallSubject(PhoneAccountHandle accountHandle) {
- if (mPhoneAccountCallWithNoteCache.containsKey(accountHandle)) {
- return mPhoneAccountCallWithNoteCache.get(accountHandle);
- } else {
- Boolean supportsCallWithNote =
- PhoneAccountUtils.getAccountSupportsCallSubject(mContext, accountHandle);
- mPhoneAccountCallWithNoteCache.put(accountHandle, supportsCallWithNote);
- return supportsCallWithNote;
- }
- }
-}
diff --git a/src/com/android/dialer/compat/DialerCompatUtils.java b/src/com/android/dialer/compat/DialerCompatUtils.java
deleted file mode 100644
index a9c9c5319..000000000
--- a/src/com/android/dialer/compat/DialerCompatUtils.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.compat;
-
-import com.android.contacts.common.compat.CompatUtils;
-
-public final class DialerCompatUtils {
- /**
- * Determines if this version has access to the
- * {@link android.provider.CallLog.Calls.CACHED_PHOTO_URI} column
- *
- * @return {@code true} if {@link android.provider.CallLog.Calls.CACHED_PHOTO_URI} is available,
- * {@code false} otherwise
- */
- public static boolean isCallsCachedPhotoUriCompatible() {
- return CompatUtils.isMarshmallowCompatible();
- }
-} \ No newline at end of file
diff --git a/src/com/android/dialer/compat/FilteredNumberCompat.java b/src/com/android/dialer/compat/FilteredNumberCompat.java
deleted file mode 100644
index 8d8e9a2a5..000000000
--- a/src/com/android/dialer/compat/FilteredNumberCompat.java
+++ /dev/null
@@ -1,393 +0,0 @@
-/*
- * Copyright (C) 2016 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.compat;
-
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
-
-import android.app.FragmentManager;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.UserManager;
-import android.preference.PreferenceManager;
-import android.support.annotation.Nullable;
-import android.telecom.TelecomManager;
-import android.telephony.PhoneNumberUtils;
-import android.util.Log;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.TelecomManagerUtil;
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.dialer.DialerApplication;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnCheckBlockedListener;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumber;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberSources;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberTypes;
-import com.android.dialer.filterednumber.BlockNumberDialogFragment;
-import com.android.dialer.filterednumber.BlockNumberDialogFragment.Callback;
-import com.android.dialer.filterednumber.BlockedNumbersMigrator;
-import com.android.dialer.filterednumber.BlockedNumbersSettingsActivity;
-import com.android.dialer.filterednumber.MigrateBlockedNumbersDialogFragment;
-import com.android.dialerbind.ObjectFactory;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Compatibility class to encapsulate logic to switch between call blocking using
- * {@link com.android.dialer.database.FilteredNumberContract} and using
- * {@link android.provider.BlockedNumberContract}. This class should be used rather than explicitly
- * referencing columns from either contract class in situations where both blocking solutions may be
- * used.
- */
-public class FilteredNumberCompat {
-
- private static final String TAG = "FilteredNumberCompat";
-
- protected static final String HAS_MIGRATED_TO_NEW_BLOCKING_KEY = "migratedToNewBlocking";
-
- private static Boolean isEnabledForTest;
-
- private static Context contextForTest;
-
- /**
- * @return The column name for ID in the filtered number database.
- */
- public static String getIdColumnName() {
- return useNewFiltering() ? BlockedNumbersSdkCompat._ID : FilteredNumberColumns._ID;
- }
-
- /**
- * @return The column name for type in the filtered number database. Will be {@code null} for
- * the framework blocking implementation.
- */
- @Nullable
- public static String getTypeColumnName() {
- return useNewFiltering() ? null : FilteredNumberColumns.TYPE;
- }
-
- /**
- * @return The column name for source in the filtered number database. Will be {@code null} for
- * the framework blocking implementation
- */
- @Nullable
- public static String getSourceColumnName() {
- return useNewFiltering() ? null : FilteredNumberColumns.SOURCE;
- }
-
- /**
- * @return The column name for the original number in the filtered number database.
- */
- public static String getOriginalNumberColumnName() {
- return useNewFiltering() ? BlockedNumbersSdkCompat.COLUMN_ORIGINAL_NUMBER
- : FilteredNumberColumns.NUMBER;
- }
-
- /**
- * @return The column name for country iso in the filtered number database. Will be {@code null}
- * the framework blocking implementation
- */
- @Nullable
- public static String getCountryIsoColumnName() {
- return useNewFiltering() ? null : FilteredNumberColumns.COUNTRY_ISO;
- }
-
- /**
- * @return The column name for the e164 formatted number in the filtered number database.
- */
- public static String getE164NumberColumnName() {
- return useNewFiltering() ? BlockedNumbersSdkCompat.E164_NUMBER
- : FilteredNumberColumns.NORMALIZED_NUMBER;
- }
-
- /**
- * @return {@code true} if the current SDK version supports using new filtering, {@code false}
- * otherwise.
- */
- public static boolean canUseNewFiltering() {
- if (isEnabledForTest != null) {
- return CompatUtils.isNCompatible() && isEnabledForTest;
- }
- return CompatUtils.isNCompatible() && ObjectFactory
- .isNewBlockingEnabled(DialerApplication.getContext());
- }
-
- /**
- * @return {@code true} if the new filtering should be used, i.e. it's enabled and any necessary
- * migration has been performed, {@code false} otherwise.
- */
- public static boolean useNewFiltering() {
- return canUseNewFiltering() && hasMigratedToNewBlocking();
- }
-
- /**
- * @return {@code true} if the user has migrated to use
- * {@link android.provider.BlockedNumberContract} blocking, {@code false} otherwise.
- */
- public static boolean hasMigratedToNewBlocking() {
- return PreferenceManager.getDefaultSharedPreferences(DialerApplication.getContext())
- .getBoolean(HAS_MIGRATED_TO_NEW_BLOCKING_KEY, false);
- }
-
- /**
- * Called to inform this class whether the user has fully migrated to use
- * {@link android.provider.BlockedNumberContract} blocking or not.
- *
- * @param hasMigrated {@code true} if the user has migrated, {@code false} otherwise.
- */
- @NeededForTesting
- public static void setHasMigratedToNewBlocking(boolean hasMigrated) {
- PreferenceManager.getDefaultSharedPreferences(
- MoreObjects.firstNonNull(contextForTest, DialerApplication.getContext())).edit()
- .putBoolean(HAS_MIGRATED_TO_NEW_BLOCKING_KEY, hasMigrated).apply();
- }
-
- @NeededForTesting
- public static void setIsEnabledForTest(Boolean isEnabled) {
- isEnabledForTest = isEnabled;
- }
-
- @NeededForTesting
- public static void setContextForTest(Context context) {
- contextForTest = context;
- }
-
- /**
- * Gets the content {@link Uri} for number filtering.
- *
- * @param id The optional id to append with the base content uri.
- * @return The Uri for number filtering.
- */
- public static Uri getContentUri(@Nullable Integer id) {
- if (id == null) {
- return getBaseUri();
- }
- return ContentUris.withAppendedId(getBaseUri(), id);
- }
-
-
- private static Uri getBaseUri() {
- return useNewFiltering() ? BlockedNumbersSdkCompat.CONTENT_URI : FilteredNumber.CONTENT_URI;
- }
-
- /**
- * Removes any null column names from the given projection array. This method is intended to be
- * used to strip out any column names that aren't available in every version of number blocking.
- * Example:
- * {@literal
- * getContext().getContentResolver().query(
- * someUri,
- * // Filtering ensures that no non-existant columns are queried
- * FilteredNumberCompat.filter(new String[] {FilteredNumberCompat.getIdColumnName(),
- * FilteredNumberCompat.getTypeColumnName()},
- * FilteredNumberCompat.getE164NumberColumnName() + " = ?",
- * new String[] {e164Number});
- * }
- *
- * @param projection The projection array.
- * @return The filtered projection array.
- */
- @Nullable
- public static String[] filter(@Nullable String[] projection) {
- if (projection == null) {
- return null;
- }
- List<String> filtered = new ArrayList<>();
- for (String column : projection) {
- if (column != null) {
- filtered.add(column);
- }
- }
- return filtered.toArray(new String[filtered.size()]);
- }
-
- /**
- * Creates a new {@link ContentValues} suitable for inserting in the filtered number table.
- *
- * @param number The unformatted number to insert.
- * @param e164Number (optional) The number to insert formatted to E164 standard.
- * @param countryIso (optional) The country iso to use to format the number.
- * @return The ContentValues to insert.
- * @throws NullPointerException If number is null.
- */
- public static ContentValues newBlockNumberContentValues(String number,
- @Nullable String e164Number, @Nullable String countryIso) {
- ContentValues contentValues = new ContentValues();
- contentValues.put(getOriginalNumberColumnName(), Preconditions.checkNotNull(number));
- if (!useNewFiltering()) {
- if (e164Number == null) {
- e164Number = PhoneNumberUtils.formatNumberToE164(number, countryIso);
- }
- contentValues.put(getE164NumberColumnName(), e164Number);
- contentValues.put(getCountryIsoColumnName(), countryIso);
- contentValues.put(getTypeColumnName(), FilteredNumberTypes.BLOCKED_NUMBER);
- contentValues.put(getSourceColumnName(), FilteredNumberSources.USER);
- }
- return contentValues;
- }
-
- /**
- * Shows block number migration dialog if necessary.
- *
- * @param fragmentManager The {@link FragmentManager} used to show fragments.
- * @param listener The {@link BlockedNumbersMigrator.Listener} to call when migration is
- * complete.
- * @return boolean True if migration dialog is shown.
- */
- public static boolean maybeShowBlockNumberMigrationDialog(
- ContentResolver contentResolver, FragmentManager fragmentManager,
- BlockedNumbersMigrator.Listener listener) {
- if (shouldShowMigrationDialog(true)) {
- Log.i(TAG, "maybeShowBlockNumberMigrationDialog - showing migration dialog");
- MigrateBlockedNumbersDialogFragment
- .newInstance(new BlockedNumbersMigrator(contentResolver), listener)
- .show(fragmentManager, "MigrateBlockedNumbers");
- return true;
- }
- return false;
- }
-
- /**
- * Shows the flow of {@link android.app.DialogFragment}s for blocking or unblocking numbers.
- *
- * @param blockId The id into the blocked numbers database.
- * @param number The number to block or unblock.
- * @param countryIso The countryIso used to format the given number.
- * @param displayNumber The form of the number to block, suitable for displaying.
- * @param parentViewId The id for the containing view of the Dialog.
- * @param fragmentManager The {@link FragmentManager} used to show fragments.
- * @param callback (optional) The {@link Callback} to call when the block or unblock operation
- * is complete.
- */
- public static void showBlockNumberDialogFlow(final ContentResolver contentResolver,
- final Integer blockId, final String number, final String countryIso,
- final String displayNumber, final Integer parentViewId,
- final FragmentManager fragmentManager, @Nullable final Callback callback) {
- Log.i(TAG, "showBlockNumberDialogFlow - start");
- // If the user is blocking a number and isn't using the framework solution when they
- // should be, show the migration dialog
- if (shouldShowMigrationDialog(blockId == null)) {
- Log.i(TAG, "showBlockNumberDialogFlow - showing migration dialog");
- MigrateBlockedNumbersDialogFragment
- .newInstance(new BlockedNumbersMigrator(contentResolver), newMigrationListener(
- DialerApplication.getContext().getContentResolver(), number, countryIso,
- displayNumber, parentViewId, fragmentManager, callback))
- .show(fragmentManager, "MigrateBlockedNumbers");
- return;
- }
- Log.i(TAG, "showBlockNumberDialogFlow - showing block number dialog");
- BlockNumberDialogFragment
- .show(blockId, number, countryIso, displayNumber, parentViewId, fragmentManager,
- callback);
- }
-
- private static boolean shouldShowMigrationDialog(boolean isBlocking) {
- return isBlocking && canUseNewFiltering() && !hasMigratedToNewBlocking();
- }
-
- private static BlockedNumbersMigrator.Listener newMigrationListener(
- final ContentResolver contentResolver, final String number, final String countryIso,
- final String displayNumber, final Integer parentViewId,
- final FragmentManager fragmentManager, @Nullable final Callback callback) {
- return new BlockedNumbersMigrator.Listener() {
- @Override
- public void onComplete() {
- Log.i(TAG, "showBlockNumberDialogFlow - listener showing block number dialog");
- if (!hasMigratedToNewBlocking()) {
- Log.i(TAG, "showBlockNumberDialogFlow - migration failed");
- return;
- }
- /*
- * Edge case to cover here: if the user initiated the migration workflow with a
- * number that's already blocked in the framework, don't show the block number
- * dialog. Doing so would allow them to block the same number twice, causing a
- * crash.
- */
- new FilteredNumberAsyncQueryHandler(contentResolver).isBlockedNumber(
- new OnCheckBlockedListener() {
- @Override
- public void onCheckComplete(Integer id) {
- if (id != null) {
- Log.i(TAG,
- "showBlockNumberDialogFlow - number already blocked");
- return;
- }
- Log.i(TAG, "showBlockNumberDialogFlow - need to block number");
- BlockNumberDialogFragment
- .show(null, number, countryIso, displayNumber, parentViewId,
- fragmentManager, callback);
- }
- }, number, countryIso);
- }
- };
- }
-
- /**
- * Creates the {@link Intent} which opens the blocked numbers management interface.
- *
- * @param context The {@link Context}.
- * @return The intent.
- */
- public static Intent createManageBlockedNumbersIntent(Context context) {
- if (canUseNewFiltering() && hasMigratedToNewBlocking()) {
- return TelecomManagerUtil.createManageBlockedNumbersIntent(
- (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE));
- }
- return new Intent(context, BlockedNumbersSettingsActivity.class);
- }
-
- /**
- * Method used to determine if block operations are possible.
- *
- * @param context The {@link Context}.
- * @return {@code true} if the app and user can block numbers, {@code false} otherwise.
- */
- public static boolean canAttemptBlockOperations(Context context) {
- if (!CompatUtils.isNCompatible()) {
- // Dialer blocking, must be primary user
- return UserManagerCompat.isSystemUser(
- (UserManager) context.getSystemService(Context.USER_SERVICE));
- }
-
- // Great Wall blocking, must be primary user and the default or system dialer
- // TODO(maxwelb): check that we're the default or system Dialer
- return BlockedNumbersSdkCompat.canCurrentUserBlockNumbers(context);
- }
-
- /**
- * Used to determine if the call blocking settings can be opened.
- *
- * @param context The {@link Context}.
- * @return {@code true} if the current user can open the call blocking settings, {@code false}
- * otherwise.
- */
- public static boolean canCurrentUserOpenBlockSettings(Context context) {
- if (!CompatUtils.isNCompatible()) {
- // Dialer blocking, must be primary user
- return UserManagerCompat.isSystemUser(
- (UserManager) context.getSystemService(Context.USER_SERVICE));
- }
- // BlockedNumberContract blocking, verify through Contract API
- return BlockedNumbersSdkCompat.canCurrentUserBlockNumbers(context);
- }
-}
diff --git a/src/com/android/dialer/compat/SettingsCompat.java b/src/com/android/dialer/compat/SettingsCompat.java
deleted file mode 100644
index 474a600a4..000000000
--- a/src/com/android/dialer/compat/SettingsCompat.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.compat;
-
-import android.content.Context;
-import android.os.Build;
-import android.os.Build.VERSION_CODES;
-import android.provider.Settings;
-
-import com.android.contacts.common.compat.SdkVersionOverride;
-
-/**
- * Compatibility class for {@link android.provider.Settings}
- */
-public class SettingsCompat {
-
- public static class System {
-
- /**
- * Compatibility version of {@link android.provider.Settings.System#canWrite(Context)}
- *
- * Note: Since checking preferences at runtime started in M, this method always returns
- * {@code true} for SDK versions prior to 23. In those versions, the app wouldn't be
- * installed if it didn't have the proper permission
- */
- public static boolean canWrite(Context context) {
- if (SdkVersionOverride.getSdkVersion(VERSION_CODES.LOLLIPOP) >= Build.VERSION_CODES.M) {
- return Settings.System.canWrite(context);
- }
- return true;
- }
- }
-
-}
diff --git a/src/com/android/dialer/compat/UserManagerCompat.java b/src/com/android/dialer/compat/UserManagerCompat.java
deleted file mode 100644
index 576703364..000000000
--- a/src/com/android/dialer/compat/UserManagerCompat.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.compat;
-
-import android.content.Context;
-import android.os.Process;
-import android.os.UserHandle;
-import android.os.UserManager;
-
-import com.android.contacts.common.compat.CompatUtils;
-
-/**
- * Compatibility class for {@link UserManager}.
- */
-public class UserManagerCompat {
- /**
- * A user id constant to indicate the "system" user of the device. Copied from
- * {@link UserHandle}.
- */
- private static final int USER_SYSTEM = 0;
- /**
- * Range of uids allocated for a user.
- */
- private static final int PER_USER_RANGE = 100000;
-
- /**
- * Used to check if this process is running under the system user. The system user is the
- * initial user that is implicitly created on first boot and hosts most of the system services.
- *
- * @return whether this process is running under the system user.
- */
- public static boolean isSystemUser(UserManager userManager) {
- if (CompatUtils.isMarshmallowCompatible()) {
- return userManager.isSystemUser();
- }
- // Adapted from {@link UserManager} and {@link UserHandle}.
- return (Process.myUid() / PER_USER_RANGE) == USER_SYSTEM;
- }
-
- /**
- * Return whether the calling user is running in an "unlocked" state. A user
- * is unlocked only after they've entered their credentials (such as a lock
- * pattern or PIN), and credential-encrypted private app data storage is
- * available.
- *
- * TODO b/26688153
- *
- * @param context the current context
- * @return {@code true} if the user is unlocked, {@code false} otherwise
- * @throws NullPointerException if context is null
- */
- public static boolean isUserUnlocked(Context context) {
- if (CompatUtils.isNCompatible()) {
- return UserManagerSdkCompat.isUserUnlocked(context);
- }
- return true;
- }
-}
diff --git a/src/com/android/dialer/contact/ContactUpdateService.java b/src/com/android/dialer/contact/ContactUpdateService.java
deleted file mode 100644
index 9edd19827..000000000
--- a/src/com/android/dialer/contact/ContactUpdateService.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2012 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.contact;
-
-import android.app.IntentService;
-import android.content.Context;
-import android.content.Intent;
-
-import com.android.contacts.common.database.ContactUpdateUtils;
-
-/**
- * Service for updating primary number on a contact.
- */
-public class ContactUpdateService extends IntentService {
-
- public static final String EXTRA_PHONE_NUMBER_DATA_ID = "phone_number_data_id";
-
- public ContactUpdateService() {
- super(ContactUpdateService.class.getSimpleName());
- setIntentRedelivery(true);
- }
-
- /** Creates an intent that sets the selected data item as super primary (default) */
- public static Intent createSetSuperPrimaryIntent(Context context, long dataId) {
- Intent serviceIntent = new Intent(context, ContactUpdateService.class);
- serviceIntent.putExtra(EXTRA_PHONE_NUMBER_DATA_ID, dataId);
- return serviceIntent;
- }
-
- @Override
- protected void onHandleIntent(Intent intent) {
- // Currently this service only handles one type of update.
- long dataId = intent.getLongExtra(EXTRA_PHONE_NUMBER_DATA_ID, -1);
-
- ContactUpdateUtils.setSuperPrimary(this, dataId);
- }
-}
diff --git a/src/com/android/dialer/contactinfo/ContactInfoCache.java b/src/com/android/dialer/contactinfo/ContactInfoCache.java
deleted file mode 100644
index 28a919430..000000000
--- a/src/com/android/dialer/contactinfo/ContactInfoCache.java
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * 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.contactinfo;
-
-import android.os.Handler;
-import android.os.Message;
-import android.text.TextUtils;
-
-import com.android.dialer.calllog.ContactInfo;
-import com.android.dialer.calllog.ContactInfoHelper;
-import com.android.dialer.util.ExpirableCache;
-import com.google.common.annotations.VisibleForTesting;
-
-import java.util.LinkedList;
-
-/**
- * This is a cache of contact details for the phone numbers in the call log. The key is the
- * phone number with the country in which the call was placed or received. The content of the
- * cache is expired (but not purged) whenever the application comes to the foreground.
- *
- * This cache queues request for information and queries for information on a background thread,
- * so {@code start()} and {@code stop()} must be called to initiate or halt that thread's exeuction
- * as needed.
- *
- * TODO: Explore whether there is a pattern to remove external dependencies for starting and
- * stopping the query thread.
- */
-public class ContactInfoCache {
- public interface OnContactInfoChangedListener {
- public void onContactInfoChanged();
- }
-
- /*
- * Handles requests for contact name and number type.
- */
- private class QueryThread extends Thread {
- private volatile boolean mDone = false;
-
- public QueryThread() {
- super("ContactInfoCache.QueryThread");
- }
-
- public void stopProcessing() {
- mDone = true;
- }
-
- @Override
- public void run() {
- boolean needRedraw = false;
- while (true) {
- // Check if thread is finished, and if so return immediately.
- if (mDone) return;
-
- // Obtain next request, if any is available.
- // Keep synchronized section small.
- ContactInfoRequest req = null;
- synchronized (mRequests) {
- if (!mRequests.isEmpty()) {
- req = mRequests.removeFirst();
- }
- }
-
- if (req != null) {
- // Process the request. If the lookup succeeds, schedule a redraw.
- needRedraw |= queryContactInfo(req.number, req.countryIso, req.callLogInfo);
- } else {
- // Throttle redraw rate by only sending them when there are
- // more requests.
- if (needRedraw) {
- needRedraw = false;
- mHandler.sendEmptyMessage(REDRAW);
- }
-
- // Wait until another request is available, or until this
- // thread is no longer needed (as indicated by being
- // interrupted).
- try {
- synchronized (mRequests) {
- mRequests.wait(1000);
- }
- } catch (InterruptedException ie) {
- // Ignore, and attempt to continue processing requests.
- }
- }
- }
- }
- }
-
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case REDRAW:
- mOnContactInfoChangedListener.onContactInfoChanged();
- break;
- case START_THREAD:
- startRequestProcessing();
- break;
- }
- }
- };
-
- private static final int REDRAW = 1;
- private static final int START_THREAD = 2;
-
- private static final int CONTACT_INFO_CACHE_SIZE = 100;
- private static final int START_PROCESSING_REQUESTS_DELAY_MS = 1000;
-
-
- /**
- * List of requests to update contact details. Each request contains a phone number to look up,
- * and the contact info currently stored in the call log for this number.
- *
- * The requests are added when displaying contacts and are processed by a background thread.
- */
- private final LinkedList<ContactInfoRequest> mRequests;
-
- private ExpirableCache<NumberWithCountryIso, ContactInfo> mCache;
-
- private ContactInfoHelper mContactInfoHelper;
- private QueryThread mContactInfoQueryThread;
- private OnContactInfoChangedListener mOnContactInfoChangedListener;
-
- public ContactInfoCache(ContactInfoHelper contactInfoHelper,
- OnContactInfoChangedListener onContactInfoChangedListener) {
- mContactInfoHelper = contactInfoHelper;
- mOnContactInfoChangedListener = onContactInfoChangedListener;
-
- mRequests = new LinkedList<ContactInfoRequest>();
- mCache = ExpirableCache.create(CONTACT_INFO_CACHE_SIZE);
- }
-
- public ContactInfo getValue(String number, String countryIso, ContactInfo cachedContactInfo) {
- NumberWithCountryIso numberCountryIso = new NumberWithCountryIso(number, countryIso);
- ExpirableCache.CachedValue<ContactInfo> cachedInfo =
- mCache.getCachedValue(numberCountryIso);
- ContactInfo info = cachedInfo == null ? null : cachedInfo.getValue();
- if (cachedInfo == null) {
- mCache.put(numberCountryIso, ContactInfo.EMPTY);
- // Use the cached contact info from the call log.
- info = cachedContactInfo;
- // The db request should happen on a non-UI thread.
- // Request the contact details immediately since they are currently missing.
- enqueueRequest(number, countryIso, cachedContactInfo, true);
- // We will format the phone number when we make the background request.
- } else {
- if (cachedInfo.isExpired()) {
- // The contact info is no longer up to date, we should request it. However, we
- // do not need to request them immediately.
- enqueueRequest(number, countryIso, cachedContactInfo, false);
- } else if (!callLogInfoMatches(cachedContactInfo, info)) {
- // The call log information does not match the one we have, look it up again.
- // We could simply update the call log directly, but that needs to be done in a
- // background thread, so it is easier to simply request a new lookup, which will, as
- // a side-effect, update the call log.
- enqueueRequest(number, countryIso, cachedContactInfo, false);
- }
-
- if (info == ContactInfo.EMPTY) {
- // Use the cached contact info from the call log.
- info = cachedContactInfo;
- }
- }
- return info;
- }
-
- /**
- * Queries the appropriate content provider for the contact associated with the number.
- *
- * Upon completion it also updates the cache in the call log, if it is different from
- * {@code callLogInfo}.
- *
- * The number might be either a SIP address or a phone number.
- *
- * It returns true if it updated the content of the cache and we should therefore tell the
- * view to update its content.
- */
- private boolean queryContactInfo(String number, String countryIso, ContactInfo callLogInfo) {
- final ContactInfo info = mContactInfoHelper.lookupNumber(number, countryIso);
-
- if (info == null) {
- // The lookup failed, just return without requesting to update the view.
- return false;
- }
-
- // Check the existing entry in the cache: only if it has changed we should update the
- // view.
- NumberWithCountryIso numberCountryIso = new NumberWithCountryIso(number, countryIso);
- ContactInfo existingInfo = mCache.getPossiblyExpired(numberCountryIso);
-
- final boolean isRemoteSource = info.sourceType != 0;
-
- // Don't force redraw if existing info in the cache is equal to {@link ContactInfo#EMPTY}
- // to avoid updating the data set for every new row that is scrolled into view.
- // see (https://googleplex-android-review.git.corp.google.com/#/c/166680/)
-
- // Exception: Photo uris for contacts from remote sources are not cached in the call log
- // cache, so we have to force a redraw for these contacts regardless.
- boolean updated = (existingInfo != ContactInfo.EMPTY || isRemoteSource) &&
- !info.equals(existingInfo);
-
- // Store the data in the cache so that the UI thread can use to display it. Store it
- // even if it has not changed so that it is marked as not expired.
- mCache.put(numberCountryIso, info);
-
- // Update the call log even if the cache it is up-to-date: it is possible that the cache
- // contains the value from a different call log entry.
- mContactInfoHelper.updateCallLogContactInfo(number, countryIso, info, callLogInfo);
- return updated;
- }
-
- /**
- * After a delay, start the thread to begin processing requests. We perform lookups on a
- * background thread, but this must be called to indicate the thread should be running.
- */
- public void start() {
- // Schedule a thread-creation message if the thread hasn't been created yet, as an
- // optimization to queue fewer messages.
- if (mContactInfoQueryThread == null) {
- // TODO: Check whether this delay before starting to process is necessary.
- mHandler.sendEmptyMessageDelayed(START_THREAD, START_PROCESSING_REQUESTS_DELAY_MS);
- }
- }
-
- /**
- * Stops the thread and clears the queue of messages to process. This cleans up the thread
- * for lookups so that it is not perpetually running.
- */
- public void stop() {
- stopRequestProcessing();
- }
-
- /**
- * Starts a background thread to process contact-lookup requests, unless one
- * has already been started.
- */
- private synchronized void startRequestProcessing() {
- // For unit-testing.
- if (mRequestProcessingDisabled) return;
-
- // If a thread is already started, don't start another.
- if (mContactInfoQueryThread != null) {
- return;
- }
-
- mContactInfoQueryThread = new QueryThread();
- mContactInfoQueryThread.setPriority(Thread.MIN_PRIORITY);
- mContactInfoQueryThread.start();
- }
-
- public void invalidate() {
- mCache.expireAll();
- stopRequestProcessing();
- }
-
- /**
- * Stops the background thread that processes updates and cancels any
- * pending requests to start it.
- */
- private synchronized void stopRequestProcessing() {
- // Remove any pending requests to start the processing thread.
- mHandler.removeMessages(START_THREAD);
- if (mContactInfoQueryThread != null) {
- // Stop the thread; we are finished with it.
- mContactInfoQueryThread.stopProcessing();
- mContactInfoQueryThread.interrupt();
- mContactInfoQueryThread = null;
- }
- }
-
- /**
- * Enqueues a request to look up the contact details for the given phone number.
- * <p>
- * It also provides the current contact info stored in the call log for this number.
- * <p>
- * If the {@code immediate} parameter is true, it will start immediately the thread that looks
- * up the contact information (if it has not been already started). Otherwise, it will be
- * started with a delay. See {@link #START_PROCESSING_REQUESTS_DELAY_MILLIS}.
- */
- protected void enqueueRequest(String number, String countryIso, ContactInfo callLogInfo,
- boolean immediate) {
- ContactInfoRequest request = new ContactInfoRequest(number, countryIso, callLogInfo);
- synchronized (mRequests) {
- if (!mRequests.contains(request)) {
- mRequests.add(request);
- mRequests.notifyAll();
- }
- }
- if (immediate) {
- startRequestProcessing();
- }
- }
-
- /**
- * Checks whether the contact info from the call log matches the one from the contacts db.
- */
- private boolean callLogInfoMatches(ContactInfo callLogInfo, ContactInfo info) {
- // The call log only contains a subset of the fields in the contacts db. Only check those.
- return TextUtils.equals(callLogInfo.name, info.name)
- && callLogInfo.type == info.type
- && TextUtils.equals(callLogInfo.label, info.label);
- }
-
- private volatile boolean mRequestProcessingDisabled = false;
-
- /**
- * Sets whether processing of requests for contact details should be enabled.
- */
- public void disableRequestProcessing() {
- mRequestProcessingDisabled = true;
- }
-
- @VisibleForTesting
- public void injectContactInfoForTest(
- String number, String countryIso, ContactInfo contactInfo) {
- NumberWithCountryIso numberCountryIso = new NumberWithCountryIso(number, countryIso);
- mCache.put(numberCountryIso, contactInfo);
- }
-}
diff --git a/src/com/android/dialer/contactinfo/ContactInfoRequest.java b/src/com/android/dialer/contactinfo/ContactInfoRequest.java
deleted file mode 100644
index ec5c1198e..000000000
--- a/src/com/android/dialer/contactinfo/ContactInfoRequest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.contactinfo;
-
-import android.text.TextUtils;
-
-import com.android.dialer.calllog.ContactInfo;
-import com.google.common.base.Objects;
-
-/**
- * A request for contact details for the given number, used by the ContactInfoCache.
- */
-public final class ContactInfoRequest {
- /** The number to look-up. */
- public final String number;
- /** The country in which a call to or from this number was placed or received. */
- public final String countryIso;
- /** The cached contact information stored in the call log. */
- public final ContactInfo callLogInfo;
-
- public ContactInfoRequest(String number, String countryIso, ContactInfo callLogInfo) {
- this.number = number;
- this.countryIso = countryIso;
- this.callLogInfo = callLogInfo;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (!(obj instanceof ContactInfoRequest)) return false;
-
- ContactInfoRequest other = (ContactInfoRequest) obj;
-
- if (!TextUtils.equals(number, other.number)) return false;
- if (!TextUtils.equals(countryIso, other.countryIso)) return false;
- if (!Objects.equal(callLogInfo, other.callLogInfo)) return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((callLogInfo == null) ? 0 : callLogInfo.hashCode());
- result = prime * result + ((countryIso == null) ? 0 : countryIso.hashCode());
- result = prime * result + ((number == null) ? 0 : number.hashCode());
- return result;
- }
-}
diff --git a/src/com/android/dialer/contactinfo/ContactPhotoLoader.java b/src/com/android/dialer/contactinfo/ContactPhotoLoader.java
deleted file mode 100644
index f36c438f6..000000000
--- a/src/com/android/dialer/contactinfo/ContactPhotoLoader.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2016 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.contactinfo;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.provider.MediaStore;
-import android.support.annotation.Nullable;
-import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
-import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
-import android.util.Log;
-
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.lettertiles.LetterTileDrawable;
-import com.android.dialer.R;
-import com.android.dialer.calllog.ContactInfo;
-import com.android.dialer.calllog.ContactInfoHelper;
-import com.android.dialer.util.Assert;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-
-import java.io.IOException;
-/**
- * Class to create the appropriate contact icon from a ContactInfo.
- * This class is for synchronous, blocking calls to generate bitmaps, while
- * ContactCommons.ContactPhotoManager is to cache, manage and update a ImageView asynchronously.
- */
-public class ContactPhotoLoader {
-
- private static final String TAG = "ContactPhotoLoader";
-
- private final Context mContext;
- private final ContactInfo mContactInfo;
-
- public ContactPhotoLoader(Context context, ContactInfo contactInfo) {
- mContext = Preconditions.checkNotNull(context);
- mContactInfo = Preconditions.checkNotNull(contactInfo);
- }
-
- /**
- * Create a contact photo icon bitmap appropriate for the ContactInfo.
- */
- public Bitmap loadPhotoIcon() {
- Assert.assertNotUiThread("ContactPhotoLoader#loadPhotoIcon called on UI thread");
- int photoSize = mContext.getResources().getDimensionPixelSize(R.dimen.contact_photo_size);
- return drawableToBitmap(getIcon(), photoSize, photoSize);
- }
-
- @VisibleForTesting
- Drawable getIcon() {
- Drawable drawable = createPhotoIconDrawable();
- if (drawable == null) {
- drawable = createLetterTileDrawable();
- }
- return drawable;
- }
-
- /**
- * @return a {@link Drawable} of circular photo icon if the photo can be loaded, {@code null}
- * otherwise.
- */
- @Nullable
- private Drawable createPhotoIconDrawable() {
- if (mContactInfo.photoUri == null) {
- return null;
- }
- try {
- Bitmap bitmap = MediaStore.Images.Media.getBitmap(mContext.getContentResolver(),
- mContactInfo.photoUri);
- final RoundedBitmapDrawable drawable =
- RoundedBitmapDrawableFactory.create(mContext.getResources(), bitmap);
- drawable.setAntiAlias(true);
- drawable.setCornerRadius(bitmap.getHeight() / 2);
- return drawable;
- } catch (IOException e) {
- Log.e(TAG, e.toString());
- return null;
- }
- }
-
- /**
- * @return a {@link LetterTileDrawable} based on the ContactInfo.
- */
- private Drawable createLetterTileDrawable() {
- LetterTileDrawable drawable = new LetterTileDrawable(mContext.getResources());
- drawable.setIsCircular(true);
- ContactInfoHelper helper =
- new ContactInfoHelper(mContext, GeoUtil.getCurrentCountryIso(mContext));
- if (helper.isBusiness(mContactInfo.sourceType)) {
- drawable.setContactType(LetterTileDrawable.TYPE_BUSINESS);
- }
- drawable.setLetterAndColorFromContactDetails(mContactInfo.name, mContactInfo.lookupKey);
- return drawable;
- }
-
- private static Bitmap drawableToBitmap(Drawable drawable, int width, int height) {
- Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
- drawable.draw(canvas);
- return bitmap;
- }
-
-}
diff --git a/src/com/android/dialer/contactinfo/NumberWithCountryIso.java b/src/com/android/dialer/contactinfo/NumberWithCountryIso.java
deleted file mode 100644
index 1383fb7e9..000000000
--- a/src/com/android/dialer/contactinfo/NumberWithCountryIso.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.contactinfo;
-
-import android.text.TextUtils;
-
-/**
- * Stores a phone number of a call with the country code where it originally occurred. This object
- * is used as a key in the {@code ContactInfoCache}.
- *
- * The country does not necessarily specify the country of the phone number itself, but rather
- * it is the country in which the user was in when the call was placed or received.
- */
-public final class NumberWithCountryIso {
- public final String number;
- public final String countryIso;
-
- public NumberWithCountryIso(String number, String countryIso) {
- this.number = number;
- this.countryIso = countryIso;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == null) return false;
- if (!(o instanceof NumberWithCountryIso)) return false;
- NumberWithCountryIso other = (NumberWithCountryIso) o;
- return TextUtils.equals(number, other.number)
- && TextUtils.equals(countryIso, other.countryIso);
- }
-
- @Override
- public int hashCode() {
- int numberHashCode = number == null ? 0 : number.hashCode();
- int countryHashCode = countryIso == null ? 0 : countryIso.hashCode();
-
- return numberHashCode ^ countryHashCode;
- }
-}
diff --git a/src/com/android/dialer/database/DialerDatabaseHelper.java b/src/com/android/dialer/database/DialerDatabaseHelper.java
deleted file mode 100644
index 5edfb270d..000000000
--- a/src/com/android/dialer/database/DialerDatabaseHelper.java
+++ /dev/null
@@ -1,1169 +0,0 @@
-/*
- * Copyright (C) 2013 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.database;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.database.sqlite.SQLiteStatement;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.provider.BaseColumns;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.Directory;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.contacts.common.util.StopWatch;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
-import com.android.dialer.database.VoicemailArchiveContract.VoicemailArchive;
-import com.android.dialer.R;
-import com.android.dialer.dialpad.SmartDialNameMatcher;
-import com.android.dialer.dialpad.SmartDialPrefix;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Database helper for smart dial. Designed as a singleton to make sure there is
- * only one access point to the database. Provides methods to maintain, update,
- * and query the database.
- */
-public class DialerDatabaseHelper extends SQLiteOpenHelper {
- private static final String TAG = "DialerDatabaseHelper";
- private static final boolean DEBUG = false;
- private boolean mIsTestInstance = false;
-
- private static DialerDatabaseHelper sSingleton = null;
-
- private static final Object mLock = new Object();
- private static final AtomicBoolean sInUpdate = new AtomicBoolean(false);
- private final Context mContext;
-
- /**
- * SmartDial DB version ranges:
- * <pre>
- * 0-98 KitKat
- * </pre>
- */
- public static final int DATABASE_VERSION = 9;
- public static final String DATABASE_NAME = "dialer.db";
-
- /**
- * Saves the last update time of smart dial databases to shared preferences.
- */
- private static final String DATABASE_LAST_CREATED_SHARED_PREF = "com.android.dialer";
- private static final String LAST_UPDATED_MILLIS = "last_updated_millis";
- private static final String DATABASE_VERSION_PROPERTY = "database_version";
-
- private static final int MAX_ENTRIES = 20;
-
- public interface Tables {
- /** Saves a list of numbers to be blocked.*/
- static final String FILTERED_NUMBER_TABLE = "filtered_numbers_table";
- /** Saves the necessary smart dial information of all contacts. */
- static final String SMARTDIAL_TABLE = "smartdial_table";
- /** Saves all possible prefixes to refer to a contacts.*/
- static final String PREFIX_TABLE = "prefix_table";
- /** Saves all archived voicemail information. */
- static final String VOICEMAIL_ARCHIVE_TABLE = "voicemail_archive_table";
- /** Database properties for internal use */
- static final String PROPERTIES = "properties";
- }
-
- public static final Uri SMART_DIAL_UPDATED_URI =
- Uri.parse("content://com.android.dialer/smart_dial_updated");
-
- public interface SmartDialDbColumns {
- static final String _ID = "id";
- static final String DATA_ID = "data_id";
- static final String NUMBER = "phone_number";
- static final String CONTACT_ID = "contact_id";
- static final String LOOKUP_KEY = "lookup_key";
- static final String DISPLAY_NAME_PRIMARY = "display_name";
- static final String PHOTO_ID = "photo_id";
- static final String LAST_TIME_USED = "last_time_used";
- static final String TIMES_USED = "times_used";
- static final String STARRED = "starred";
- static final String IS_SUPER_PRIMARY = "is_super_primary";
- static final String IN_VISIBLE_GROUP = "in_visible_group";
- static final String IS_PRIMARY = "is_primary";
- static final String CARRIER_PRESENCE = "carrier_presence";
- static final String LAST_SMARTDIAL_UPDATE_TIME = "last_smartdial_update_time";
- }
-
- public static interface PrefixColumns extends BaseColumns {
- static final String PREFIX = "prefix";
- static final String CONTACT_ID = "contact_id";
- }
-
- public interface PropertiesColumns {
- String PROPERTY_KEY = "property_key";
- String PROPERTY_VALUE = "property_value";
- }
-
- /** Query options for querying the contact database.*/
- public static interface PhoneQuery {
- static final Uri URI = Phone.CONTENT_URI.buildUpon().
- appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
- String.valueOf(Directory.DEFAULT)).
- appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true").
- build();
-
- static final String[] PROJECTION = new String[] {
- Phone._ID, // 0
- Phone.TYPE, // 1
- Phone.LABEL, // 2
- Phone.NUMBER, // 3
- Phone.CONTACT_ID, // 4
- Phone.LOOKUP_KEY, // 5
- Phone.DISPLAY_NAME_PRIMARY, // 6
- Phone.PHOTO_ID, // 7
- Data.LAST_TIME_USED, // 8
- Data.TIMES_USED, // 9
- Contacts.STARRED, // 10
- Data.IS_SUPER_PRIMARY, // 11
- Contacts.IN_VISIBLE_GROUP, // 12
- Data.IS_PRIMARY, // 13
- Data.CARRIER_PRESENCE, // 14
- };
-
- static final int PHONE_ID = 0;
- static final int PHONE_TYPE = 1;
- static final int PHONE_LABEL = 2;
- static final int PHONE_NUMBER = 3;
- static final int PHONE_CONTACT_ID = 4;
- static final int PHONE_LOOKUP_KEY = 5;
- static final int PHONE_DISPLAY_NAME = 6;
- static final int PHONE_PHOTO_ID = 7;
- static final int PHONE_LAST_TIME_USED = 8;
- static final int PHONE_TIMES_USED = 9;
- static final int PHONE_STARRED = 10;
- static final int PHONE_IS_SUPER_PRIMARY = 11;
- static final int PHONE_IN_VISIBLE_GROUP = 12;
- static final int PHONE_IS_PRIMARY = 13;
- static final int PHONE_CARRIER_PRESENCE = 14;
-
- /** Selects only rows that have been updated after a certain time stamp.*/
- static final String SELECT_UPDATED_CLAUSE =
- Phone.CONTACT_LAST_UPDATED_TIMESTAMP + " > ?";
-
- /** Ignores contacts that have an unreasonably long lookup key. These are likely to be
- * the result of multiple (> 50) merged raw contacts, and are likely to cause
- * OutOfMemoryExceptions within SQLite, or cause memory allocation problems later on
- * when iterating through the cursor set (see b/13133579)
- */
- static final String SELECT_IGNORE_LOOKUP_KEY_TOO_LONG_CLAUSE =
- "length(" + Phone.LOOKUP_KEY + ") < 1000";
-
- static final String SELECTION = SELECT_UPDATED_CLAUSE + " AND " +
- SELECT_IGNORE_LOOKUP_KEY_TOO_LONG_CLAUSE;
- }
-
- /**
- * Query for all contacts that have been updated since the last time the smart dial database
- * was updated.
- */
- public static interface UpdatedContactQuery {
- static final Uri URI = ContactsContract.Contacts.CONTENT_URI;
-
- static final String[] PROJECTION = new String[] {
- ContactsContract.Contacts._ID // 0
- };
-
- static final int UPDATED_CONTACT_ID = 0;
-
- static final String SELECT_UPDATED_CLAUSE =
- ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP + " > ?";
- }
-
- /** Query options for querying the deleted contact database.*/
- public static interface DeleteContactQuery {
- static final Uri URI = ContactsContract.DeletedContacts.CONTENT_URI;
-
- static final String[] PROJECTION = new String[] {
- ContactsContract.DeletedContacts.CONTACT_ID, // 0
- ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP, // 1
- };
-
- static final int DELETED_CONTACT_ID = 0;
- static final int DELECTED_TIMESTAMP = 1;
-
- /** Selects only rows that have been deleted after a certain time stamp.*/
- public static final String SELECT_UPDATED_CLAUSE =
- ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP + " > ?";
- }
-
- /**
- * Gets the sorting order for the smartdial table. This computes a SQL "ORDER BY" argument by
- * composing contact status and recent contact details together.
- */
- private static interface SmartDialSortingOrder {
- /** Current contacts - those contacted within the last 3 days (in milliseconds) */
- static final long LAST_TIME_USED_CURRENT_MS = 3L * 24 * 60 * 60 * 1000;
- /** Recent contacts - those contacted within the last 30 days (in milliseconds) */
- static final long LAST_TIME_USED_RECENT_MS = 30L * 24 * 60 * 60 * 1000;
-
- /** Time since last contact. */
- static final String TIME_SINCE_LAST_USED_MS = "( ?1 - " +
- Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.LAST_TIME_USED + ")";
-
- /** Contacts that have been used in the past 3 days rank higher than contacts that have
- * been used in the past 30 days, which rank higher than contacts that have not been used
- * in recent 30 days.
- */
- static final String SORT_BY_DATA_USAGE =
- "(CASE WHEN " + TIME_SINCE_LAST_USED_MS + " < " + LAST_TIME_USED_CURRENT_MS +
- " THEN 0 " +
- " WHEN " + TIME_SINCE_LAST_USED_MS + " < " + LAST_TIME_USED_RECENT_MS +
- " THEN 1 " +
- " ELSE 2 END)";
-
- /** This sort order is similar to that used by the ContactsProvider when returning a list
- * of frequently called contacts.
- */
- static final String SORT_ORDER =
- Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.STARRED + " DESC, "
- + Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.IS_SUPER_PRIMARY + " DESC, "
- + SORT_BY_DATA_USAGE + ", "
- + Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.TIMES_USED + " DESC, "
- + Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.IN_VISIBLE_GROUP + " DESC, "
- + Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.DISPLAY_NAME_PRIMARY + ", "
- + Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.CONTACT_ID + ", "
- + Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.IS_PRIMARY + " DESC";
- }
-
- /**
- * Simple data format for a contact, containing only information needed for showing up in
- * smart dial interface.
- */
- public static class ContactNumber {
- public final long id;
- public final long dataId;
- public final String displayName;
- public final String phoneNumber;
- public final String lookupKey;
- public final long photoId;
- public final int carrierPresence;
-
- public ContactNumber(long id, long dataID, String displayName, String phoneNumber,
- String lookupKey, long photoId, int carrierPresence) {
- this.dataId = dataID;
- this.id = id;
- this.displayName = displayName;
- this.phoneNumber = phoneNumber;
- this.lookupKey = lookupKey;
- this.photoId = photoId;
- this.carrierPresence = carrierPresence;
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(id, dataId, displayName, phoneNumber, lookupKey, photoId,
- carrierPresence);
- }
-
- @Override
- public boolean equals(Object object) {
- if (this == object) {
- return true;
- }
- if (object instanceof ContactNumber) {
- final ContactNumber that = (ContactNumber) object;
- return Objects.equal(this.id, that.id)
- && Objects.equal(this.dataId, that.dataId)
- && Objects.equal(this.displayName, that.displayName)
- && Objects.equal(this.phoneNumber, that.phoneNumber)
- && Objects.equal(this.lookupKey, that.lookupKey)
- && Objects.equal(this.photoId, that.photoId)
- && Objects.equal(this.carrierPresence, that.carrierPresence);
- }
- return false;
- }
- }
-
- /**
- * Data format for finding duplicated contacts.
- */
- private class ContactMatch {
- private final String lookupKey;
- private final long id;
-
- public ContactMatch(String lookupKey, long id) {
- this.lookupKey = lookupKey;
- this.id = id;
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(lookupKey, id);
- }
-
- @Override
- public boolean equals(Object object) {
- if (this == object) {
- return true;
- }
- if (object instanceof ContactMatch) {
- final ContactMatch that = (ContactMatch) object;
- return Objects.equal(this.lookupKey, that.lookupKey)
- && Objects.equal(this.id, that.id);
- }
- return false;
- }
- }
-
- /**
- * Access function to get the singleton instance of DialerDatabaseHelper.
- */
- public static synchronized DialerDatabaseHelper getInstance(Context context) {
- if (DEBUG) {
- Log.v(TAG, "Getting Instance");
- }
- if (sSingleton == null) {
- // Use application context instead of activity context because this is a singleton,
- // and we don't want to leak the activity if the activity is not running but the
- // dialer database helper is still doing work.
- sSingleton = new DialerDatabaseHelper(context.getApplicationContext(),
- DATABASE_NAME);
- }
- return sSingleton;
- }
-
- /**
- * Returns a new instance for unit tests. The database will be created in memory.
- */
- @VisibleForTesting
- static DialerDatabaseHelper getNewInstanceForTest(Context context) {
- return new DialerDatabaseHelper(context, null, true);
- }
-
- protected DialerDatabaseHelper(Context context, String databaseName, boolean isTestInstance) {
- this(context, databaseName, DATABASE_VERSION);
- mIsTestInstance = isTestInstance;
- }
-
- protected DialerDatabaseHelper(Context context, String databaseName) {
- this(context, databaseName, DATABASE_VERSION);
- }
-
- protected DialerDatabaseHelper(Context context, String databaseName, int dbVersion) {
- super(context, databaseName, null, dbVersion);
- mContext = Preconditions.checkNotNull(context, "Context must not be null");
- }
-
- /**
- * Creates tables in the database when database is created for the first time.
- *
- * @param db The database.
- */
- @Override
- public void onCreate(SQLiteDatabase db) {
- setupTables(db);
- }
-
- private void setupTables(SQLiteDatabase db) {
- dropTables(db);
- db.execSQL("CREATE TABLE " + Tables.SMARTDIAL_TABLE + " ("
- + SmartDialDbColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
- + SmartDialDbColumns.DATA_ID + " INTEGER, "
- + SmartDialDbColumns.NUMBER + " TEXT,"
- + SmartDialDbColumns.CONTACT_ID + " INTEGER,"
- + SmartDialDbColumns.LOOKUP_KEY + " TEXT,"
- + SmartDialDbColumns.DISPLAY_NAME_PRIMARY + " TEXT, "
- + SmartDialDbColumns.PHOTO_ID + " INTEGER, "
- + SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + " LONG, "
- + SmartDialDbColumns.LAST_TIME_USED + " LONG, "
- + SmartDialDbColumns.TIMES_USED + " INTEGER, "
- + SmartDialDbColumns.STARRED + " INTEGER, "
- + SmartDialDbColumns.IS_SUPER_PRIMARY + " INTEGER, "
- + SmartDialDbColumns.IN_VISIBLE_GROUP + " INTEGER, "
- + SmartDialDbColumns.IS_PRIMARY + " INTEGER, "
- + SmartDialDbColumns.CARRIER_PRESENCE + " INTEGER NOT NULL DEFAULT 0"
- + ");");
-
- db.execSQL("CREATE TABLE " + Tables.PREFIX_TABLE + " ("
- + PrefixColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
- + PrefixColumns.PREFIX + " TEXT COLLATE NOCASE, "
- + PrefixColumns.CONTACT_ID + " INTEGER"
- + ");");
-
- db.execSQL("CREATE TABLE " + Tables.PROPERTIES + " ("
- + PropertiesColumns.PROPERTY_KEY + " TEXT PRIMARY KEY, "
- + PropertiesColumns.PROPERTY_VALUE + " TEXT "
- + ");");
-
- // This will need to also be updated in setupTablesForFilteredNumberTest and onUpgrade.
- // Hardcoded so we know on glance what columns are updated in setupTables,
- // and to be able to guarantee the state of the DB at each upgrade step.
- db.execSQL("CREATE TABLE " + Tables.FILTERED_NUMBER_TABLE + " ("
- + FilteredNumberColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
- + FilteredNumberColumns.NORMALIZED_NUMBER + " TEXT UNIQUE,"
- + FilteredNumberColumns.NUMBER + " TEXT,"
- + FilteredNumberColumns.COUNTRY_ISO + " TEXT,"
- + FilteredNumberColumns.TIMES_FILTERED + " INTEGER,"
- + FilteredNumberColumns.LAST_TIME_FILTERED + " LONG,"
- + FilteredNumberColumns.CREATION_TIME + " LONG,"
- + FilteredNumberColumns.TYPE + " INTEGER,"
- + FilteredNumberColumns.SOURCE + " INTEGER"
- + ");");
-
- createVoicemailArchiveTable(db);
- setProperty(db, DATABASE_VERSION_PROPERTY, String.valueOf(DATABASE_VERSION));
- if (!mIsTestInstance) {
- resetSmartDialLastUpdatedTime();
- }
- }
-
- public void dropTables(SQLiteDatabase db) {
- db.execSQL("DROP TABLE IF EXISTS " + Tables.PREFIX_TABLE);
- db.execSQL("DROP TABLE IF EXISTS " + Tables.SMARTDIAL_TABLE);
- db.execSQL("DROP TABLE IF EXISTS " + Tables.PROPERTIES);
- db.execSQL("DROP TABLE IF EXISTS " + Tables.FILTERED_NUMBER_TABLE);
- db.execSQL("DROP TABLE IF EXISTS " + Tables.VOICEMAIL_ARCHIVE_TABLE);
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldNumber, int newNumber) {
- // Disregard the old version and new versions provided by SQLiteOpenHelper, we will read
- // our own from the database.
-
- int oldVersion;
-
- oldVersion = getPropertyAsInt(db, DATABASE_VERSION_PROPERTY, 0);
-
- if (oldVersion == 0) {
- Log.e(TAG, "Malformed database version..recreating database");
- }
-
- if (oldVersion < 4) {
- setupTables(db);
- return;
- }
-
- if (oldVersion < 7) {
- db.execSQL("DROP TABLE IF EXISTS " + Tables.FILTERED_NUMBER_TABLE);
- db.execSQL("CREATE TABLE " + Tables.FILTERED_NUMBER_TABLE + " ("
- + FilteredNumberColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
- + FilteredNumberColumns.NORMALIZED_NUMBER + " TEXT UNIQUE,"
- + FilteredNumberColumns.NUMBER + " TEXT,"
- + FilteredNumberColumns.COUNTRY_ISO + " TEXT,"
- + FilteredNumberColumns.TIMES_FILTERED + " INTEGER,"
- + FilteredNumberColumns.LAST_TIME_FILTERED + " LONG,"
- + FilteredNumberColumns.CREATION_TIME + " LONG,"
- + FilteredNumberColumns.TYPE + " INTEGER,"
- + FilteredNumberColumns.SOURCE + " INTEGER"
- + ");");
- oldVersion = 7;
- }
-
- if (oldVersion < 8) {
- upgradeToVersion8(db);
- oldVersion = 8;
- }
-
- if (oldVersion < 9) {
- db.execSQL("DROP TABLE IF EXISTS " + Tables.VOICEMAIL_ARCHIVE_TABLE);
- createVoicemailArchiveTable(db);
- oldVersion = 9;
- }
-
- if (oldVersion != DATABASE_VERSION) {
- throw new IllegalStateException(
- "error upgrading the database to version " + DATABASE_VERSION);
- }
-
- setProperty(db, DATABASE_VERSION_PROPERTY, String.valueOf(DATABASE_VERSION));
- }
-
- public void upgradeToVersion8(SQLiteDatabase db) {
- db.execSQL("ALTER TABLE smartdial_table ADD carrier_presence INTEGER NOT NULL DEFAULT 0");
- }
-
- /**
- * Stores a key-value pair in the {@link Tables#PROPERTIES} table.
- */
- public void setProperty(String key, String value) {
- setProperty(getWritableDatabase(), key, value);
- }
-
- public void setProperty(SQLiteDatabase db, String key, String value) {
- final ContentValues values = new ContentValues();
- values.put(PropertiesColumns.PROPERTY_KEY, key);
- values.put(PropertiesColumns.PROPERTY_VALUE, value);
- db.replace(Tables.PROPERTIES, null, values);
- }
-
- /**
- * Returns the value from the {@link Tables#PROPERTIES} table.
- */
- public String getProperty(String key, String defaultValue) {
- return getProperty(getReadableDatabase(), key, defaultValue);
- }
-
- public String getProperty(SQLiteDatabase db, String key, String defaultValue) {
- try {
- String value = null;
- final Cursor cursor = db.query(Tables.PROPERTIES,
- new String[] {PropertiesColumns.PROPERTY_VALUE},
- PropertiesColumns.PROPERTY_KEY + "=?",
- new String[] {key}, null, null, null);
- if (cursor != null) {
- try {
- if (cursor.moveToFirst()) {
- value = cursor.getString(0);
- }
- } finally {
- cursor.close();
- }
- }
- return value != null ? value : defaultValue;
- } catch (SQLiteException e) {
- return defaultValue;
- }
- }
-
- public int getPropertyAsInt(SQLiteDatabase db, String key, int defaultValue) {
- final String stored = getProperty(db, key, "");
- try {
- return Integer.parseInt(stored);
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- }
-
- private void resetSmartDialLastUpdatedTime() {
- final SharedPreferences databaseLastUpdateSharedPref = mContext.getSharedPreferences(
- DATABASE_LAST_CREATED_SHARED_PREF, Context.MODE_PRIVATE);
- final SharedPreferences.Editor editor = databaseLastUpdateSharedPref.edit();
- editor.putLong(LAST_UPDATED_MILLIS, 0);
- editor.commit();
- }
-
- /**
- * Starts the database upgrade process in the background.
- */
- public void startSmartDialUpdateThread() {
- if (PermissionsUtil.hasContactsPermissions(mContext)) {
- new SmartDialUpdateAsyncTask().execute();
- }
- }
-
- private class SmartDialUpdateAsyncTask extends AsyncTask {
- @Override
- protected Object doInBackground(Object[] objects) {
- if (DEBUG) {
- Log.v(TAG, "Updating database");
- }
- updateSmartDialDatabase();
- return null;
- }
-
- @Override
- protected void onCancelled() {
- if (DEBUG) {
- Log.v(TAG, "Updating Cancelled");
- }
- super.onCancelled();
- }
-
- @Override
- protected void onPostExecute(Object o) {
- if (DEBUG) {
- Log.v(TAG, "Updating Finished");
- }
- super.onPostExecute(o);
- }
- }
- /**
- * Removes rows in the smartdial database that matches the contacts that have been deleted
- * by other apps since last update.
- *
- * @param db Database to operate on.
- * @param deletedContactCursor Cursor containing rows of deleted contacts
- */
- @VisibleForTesting
- void removeDeletedContacts(SQLiteDatabase db, Cursor deletedContactCursor) {
- if (deletedContactCursor == null) {
- return;
- }
-
- db.beginTransaction();
- try {
- while (deletedContactCursor.moveToNext()) {
- final Long deleteContactId =
- deletedContactCursor.getLong(DeleteContactQuery.DELETED_CONTACT_ID);
- db.delete(Tables.SMARTDIAL_TABLE,
- SmartDialDbColumns.CONTACT_ID + "=" + deleteContactId, null);
- db.delete(Tables.PREFIX_TABLE,
- PrefixColumns.CONTACT_ID + "=" + deleteContactId, null);
- }
-
- db.setTransactionSuccessful();
- } finally {
- deletedContactCursor.close();
- db.endTransaction();
- }
- }
-
- private Cursor getDeletedContactCursor(String lastUpdateMillis) {
- return mContext.getContentResolver().query(
- DeleteContactQuery.URI,
- DeleteContactQuery.PROJECTION,
- DeleteContactQuery.SELECT_UPDATED_CLAUSE,
- new String[] {lastUpdateMillis},
- null);
- }
-
- /**
- * Removes potentially corrupted entries in the database. These contacts may be added before
- * the previous instance of the dialer was destroyed for some reason. For data integrity, we
- * delete all of them.
-
- * @param db Database pointer to the dialer database.
- * @param last_update_time Time stamp of last successful update of the dialer database.
- */
- private void removePotentiallyCorruptedContacts(SQLiteDatabase db, String last_update_time) {
- db.delete(Tables.PREFIX_TABLE,
- PrefixColumns.CONTACT_ID + " IN " +
- "(SELECT " + SmartDialDbColumns.CONTACT_ID + " FROM " + Tables.SMARTDIAL_TABLE +
- " WHERE " + SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + " > " +
- last_update_time + ")",
- null);
- db.delete(Tables.SMARTDIAL_TABLE,
- SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + " > " + last_update_time, null);
- }
-
- /**
- * All columns excluding MIME_TYPE, _DATA, ARCHIVED, SERVER_ID, are the same as
- * the columns in the {@link android.provider.CallLog.Calls} table.
- *
- * @param db Database pointer to the dialer database.
- */
- private void createVoicemailArchiveTable(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE " + Tables.VOICEMAIL_ARCHIVE_TABLE + " ("
- + VoicemailArchive._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
- + VoicemailArchive.NUMBER + " TEXT,"
- + VoicemailArchive.DATE + " LONG,"
- + VoicemailArchive.DURATION + " LONG,"
- + VoicemailArchive.MIME_TYPE + " TEXT,"
- + VoicemailArchive.COUNTRY_ISO + " TEXT,"
- + VoicemailArchive._DATA + " TEXT,"
- + VoicemailArchive.GEOCODED_LOCATION + " TEXT,"
- + VoicemailArchive.CACHED_NAME + " TEXT,"
- + VoicemailArchive.CACHED_NUMBER_TYPE + " INTEGER,"
- + VoicemailArchive.CACHED_NUMBER_LABEL + " TEXT,"
- + VoicemailArchive.CACHED_LOOKUP_URI + " TEXT,"
- + VoicemailArchive.CACHED_MATCHED_NUMBER + " TEXT,"
- + VoicemailArchive.CACHED_NORMALIZED_NUMBER + " TEXT,"
- + VoicemailArchive.CACHED_PHOTO_ID + " LONG,"
- + VoicemailArchive.CACHED_FORMATTED_NUMBER + " TEXT,"
- + VoicemailArchive.ARCHIVED + " INTEGER,"
- + VoicemailArchive.NUMBER_PRESENTATION + " INTEGER,"
- + VoicemailArchive.ACCOUNT_COMPONENT_NAME + " TEXT,"
- + VoicemailArchive.ACCOUNT_ID + " TEXT,"
- + VoicemailArchive.FEATURES + " INTEGER,"
- + VoicemailArchive.SERVER_ID + " INTEGER,"
- + VoicemailArchive.TRANSCRIPTION + " TEXT,"
- + VoicemailArchive.CACHED_PHOTO_URI + " TEXT"
- + ");");
- }
-
- /**
- * Removes all entries in the smartdial contact database.
- */
- @VisibleForTesting
- void removeAllContacts(SQLiteDatabase db) {
- db.delete(Tables.SMARTDIAL_TABLE, null, null);
- db.delete(Tables.PREFIX_TABLE, null, null);
- }
-
- /**
- * Counts number of rows of the prefix table.
- */
- @VisibleForTesting
- int countPrefixTableRows(SQLiteDatabase db) {
- return (int)DatabaseUtils.longForQuery(db, "SELECT COUNT(1) FROM " + Tables.PREFIX_TABLE,
- null);
- }
-
- /**
- * Removes rows in the smartdial database that matches updated contacts.
- *
- * @param db Database pointer to the smartdial database
- * @param updatedContactCursor Cursor pointing to the list of recently updated contacts.
- */
- @VisibleForTesting
- void removeUpdatedContacts(SQLiteDatabase db, Cursor updatedContactCursor) {
- db.beginTransaction();
- try {
- updatedContactCursor.moveToPosition(-1);
- while (updatedContactCursor.moveToNext()) {
- final Long contactId =
- updatedContactCursor.getLong(UpdatedContactQuery.UPDATED_CONTACT_ID);
-
- db.delete(Tables.SMARTDIAL_TABLE, SmartDialDbColumns.CONTACT_ID + "=" +
- contactId, null);
- db.delete(Tables.PREFIX_TABLE, PrefixColumns.CONTACT_ID + "=" +
- contactId, null);
- }
-
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- }
-
- /**
- * Inserts updated contacts as rows to the smartdial table.
- *
- * @param db Database pointer to the smartdial database.
- * @param updatedContactCursor Cursor pointing to the list of recently updated contacts.
- * @param currentMillis Current time to be recorded in the smartdial table as update timestamp.
- */
- @VisibleForTesting
- protected void insertUpdatedContactsAndNumberPrefix(SQLiteDatabase db,
- Cursor updatedContactCursor, Long currentMillis) {
- db.beginTransaction();
- try {
- final String sqlInsert = "INSERT INTO " + Tables.SMARTDIAL_TABLE + " (" +
- SmartDialDbColumns.DATA_ID + ", " +
- SmartDialDbColumns.NUMBER + ", " +
- SmartDialDbColumns.CONTACT_ID + ", " +
- SmartDialDbColumns.LOOKUP_KEY + ", " +
- SmartDialDbColumns.DISPLAY_NAME_PRIMARY + ", " +
- SmartDialDbColumns.PHOTO_ID + ", " +
- SmartDialDbColumns.LAST_TIME_USED + ", " +
- SmartDialDbColumns.TIMES_USED + ", " +
- SmartDialDbColumns.STARRED + ", " +
- SmartDialDbColumns.IS_SUPER_PRIMARY + ", " +
- SmartDialDbColumns.IN_VISIBLE_GROUP+ ", " +
- SmartDialDbColumns.IS_PRIMARY + ", " +
- SmartDialDbColumns.CARRIER_PRESENCE + ", " +
- SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + ") " +
- " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
- final SQLiteStatement insert = db.compileStatement(sqlInsert);
-
- final String numberSqlInsert = "INSERT INTO " + Tables.PREFIX_TABLE + " (" +
- PrefixColumns.CONTACT_ID + ", " +
- PrefixColumns.PREFIX + ") " +
- " VALUES (?, ?)";
- final SQLiteStatement numberInsert = db.compileStatement(numberSqlInsert);
-
- updatedContactCursor.moveToPosition(-1);
- while (updatedContactCursor.moveToNext()) {
- insert.clearBindings();
-
- // Handle string columns which can possibly be null first. In the case of certain
- // null columns (due to malformed rows possibly inserted by third-party apps
- // or sync adapters), skip the phone number row.
- final String number = updatedContactCursor.getString(PhoneQuery.PHONE_NUMBER);
- if (TextUtils.isEmpty(number)) {
- continue;
- } else {
- insert.bindString(2, number);
- }
-
- final String lookupKey = updatedContactCursor.getString(
- PhoneQuery.PHONE_LOOKUP_KEY);
- if (TextUtils.isEmpty(lookupKey)) {
- continue;
- } else {
- insert.bindString(4, lookupKey);
- }
-
- final String displayName = updatedContactCursor.getString(
- PhoneQuery.PHONE_DISPLAY_NAME);
- if (displayName == null) {
- insert.bindString(5, mContext.getResources().getString(R.string.missing_name));
- } else {
- insert.bindString(5, displayName);
- }
- insert.bindLong(1, updatedContactCursor.getLong(PhoneQuery.PHONE_ID));
- insert.bindLong(3, updatedContactCursor.getLong(PhoneQuery.PHONE_CONTACT_ID));
- insert.bindLong(6, updatedContactCursor.getLong(PhoneQuery.PHONE_PHOTO_ID));
- insert.bindLong(7, updatedContactCursor.getLong(PhoneQuery.PHONE_LAST_TIME_USED));
- insert.bindLong(8, updatedContactCursor.getInt(PhoneQuery.PHONE_TIMES_USED));
- insert.bindLong(9, updatedContactCursor.getInt(PhoneQuery.PHONE_STARRED));
- insert.bindLong(10, updatedContactCursor.getInt(PhoneQuery.PHONE_IS_SUPER_PRIMARY));
- insert.bindLong(11, updatedContactCursor.getInt(PhoneQuery.PHONE_IN_VISIBLE_GROUP));
- insert.bindLong(12, updatedContactCursor.getInt(PhoneQuery.PHONE_IS_PRIMARY));
- insert.bindLong(13, updatedContactCursor.getInt(PhoneQuery.PHONE_CARRIER_PRESENCE));
- insert.bindLong(14, currentMillis);
- insert.executeInsert();
- final String contactPhoneNumber =
- updatedContactCursor.getString(PhoneQuery.PHONE_NUMBER);
- final ArrayList<String> numberPrefixes =
- SmartDialPrefix.parseToNumberTokens(contactPhoneNumber);
-
- for (String numberPrefix : numberPrefixes) {
- numberInsert.bindLong(1, updatedContactCursor.getLong(
- PhoneQuery.PHONE_CONTACT_ID));
- numberInsert.bindString(2, numberPrefix);
- numberInsert.executeInsert();
- numberInsert.clearBindings();
- }
- }
-
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- }
-
- /**
- * Inserts prefixes of contact names to the prefix table.
- *
- * @param db Database pointer to the smartdial database.
- * @param nameCursor Cursor pointing to the list of distinct updated contacts.
- */
- @VisibleForTesting
- void insertNamePrefixes(SQLiteDatabase db, Cursor nameCursor) {
- final int columnIndexName = nameCursor.getColumnIndex(
- SmartDialDbColumns.DISPLAY_NAME_PRIMARY);
- final int columnIndexContactId = nameCursor.getColumnIndex(SmartDialDbColumns.CONTACT_ID);
-
- db.beginTransaction();
- try {
- final String sqlInsert = "INSERT INTO " + Tables.PREFIX_TABLE + " (" +
- PrefixColumns.CONTACT_ID + ", " +
- PrefixColumns.PREFIX + ") " +
- " VALUES (?, ?)";
- final SQLiteStatement insert = db.compileStatement(sqlInsert);
-
- while (nameCursor.moveToNext()) {
- /** Computes a list of prefixes of a given contact name. */
- final ArrayList<String> namePrefixes =
- SmartDialPrefix.generateNamePrefixes(nameCursor.getString(columnIndexName));
-
- for (String namePrefix : namePrefixes) {
- insert.bindLong(1, nameCursor.getLong(columnIndexContactId));
- insert.bindString(2, namePrefix);
- insert.executeInsert();
- insert.clearBindings();
- }
- }
-
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- }
-
- /**
- * Updates the smart dial and prefix database.
- * This method queries the Delta API to get changed contacts since last update, and updates the
- * records in smartdial database and prefix database accordingly.
- * It also queries the deleted contact database to remove newly deleted contacts since last
- * update.
- */
- public void updateSmartDialDatabase() {
- final SQLiteDatabase db = getWritableDatabase();
-
- synchronized(mLock) {
- if (DEBUG) {
- Log.v(TAG, "Starting to update database");
- }
- final StopWatch stopWatch = DEBUG ? StopWatch.start("Updating databases") : null;
-
- /** Gets the last update time on the database. */
- final SharedPreferences databaseLastUpdateSharedPref = mContext.getSharedPreferences(
- DATABASE_LAST_CREATED_SHARED_PREF, Context.MODE_PRIVATE);
- final String lastUpdateMillis = String.valueOf(
- databaseLastUpdateSharedPref.getLong(LAST_UPDATED_MILLIS, 0));
-
- if (DEBUG) {
- Log.v(TAG, "Last updated at " + lastUpdateMillis);
- }
-
- /** Sets the time after querying the database as the current update time. */
- final Long currentMillis = System.currentTimeMillis();
-
- if (DEBUG) {
- stopWatch.lap("Queried the Contacts database");
- }
-
- /** Prevents the app from reading the dialer database when updating. */
- sInUpdate.getAndSet(true);
-
- /** Removes contacts that have been deleted. */
- removeDeletedContacts(db, getDeletedContactCursor(lastUpdateMillis));
- removePotentiallyCorruptedContacts(db, lastUpdateMillis);
-
- if (DEBUG) {
- stopWatch.lap("Finished deleting deleted entries");
- }
-
- /** If the database did not exist before, jump through deletion as there is nothing
- * to delete.
- */
- if (!lastUpdateMillis.equals("0")) {
- /** Removes contacts that have been updated. Updated contact information will be
- * inserted later. Note that this has to use a separate result set from
- * updatePhoneCursor, since it is possible for a contact to be updated (e.g.
- * phone number deleted), but have no results show up in updatedPhoneCursor (since
- * all of its phone numbers have been deleted).
- */
- final Cursor updatedContactCursor = mContext.getContentResolver().query(
- UpdatedContactQuery.URI,
- UpdatedContactQuery.PROJECTION,
- UpdatedContactQuery.SELECT_UPDATED_CLAUSE,
- new String[] {lastUpdateMillis},
- null
- );
- if (updatedContactCursor == null) {
- Log.e(TAG, "SmartDial query received null for cursor");
- return;
- }
- try {
- removeUpdatedContacts(db, updatedContactCursor);
- } finally {
- updatedContactCursor.close();
- }
- if (DEBUG) {
- stopWatch.lap("Finished deleting entries belonging to updated contacts");
- }
- }
-
- /** Queries the contact database to get all phone numbers that have been updated since the last
- * update time.
- */
- final Cursor updatedPhoneCursor = mContext.getContentResolver().query(PhoneQuery.URI,
- PhoneQuery.PROJECTION, PhoneQuery.SELECTION,
- new String[]{lastUpdateMillis}, null);
- if (updatedPhoneCursor == null) {
- Log.e(TAG, "SmartDial query received null for cursor");
- return;
- }
-
- try {
- /** Inserts recently updated phone numbers to the smartdial database.*/
- insertUpdatedContactsAndNumberPrefix(db, updatedPhoneCursor, currentMillis);
- if (DEBUG) {
- stopWatch.lap("Finished building the smart dial table");
- }
- } finally {
- updatedPhoneCursor.close();
- }
-
- /** Gets a list of distinct contacts which have been updated, and adds the name prefixes
- * of these contacts to the prefix table.
- */
- final Cursor nameCursor = db.rawQuery(
- "SELECT DISTINCT " +
- SmartDialDbColumns.DISPLAY_NAME_PRIMARY + ", " + SmartDialDbColumns.CONTACT_ID +
- " FROM " + Tables.SMARTDIAL_TABLE +
- " WHERE " + SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME +
- " = " + Long.toString(currentMillis),
- new String[] {});
- if (nameCursor != null) {
- try {
- if (DEBUG) {
- stopWatch.lap("Queried the smart dial table for contact names");
- }
-
- /** Inserts prefixes of names into the prefix table.*/
- insertNamePrefixes(db, nameCursor);
- if (DEBUG) {
- stopWatch.lap("Finished building the name prefix table");
- }
- } finally {
- nameCursor.close();
- }
- }
-
- /** Creates index on contact_id for fast JOIN operation. */
- db.execSQL("CREATE INDEX IF NOT EXISTS smartdial_contact_id_index ON " +
- Tables.SMARTDIAL_TABLE + " (" + SmartDialDbColumns.CONTACT_ID + ");");
- /** Creates index on last_smartdial_update_time for fast SELECT operation. */
- db.execSQL("CREATE INDEX IF NOT EXISTS smartdial_last_update_index ON " +
- Tables.SMARTDIAL_TABLE + " (" +
- SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + ");");
- /** Creates index on sorting fields for fast sort operation. */
- db.execSQL("CREATE INDEX IF NOT EXISTS smartdial_sort_index ON " +
- Tables.SMARTDIAL_TABLE + " (" +
- SmartDialDbColumns.STARRED + ", " +
- SmartDialDbColumns.IS_SUPER_PRIMARY + ", " +
- SmartDialDbColumns.LAST_TIME_USED + ", " +
- SmartDialDbColumns.TIMES_USED + ", " +
- SmartDialDbColumns.IN_VISIBLE_GROUP + ", " +
- SmartDialDbColumns.DISPLAY_NAME_PRIMARY + ", " +
- SmartDialDbColumns.CONTACT_ID + ", " +
- SmartDialDbColumns.IS_PRIMARY +
- ");");
- /** Creates index on prefix for fast SELECT operation. */
- db.execSQL("CREATE INDEX IF NOT EXISTS nameprefix_index ON " +
- Tables.PREFIX_TABLE + " (" + PrefixColumns.PREFIX + ");");
- /** Creates index on contact_id for fast JOIN operation. */
- db.execSQL("CREATE INDEX IF NOT EXISTS nameprefix_contact_id_index ON " +
- Tables.PREFIX_TABLE + " (" + PrefixColumns.CONTACT_ID + ");");
-
- if (DEBUG) {
- stopWatch.lap(TAG + "Finished recreating index");
- }
-
- /** Updates the database index statistics.*/
- db.execSQL("ANALYZE " + Tables.SMARTDIAL_TABLE);
- db.execSQL("ANALYZE " + Tables.PREFIX_TABLE);
- db.execSQL("ANALYZE smartdial_contact_id_index");
- db.execSQL("ANALYZE smartdial_last_update_index");
- db.execSQL("ANALYZE nameprefix_index");
- db.execSQL("ANALYZE nameprefix_contact_id_index");
- if (DEBUG) {
- stopWatch.stopAndLog(TAG + "Finished updating index stats", 0);
- }
-
- sInUpdate.getAndSet(false);
-
- final SharedPreferences.Editor editor = databaseLastUpdateSharedPref.edit();
- editor.putLong(LAST_UPDATED_MILLIS, currentMillis);
- editor.commit();
-
- // Notify content observers that smart dial database has been updated.
- mContext.getContentResolver().notifyChange(SMART_DIAL_UPDATED_URI, null, false);
- }
- }
-
- /**
- * Returns a list of candidate contacts where the query is a prefix of the dialpad index of
- * the contact's name or phone number.
- *
- * @param query The prefix of a contact's dialpad index.
- * @return A list of top candidate contacts that will be suggested to user to match their input.
- */
- public ArrayList<ContactNumber> getLooseMatches(String query,
- SmartDialNameMatcher nameMatcher) {
- final boolean inUpdate = sInUpdate.get();
- if (inUpdate) {
- return Lists.newArrayList();
- }
-
- final SQLiteDatabase db = getReadableDatabase();
-
- /** Uses SQL query wildcard '%' to represent prefix matching.*/
- final String looseQuery = query + "%";
-
- final ArrayList<ContactNumber> result = Lists.newArrayList();
-
- final StopWatch stopWatch = DEBUG ? StopWatch.start(":Name Prefix query") : null;
-
- final String currentTimeStamp = Long.toString(System.currentTimeMillis());
-
- /** Queries the database to find contacts that have an index matching the query prefix. */
- final Cursor cursor = db.rawQuery("SELECT " +
- SmartDialDbColumns.DATA_ID + ", " +
- SmartDialDbColumns.DISPLAY_NAME_PRIMARY + ", " +
- SmartDialDbColumns.PHOTO_ID + ", " +
- SmartDialDbColumns.NUMBER + ", " +
- SmartDialDbColumns.CONTACT_ID + ", " +
- SmartDialDbColumns.LOOKUP_KEY + ", " +
- SmartDialDbColumns.CARRIER_PRESENCE +
- " FROM " + Tables.SMARTDIAL_TABLE + " WHERE " +
- SmartDialDbColumns.CONTACT_ID + " IN " +
- " (SELECT " + PrefixColumns.CONTACT_ID +
- " FROM " + Tables.PREFIX_TABLE +
- " WHERE " + Tables.PREFIX_TABLE + "." + PrefixColumns.PREFIX +
- " LIKE '" + looseQuery + "')" +
- " ORDER BY " + SmartDialSortingOrder.SORT_ORDER,
- new String[] {currentTimeStamp});
- if (cursor == null) {
- return result;
- }
- try {
- if (DEBUG) {
- stopWatch.lap("Prefix query completed");
- }
-
- /** Gets the column ID from the cursor.*/
- final int columnDataId = 0;
- final int columnDisplayNamePrimary = 1;
- final int columnPhotoId = 2;
- final int columnNumber = 3;
- final int columnId = 4;
- final int columnLookupKey = 5;
- final int columnCarrierPresence = 6;
- if (DEBUG) {
- stopWatch.lap("Found column IDs");
- }
-
- final Set<ContactMatch> duplicates = new HashSet<ContactMatch>();
- int counter = 0;
- if (DEBUG) {
- stopWatch.lap("Moved cursor to start");
- }
- /** Iterates the cursor to find top contact suggestions without duplication.*/
- while ((cursor.moveToNext()) && (counter < MAX_ENTRIES)) {
- final long dataID = cursor.getLong(columnDataId);
- final String displayName = cursor.getString(columnDisplayNamePrimary);
- final String phoneNumber = cursor.getString(columnNumber);
- final long id = cursor.getLong(columnId);
- final long photoId = cursor.getLong(columnPhotoId);
- final String lookupKey = cursor.getString(columnLookupKey);
- final int carrierPresence = cursor.getInt(columnCarrierPresence);
-
- /** If a contact already exists and another phone number of the contact is being
- * processed, skip the second instance.
- */
- final ContactMatch contactMatch = new ContactMatch(lookupKey, id);
- if (duplicates.contains(contactMatch)) {
- continue;
- }
-
- /**
- * If the contact has either the name or number that matches the query, add to the
- * result.
- */
- final boolean nameMatches = nameMatcher.matches(displayName);
- final boolean numberMatches =
- (nameMatcher.matchesNumber(phoneNumber, query) != null);
- if (nameMatches || numberMatches) {
- /** If a contact has not been added, add it to the result and the hash set.*/
- duplicates.add(contactMatch);
- result.add(new ContactNumber(id, dataID, displayName, phoneNumber, lookupKey,
- photoId, carrierPresence));
- counter++;
- if (DEBUG) {
- stopWatch.lap("Added one result: Name: " + displayName);
- }
- }
- }
-
- if (DEBUG) {
- stopWatch.stopAndLog(TAG + "Finished loading cursor", 0);
- }
- } finally {
- cursor.close();
- }
- return result;
- }
-}
diff --git a/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java b/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java
deleted file mode 100644
index 68a2e85d5..000000000
--- a/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * 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.database;
-
-import android.content.AsyncQueryHandler;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabaseCorruptException;
-import android.net.Uri;
-import android.support.annotation.Nullable;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumber;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberTypes;
-
-public class FilteredNumberAsyncQueryHandler extends AsyncQueryHandler {
- private static final int NO_TOKEN = 0;
-
- public FilteredNumberAsyncQueryHandler(ContentResolver cr) {
- super(cr);
- }
-
- /**
- * Methods for FilteredNumberAsyncQueryHandler result returns.
- */
- private static abstract class Listener {
- protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
- }
- protected void onInsertComplete(int token, Object cookie, Uri uri) {
- }
- protected void onUpdateComplete(int token, Object cookie, int result) {
- }
- protected void onDeleteComplete(int token, Object cookie, int result) {
- }
- }
-
- public interface OnCheckBlockedListener {
- /**
- * Invoked after querying if a number is blocked.
- * @param id The ID of the row if blocked, null otherwise.
- */
- void onCheckComplete(Integer id);
- }
-
- public interface OnBlockNumberListener {
- /**
- * Invoked after inserting a blocked number.
- * @param uri The uri of the newly created row.
- */
- void onBlockComplete(Uri uri);
- }
-
- public interface OnUnblockNumberListener {
- /**
- * Invoked after removing a blocked number
- * @param rows The number of rows affected (expected value 1).
- * @param values The deleted data (used for restoration).
- */
- void onUnblockComplete(int rows, ContentValues values);
- }
-
- public interface OnHasBlockedNumbersListener {
- /**
- * @param hasBlockedNumbers {@code true} if any blocked numbers are stored.
- * {@code false} otherwise.
- */
- void onHasBlockedNumbers(boolean hasBlockedNumbers);
- }
-
- @Override
- protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
- if (cookie != null) {
- ((Listener) cookie).onQueryComplete(token, cookie, cursor);
- }
- }
-
- @Override
- protected void onInsertComplete(int token, Object cookie, Uri uri) {
- if (cookie != null) {
- ((Listener) cookie).onInsertComplete(token, cookie, uri);
- }
- }
-
- @Override
- protected void onUpdateComplete(int token, Object cookie, int result) {
- if (cookie != null) {
- ((Listener) cookie).onUpdateComplete(token, cookie, result);
- }
- }
-
- @Override
- protected void onDeleteComplete(int token, Object cookie, int result) {
- if (cookie != null) {
- ((Listener) cookie).onDeleteComplete(token, cookie, result);
- }
- }
-
- public final void incrementFilteredCount(Integer id) {
- // No concept of counts with new filtering
- if (FilteredNumberCompat.useNewFiltering()) {
- return;
- }
- startUpdate(NO_TOKEN, null,
- ContentUris.withAppendedId(FilteredNumber.CONTENT_URI_INCREMENT_FILTERED_COUNT, id),
- null, null, null);
- }
-
- public void hasBlockedNumbers(final OnHasBlockedNumbersListener listener) {
- startQuery(NO_TOKEN,
- new Listener() {
- @Override
- protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
- listener.onHasBlockedNumbers(cursor != null && cursor.getCount() > 0);
- }
- },
- FilteredNumberCompat.getContentUri(null),
- new String[]{ FilteredNumberCompat.getIdColumnName() },
- FilteredNumberCompat.useNewFiltering() ? null : FilteredNumberColumns.TYPE
- + "=" + FilteredNumberTypes.BLOCKED_NUMBER,
- null,
- null);
- }
-
- /**
- * Check if this number has been blocked.
- *
- * @return {@code false} if the number was invalid and couldn't be checked,
- * {@code true} otherwise,
- */
- public boolean isBlockedNumber(
- final OnCheckBlockedListener listener, String number, String countryIso) {
- final String e164Number = PhoneNumberUtils.formatNumberToE164(number, countryIso);
- if (TextUtils.isEmpty(e164Number)) {
- return false;
- }
-
- startQuery(NO_TOKEN,
- new Listener() {
- @Override
- protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
- /*
- * In the frameworking blocking, numbers can be blocked in both e164 format
- * and not, resulting in multiple rows being returned for this query. For
- * example, both '16502530000' and '6502530000' can exist at the same time
- * and will be returned by this query.
- */
- if (cursor == null || cursor.getCount() == 0) {
- listener.onCheckComplete(null);
- return;
- }
- cursor.moveToFirst();
- // New filtering doesn't have a concept of type
- if (!FilteredNumberCompat.useNewFiltering()
- && cursor.getInt(cursor.getColumnIndex(FilteredNumberColumns.TYPE))
- != FilteredNumberTypes.BLOCKED_NUMBER) {
- listener.onCheckComplete(null);
- return;
- }
- listener.onCheckComplete(
- cursor.getInt(cursor.getColumnIndex(FilteredNumberColumns._ID)));
- }
- },
- FilteredNumberCompat.getContentUri(null),
- FilteredNumberCompat.filter(new String[]{FilteredNumberCompat.getIdColumnName(),
- FilteredNumberCompat.getTypeColumnName()}),
- FilteredNumberCompat.getE164NumberColumnName() + " = ?",
- new String[]{e164Number},
- null);
-
- return true;
- }
-
- public void blockNumber(
- final OnBlockNumberListener listener, String number, @Nullable String countryIso) {
- blockNumber(listener, null, number, countryIso);
- }
-
- /**
- * Add a number manually blocked by the user.
- */
- public void blockNumber(
- final OnBlockNumberListener listener,
- @Nullable String normalizedNumber,
- String number,
- @Nullable String countryIso) {
- blockNumber(listener, FilteredNumberCompat.newBlockNumberContentValues(number,
- normalizedNumber, countryIso));
- }
-
- /**
- * Block a number with specified ContentValues. Can be manually added or a restored row
- * from performing the 'undo' action after unblocking.
- */
- public void blockNumber(final OnBlockNumberListener listener, ContentValues values) {
- startInsert(NO_TOKEN,
- new Listener() {
- @Override
- public void onInsertComplete(int token, Object cookie, Uri uri) {
- if (listener != null ) {
- listener.onBlockComplete(uri);
- }
- }
- }, FilteredNumberCompat.getContentUri(null), values);
- }
-
- /**
- * Unblocks the number with the given id.
- *
- * @param listener (optional) The {@link OnUnblockNumberListener} called after the number is
- * unblocked.
- * @param id The id of the number to unblock.
- */
- public void unblock(@Nullable final OnUnblockNumberListener listener, Integer id) {
- if (id == null) {
- throw new IllegalArgumentException("Null id passed into unblock");
- }
- unblock(listener, FilteredNumberCompat.getContentUri(id));
- }
-
- /**
- * Removes row from database.
- * @param listener (optional) The {@link OnUnblockNumberListener} called after the number is
- * unblocked.
- * @param uri The uri of row to remove, from
- * {@link FilteredNumberAsyncQueryHandler#blockNumber}.
- */
- public void unblock(@Nullable final OnUnblockNumberListener listener, final Uri uri) {
- startQuery(NO_TOKEN, new Listener() {
- @Override
- public void onQueryComplete(int token, Object cookie, Cursor cursor) {
- int rowsReturned = cursor == null ? 0 : cursor.getCount();
- if (rowsReturned != 1) {
- throw new SQLiteDatabaseCorruptException
- ("Returned " + rowsReturned + " rows for uri "
- + uri + "where 1 expected.");
- }
- cursor.moveToFirst();
- final ContentValues values = new ContentValues();
- DatabaseUtils.cursorRowToContentValues(cursor, values);
- values.remove(FilteredNumberCompat.getIdColumnName());
-
- startDelete(NO_TOKEN, new Listener() {
- @Override
- public void onDeleteComplete(int token, Object cookie, int result) {
- if (listener != null) {
- listener.onUnblockComplete(result, values);
- }
- }
- }, uri, null, null);
- }
- }, uri, null, null, null, null);
- }
-}
diff --git a/src/com/android/dialer/database/FilteredNumberContract.java b/src/com/android/dialer/database/FilteredNumberContract.java
deleted file mode 100644
index f3966816c..000000000
--- a/src/com/android/dialer/database/FilteredNumberContract.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * 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.database;
-
-import android.net.Uri;
-import android.provider.BaseColumns;
-
-import com.android.dialerbind.ObjectFactory;
-
-/**
- * <p>
- * The contract between the filtered number provider and applications. Contains
- * definitions for the supported URIs and columns.
- * Currently only accessible within Dialer.
- * </p>
- */
-public final class FilteredNumberContract {
-
- /** The authority for the filtered numbers provider */
- public static final String AUTHORITY = ObjectFactory.getFilteredNumberProviderAuthority();
-
- /** A content:// style uri to the authority for the filtered numbers provider */
- public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
-
- /** The type of filtering to be applied, e.g. block the number or whitelist the number. */
- public interface FilteredNumberTypes {
- static final int UNDEFINED = 0;
- /**
- * Dialer will disconnect the call without sending the caller to voicemail.
- */
- static final int BLOCKED_NUMBER = 1;
- }
-
- /** The original source of the filtered number, e.g. the user manually added it. */
- public interface FilteredNumberSources {
- static final int UNDEFINED = 0;
- /**
- * The user manually added this number through Dialer (e.g. from the call log or InCallUI).
- */
- static final int USER = 1;
- }
-
- public interface FilteredNumberColumns {
- // TYPE: INTEGER
- static final String _ID = "_id";
- /**
- * Represents the number to be filtered, normalized to compare phone numbers for equality.
- *
- * TYPE: TEXT
- */
- static final String NORMALIZED_NUMBER = "normalized_number";
- /**
- * Represents the number to be filtered, for formatting and
- * used with country iso for contact lookups.
- *
- * TYPE: TEXT
- */
- static final String NUMBER = "number";
- /**
- * The country code representing the country detected when
- * the phone number was added to the database.
- * Most numbers don't have the country code, so a best guess is provided by
- * the country detector system. The country iso is also needed in order to format
- * phone numbers correctly.
- *
- * TYPE: TEXT
- */
- static final String COUNTRY_ISO = "country_iso";
- /**
- * The number of times the number has been filtered by Dialer.
- * When this number is incremented, LAST_TIME_FILTERED should also be updated to
- * the current time.
- *
- * TYPE: INTEGER
- */
- static final String TIMES_FILTERED = "times_filtered";
- /**
- * Set to the current time when the phone number is filtered.
- * When this is updated, TIMES_FILTERED should also be incremented.
- *
- * TYPE: LONG
- */
- static final String LAST_TIME_FILTERED = "last_time_filtered";
- // TYPE: LONG
- static final String CREATION_TIME = "creation_time";
- /**
- * Indicates the type of filtering to be applied.
- *
- * TYPE: INTEGER
- * See {@link FilteredNumberTypes}
- */
- static final String TYPE = "type";
- /**
- * Integer representing the original source of the filtered number.
- *
- * TYPE: INTEGER
- * See {@link FilteredNumberSources}
- */
- static final String SOURCE = "source";
- }
-
- /**
- * <p>
- * Constants for the table of filtered numbers.
- * </p>
- * <h3>Operations</h3>
- * <dl>
- * <dt><b>Insert</b></dt>
- * <dd>Required fields: NUMBER, NORMALIZED_NUMBER, TYPE, SOURCE.
- * A default value will be used for the other fields if left null.</dd>
- * <dt><b>Update</b></dt>
- * <dt><b>Delete</b></dt>
- * <dt><b>Query</b></dt>
- * <dd>{@link #CONTENT_URI} can be used for any query, append an ID to
- * retrieve a specific filtered number entry.</dd>
- * </dl>
- */
- public static class FilteredNumber implements BaseColumns {
-
- public static final String FILTERED_NUMBERS_TABLE = "filtered_numbers_table";
- public static final String FILTERED_NUMBERS_INCREMENT_FILTERED_COUNT =
- "filtered_numbers_increment_filtered_count";
-
- public static final Uri CONTENT_URI = Uri.withAppendedPath(
- AUTHORITY_URI,
- FILTERED_NUMBERS_TABLE);
-
- public static final Uri CONTENT_URI_INCREMENT_FILTERED_COUNT = Uri.withAppendedPath(
- AUTHORITY_URI,
- FILTERED_NUMBERS_INCREMENT_FILTERED_COUNT);
-
- /**
- * This utility class cannot be instantiated.
- */
- private FilteredNumber () {}
-
- /**
- * The MIME type of {@link #CONTENT_URI} providing a directory of
- * filtered numbers.
- */
- public static final String CONTENT_TYPE = "vnd.android.cursor.dir/filtered_numbers_table";
-
- /**
- * The MIME type of a {@link #CONTENT_URI} single filtered number.
- */
- public static final String CONTENT_ITEM_TYPE =
- "vnd.android.cursor.item/filtered_numbers_table";
- }
-}
diff --git a/src/com/android/dialer/database/FilteredNumberProvider.java b/src/com/android/dialer/database/FilteredNumberProvider.java
deleted file mode 100644
index 3b63d4b50..000000000
--- a/src/com/android/dialer/database/FilteredNumberProvider.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * 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.database;
-
-import android.content.ContentProvider;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.UriMatcher;
-import android.database.Cursor;
-import android.database.SQLException;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteQueryBuilder;
-import android.net.Uri;
-import android.os.Binder;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.common.GeoUtil;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
-import com.android.dialerbind.DatabaseHelperManager;
-import com.android.dialerbind.ObjectFactory;
-import com.google.common.annotations.VisibleForTesting;
-
-import java.util.Arrays;
-
-/**
- * Filtered number content provider.
- */
-public class FilteredNumberProvider extends ContentProvider {
-
- private static String TAG = FilteredNumberProvider.class.getSimpleName();
-
- private DialerDatabaseHelper mDialerDatabaseHelper;
-
- private static final int FILTERED_NUMBERS_TABLE = 1;
- private static final int FILTERED_NUMBERS_TABLE_ID = 2;
- private static final int FILTERED_NUMBERS_INCREMENT_FILTERED_COUNT = 3;
-
- private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
-
- @Override
- public boolean onCreate() {
- mDialerDatabaseHelper = getDatabaseHelper(getContext());
- if (mDialerDatabaseHelper == null) {
- return false;
- }
- sUriMatcher.addURI(ObjectFactory.getFilteredNumberProviderAuthority(),
- FilteredNumberContract.FilteredNumber.FILTERED_NUMBERS_TABLE,
- FILTERED_NUMBERS_TABLE);
- sUriMatcher.addURI(ObjectFactory.getFilteredNumberProviderAuthority(),
- FilteredNumberContract.FilteredNumber.FILTERED_NUMBERS_TABLE + "/#",
- FILTERED_NUMBERS_TABLE_ID);
- sUriMatcher.addURI(ObjectFactory.getFilteredNumberProviderAuthority(),
- FilteredNumberContract.FilteredNumber.FILTERED_NUMBERS_INCREMENT_FILTERED_COUNT
- + "/#",
- FILTERED_NUMBERS_INCREMENT_FILTERED_COUNT);
- return true;
- }
-
- @VisibleForTesting
- protected DialerDatabaseHelper getDatabaseHelper(Context context) {
- return DatabaseHelperManager.getDatabaseHelper(context);
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
- final SQLiteDatabase db = mDialerDatabaseHelper.getReadableDatabase();
- SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- qb.setTables(DialerDatabaseHelper.Tables.FILTERED_NUMBER_TABLE);
- final int match = sUriMatcher.match(uri);
- switch (match) {
- case FILTERED_NUMBERS_TABLE:
- break;
- case FILTERED_NUMBERS_TABLE_ID:
- qb.appendWhere(FilteredNumberColumns._ID + "=" + ContentUris.parseId(uri));
- break;
- default:
- throw new IllegalArgumentException("Unknown uri: " + uri);
- }
- final Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, null);
- if (c != null) {
- c.setNotificationUri(getContext().getContentResolver(),
- FilteredNumberContract.FilteredNumber.CONTENT_URI);
- } else {
- Log.d(TAG, "CURSOR WAS NULL");
- }
- return c;
- }
-
- @Override
- public String getType(Uri uri) {
- return FilteredNumberContract.FilteredNumber.CONTENT_ITEM_TYPE;
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- SQLiteDatabase db = mDialerDatabaseHelper.getWritableDatabase();
- setDefaultValues(values);
- long id = db.insert(DialerDatabaseHelper.Tables.FILTERED_NUMBER_TABLE, null, values);
- if (id < 0) {
- return null;
- }
- notifyChange(uri);
- return ContentUris.withAppendedId(uri, id);
- }
-
- @VisibleForTesting
- protected long getCurrentTimeMs() {
- return System.currentTimeMillis();
- }
-
- private void setDefaultValues(ContentValues values) {
- if (values.getAsString(FilteredNumberColumns.COUNTRY_ISO) == null) {
- values.put(FilteredNumberColumns.COUNTRY_ISO,
- GeoUtil.getCurrentCountryIso(getContext()));
- }
- if (values.getAsInteger(FilteredNumberColumns.TIMES_FILTERED) == null) {
- values.put(FilteredNumberContract.FilteredNumberColumns.TIMES_FILTERED, 0);
- }
- if (values.getAsLong(FilteredNumberColumns.CREATION_TIME) == null) {
- values.put(FilteredNumberColumns.CREATION_TIME, getCurrentTimeMs());
- }
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- SQLiteDatabase db = mDialerDatabaseHelper.getWritableDatabase();
- final int match = sUriMatcher.match(uri);
- switch (match) {
- case FILTERED_NUMBERS_TABLE:
- break;
- case FILTERED_NUMBERS_TABLE_ID:
- selection = getSelectionWithId(selection, ContentUris.parseId(uri));
- break;
- default:
- throw new IllegalArgumentException("Unknown uri: " + uri);
- }
- int rows = db.delete(DialerDatabaseHelper.Tables.FILTERED_NUMBER_TABLE,
- selection,
- selectionArgs);
- if (rows > 0) {
- notifyChange(uri);
- }
- return rows;
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- SQLiteDatabase db = mDialerDatabaseHelper.getWritableDatabase();
- final int match = sUriMatcher.match(uri);
- switch (match) {
- case FILTERED_NUMBERS_TABLE:
- break;
- case FILTERED_NUMBERS_TABLE_ID:
- selection = getSelectionWithId(selection, ContentUris.parseId(uri));
- break;
- case FILTERED_NUMBERS_INCREMENT_FILTERED_COUNT:
- final long id = ContentUris.parseId(uri);
- try {
- db.execSQL(" UPDATE " + DialerDatabaseHelper.Tables.FILTERED_NUMBER_TABLE
- + " SET" + FilteredNumberColumns.TIMES_FILTERED + "="
- + FilteredNumberColumns.TIMES_FILTERED + "+1,"
- + FilteredNumberColumns.LAST_TIME_FILTERED + "="
- + getCurrentTimeMs()
- + " WHERE " + FilteredNumberColumns._ID + "=" + id);
- } catch (SQLException e) {
- Log.d(TAG, "Could not update blocked statistics for " + id);
- return 0;
- }
- return 1;
- default:
- throw new IllegalArgumentException("Unknown uri: " + uri);
- }
- int rows = db.update(DialerDatabaseHelper.Tables.FILTERED_NUMBER_TABLE,
- values,
- selection,
- selectionArgs);
- if (rows > 0 ) {
- notifyChange(uri);
- }
- return rows;
- }
-
- private String getSelectionWithId(String selection, long id) {
- if (TextUtils.isEmpty(selection)) {
- return FilteredNumberContract.FilteredNumberColumns._ID + "=" + id;
- } else {
- return selection + "AND " + FilteredNumberContract.FilteredNumberColumns._ID + "=" + id;
- }
- }
-
- private void notifyChange(Uri uri) {
- getContext().getContentResolver().notifyChange(uri, null);
- }
-}
diff --git a/src/com/android/dialer/database/VoicemailArchiveContract.java b/src/com/android/dialer/database/VoicemailArchiveContract.java
deleted file mode 100644
index f332932c3..000000000
--- a/src/com/android/dialer/database/VoicemailArchiveContract.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2016 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.database;
-
-import android.net.Uri;
-import android.provider.BaseColumns;
-import android.provider.CallLog;
-import android.provider.OpenableColumns;
-
-import com.android.dialerbind.ObjectFactory;
-
-/**
- * Contains definitions for the supported URIs and columns for the voicemail archive table.
- * All the fields excluding MIME_TYPE, _DATA, ARCHIVED, SERVER_ID, mirror the fields in the
- * contract provided in {@link CallLog.Calls}.
- */
-public final class VoicemailArchiveContract {
-
- /** The authority used by the voicemail archive provider. */
- public static final String AUTHORITY = ObjectFactory.getVoicemailArchiveProviderAuthority();
-
- /** A content:// style uri for the voicemail archive provider */
- public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
-
- public static final class VoicemailArchive implements BaseColumns, OpenableColumns {
-
- public static final String VOICEMAIL_ARCHIVE_TABLE = "voicemail_archive_table";
-
- public static final Uri CONTENT_URI = Uri.withAppendedPath(
- AUTHORITY_URI,
- VOICEMAIL_ARCHIVE_TABLE);
-
- /**
- * @see android.provider.CallLog.Calls#NUMBER
- * TYPE: TEXT
- */
- public static final String NUMBER = CallLog.Calls.NUMBER;
-
- /**
- * @see android.provider.CallLog.Calls#DATE
- * TYPE: LONG
- */
- public static final String DATE = CallLog.Calls.DATE;
-
- /**
- * @see android.provider.CallLog.Calls#DURATION
- * TYPE: LONG
- */
- public static final String DURATION = CallLog.Calls.DURATION;
-
- /**
- * The mime type of the archived voicemail file.
- * TYPE: TEXT
- */
- public static final String MIME_TYPE = "mime_type";
-
- /**
- * @see android.provider.CallLog.Calls#COUNTRY_ISO
- * TYPE: LONG
- */
- public static final String COUNTRY_ISO = CallLog.Calls.COUNTRY_ISO;
-
- /**
- * The path of the archived voicemail file.
- * TYPE: TEXT
- */
- public static final String _DATA = "_data";
-
- /**
- * @see android.provider.CallLog.Calls#GEOCODED_LOCATION
- * TYPE: TEXT
- */
- public static final String GEOCODED_LOCATION = CallLog.Calls.GEOCODED_LOCATION;
-
- /**
- * @see android.provider.CallLog.Calls#CACHED_NAME
- * TYPE: TEXT
- */
- public static final String CACHED_NAME = CallLog.Calls.CACHED_NAME;
-
- /**
- * @see android.provider.CallLog.Calls#CACHED_NUMBER_TYPE
- * TYPE: INTEGER
- */
- public static final String CACHED_NUMBER_TYPE = CallLog.Calls.CACHED_NUMBER_TYPE;
-
- /**
- * @see android.provider.CallLog.Calls#CACHED_NUMBER_LABEL
- * TYPE: TEXT
- */
- public static final String CACHED_NUMBER_LABEL = CallLog.Calls.CACHED_NUMBER_LABEL;
-
- /**
- * @see android.provider.CallLog.Calls#CACHED_LOOKUP_URI
- * TYPE: TEXT
- */
- public static final String CACHED_LOOKUP_URI = CallLog.Calls.CACHED_LOOKUP_URI;
-
- /**
- * @see android.provider.CallLog.Calls#CACHED_MATCHED_NUMBER
- * TYPE: TEXT
- */
- public static final String CACHED_MATCHED_NUMBER = CallLog.Calls.CACHED_MATCHED_NUMBER;
-
- /**
- * @see android.provider.CallLog.Calls#CACHED_NORMALIZED_NUMBER
- * TYPE: TEXT
- */
- public static final String CACHED_NORMALIZED_NUMBER =
- CallLog.Calls.CACHED_NORMALIZED_NUMBER;
-
- /**
- * @see android.provider.CallLog.Calls#CACHED_PHOTO_ID
- * TYPE: LONG
- */
- public static final String CACHED_PHOTO_ID = CallLog.Calls.CACHED_PHOTO_ID;
-
- /**
- * @see android.provider.CallLog.Calls#CACHED_FORMATTED_NUMBER
- * TYPE: TEXT
- */
- public static final String CACHED_FORMATTED_NUMBER = CallLog.Calls.CACHED_FORMATTED_NUMBER;
-
- /**
- * If the voicemail was archived by the user by pressing the archive button, this is set to
- * 1 (true). If the voicemail was archived for the purpose of forwarding to other
- * applications, this is set to 0 (false).
- * TYPE: INTEGER
- */
- public static final String ARCHIVED = "archived_by_user";
-
- /**
- * @see android.provider.CallLog.Calls#NUMBER_PRESENTATION
- * TYPE: INTEGER
- */
- public static final String NUMBER_PRESENTATION = CallLog.Calls.NUMBER_PRESENTATION;
-
- /**
- * @see android.provider.CallLog.Calls#PHONE_ACCOUNT_COMPONENT_NAME
- * TYPE: TEXT
- */
- public static final String ACCOUNT_COMPONENT_NAME =
- CallLog.Calls.PHONE_ACCOUNT_COMPONENT_NAME;
-
- /**
- * @see android.provider.CallLog.Calls#PHONE_ACCOUNT_ID
- * TYPE: TEXT
- */
- public static final String ACCOUNT_ID = CallLog.Calls.PHONE_ACCOUNT_ID;
-
- /**
- * @see android.provider.CallLog.Calls#FEATURES
- * TYPE: INTEGER
- */
- public static final String FEATURES = CallLog.Calls.FEATURES;
-
- /**
- * The id of the voicemail on the server.
- * TYPE: INTEGER
- */
- public static final String SERVER_ID = "server_id";
-
- /**
- * @see android.provider.CallLog.Calls#TRANSCRIPTION
- * TYPE: TEXT
- */
- public static final String TRANSCRIPTION = CallLog.Calls.TRANSCRIPTION;
-
- /**
- * @see android.provider.CallLog.Calls#CACHED_PHOTO_URI
- * TYPE: TEXT
- */
- public static final String CACHED_PHOTO_URI = CallLog.Calls.CACHED_PHOTO_URI;
-
- /**
- * The MIME type of a {@link #CONTENT_URI} single voicemail.
- */
- public static final String CONTENT_ITEM_TYPE =
- "vnd.android.cursor.item/voicmail_archive_table";
-
- public static final Uri buildWithId(int id) {
- return Uri.withAppendedPath(CONTENT_URI, Integer.toString(id));
- }
-
- /** Not instantiable. */
- private VoicemailArchive() {
- }
- }
-}
diff --git a/src/com/android/dialer/database/VoicemailArchiveProvider.java b/src/com/android/dialer/database/VoicemailArchiveProvider.java
deleted file mode 100644
index b3306bc4c..000000000
--- a/src/com/android/dialer/database/VoicemailArchiveProvider.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * Copyright (C) 2016 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.database;
-
-import android.content.ContentProvider;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.UriMatcher;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteQueryBuilder;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.support.annotation.Nullable;
-import android.text.TextUtils;
-import android.webkit.MimeTypeMap;
-
-import com.android.dialerbind.DatabaseHelperManager;
-import com.google.common.annotations.VisibleForTesting;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-
-/**
- * An implementation of the Voicemail Archive content provider. This class performs
- * all database level operations on the voicemail_archive_table.
- */
-public class VoicemailArchiveProvider extends ContentProvider {
- private static final String TAG = "VMArchiveProvider";
- private static final int VOICEMAIL_ARCHIVE_TABLE = 1;
- private static final int VOICEMAIL_ARCHIVE_TABLE_ID = 2;
- private static final String VOICEMAIL_FOLDER = "voicemails";
-
- private DialerDatabaseHelper mDialerDatabaseHelper;
- private final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
-
- @Override
- public boolean onCreate() {
- mDialerDatabaseHelper = getDatabaseHelper(getContext());
- if (mDialerDatabaseHelper == null) {
- return false;
- }
- mUriMatcher.addURI(VoicemailArchiveContract.AUTHORITY,
- VoicemailArchiveContract.VoicemailArchive.VOICEMAIL_ARCHIVE_TABLE,
- VOICEMAIL_ARCHIVE_TABLE);
- mUriMatcher.addURI(VoicemailArchiveContract.AUTHORITY,
- VoicemailArchiveContract.VoicemailArchive.VOICEMAIL_ARCHIVE_TABLE + "/#",
- VOICEMAIL_ARCHIVE_TABLE_ID);
- return true;
- }
-
- @VisibleForTesting
- protected DialerDatabaseHelper getDatabaseHelper(Context context) {
- return DatabaseHelperManager.getDatabaseHelper(context);
- }
-
- /**
- * Used by the test class because it extends {@link android.test.ProviderTestCase2} in which the
- * {@link android.test.IsolatedContext} returns /dev/null when getFilesDir() is called.
- *
- * @see android.test.IsolatedContext#getFilesDir
- */
- @VisibleForTesting
- protected File getFilesDir() {
- return getContext().getFilesDir();
- }
-
- @Nullable
- @Override
- public Cursor query(Uri uri,
- @Nullable String[] projection,
- @Nullable String selection,
- @Nullable String[] selectionArgs,
- @Nullable String sortOrder) {
- SQLiteDatabase db = mDialerDatabaseHelper.getReadableDatabase();
- SQLiteQueryBuilder queryBuilder = getQueryBuilder(uri);
- Cursor cursor = queryBuilder
- .query(db, projection, selection, selectionArgs, null, null, sortOrder);
- if (cursor != null) {
- cursor.setNotificationUri(getContext().getContentResolver(),
- VoicemailArchiveContract.VoicemailArchive.CONTENT_URI);
- }
- return cursor;
- }
-
- @Override
- public String getType(Uri uri) {
- return VoicemailArchiveContract.VoicemailArchive.CONTENT_ITEM_TYPE;
- }
-
- @Nullable
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- SQLiteDatabase db = mDialerDatabaseHelper.getWritableDatabase();
- long id = db.insert(DialerDatabaseHelper.Tables.VOICEMAIL_ARCHIVE_TABLE,
- null, values);
- if (id < 0) {
- return null;
- }
- notifyChange(uri);
- // Create the directory for archived voicemails if it doesn't already exist
- File directory = new File(getFilesDir(), VOICEMAIL_FOLDER);
- directory.mkdirs();
- Uri newUri = ContentUris.withAppendedId(uri, id);
-
- // Create new file only if path is not provided to one
- if (!values.containsKey(VoicemailArchiveContract.VoicemailArchive._DATA)) {
- String fileExtension = MimeTypeMap.getSingleton().getExtensionFromMimeType(
- values.getAsString(VoicemailArchiveContract.VoicemailArchive.MIME_TYPE));
- File voicemailFile = new File(directory,
- TextUtils.isEmpty(fileExtension) ? Long.toString(id) :
- id + "." + fileExtension);
- values.put(VoicemailArchiveContract.VoicemailArchive._DATA, voicemailFile.getPath());
- }
- update(newUri, values, null, null);
- return newUri;
- }
-
-
- @Override
- public int delete(Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
- SQLiteDatabase db = mDialerDatabaseHelper.getWritableDatabase();
- SQLiteQueryBuilder queryBuilder = getQueryBuilder(uri);
- Cursor cursor = queryBuilder.query(db, null, selection, selectionArgs, null, null, null);
-
- // Delete all the voicemail files related to the selected rows
- while (cursor.moveToNext()) {
- deleteFile(cursor.getString(cursor.getColumnIndex(
- VoicemailArchiveContract.VoicemailArchive._DATA)));
- }
-
- int rows = db.delete(DialerDatabaseHelper.Tables.VOICEMAIL_ARCHIVE_TABLE,
- getSelectionWithId(selection, uri),
- selectionArgs);
- if (rows > 0) {
- notifyChange(uri);
- }
- return rows;
- }
-
- @Override
- public int update(Uri uri,
- ContentValues values,
- @Nullable String selection,
- @Nullable String[] selectionArgs) {
- SQLiteDatabase db = mDialerDatabaseHelper.getWritableDatabase();
- selection = getSelectionWithId(selection, uri);
- int rows = db.update(DialerDatabaseHelper.Tables.VOICEMAIL_ARCHIVE_TABLE,
- values,
- selection,
- selectionArgs);
- if (rows > 0) {
- notifyChange(uri);
- }
- return rows;
- }
-
- @Override
- public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
- if (mUriMatcher.match(uri) != VOICEMAIL_ARCHIVE_TABLE_ID) {
- throw new IllegalArgumentException("URI Invalid.");
- }
- return openFileHelper(uri, mode);
- }
-
- private void deleteFile(@Nullable String path) {
- if (TextUtils.isEmpty(path)) {
- return;
- }
- File file = new File(path);
- if (file.exists()) {
- file.delete();
- }
- }
-
- private SQLiteQueryBuilder getQueryBuilder(Uri uri) {
- SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
- queryBuilder.setTables(DialerDatabaseHelper.Tables.VOICEMAIL_ARCHIVE_TABLE);
- String selectionWithId = getSelectionWithId(null, uri);
- if (!TextUtils.isEmpty(selectionWithId)) {
- queryBuilder.appendWhere(selectionWithId);
- }
- return queryBuilder;
- }
-
- private String getSelectionWithId(String selection, Uri uri) {
- int match = mUriMatcher.match(uri);
- switch (match) {
- case VOICEMAIL_ARCHIVE_TABLE:
- return selection;
- case VOICEMAIL_ARCHIVE_TABLE_ID:
- String idStr = VoicemailArchiveContract.VoicemailArchive._ID + "=" +
- ContentUris.parseId(uri);
- return TextUtils.isEmpty(selection) ? idStr : selection + " AND " + idStr;
- default:
- throw new IllegalArgumentException("Unknown uri: " + uri);
- }
- }
-
- private void notifyChange(Uri uri) {
- getContext().getContentResolver().notifyChange(uri, null);
- }
-}
diff --git a/src/com/android/dialer/dialpad/DialpadFragment.java b/src/com/android/dialer/dialpad/DialpadFragment.java
deleted file mode 100644
index 55d534676..000000000
--- a/src/com/android/dialer/dialpad/DialpadFragment.java
+++ /dev/null
@@ -1,1695 +0,0 @@
-/*
- * Copyright (C) 2011 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.dialpad;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.Fragment;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.media.AudioManager;
-import android.media.ToneGenerator;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Trace;
-import android.provider.Contacts.People;
-import android.provider.Contacts.Phones;
-import android.provider.Contacts.PhonesColumns;
-import android.provider.Settings;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.TelephonyManager;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.HapticFeedbackConstants;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.EditText;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.PopupMenu;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-import com.android.contacts.common.CallUtil;
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.dialog.CallSubjectDialog;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.contacts.common.util.PhoneNumberFormatter;
-import com.android.contacts.common.util.StopWatch;
-import com.android.contacts.common.widget.FloatingActionButtonController;
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.NeededForReflection;
-import com.android.dialer.R;
-import com.android.dialer.SpecialCharSequenceMgr;
-import com.android.dialer.calllog.PhoneAccountUtils;
-import com.android.dialer.util.DialerUtils;
-import com.android.dialer.util.IntentUtil.CallIntentBuilder;
-import com.android.dialer.util.TelecomUtil;
-import com.android.incallui.Call.LogState;
-import com.android.phone.common.CallLogAsync;
-import com.android.phone.common.animation.AnimUtils;
-import com.android.phone.common.dialpad.DialpadKeyButton;
-import com.android.phone.common.dialpad.DialpadView;
-
-import java.util.HashSet;
-import java.util.List;
-
-/**
- * Fragment that displays a twelve-key phone dialpad.
- */
-public class DialpadFragment extends Fragment
- implements View.OnClickListener,
- View.OnLongClickListener, View.OnKeyListener,
- AdapterView.OnItemClickListener, TextWatcher,
- PopupMenu.OnMenuItemClickListener,
- DialpadKeyButton.OnPressedListener {
- private static final String TAG = "DialpadFragment";
-
- /**
- * LinearLayout with getter and setter methods for the translationY property using floats,
- * for animation purposes.
- */
- public static class DialpadSlidingRelativeLayout extends RelativeLayout {
-
- public DialpadSlidingRelativeLayout(Context context) {
- super(context);
- }
-
- public DialpadSlidingRelativeLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public DialpadSlidingRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @NeededForReflection
- public float getYFraction() {
- final int height = getHeight();
- if (height == 0) return 0;
- return getTranslationY() / height;
- }
-
- @NeededForReflection
- public void setYFraction(float yFraction) {
- setTranslationY(yFraction * getHeight());
- }
- }
-
- public interface OnDialpadQueryChangedListener {
- void onDialpadQueryChanged(String query);
- }
-
- public interface HostInterface {
- /**
- * Notifies the parent activity that the space above the dialpad has been tapped with
- * no query in the dialpad present. In most situations this will cause the dialpad to
- * be dismissed, unless there happens to be content showing.
- */
- boolean onDialpadSpacerTouchWithEmptyQuery();
- }
-
- private static final boolean DEBUG = DialtactsActivity.DEBUG;
-
- // This is the amount of screen the dialpad fragment takes up when fully displayed
- private static final float DIALPAD_SLIDE_FRACTION = 0.67f;
-
- private static final String EMPTY_NUMBER = "";
- private static final char PAUSE = ',';
- private static final char WAIT = ';';
-
- /** The length of DTMF tones in milliseconds */
- private static final int TONE_LENGTH_MS = 150;
- private static final int TONE_LENGTH_INFINITE = -1;
-
- /** The DTMF tone volume relative to other sounds in the stream */
- private static final int TONE_RELATIVE_VOLUME = 80;
-
- /** Stream type used to play the DTMF tones off call, and mapped to the volume control keys */
- private static final int DIAL_TONE_STREAM_TYPE = AudioManager.STREAM_DTMF;
-
-
- private OnDialpadQueryChangedListener mDialpadQueryListener;
-
- private DialpadView mDialpadView;
- private EditText mDigits;
- private int mDialpadSlideInDuration;
-
- /** Remembers if we need to clear digits field when the screen is completely gone. */
- private boolean mClearDigitsOnStop;
-
- private View mOverflowMenuButton;
- private PopupMenu mOverflowPopupMenu;
- private View mDelete;
- private ToneGenerator mToneGenerator;
- private final Object mToneGeneratorLock = new Object();
- private View mSpacer;
-
- private FloatingActionButtonController mFloatingActionButtonController;
-
- /**
- * Set of dialpad keys that are currently being pressed
- */
- private final HashSet<View> mPressedDialpadKeys = new HashSet<View>(12);
-
- private ListView mDialpadChooser;
- private DialpadChooserAdapter mDialpadChooserAdapter;
-
- /**
- * Regular expression prohibiting manual phone call. Can be empty, which means "no rule".
- */
- private String mProhibitedPhoneNumberRegexp;
-
- private PseudoEmergencyAnimator mPseudoEmergencyAnimator;
-
- // Last number dialed, retrieved asynchronously from the call DB
- // in onCreate. This number is displayed when the user hits the
- // send key and cleared in onPause.
- private final CallLogAsync mCallLog = new CallLogAsync();
- private String mLastNumberDialed = EMPTY_NUMBER;
-
- // determines if we want to playback local DTMF tones.
- private boolean mDTMFToneEnabled;
-
- /** Identifier for the "Add Call" intent extra. */
- private static final String ADD_CALL_MODE_KEY = "add_call_mode";
-
- /**
- * Identifier for intent extra for sending an empty Flash message for
- * CDMA networks. This message is used by the network to simulate a
- * press/depress of the "hookswitch" of a landline phone. Aka "empty flash".
- *
- * TODO: Using an intent extra to tell the phone to send this flash is a
- * temporary measure. To be replaced with an Telephony/TelecomManager call in the future.
- * TODO: Keep in sync with the string defined in OutgoingCallBroadcaster.java
- * in Phone app until this is replaced with the Telephony/Telecom API.
- */
- private static final String EXTRA_SEND_EMPTY_FLASH
- = "com.android.phone.extra.SEND_EMPTY_FLASH";
-
- private String mCurrentCountryIso;
-
- private CallStateReceiver mCallStateReceiver;
-
- private class CallStateReceiver extends BroadcastReceiver {
- /**
- * Receive call state changes so that we can take down the
- * "dialpad chooser" if the phone becomes idle while the
- * chooser UI is visible.
- */
- @Override
- public void onReceive(Context context, Intent intent) {
- // Log.i(TAG, "CallStateReceiver.onReceive");
- String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
- if ((TextUtils.equals(state, TelephonyManager.EXTRA_STATE_IDLE) ||
- TextUtils.equals(state, TelephonyManager.EXTRA_STATE_OFFHOOK))
- && isDialpadChooserVisible()) {
- // Log.i(TAG, "Call ended with dialpad chooser visible! Taking it down...");
- // Note there's a race condition in the UI here: the
- // dialpad chooser could conceivably disappear (on its
- // own) at the exact moment the user was trying to select
- // one of the choices, which would be confusing. (But at
- // least that's better than leaving the dialpad chooser
- // onscreen, but useless...)
- showDialpadChooser(false);
- }
- }
- }
-
- private boolean mWasEmptyBeforeTextChange;
-
- /**
- * This field is set to true while processing an incoming DIAL intent, in order to make sure
- * that SpecialCharSequenceMgr actions can be triggered by user input but *not* by a
- * tel: URI passed by some other app. It will be set to false when all digits are cleared.
- */
- private boolean mDigitsFilledByIntent;
-
- private boolean mStartedFromNewIntent = false;
- private boolean mFirstLaunch = false;
- private boolean mAnimate = false;
-
- private static final String PREF_DIGITS_FILLED_BY_INTENT = "pref_digits_filled_by_intent";
-
- private TelephonyManager getTelephonyManager() {
- return (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
- }
-
- @Override
- public Context getContext() {
- return getActivity();
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- mWasEmptyBeforeTextChange = TextUtils.isEmpty(s);
- }
-
- @Override
- public void onTextChanged(CharSequence input, int start, int before, int changeCount) {
- if (mWasEmptyBeforeTextChange != TextUtils.isEmpty(input)) {
- final Activity activity = getActivity();
- if (activity != null) {
- activity.invalidateOptionsMenu();
- updateMenuOverflowButton(mWasEmptyBeforeTextChange);
- }
- }
-
- // DTMF Tones do not need to be played here any longer -
- // the DTMF dialer handles that functionality now.
- }
-
- @Override
- public void afterTextChanged(Editable input) {
- // When DTMF dialpad buttons are being pressed, we delay SpecialCharSequenceMgr sequence,
- // since some of SpecialCharSequenceMgr's behavior is too abrupt for the "touch-down"
- // behavior.
- if (!mDigitsFilledByIntent &&
- SpecialCharSequenceMgr.handleChars(getActivity(), input.toString(), mDigits)) {
- // A special sequence was entered, clear the digits
- mDigits.getText().clear();
- }
-
- if (isDigitsEmpty()) {
- mDigitsFilledByIntent = false;
- mDigits.setCursorVisible(false);
- }
-
- if (mDialpadQueryListener != null) {
- mDialpadQueryListener.onDialpadQueryChanged(mDigits.getText().toString());
- }
-
- updateDeleteButtonEnabledState();
- }
-
- @Override
- public void onCreate(Bundle state) {
- Trace.beginSection(TAG + " onCreate");
- super.onCreate(state);
-
- mFirstLaunch = state == null;
-
- mCurrentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
-
- mProhibitedPhoneNumberRegexp = getResources().getString(
- R.string.config_prohibited_phone_number_regexp);
-
- if (state != null) {
- mDigitsFilledByIntent = state.getBoolean(PREF_DIGITS_FILLED_BY_INTENT);
- }
-
- mDialpadSlideInDuration = getResources().getInteger(R.integer.dialpad_slide_in_duration);
-
- if (mCallStateReceiver == null) {
- IntentFilter callStateIntentFilter = new IntentFilter(
- TelephonyManager.ACTION_PHONE_STATE_CHANGED);
- mCallStateReceiver = new CallStateReceiver();
- ((Context) getActivity()).registerReceiver(mCallStateReceiver, callStateIntentFilter);
- }
- Trace.endSection();
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
- Trace.beginSection(TAG + " onCreateView");
- Trace.beginSection(TAG + " inflate view");
- final View fragmentView = inflater.inflate(R.layout.dialpad_fragment, container,
- false);
- Trace.endSection();
- Trace.beginSection(TAG + " buildLayer");
- fragmentView.buildLayer();
- Trace.endSection();
-
- Trace.beginSection(TAG + " setup views");
-
- mDialpadView = (DialpadView) fragmentView.findViewById(R.id.dialpad_view);
- mDialpadView.setCanDigitsBeEdited(true);
- mDigits = mDialpadView.getDigits();
- mDigits.setKeyListener(UnicodeDialerKeyListener.INSTANCE);
- mDigits.setOnClickListener(this);
- mDigits.setOnKeyListener(this);
- mDigits.setOnLongClickListener(this);
- mDigits.addTextChangedListener(this);
- mDigits.setElegantTextHeight(false);
- PhoneNumberFormatter.setPhoneNumberFormattingTextWatcher(getActivity(), mDigits);
- // Check for the presence of the keypad
- View oneButton = fragmentView.findViewById(R.id.one);
- if (oneButton != null) {
- configureKeypadListeners(fragmentView);
- }
-
- mDelete = mDialpadView.getDeleteButton();
-
- if (mDelete != null) {
- mDelete.setOnClickListener(this);
- mDelete.setOnLongClickListener(this);
- }
-
- mSpacer = fragmentView.findViewById(R.id.spacer);
- mSpacer.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (isDigitsEmpty()) {
- if (getActivity() != null) {
- return ((HostInterface) getActivity()).onDialpadSpacerTouchWithEmptyQuery();
- }
- return true;
- }
- return false;
- }
- });
-
- mDigits.setCursorVisible(false);
-
- // Set up the "dialpad chooser" UI; see showDialpadChooser().
- mDialpadChooser = (ListView) fragmentView.findViewById(R.id.dialpadChooser);
- mDialpadChooser.setOnItemClickListener(this);
-
- final View floatingActionButtonContainer =
- fragmentView.findViewById(R.id.dialpad_floating_action_button_container);
- final ImageButton floatingActionButton =
- (ImageButton) fragmentView.findViewById(R.id.dialpad_floating_action_button);
- floatingActionButton.setOnClickListener(this);
- mFloatingActionButtonController = new FloatingActionButtonController(getActivity(),
- floatingActionButtonContainer, floatingActionButton);
- Trace.endSection();
- Trace.endSection();
- return fragmentView;
- }
-
- private boolean isLayoutReady() {
- return mDigits != null;
- }
-
- @VisibleForTesting
- public EditText getDigitsWidget() {
- return mDigits;
- }
-
- /**
- * @return true when {@link #mDigits} is actually filled by the Intent.
- */
- private boolean fillDigitsIfNecessary(Intent intent) {
- // Only fills digits from an intent if it is a new intent.
- // Otherwise falls back to the previously used number.
- if (!mFirstLaunch && !mStartedFromNewIntent) {
- return false;
- }
-
- final String action = intent.getAction();
- if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
- Uri uri = intent.getData();
- if (uri != null) {
- if (PhoneAccount.SCHEME_TEL.equals(uri.getScheme())) {
- // Put the requested number into the input area
- String data = uri.getSchemeSpecificPart();
- // Remember it is filled via Intent.
- mDigitsFilledByIntent = true;
- final String converted = PhoneNumberUtils.convertKeypadLettersToDigits(
- PhoneNumberUtils.replaceUnicodeDigits(data));
- setFormattedDigits(converted, null);
- return true;
- } else {
- if (!PermissionsUtil.hasContactsPermissions(getActivity())) {
- return false;
- }
- String type = intent.getType();
- if (People.CONTENT_ITEM_TYPE.equals(type)
- || Phones.CONTENT_ITEM_TYPE.equals(type)) {
- // Query the phone number
- Cursor c = getActivity().getContentResolver().query(intent.getData(),
- new String[] {PhonesColumns.NUMBER, PhonesColumns.NUMBER_KEY},
- null, null, null);
- if (c != null) {
- try {
- if (c.moveToFirst()) {
- // Remember it is filled via Intent.
- mDigitsFilledByIntent = true;
- // Put the number into the input area
- setFormattedDigits(c.getString(0), c.getString(1));
- return true;
- }
- } finally {
- c.close();
- }
- }
- }
- }
- }
- }
- return false;
- }
-
- /**
- * Determines whether an add call operation is requested.
- *
- * @param intent The intent.
- * @return {@literal true} if add call operation was requested. {@literal false} otherwise.
- */
- public static boolean isAddCallMode(Intent intent) {
- if (intent == null) {
- return false;
- }
- final String action = intent.getAction();
- if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
- // see if we are "adding a call" from the InCallScreen; false by default.
- return intent.getBooleanExtra(ADD_CALL_MODE_KEY, false);
- } else {
- return false;
- }
- }
-
- /**
- * Checks the given Intent and changes dialpad's UI state. For example, if the Intent requires
- * the screen to enter "Add Call" mode, this method will show correct UI for the mode.
- */
- private void configureScreenFromIntent(Activity parent) {
- // If we were not invoked with a DIAL intent,
- if (!(parent instanceof DialtactsActivity)) {
- setStartedFromNewIntent(false);
- return;
- }
- // See if we were invoked with a DIAL intent. If we were, fill in the appropriate
- // digits in the dialer field.
- Intent intent = parent.getIntent();
-
- if (!isLayoutReady()) {
- // This happens typically when parent's Activity#onNewIntent() is called while
- // Fragment#onCreateView() isn't called yet, and thus we cannot configure Views at
- // this point. onViewCreate() should call this method after preparing layouts, so
- // just ignore this call now.
- Log.i(TAG,
- "Screen configuration is requested before onCreateView() is called. Ignored");
- return;
- }
-
- boolean needToShowDialpadChooser = false;
-
- // Be sure *not* to show the dialpad chooser if this is an
- // explicit "Add call" action, though.
- final boolean isAddCallMode = isAddCallMode(intent);
- if (!isAddCallMode) {
-
- // Don't show the chooser when called via onNewIntent() and phone number is present.
- // i.e. User clicks a telephone link from gmail for example.
- // In this case, we want to show the dialpad with the phone number.
- final boolean digitsFilled = fillDigitsIfNecessary(intent);
- if (!(mStartedFromNewIntent && digitsFilled)) {
-
- final String action = intent.getAction();
- if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)
- || Intent.ACTION_MAIN.equals(action)) {
- // If there's already an active call, bring up an intermediate UI to
- // make the user confirm what they really want to do.
- if (isPhoneInUse()) {
- needToShowDialpadChooser = true;
- }
- }
-
- }
- }
- showDialpadChooser(needToShowDialpadChooser);
- setStartedFromNewIntent(false);
- }
-
- public void setStartedFromNewIntent(boolean value) {
- mStartedFromNewIntent = value;
- }
-
- public void clearCallRateInformation() {
- setCallRateInformation(null, null);
- }
-
- public void setCallRateInformation(String countryName, String displayRate) {
- mDialpadView.setCallRateInformation(countryName, displayRate);
- }
-
- /**
- * Sets formatted digits to digits field.
- */
- private void setFormattedDigits(String data, String normalizedNumber) {
- final String formatted = getFormattedDigits(data, normalizedNumber, mCurrentCountryIso);
- if (!TextUtils.isEmpty(formatted)) {
- Editable digits = mDigits.getText();
- digits.replace(0, digits.length(), formatted);
- // for some reason this isn't getting called in the digits.replace call above..
- // but in any case, this will make sure the background drawable looks right
- afterTextChanged(digits);
- }
- }
-
- /**
- * Format the provided string of digits into one that represents a properly formatted phone
- * number.
- *
- * @param dialString String of characters to format
- * @param normalizedNumber the E164 format number whose country code is used if the given
- * phoneNumber doesn't have the country code.
- * @param countryIso The country code representing the format to use if the provided normalized
- * number is null or invalid.
- * @return the provided string of digits as a formatted phone number, retaining any
- * post-dial portion of the string.
- */
- @VisibleForTesting
- static String getFormattedDigits(String dialString, String normalizedNumber, String countryIso) {
- String number = PhoneNumberUtils.extractNetworkPortion(dialString);
- // Also retrieve the post dial portion of the provided data, so that the entire dial
- // string can be reconstituted later.
- final String postDial = PhoneNumberUtils.extractPostDialPortion(dialString);
-
- if (TextUtils.isEmpty(number)) {
- return postDial;
- }
-
- number = PhoneNumberUtils.formatNumber(number, normalizedNumber, countryIso);
-
- if (TextUtils.isEmpty(postDial)) {
- return number;
- }
-
- return number.concat(postDial);
- }
-
- private void configureKeypadListeners(View fragmentView) {
- final int[] buttonIds = new int[] {R.id.one, R.id.two, R.id.three, R.id.four, R.id.five,
- R.id.six, R.id.seven, R.id.eight, R.id.nine, R.id.star, R.id.zero, R.id.pound};
-
- DialpadKeyButton dialpadKey;
-
- for (int i = 0; i < buttonIds.length; i++) {
- dialpadKey = (DialpadKeyButton) fragmentView.findViewById(buttonIds[i]);
- dialpadKey.setOnPressedListener(this);
- }
-
- // Long-pressing one button will initiate Voicemail.
- final DialpadKeyButton one = (DialpadKeyButton) fragmentView.findViewById(R.id.one);
- one.setOnLongClickListener(this);
-
- // Long-pressing zero button will enter '+' instead.
- final DialpadKeyButton zero = (DialpadKeyButton) fragmentView.findViewById(R.id.zero);
- zero.setOnLongClickListener(this);
- }
-
- @Override
- public void onStart() {
- Trace.beginSection(TAG + " onStart");
- super.onStart();
- // if the mToneGenerator creation fails, just continue without it. It is
- // a local audio signal, and is not as important as the dtmf tone itself.
- final long start = System.currentTimeMillis();
- synchronized (mToneGeneratorLock) {
- if (mToneGenerator == null) {
- try {
- mToneGenerator = new ToneGenerator(DIAL_TONE_STREAM_TYPE, TONE_RELATIVE_VOLUME);
- } catch (RuntimeException e) {
- Log.w(TAG, "Exception caught while creating local tone generator: " + e);
- mToneGenerator = null;
- }
- }
- }
- final long total = System.currentTimeMillis() - start;
- if (total > 50) {
- Log.i(TAG, "Time for ToneGenerator creation: " + total);
- }
- Trace.endSection();
- };
-
- @Override
- public void onResume() {
- Trace.beginSection(TAG + " onResume");
- super.onResume();
-
- final DialtactsActivity activity = (DialtactsActivity) getActivity();
- mDialpadQueryListener = activity;
-
- final StopWatch stopWatch = StopWatch.start("Dialpad.onResume");
-
- // Query the last dialed number. Do it first because hitting
- // the DB is 'slow'. This call is asynchronous.
- queryLastOutgoingCall();
-
- stopWatch.lap("qloc");
-
- final ContentResolver contentResolver = activity.getContentResolver();
-
- // retrieve the DTMF tone play back setting.
- mDTMFToneEnabled = Settings.System.getInt(contentResolver,
- Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
-
- stopWatch.lap("dtwd");
-
- stopWatch.lap("hptc");
-
- mPressedDialpadKeys.clear();
-
- configureScreenFromIntent(getActivity());
-
- stopWatch.lap("fdin");
-
- if (!isPhoneInUse()) {
- // A sanity-check: the "dialpad chooser" UI should not be visible if the phone is idle.
- showDialpadChooser(false);
- }
-
- stopWatch.lap("hnt");
-
- updateDeleteButtonEnabledState();
-
- stopWatch.lap("bes");
-
- stopWatch.stopAndLog(TAG, 50);
-
- // Populate the overflow menu in onResume instead of onCreate, so that if the SMS activity
- // is disabled while Dialer is paused, the "Send a text message" option can be correctly
- // removed when resumed.
- mOverflowMenuButton = mDialpadView.getOverflowMenuButton();
- mOverflowPopupMenu = buildOptionsMenu(mOverflowMenuButton);
- mOverflowMenuButton.setOnTouchListener(mOverflowPopupMenu.getDragToOpenListener());
- mOverflowMenuButton.setOnClickListener(this);
- mOverflowMenuButton.setVisibility(isDigitsEmpty() ? View.INVISIBLE : View.VISIBLE);
-
- if (mFirstLaunch) {
- // The onHiddenChanged callback does not get called the first time the fragment is
- // attached, so call it ourselves here.
- onHiddenChanged(false);
- }
-
- mFirstLaunch = false;
- Trace.endSection();
- }
-
- @Override
- public void onPause() {
- super.onPause();
-
- // Make sure we don't leave this activity with a tone still playing.
- stopTone();
- mPressedDialpadKeys.clear();
-
- // TODO: I wonder if we should not check if the AsyncTask that
- // lookup the last dialed number has completed.
- mLastNumberDialed = EMPTY_NUMBER; // Since we are going to query again, free stale number.
-
- SpecialCharSequenceMgr.cleanup();
- }
-
- @Override
- public void onStop() {
- super.onStop();
-
- synchronized (mToneGeneratorLock) {
- if (mToneGenerator != null) {
- mToneGenerator.release();
- mToneGenerator = null;
- }
- }
-
- if (mClearDigitsOnStop) {
- mClearDigitsOnStop = false;
- clearDialpad();
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putBoolean(PREF_DIGITS_FILLED_BY_INTENT, mDigitsFilledByIntent);
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- if (mPseudoEmergencyAnimator != null) {
- mPseudoEmergencyAnimator.destroy();
- mPseudoEmergencyAnimator = null;
- }
- ((Context) getActivity()).unregisterReceiver(mCallStateReceiver);
- }
-
- private void keyPressed(int keyCode) {
- if (getView() == null || getView().getTranslationY() != 0) {
- return;
- }
- switch (keyCode) {
- case KeyEvent.KEYCODE_1:
- playTone(ToneGenerator.TONE_DTMF_1, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_2:
- playTone(ToneGenerator.TONE_DTMF_2, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_3:
- playTone(ToneGenerator.TONE_DTMF_3, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_4:
- playTone(ToneGenerator.TONE_DTMF_4, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_5:
- playTone(ToneGenerator.TONE_DTMF_5, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_6:
- playTone(ToneGenerator.TONE_DTMF_6, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_7:
- playTone(ToneGenerator.TONE_DTMF_7, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_8:
- playTone(ToneGenerator.TONE_DTMF_8, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_9:
- playTone(ToneGenerator.TONE_DTMF_9, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_0:
- playTone(ToneGenerator.TONE_DTMF_0, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_POUND:
- playTone(ToneGenerator.TONE_DTMF_P, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_STAR:
- playTone(ToneGenerator.TONE_DTMF_S, TONE_LENGTH_INFINITE);
- break;
- default:
- break;
- }
-
- getView().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
- KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
- mDigits.onKeyDown(keyCode, event);
-
- // If the cursor is at the end of the text we hide it.
- final int length = mDigits.length();
- if (length == mDigits.getSelectionStart() && length == mDigits.getSelectionEnd()) {
- mDigits.setCursorVisible(false);
- }
- }
-
- @Override
- public boolean onKey(View view, int keyCode, KeyEvent event) {
- if (view.getId() == R.id.digits) {
- if (keyCode == KeyEvent.KEYCODE_ENTER) {
- handleDialButtonPressed();
- return true;
- }
-
- }
- return false;
- }
-
- /**
- * When a key is pressed, we start playing DTMF tone, do vibration, and enter the digit
- * immediately. When a key is released, we stop the tone. Note that the "key press" event will
- * be delivered by the system with certain amount of delay, it won't be synced with user's
- * actual "touch-down" behavior.
- */
- @Override
- public void onPressed(View view, boolean pressed) {
- if (DEBUG) Log.d(TAG, "onPressed(). view: " + view + ", pressed: " + pressed);
- if (pressed) {
- int resId = view.getId();
- if (resId == R.id.one) {
- keyPressed(KeyEvent.KEYCODE_1);
- } else if (resId == R.id.two) {
- keyPressed(KeyEvent.KEYCODE_2);
- } else if (resId == R.id.three) {
- keyPressed(KeyEvent.KEYCODE_3);
- } else if (resId == R.id.four) {
- keyPressed(KeyEvent.KEYCODE_4);
- } else if (resId == R.id.five) {
- keyPressed(KeyEvent.KEYCODE_5);
- } else if (resId == R.id.six) {
- keyPressed(KeyEvent.KEYCODE_6);
- } else if (resId == R.id.seven) {
- keyPressed(KeyEvent.KEYCODE_7);
- } else if (resId == R.id.eight) {
- keyPressed(KeyEvent.KEYCODE_8);
- } else if (resId == R.id.nine) {
- keyPressed(KeyEvent.KEYCODE_9);
- } else if (resId == R.id.zero) {
- keyPressed(KeyEvent.KEYCODE_0);
- } else if (resId == R.id.pound) {
- keyPressed(KeyEvent.KEYCODE_POUND);
- } else if (resId == R.id.star) {
- keyPressed(KeyEvent.KEYCODE_STAR);
- } else {
- Log.wtf(TAG, "Unexpected onTouch(ACTION_DOWN) event from: " + view);
- }
- mPressedDialpadKeys.add(view);
- } else {
- mPressedDialpadKeys.remove(view);
- if (mPressedDialpadKeys.isEmpty()) {
- stopTone();
- }
- }
- }
-
- /**
- * Called by the containing Activity to tell this Fragment to build an overflow options
- * menu for display by the container when appropriate.
- *
- * @param invoker the View that invoked the options menu, to act as an anchor location.
- */
- private PopupMenu buildOptionsMenu(View invoker) {
- final PopupMenu popupMenu = new PopupMenu(getActivity(), invoker) {
- @Override
- public void show() {
- final Menu menu = getMenu();
-
- boolean enable = !isDigitsEmpty();
- for (int i = 0; i < menu.size(); i++) {
- MenuItem item = menu.getItem(i);
- item.setEnabled(enable);
- if (item.getItemId() == R.id.menu_call_with_note) {
- item.setVisible(CallUtil.isCallWithSubjectSupported(getContext()));
- }
- }
- super.show();
- }
- };
- popupMenu.inflate(R.menu.dialpad_options);
- popupMenu.setOnMenuItemClickListener(this);
- return popupMenu;
- }
-
- @Override
- public void onClick(View view) {
- int resId = view.getId();
- if (resId == R.id.dialpad_floating_action_button) {
- view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
- handleDialButtonPressed();
- } else if (resId == R.id.deleteButton) {
- keyPressed(KeyEvent.KEYCODE_DEL);
- } else if (resId == R.id.digits) {
- if (!isDigitsEmpty()) {
- mDigits.setCursorVisible(true);
- }
- } else if (resId == R.id.dialpad_overflow) {
- mOverflowPopupMenu.show();
- } else {
- Log.wtf(TAG, "Unexpected onClick() event from: " + view);
- return;
- }
- }
-
- @Override
- public boolean onLongClick(View view) {
- final Editable digits = mDigits.getText();
- final int id = view.getId();
- if (id == R.id.deleteButton) {
- digits.clear();
- return true;
- } else if (id == R.id.one) {
- if (isDigitsEmpty() || TextUtils.equals(mDigits.getText(), "1")) {
- // We'll try to initiate voicemail and thus we want to remove irrelevant string.
- removePreviousDigitIfPossible('1');
-
- List<PhoneAccountHandle> subscriptionAccountHandles =
- PhoneAccountUtils.getSubscriptionPhoneAccounts(getActivity());
- boolean hasUserSelectedDefault = subscriptionAccountHandles.contains(
- TelecomUtil.getDefaultOutgoingPhoneAccount(getActivity(),
- PhoneAccount.SCHEME_VOICEMAIL));
- boolean needsAccountDisambiguation = subscriptionAccountHandles.size() > 1
- && !hasUserSelectedDefault;
-
- if (needsAccountDisambiguation || isVoicemailAvailable()) {
- // On a multi-SIM phone, if the user has not selected a default
- // subscription, initiate a call to voicemail so they can select an account
- // from the "Call with" dialog.
- callVoicemail();
- } else if (getActivity() != null) {
- // Voicemail is unavailable maybe because Airplane mode is turned on.
- // Check the current status and show the most appropriate error message.
- final boolean isAirplaneModeOn =
- Settings.System.getInt(getActivity().getContentResolver(),
- Settings.System.AIRPLANE_MODE_ON, 0) != 0;
- if (isAirplaneModeOn) {
- DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
- R.string.dialog_voicemail_airplane_mode_message);
- dialogFragment.show(getFragmentManager(),
- "voicemail_request_during_airplane_mode");
- } else {
- DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
- R.string.dialog_voicemail_not_ready_message);
- dialogFragment.show(getFragmentManager(), "voicemail_not_ready");
- }
- }
- return true;
- }
- return false;
- } else if (id == R.id.zero) {
- if (mPressedDialpadKeys.contains(view)) {
- // If the zero key is currently pressed, then the long press occurred by touch
- // (and not via other means like certain accessibility input methods).
- // Remove the '0' that was input when the key was first pressed.
- removePreviousDigitIfPossible('0');
- }
- keyPressed(KeyEvent.KEYCODE_PLUS);
- stopTone();
- mPressedDialpadKeys.remove(view);
- return true;
- } else if (id == R.id.digits) {
- mDigits.setCursorVisible(true);
- return false;
- }
- return false;
- }
-
- /**
- * Remove the digit just before the current position of the cursor, iff the following conditions
- * are true:
- * 1) The cursor is not positioned at index 0.
- * 2) The digit before the current cursor position matches the current digit.
- *
- * @param digit to remove from the digits view.
- */
- private void removePreviousDigitIfPossible(char digit) {
- final int currentPosition = mDigits.getSelectionStart();
- if (currentPosition > 0 && digit == mDigits.getText().charAt(currentPosition - 1)) {
- mDigits.setSelection(currentPosition);
- mDigits.getText().delete(currentPosition - 1, currentPosition);
- }
- }
-
- public void callVoicemail() {
- DialerUtils.startActivityWithErrorToast(getActivity(),
- new CallIntentBuilder(CallUtil.getVoicemailUri())
- .setCallInitiationType(LogState.INITIATION_DIALPAD)
- .build());
- hideAndClearDialpad(false);
- }
-
- private void hideAndClearDialpad(boolean animate) {
- ((DialtactsActivity) getActivity()).hideDialpadFragment(animate, true);
- }
-
- public static class ErrorDialogFragment extends DialogFragment {
- private int mTitleResId;
- private int mMessageResId;
-
- private static final String ARG_TITLE_RES_ID = "argTitleResId";
- private static final String ARG_MESSAGE_RES_ID = "argMessageResId";
-
- public static ErrorDialogFragment newInstance(int messageResId) {
- return newInstance(0, messageResId);
- }
-
- public static ErrorDialogFragment newInstance(int titleResId, int messageResId) {
- final ErrorDialogFragment fragment = new ErrorDialogFragment();
- final Bundle args = new Bundle();
- args.putInt(ARG_TITLE_RES_ID, titleResId);
- args.putInt(ARG_MESSAGE_RES_ID, messageResId);
- fragment.setArguments(args);
- return fragment;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mTitleResId = getArguments().getInt(ARG_TITLE_RES_ID);
- mMessageResId = getArguments().getInt(ARG_MESSAGE_RES_ID);
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- if (mTitleResId != 0) {
- builder.setTitle(mTitleResId);
- }
- if (mMessageResId != 0) {
- builder.setMessage(mMessageResId);
- }
- builder.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dismiss();
- }
- });
- return builder.create();
- }
- }
-
- /**
- * In most cases, when the dial button is pressed, there is a
- * number in digits area. Pack it in the intent, start the
- * outgoing call broadcast as a separate task and finish this
- * activity.
- *
- * When there is no digit and the phone is CDMA and off hook,
- * we're sending a blank flash for CDMA. CDMA networks use Flash
- * messages when special processing needs to be done, mainly for
- * 3-way or call waiting scenarios. Presumably, here we're in a
- * special 3-way scenario where the network needs a blank flash
- * before being able to add the new participant. (This is not the
- * case with all 3-way calls, just certain CDMA infrastructures.)
- *
- * Otherwise, there is no digit, display the last dialed
- * number. Don't finish since the user may want to edit it. The
- * user needs to press the dial button again, to dial it (general
- * case described above).
- */
- private void handleDialButtonPressed() {
- if (isDigitsEmpty()) { // No number entered.
- handleDialButtonClickWithEmptyDigits();
- } else {
- final String number = mDigits.getText().toString();
-
- // "persist.radio.otaspdial" is a temporary hack needed for one carrier's automated
- // test equipment.
- // TODO: clean it up.
- if (number != null
- && !TextUtils.isEmpty(mProhibitedPhoneNumberRegexp)
- && number.matches(mProhibitedPhoneNumberRegexp)) {
- Log.i(TAG, "The phone number is prohibited explicitly by a rule.");
- if (getActivity() != null) {
- DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
- R.string.dialog_phone_call_prohibited_message);
- dialogFragment.show(getFragmentManager(), "phone_prohibited_dialog");
- }
-
- // Clear the digits just in case.
- clearDialpad();
- } else {
- final Intent intent = new CallIntentBuilder(number).
- setCallInitiationType(LogState.INITIATION_DIALPAD)
- .build();
- DialerUtils.startActivityWithErrorToast(getActivity(), intent);
- hideAndClearDialpad(false);
- }
- }
- }
-
- public void clearDialpad() {
- if (mDigits != null) {
- mDigits.getText().clear();
- }
- }
-
- private void handleDialButtonClickWithEmptyDigits() {
- if (phoneIsCdma() && isPhoneInUse()) {
- // TODO: Move this logic into services/Telephony
- //
- // This is really CDMA specific. On GSM is it possible
- // to be off hook and wanted to add a 3rd party using
- // the redial feature.
- startActivity(newFlashIntent());
- } else {
- if (!TextUtils.isEmpty(mLastNumberDialed)) {
- // Recall the last number dialed.
- mDigits.setText(mLastNumberDialed);
-
- // ...and move the cursor to the end of the digits string,
- // so you'll be able to delete digits using the Delete
- // button (just as if you had typed the number manually.)
- //
- // Note we use mDigits.getText().length() here, not
- // mLastNumberDialed.length(), since the EditText widget now
- // contains a *formatted* version of mLastNumberDialed (due to
- // mTextWatcher) and its length may have changed.
- mDigits.setSelection(mDigits.getText().length());
- } else {
- // There's no "last number dialed" or the
- // background query is still running. There's
- // nothing useful for the Dial button to do in
- // this case. Note: with a soft dial button, this
- // can never happens since the dial button is
- // disabled under these conditons.
- playTone(ToneGenerator.TONE_PROP_NACK);
- }
- }
- }
-
- /**
- * Plays the specified tone for TONE_LENGTH_MS milliseconds.
- */
- private void playTone(int tone) {
- playTone(tone, TONE_LENGTH_MS);
- }
-
- /**
- * Play the specified tone for the specified milliseconds
- *
- * The tone is played locally, using the audio stream for phone calls.
- * Tones are played only if the "Audible touch tones" user preference
- * is checked, and are NOT played if the device is in silent mode.
- *
- * The tone length can be -1, meaning "keep playing the tone." If the caller does so, it should
- * call stopTone() afterward.
- *
- * @param tone a tone code from {@link ToneGenerator}
- * @param durationMs tone length.
- */
- private void playTone(int tone, int durationMs) {
- // if local tone playback is disabled, just return.
- if (!mDTMFToneEnabled) {
- return;
- }
-
- // Also do nothing if the phone is in silent mode.
- // We need to re-check the ringer mode for *every* playTone()
- // call, rather than keeping a local flag that's updated in
- // onResume(), since it's possible to toggle silent mode without
- // leaving the current activity (via the ENDCALL-longpress menu.)
- AudioManager audioManager =
- (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
- int ringerMode = audioManager.getRingerMode();
- if ((ringerMode == AudioManager.RINGER_MODE_SILENT)
- || (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) {
- return;
- }
-
- synchronized (mToneGeneratorLock) {
- if (mToneGenerator == null) {
- Log.w(TAG, "playTone: mToneGenerator == null, tone: " + tone);
- return;
- }
-
- // Start the new tone (will stop any playing tone)
- mToneGenerator.startTone(tone, durationMs);
- }
- }
-
- /**
- * Stop the tone if it is played.
- */
- private void stopTone() {
- // if local tone playback is disabled, just return.
- if (!mDTMFToneEnabled) {
- return;
- }
- synchronized (mToneGeneratorLock) {
- if (mToneGenerator == null) {
- Log.w(TAG, "stopTone: mToneGenerator == null");
- return;
- }
- mToneGenerator.stopTone();
- }
- }
-
- /**
- * Brings up the "dialpad chooser" UI in place of the usual Dialer
- * elements (the textfield/button and the dialpad underneath).
- *
- * We show this UI if the user brings up the Dialer while a call is
- * already in progress, since there's a good chance we got here
- * accidentally (and the user really wanted the in-call dialpad instead).
- * So in this situation we display an intermediate UI that lets the user
- * explicitly choose between the in-call dialpad ("Use touch tone
- * keypad") and the regular Dialer ("Add call"). (Or, the option "Return
- * to call in progress" just goes back to the in-call UI with no dialpad
- * at all.)
- *
- * @param enabled If true, show the "dialpad chooser" instead
- * of the regular Dialer UI
- */
- private void showDialpadChooser(boolean enabled) {
- if (getActivity() == null) {
- return;
- }
- // Check if onCreateView() is already called by checking one of View objects.
- if (!isLayoutReady()) {
- return;
- }
-
- if (enabled) {
- Log.d(TAG, "Showing dialpad chooser!");
- if (mDialpadView != null) {
- mDialpadView.setVisibility(View.GONE);
- }
-
- mFloatingActionButtonController.setVisible(false);
- mDialpadChooser.setVisibility(View.VISIBLE);
-
- // Instantiate the DialpadChooserAdapter and hook it up to the
- // ListView. We do this only once.
- if (mDialpadChooserAdapter == null) {
- mDialpadChooserAdapter = new DialpadChooserAdapter(getActivity());
- }
- mDialpadChooser.setAdapter(mDialpadChooserAdapter);
- } else {
- Log.d(TAG, "Displaying normal Dialer UI.");
- if (mDialpadView != null) {
- mDialpadView.setVisibility(View.VISIBLE);
- } else {
- mDigits.setVisibility(View.VISIBLE);
- }
-
- mFloatingActionButtonController.setVisible(true);
- mDialpadChooser.setVisibility(View.GONE);
- }
- }
-
- /**
- * @return true if we're currently showing the "dialpad chooser" UI.
- */
- private boolean isDialpadChooserVisible() {
- return mDialpadChooser.getVisibility() == View.VISIBLE;
- }
-
- /**
- * Simple list adapter, binding to an icon + text label
- * for each item in the "dialpad chooser" list.
- */
- private static class DialpadChooserAdapter extends BaseAdapter {
- private LayoutInflater mInflater;
-
- // Simple struct for a single "choice" item.
- static class ChoiceItem {
- String text;
- Bitmap icon;
- int id;
-
- public ChoiceItem(String s, Bitmap b, int i) {
- text = s;
- icon = b;
- id = i;
- }
- }
-
- // IDs for the possible "choices":
- static final int DIALPAD_CHOICE_USE_DTMF_DIALPAD = 101;
- static final int DIALPAD_CHOICE_RETURN_TO_CALL = 102;
- static final int DIALPAD_CHOICE_ADD_NEW_CALL = 103;
-
- private static final int NUM_ITEMS = 3;
- private ChoiceItem mChoiceItems[] = new ChoiceItem[NUM_ITEMS];
-
- public DialpadChooserAdapter(Context context) {
- // Cache the LayoutInflate to avoid asking for a new one each time.
- mInflater = LayoutInflater.from(context);
-
- // Initialize the possible choices.
- // TODO: could this be specified entirely in XML?
-
- // - "Use touch tone keypad"
- mChoiceItems[0] = new ChoiceItem(
- context.getString(R.string.dialer_useDtmfDialpad),
- BitmapFactory.decodeResource(context.getResources(),
- R.drawable.ic_dialer_fork_tt_keypad),
- DIALPAD_CHOICE_USE_DTMF_DIALPAD);
-
- // - "Return to call in progress"
- mChoiceItems[1] = new ChoiceItem(
- context.getString(R.string.dialer_returnToInCallScreen),
- BitmapFactory.decodeResource(context.getResources(),
- R.drawable.ic_dialer_fork_current_call),
- DIALPAD_CHOICE_RETURN_TO_CALL);
-
- // - "Add call"
- mChoiceItems[2] = new ChoiceItem(
- context.getString(R.string.dialer_addAnotherCall),
- BitmapFactory.decodeResource(context.getResources(),
- R.drawable.ic_dialer_fork_add_call),
- DIALPAD_CHOICE_ADD_NEW_CALL);
- }
-
- @Override
- public int getCount() {
- return NUM_ITEMS;
- }
-
- /**
- * Return the ChoiceItem for a given position.
- */
- @Override
- public Object getItem(int position) {
- return mChoiceItems[position];
- }
-
- /**
- * Return a unique ID for each possible choice.
- */
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- /**
- * Make a view for each row.
- */
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- // When convertView is non-null, we can reuse it (there's no need
- // to reinflate it.)
- if (convertView == null) {
- convertView = mInflater.inflate(R.layout.dialpad_chooser_list_item, null);
- }
-
- TextView text = (TextView) convertView.findViewById(R.id.text);
- text.setText(mChoiceItems[position].text);
-
- ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
- icon.setImageBitmap(mChoiceItems[position].icon);
-
- return convertView;
- }
- }
-
- /**
- * Handle clicks from the dialpad chooser.
- */
- @Override
- public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
- DialpadChooserAdapter.ChoiceItem item =
- (DialpadChooserAdapter.ChoiceItem) parent.getItemAtPosition(position);
- int itemId = item.id;
- if (itemId == DialpadChooserAdapter.DIALPAD_CHOICE_USE_DTMF_DIALPAD) {// Log.i(TAG, "DIALPAD_CHOICE_USE_DTMF_DIALPAD");
- // Fire off an intent to go back to the in-call UI
- // with the dialpad visible.
- returnToInCallScreen(true);
- } else if (itemId == DialpadChooserAdapter.DIALPAD_CHOICE_RETURN_TO_CALL) {// Log.i(TAG, "DIALPAD_CHOICE_RETURN_TO_CALL");
- // Fire off an intent to go back to the in-call UI
- // (with the dialpad hidden).
- returnToInCallScreen(false);
- } else if (itemId == DialpadChooserAdapter.DIALPAD_CHOICE_ADD_NEW_CALL) {// Log.i(TAG, "DIALPAD_CHOICE_ADD_NEW_CALL");
- // Ok, guess the user really did want to be here (in the
- // regular Dialer) after all. Bring back the normal Dialer UI.
- showDialpadChooser(false);
- } else {
- Log.w(TAG, "onItemClick: unexpected itemId: " + itemId);
- }
- }
-
- /**
- * Returns to the in-call UI (where there's presumably a call in
- * progress) in response to the user selecting "use touch tone keypad"
- * or "return to call" from the dialpad chooser.
- */
- private void returnToInCallScreen(boolean showDialpad) {
- TelecomUtil.showInCallScreen(getActivity(), showDialpad);
-
- // Finally, finish() ourselves so that we don't stay on the
- // activity stack.
- // Note that we do this whether or not the showCallScreenWithDialpad()
- // call above had any effect or not! (That call is a no-op if the
- // phone is idle, which can happen if the current call ends while
- // the dialpad chooser is up. In this case we can't show the
- // InCallScreen, and there's no point staying here in the Dialer,
- // so we just take the user back where he came from...)
- getActivity().finish();
- }
-
- /**
- * @return true if the phone is "in use", meaning that at least one line
- * is active (ie. off hook or ringing or dialing, or on hold).
- */
- private boolean isPhoneInUse() {
- final Context context = getActivity();
- if (context != null) {
- return TelecomUtil.isInCall(context);
- }
- return false;
- }
-
- /**
- * @return true if the phone is a CDMA phone type
- */
- private boolean phoneIsCdma() {
- return getTelephonyManager().getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA;
- }
-
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- int resId = item.getItemId();
- if (resId == R.id.menu_2s_pause) {
- updateDialString(PAUSE);
- return true;
- } else if (resId == R.id.menu_add_wait) {
- updateDialString(WAIT);
- return true;
- } else if (resId == R.id.menu_call_with_note) {
- CallSubjectDialog.start(getActivity(), mDigits.getText().toString());
- hideAndClearDialpad(false);
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Updates the dial string (mDigits) after inserting a Pause character (,)
- * or Wait character (;).
- */
- private void updateDialString(char newDigit) {
- if (newDigit != WAIT && newDigit != PAUSE) {
- throw new IllegalArgumentException(
- "Not expected for anything other than PAUSE & WAIT");
- }
-
- int selectionStart;
- int selectionEnd;
-
- // SpannableStringBuilder editable_text = new SpannableStringBuilder(mDigits.getText());
- int anchor = mDigits.getSelectionStart();
- int point = mDigits.getSelectionEnd();
-
- selectionStart = Math.min(anchor, point);
- selectionEnd = Math.max(anchor, point);
-
- if (selectionStart == -1) {
- selectionStart = selectionEnd = mDigits.length();
- }
-
- Editable digits = mDigits.getText();
-
- if (canAddDigit(digits, selectionStart, selectionEnd, newDigit)) {
- digits.replace(selectionStart, selectionEnd, Character.toString(newDigit));
-
- if (selectionStart != selectionEnd) {
- // Unselect: back to a regular cursor, just pass the character inserted.
- mDigits.setSelection(selectionStart + 1);
- }
- }
- }
-
- /**
- * Update the enabledness of the "Dial" and "Backspace" buttons if applicable.
- */
- private void updateDeleteButtonEnabledState() {
- if (getActivity() == null) {
- return;
- }
- final boolean digitsNotEmpty = !isDigitsEmpty();
- mDelete.setEnabled(digitsNotEmpty);
- }
-
- /**
- * Handle transitions for the menu button depending on the state of the digits edit text.
- * Transition out when going from digits to no digits and transition in when the first digit
- * is pressed.
- * @param transitionIn True if transitioning in, False if transitioning out
- */
- private void updateMenuOverflowButton(boolean transitionIn) {
- mOverflowMenuButton = mDialpadView.getOverflowMenuButton();
- if (transitionIn) {
- AnimUtils.fadeIn(mOverflowMenuButton, AnimUtils.DEFAULT_DURATION);
- } else {
- AnimUtils.fadeOut(mOverflowMenuButton, AnimUtils.DEFAULT_DURATION);
- }
- }
-
- /**
- * Check if voicemail is enabled/accessible.
- *
- * @return true if voicemail is enabled and accessible. Note that this can be false
- * "temporarily" after the app boot.
- */
- private boolean isVoicemailAvailable() {
- try {
- PhoneAccountHandle defaultUserSelectedAccount =
- TelecomUtil.getDefaultOutgoingPhoneAccount(getActivity(),
- PhoneAccount.SCHEME_VOICEMAIL);
- if (defaultUserSelectedAccount == null) {
- // In a single-SIM phone, there is no default outgoing phone account selected by
- // the user, so just call TelephonyManager#getVoicemailNumber directly.
- return !TextUtils.isEmpty(getTelephonyManager().getVoiceMailNumber());
- } else {
- return !TextUtils.isEmpty(TelecomUtil.getVoicemailNumber(getActivity(),
- defaultUserSelectedAccount));
- }
- } catch (SecurityException se) {
- // Possibly no READ_PHONE_STATE privilege.
- Log.w(TAG, "SecurityException is thrown. Maybe privilege isn't sufficient.");
- }
- return false;
- }
-
- /**
- * Returns true of the newDigit parameter can be added at the current selection
- * point, otherwise returns false.
- * Only prevents input of WAIT and PAUSE digits at an unsupported position.
- * Fails early if start == -1 or start is larger than end.
- */
- @VisibleForTesting
- /* package */ static boolean canAddDigit(CharSequence digits, int start, int end,
- char newDigit) {
- if(newDigit != WAIT && newDigit != PAUSE) {
- throw new IllegalArgumentException(
- "Should not be called for anything other than PAUSE & WAIT");
- }
-
- // False if no selection, or selection is reversed (end < start)
- if (start == -1 || end < start) {
- return false;
- }
-
- // unsupported selection-out-of-bounds state
- if (start > digits.length() || end > digits.length()) return false;
-
- // Special digit cannot be the first digit
- if (start == 0) return false;
-
- if (newDigit == WAIT) {
- // preceding char is ';' (WAIT)
- if (digits.charAt(start - 1) == WAIT) return false;
-
- // next char is ';' (WAIT)
- if ((digits.length() > end) && (digits.charAt(end) == WAIT)) return false;
- }
-
- return true;
- }
-
- /**
- * @return true if the widget with the phone number digits is empty.
- */
- private boolean isDigitsEmpty() {
- return mDigits.length() == 0;
- }
-
- /**
- * Starts the asyn query to get the last dialed/outgoing
- * number. When the background query finishes, mLastNumberDialed
- * is set to the last dialed number or an empty string if none
- * exists yet.
- */
- private void queryLastOutgoingCall() {
- mLastNumberDialed = EMPTY_NUMBER;
- if (!PermissionsUtil.hasPhonePermissions(getActivity())) {
- return;
- }
- CallLogAsync.GetLastOutgoingCallArgs lastCallArgs =
- new CallLogAsync.GetLastOutgoingCallArgs(
- getActivity(),
- new CallLogAsync.OnLastOutgoingCallComplete() {
- @Override
- public void lastOutgoingCall(String number) {
- // TODO: Filter out emergency numbers if
- // the carrier does not want redial for
- // these.
- // If the fragment has already been detached since the last time
- // we called queryLastOutgoingCall in onResume there is no point
- // doing anything here.
- if (getActivity() == null) return;
- mLastNumberDialed = number;
- updateDeleteButtonEnabledState();
- }
- });
- mCallLog.getLastOutgoingCall(lastCallArgs);
- }
-
- private Intent newFlashIntent() {
- final Intent intent = new CallIntentBuilder(EMPTY_NUMBER).build();
- intent.putExtra(EXTRA_SEND_EMPTY_FLASH, true);
- return intent;
- }
-
- @Override
- public void onHiddenChanged(boolean hidden) {
- super.onHiddenChanged(hidden);
- final DialtactsActivity activity = (DialtactsActivity) getActivity();
- final DialpadView dialpadView = (DialpadView) getView().findViewById(R.id.dialpad_view);
- if (activity == null) return;
- if (!hidden && !isDialpadChooserVisible()) {
- if (mAnimate) {
- dialpadView.animateShow();
- }
- mFloatingActionButtonController.setVisible(false);
- mFloatingActionButtonController.scaleIn(mAnimate ? mDialpadSlideInDuration : 0);
- activity.onDialpadShown();
- mDigits.requestFocus();
- }
- if (hidden) {
- if (mAnimate) {
- mFloatingActionButtonController.scaleOut();
- } else {
- mFloatingActionButtonController.setVisible(false);
- }
- }
- }
-
- public void setAnimate(boolean value) {
- mAnimate = value;
- }
-
- public boolean getAnimate() {
- return mAnimate;
- }
-
- public void setYFraction(float yFraction) {
- ((DialpadSlidingRelativeLayout) getView()).setYFraction(yFraction);
- }
-
- public int getDialpadHeight() {
- if (mDialpadView == null) {
- return 0;
- }
- return mDialpadView.getHeight();
- }
-
- public void process_quote_emergency_unquote(String query) {
- if (PseudoEmergencyAnimator.PSEUDO_EMERGENCY_NUMBER.equals(query)) {
- if (mPseudoEmergencyAnimator == null) {
- mPseudoEmergencyAnimator = new PseudoEmergencyAnimator(
- new PseudoEmergencyAnimator.ViewProvider() {
- @Override
- public View getView() {
- return DialpadFragment.this.getView();
- }
- });
- }
- mPseudoEmergencyAnimator.start();
- } else {
- if (mPseudoEmergencyAnimator != null) {
- mPseudoEmergencyAnimator.end();
- }
- }
- }
-
-}
diff --git a/src/com/android/dialer/dialpad/LatinSmartDialMap.java b/src/com/android/dialer/dialpad/LatinSmartDialMap.java
deleted file mode 100644
index ef1ec0adc..000000000
--- a/src/com/android/dialer/dialpad/LatinSmartDialMap.java
+++ /dev/null
@@ -1,413 +0,0 @@
-package com.android.dialer.dialpad;
-
-public class LatinSmartDialMap implements SmartDialMap {
-
- private static final char[] LATIN_LETTERS_TO_DIGITS = {
- '2', '2', '2', // A,B,C -> 2
- '3', '3', '3', // D,E,F -> 3
- '4', '4', '4', // G,H,I -> 4
- '5', '5', '5', // J,K,L -> 5
- '6', '6', '6', // M,N,O -> 6
- '7', '7', '7', '7', // P,Q,R,S -> 7
- '8', '8', '8', // T,U,V -> 8
- '9', '9', '9', '9' // W,X,Y,Z -> 9
- };
-
- @Override
- public boolean isValidDialpadAlphabeticChar(char ch) {
- return (ch >= 'a' && ch <= 'z');
- }
-
- @Override
- public boolean isValidDialpadNumericChar(char ch) {
- return (ch >= '0' && ch <= '9');
- }
-
- @Override
- public boolean isValidDialpadCharacter(char ch) {
- return (isValidDialpadAlphabeticChar(ch) || isValidDialpadNumericChar(ch));
- }
-
- /*
- * The switch statement in this function was generated using the python code:
- * from unidecode import unidecode
- * for i in range(192, 564):
- * char = unichr(i)
- * decoded = unidecode(char)
- * # Unicode characters that decompose into multiple characters i.e.
- * # into ss are not supported for now
- * if (len(decoded) == 1 and decoded.isalpha()):
- * print "case '" + char + "': return '" + unidecode(char) + "';"
- *
- * This gives us a way to map characters containing accents/diacritics to their
- * alphabetic equivalents. The unidecode library can be found at:
- * http://pypi.python.org/pypi/Unidecode/0.04.1
- *
- * Also remaps all upper case latin characters to their lower case equivalents.
- */
- @Override
- public char normalizeCharacter(char ch) {
- switch (ch) {
- case 'À': return 'a';
- case 'Á': return 'a';
- case 'Â': return 'a';
- case 'Ã': return 'a';
- case 'Ä': return 'a';
- case 'Å': return 'a';
- case 'Ç': return 'c';
- case 'È': return 'e';
- case 'É': return 'e';
- case 'Ê': return 'e';
- case 'Ë': return 'e';
- case 'Ì': return 'i';
- case 'Í': return 'i';
- case 'Î': return 'i';
- case 'Ï': return 'i';
- case 'Ð': return 'd';
- case 'Ñ': return 'n';
- case 'Ò': return 'o';
- case 'Ó': return 'o';
- case 'Ô': return 'o';
- case 'Õ': return 'o';
- case 'Ö': return 'o';
- case '×': return 'x';
- case 'Ø': return 'o';
- case 'Ù': return 'u';
- case 'Ú': return 'u';
- case 'Û': return 'u';
- case 'Ü': return 'u';
- case 'Ý': return 'u';
- case 'à': return 'a';
- case 'á': return 'a';
- case 'â': return 'a';
- case 'ã': return 'a';
- case 'ä': return 'a';
- case 'å': return 'a';
- case 'ç': return 'c';
- case 'è': return 'e';
- case 'é': return 'e';
- case 'ê': return 'e';
- case 'ë': return 'e';
- case 'ì': return 'i';
- case 'í': return 'i';
- case 'î': return 'i';
- case 'ï': return 'i';
- case 'ð': return 'd';
- case 'ñ': return 'n';
- case 'ò': return 'o';
- case 'ó': return 'o';
- case 'ô': return 'o';
- case 'õ': return 'o';
- case 'ö': return 'o';
- case 'ø': return 'o';
- case 'ù': return 'u';
- case 'ú': return 'u';
- case 'û': return 'u';
- case 'ü': return 'u';
- case 'ý': return 'y';
- case 'ÿ': return 'y';
- case 'Ā': return 'a';
- case 'ā': return 'a';
- case 'Ă': return 'a';
- case 'ă': return 'a';
- case 'Ą': return 'a';
- case 'ą': return 'a';
- case 'Ć': return 'c';
- case 'ć': return 'c';
- case 'Ĉ': return 'c';
- case 'ĉ': return 'c';
- case 'Ċ': return 'c';
- case 'ċ': return 'c';
- case 'Č': return 'c';
- case 'č': return 'c';
- case 'Ď': return 'd';
- case 'ď': return 'd';
- case 'Đ': return 'd';
- case 'đ': return 'd';
- case 'Ē': return 'e';
- case 'ē': return 'e';
- case 'Ĕ': return 'e';
- case 'ĕ': return 'e';
- case 'Ė': return 'e';
- case 'ė': return 'e';
- case 'Ę': return 'e';
- case 'ę': return 'e';
- case 'Ě': return 'e';
- case 'ě': return 'e';
- case 'Ĝ': return 'g';
- case 'ĝ': return 'g';
- case 'Ğ': return 'g';
- case 'ğ': return 'g';
- case 'Ġ': return 'g';
- case 'ġ': return 'g';
- case 'Ģ': return 'g';
- case 'ģ': return 'g';
- case 'Ĥ': return 'h';
- case 'ĥ': return 'h';
- case 'Ħ': return 'h';
- case 'ħ': return 'h';
- case 'Ĩ': return 'i';
- case 'ĩ': return 'i';
- case 'Ī': return 'i';
- case 'ī': return 'i';
- case 'Ĭ': return 'i';
- case 'ĭ': return 'i';
- case 'Į': return 'i';
- case 'į': return 'i';
- case 'İ': return 'i';
- case 'ı': return 'i';
- case 'Ĵ': return 'j';
- case 'ĵ': return 'j';
- case 'Ķ': return 'k';
- case 'ķ': return 'k';
- case 'ĸ': return 'k';
- case 'Ĺ': return 'l';
- case 'ĺ': return 'l';
- case 'Ļ': return 'l';
- case 'ļ': return 'l';
- case 'Ľ': return 'l';
- case 'ľ': return 'l';
- case 'Ŀ': return 'l';
- case 'ŀ': return 'l';
- case 'Ł': return 'l';
- case 'ł': return 'l';
- case 'Ń': return 'n';
- case 'ń': return 'n';
- case 'Ņ': return 'n';
- case 'ņ': return 'n';
- case 'Ň': return 'n';
- case 'ň': return 'n';
- case 'Ō': return 'o';
- case 'ō': return 'o';
- case 'Ŏ': return 'o';
- case 'ŏ': return 'o';
- case 'Ő': return 'o';
- case 'ő': return 'o';
- case 'Ŕ': return 'r';
- case 'ŕ': return 'r';
- case 'Ŗ': return 'r';
- case 'ŗ': return 'r';
- case 'Ř': return 'r';
- case 'ř': return 'r';
- case 'Ś': return 's';
- case 'ś': return 's';
- case 'Ŝ': return 's';
- case 'ŝ': return 's';
- case 'Ş': return 's';
- case 'ş': return 's';
- case 'Š': return 's';
- case 'š': return 's';
- case 'Ţ': return 't';
- case 'ţ': return 't';
- case 'Ť': return 't';
- case 'ť': return 't';
- case 'Ŧ': return 't';
- case 'ŧ': return 't';
- case 'Ũ': return 'u';
- case 'ũ': return 'u';
- case 'Ū': return 'u';
- case 'ū': return 'u';
- case 'Ŭ': return 'u';
- case 'ŭ': return 'u';
- case 'Ů': return 'u';
- case 'ů': return 'u';
- case 'Ű': return 'u';
- case 'ű': return 'u';
- case 'Ų': return 'u';
- case 'ų': return 'u';
- case 'Ŵ': return 'w';
- case 'ŵ': return 'w';
- case 'Ŷ': return 'y';
- case 'ŷ': return 'y';
- case 'Ÿ': return 'y';
- case 'Ź': return 'z';
- case 'ź': return 'z';
- case 'Ż': return 'z';
- case 'ż': return 'z';
- case 'Ž': return 'z';
- case 'ž': return 'z';
- case 'ſ': return 's';
- case 'ƀ': return 'b';
- case 'Ɓ': return 'b';
- case 'Ƃ': return 'b';
- case 'ƃ': return 'b';
- case 'Ɔ': return 'o';
- case 'Ƈ': return 'c';
- case 'ƈ': return 'c';
- case 'Ɖ': return 'd';
- case 'Ɗ': return 'd';
- case 'Ƌ': return 'd';
- case 'ƌ': return 'd';
- case 'ƍ': return 'd';
- case 'Ɛ': return 'e';
- case 'Ƒ': return 'f';
- case 'ƒ': return 'f';
- case 'Ɠ': return 'g';
- case 'Ɣ': return 'g';
- case 'Ɩ': return 'i';
- case 'Ɨ': return 'i';
- case 'Ƙ': return 'k';
- case 'ƙ': return 'k';
- case 'ƚ': return 'l';
- case 'ƛ': return 'l';
- case 'Ɯ': return 'w';
- case 'Ɲ': return 'n';
- case 'ƞ': return 'n';
- case 'Ɵ': return 'o';
- case 'Ơ': return 'o';
- case 'ơ': return 'o';
- case 'Ƥ': return 'p';
- case 'ƥ': return 'p';
- case 'ƫ': return 't';
- case 'Ƭ': return 't';
- case 'ƭ': return 't';
- case 'Ʈ': return 't';
- case 'Ư': return 'u';
- case 'ư': return 'u';
- case 'Ʊ': return 'y';
- case 'Ʋ': return 'v';
- case 'Ƴ': return 'y';
- case 'ƴ': return 'y';
- case 'Ƶ': return 'z';
- case 'ƶ': return 'z';
- case 'ƿ': return 'w';
- case 'Ǎ': return 'a';
- case 'ǎ': return 'a';
- case 'Ǐ': return 'i';
- case 'ǐ': return 'i';
- case 'Ǒ': return 'o';
- case 'ǒ': return 'o';
- case 'Ǔ': return 'u';
- case 'ǔ': return 'u';
- case 'Ǖ': return 'u';
- case 'ǖ': return 'u';
- case 'Ǘ': return 'u';
- case 'ǘ': return 'u';
- case 'Ǚ': return 'u';
- case 'ǚ': return 'u';
- case 'Ǜ': return 'u';
- case 'ǜ': return 'u';
- case 'Ǟ': return 'a';
- case 'ǟ': return 'a';
- case 'Ǡ': return 'a';
- case 'ǡ': return 'a';
- case 'Ǥ': return 'g';
- case 'ǥ': return 'g';
- case 'Ǧ': return 'g';
- case 'ǧ': return 'g';
- case 'Ǩ': return 'k';
- case 'ǩ': return 'k';
- case 'Ǫ': return 'o';
- case 'ǫ': return 'o';
- case 'Ǭ': return 'o';
- case 'ǭ': return 'o';
- case 'ǰ': return 'j';
- case 'Dz': return 'd';
- case 'Ǵ': return 'g';
- case 'ǵ': return 'g';
- case 'Ƿ': return 'w';
- case 'Ǹ': return 'n';
- case 'ǹ': return 'n';
- case 'Ǻ': return 'a';
- case 'ǻ': return 'a';
- case 'Ǿ': return 'o';
- case 'ǿ': return 'o';
- case 'Ȁ': return 'a';
- case 'ȁ': return 'a';
- case 'Ȃ': return 'a';
- case 'ȃ': return 'a';
- case 'Ȅ': return 'e';
- case 'ȅ': return 'e';
- case 'Ȇ': return 'e';
- case 'ȇ': return 'e';
- case 'Ȉ': return 'i';
- case 'ȉ': return 'i';
- case 'Ȋ': return 'i';
- case 'ȋ': return 'i';
- case 'Ȍ': return 'o';
- case 'ȍ': return 'o';
- case 'Ȏ': return 'o';
- case 'ȏ': return 'o';
- case 'Ȑ': return 'r';
- case 'ȑ': return 'r';
- case 'Ȓ': return 'r';
- case 'ȓ': return 'r';
- case 'Ȕ': return 'u';
- case 'ȕ': return 'u';
- case 'Ȗ': return 'u';
- case 'ȗ': return 'u';
- case 'Ș': return 's';
- case 'ș': return 's';
- case 'Ț': return 't';
- case 'ț': return 't';
- case 'Ȝ': return 'y';
- case 'ȝ': return 'y';
- case 'Ȟ': return 'h';
- case 'ȟ': return 'h';
- case 'Ȥ': return 'z';
- case 'ȥ': return 'z';
- case 'Ȧ': return 'a';
- case 'ȧ': return 'a';
- case 'Ȩ': return 'e';
- case 'ȩ': return 'e';
- case 'Ȫ': return 'o';
- case 'ȫ': return 'o';
- case 'Ȭ': return 'o';
- case 'ȭ': return 'o';
- case 'Ȯ': return 'o';
- case 'ȯ': return 'o';
- case 'Ȱ': return 'o';
- case 'ȱ': return 'o';
- case 'Ȳ': return 'y';
- case 'ȳ': return 'y';
- case 'A': return 'a';
- case 'B': return 'b';
- case 'C': return 'c';
- case 'D': return 'd';
- case 'E': return 'e';
- case 'F': return 'f';
- case 'G': return 'g';
- case 'H': return 'h';
- case 'I': return 'i';
- case 'J': return 'j';
- case 'K': return 'k';
- case 'L': return 'l';
- case 'M': return 'm';
- case 'N': return 'n';
- case 'O': return 'o';
- case 'P': return 'p';
- case 'Q': return 'q';
- case 'R': return 'r';
- case 'S': return 's';
- case 'T': return 't';
- case 'U': return 'u';
- case 'V': return 'v';
- case 'W': return 'w';
- case 'X': return 'x';
- case 'Y': return 'y';
- case 'Z': return 'z';
- default:
- return ch;
- }
- }
-
- @Override
- public byte getDialpadIndex(char ch) {
- if (ch >= '0' && ch <= '9') {
- return (byte) (ch - '0');
- } else if (ch >= 'a' && ch <= 'z') {
- return (byte) (LATIN_LETTERS_TO_DIGITS[ch - 'a'] - '0');
- } else {
- return -1;
- }
- }
-
- @Override
- public char getDialpadNumericCharacter(char ch) {
- if (ch >= 'a' && ch <= 'z') {
- return LATIN_LETTERS_TO_DIGITS[ch - 'a'];
- }
- return ch;
- }
-
-}
diff --git a/src/com/android/dialer/dialpad/PseudoEmergencyAnimator.java b/src/com/android/dialer/dialpad/PseudoEmergencyAnimator.java
deleted file mode 100644
index d4f32b5d4..000000000
--- a/src/com/android/dialer/dialpad/PseudoEmergencyAnimator.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * 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.dialpad;
-
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
-import android.animation.ArgbEvaluator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.LightingColorFilter;
-import android.os.Handler;
-import android.os.Vibrator;
-import android.view.View;
-
-import com.android.dialer.R;
-
-/**
- * Animates the dial button on "emergency" phone numbers.
- */
-public class PseudoEmergencyAnimator {
- public interface ViewProvider {
- View getView();
- }
-
- public static final String PSEUDO_EMERGENCY_NUMBER = "01189998819991197253";
-
- private static final int VIBRATE_LENGTH_MILLIS = 200;
- private static final int ITERATION_LENGTH_MILLIS = 1000;
- private static final int ANIMATION_ITERATION_COUNT = 6;
-
- private ViewProvider mViewProvider;
- private ValueAnimator mPseudoEmergencyColorAnimator;
-
- PseudoEmergencyAnimator(ViewProvider viewProvider) {
- mViewProvider = viewProvider;
- }
-
- public void destroy() {
- end();
- mViewProvider = null;
- }
-
- public void start() {
- if (mPseudoEmergencyColorAnimator == null) {
- Integer colorFrom = Color.BLUE;
- Integer colorTo = Color.RED;
- mPseudoEmergencyColorAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
-
- mPseudoEmergencyColorAnimator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animator) {
- try {
- int color = (int) animator.getAnimatedValue();
- ColorFilter colorFilter =
- new LightingColorFilter(Color.BLACK, color);
-
- View floatingActionButtonContainer = getView().findViewById(
- R.id.dialpad_floating_action_button_container);
- if (floatingActionButtonContainer != null) {
- floatingActionButtonContainer.getBackground().setColorFilter(
- colorFilter);
- }
- } catch (Exception e) {
- animator.cancel();
- }
- }
- });
-
- mPseudoEmergencyColorAnimator.addListener(new AnimatorListener() {
- @Override
- public void onAnimationCancel(Animator animation) { }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- try {
- vibrate(VIBRATE_LENGTH_MILLIS);
- } catch (Exception e) {
- animation.cancel();
- }
- }
-
- @Override
- public void onAnimationStart(Animator animation) { }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- try {
- View floatingActionButtonContainer = getView().findViewById(
- R.id.dialpad_floating_action_button_container);
- if (floatingActionButtonContainer != null) {
- floatingActionButtonContainer.getBackground().clearColorFilter();
- }
-
- new Handler().postDelayed(new Runnable() {
- @Override public void run() {
- try {
- vibrate(VIBRATE_LENGTH_MILLIS);
- } catch (Exception e) {
- // ignored
- }
- }
- }, ITERATION_LENGTH_MILLIS);
- } catch (Exception e) {
- animation.cancel();
- }
- }
- });
-
- mPseudoEmergencyColorAnimator.setDuration(VIBRATE_LENGTH_MILLIS);
- mPseudoEmergencyColorAnimator.setRepeatMode(ValueAnimator.REVERSE);
- mPseudoEmergencyColorAnimator.setRepeatCount(ANIMATION_ITERATION_COUNT);
- }
- if (!mPseudoEmergencyColorAnimator.isStarted()) {
- mPseudoEmergencyColorAnimator.start();
- }
- }
-
- public void end() {
- if (mPseudoEmergencyColorAnimator != null && mPseudoEmergencyColorAnimator.isStarted()) {
- mPseudoEmergencyColorAnimator.end();
- }
- }
-
- private View getView() {
- return mViewProvider == null ? null : mViewProvider.getView();
- }
-
- private Context getContext() {
- View view = getView();
- return view != null ? view.getContext() : null;
- }
-
- private void vibrate(long milliseconds) {
- Context context = getContext();
- if (context != null) {
- Vibrator vibrator =
- (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
- if (vibrator != null) {
- vibrator.vibrate(milliseconds);
- }
- }
- }
-}
diff --git a/src/com/android/dialer/dialpad/SmartDialCursorLoader.java b/src/com/android/dialer/dialpad/SmartDialCursorLoader.java
deleted file mode 100644
index 93b649b6d..000000000
--- a/src/com/android/dialer/dialpad/SmartDialCursorLoader.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialpad;
-
-import android.content.AsyncTaskLoader;
-import android.content.Context;
-import android.content.Loader.ForceLoadContentObserver;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.net.Uri;
-import android.util.Log;
-
-import com.android.contacts.common.list.PhoneNumberListAdapter.PhoneQuery;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.dialer.database.DialerDatabaseHelper;
-import com.android.dialer.database.DialerDatabaseHelper.ContactNumber;
-import com.android.dialerbind.DatabaseHelperManager;
-
-import java.util.ArrayList;
-
-/**
- * Implements a Loader<Cursor> class to asynchronously load SmartDial search results.
- */
-public class SmartDialCursorLoader extends AsyncTaskLoader<Cursor> {
-
- private final String TAG = SmartDialCursorLoader.class.getSimpleName();
- private final boolean DEBUG = false;
-
- private final Context mContext;
-
- private Cursor mCursor;
-
- private String mQuery;
- private SmartDialNameMatcher mNameMatcher;
-
- private ForceLoadContentObserver mObserver;
-
- public SmartDialCursorLoader(Context context) {
- super(context);
- mContext = context;
- }
-
- /**
- * Configures the query string to be used to find SmartDial matches.
- * @param query The query string user typed.
- */
- public void configureQuery(String query) {
- if (DEBUG) {
- Log.v(TAG, "Configure new query to be " + query);
- }
- mQuery = SmartDialNameMatcher.normalizeNumber(query, SmartDialPrefix.getMap());
-
- /** Constructs a name matcher object for matching names. */
- mNameMatcher = new SmartDialNameMatcher(mQuery, SmartDialPrefix.getMap());
- }
-
- /**
- * Queries the SmartDial database and loads results in background.
- * @return Cursor of contacts that matches the SmartDial query.
- */
- @Override
- public Cursor loadInBackground() {
- if (DEBUG) {
- Log.v(TAG, "Load in background " + mQuery);
- }
-
- if (!PermissionsUtil.hasContactsPermissions(mContext)) {
- return new MatrixCursor(PhoneQuery.PROJECTION_PRIMARY);
- }
-
- /** Loads results from the database helper. */
- final DialerDatabaseHelper dialerDatabaseHelper = DatabaseHelperManager.getDatabaseHelper(
- mContext);
- final ArrayList<ContactNumber> allMatches = dialerDatabaseHelper.getLooseMatches(mQuery,
- mNameMatcher);
-
- if (DEBUG) {
- Log.v(TAG, "Loaded matches " + String.valueOf(allMatches.size()));
- }
-
- /** Constructs a cursor for the returned array of results. */
- final MatrixCursor cursor = new MatrixCursor(PhoneQuery.PROJECTION_PRIMARY);
- Object[] row = new Object[PhoneQuery.PROJECTION_PRIMARY.length];
- for (ContactNumber contact : allMatches) {
- row[PhoneQuery.PHONE_ID] = contact.dataId;
- row[PhoneQuery.PHONE_NUMBER] = contact.phoneNumber;
- row[PhoneQuery.CONTACT_ID] = contact.id;
- row[PhoneQuery.LOOKUP_KEY] = contact.lookupKey;
- row[PhoneQuery.PHOTO_ID] = contact.photoId;
- row[PhoneQuery.DISPLAY_NAME] = contact.displayName;
- row[PhoneQuery.CARRIER_PRESENCE] = contact.carrierPresence;
- cursor.addRow(row);
- }
- return cursor;
- }
-
- @Override
- public void deliverResult(Cursor cursor) {
- if (isReset()) {
- /** The Loader has been reset; ignore the result and invalidate the data. */
- releaseResources(cursor);
- return;
- }
-
- /** Hold a reference to the old data so it doesn't get garbage collected. */
- Cursor oldCursor = mCursor;
- mCursor = cursor;
-
- if (mObserver == null) {
- mObserver = new ForceLoadContentObserver();
- mContext.getContentResolver().registerContentObserver(
- DialerDatabaseHelper.SMART_DIAL_UPDATED_URI, true, mObserver);
- }
-
- if (isStarted()) {
- /** If the Loader is in a started state, deliver the results to the client. */
- super.deliverResult(cursor);
- }
-
- /** Invalidate the old data as we don't need it any more. */
- if (oldCursor != null && oldCursor != cursor) {
- releaseResources(oldCursor);
- }
- }
-
- @Override
- protected void onStartLoading() {
- if (mCursor != null) {
- /** Deliver any previously loaded data immediately. */
- deliverResult(mCursor);
- }
- if (mCursor == null) {
- /** Force loads every time as our results change with queries. */
- forceLoad();
- }
- }
-
- @Override
- protected void onStopLoading() {
- /** The Loader is in a stopped state, so we should attempt to cancel the current load. */
- cancelLoad();
- }
-
- @Override
- protected void onReset() {
- /** Ensure the loader has been stopped. */
- onStopLoading();
-
- if (mObserver != null) {
- mContext.getContentResolver().unregisterContentObserver(mObserver);
- mObserver = null;
- }
-
- /** Release all previously saved query results. */
- if (mCursor != null) {
- releaseResources(mCursor);
- mCursor = null;
- }
- }
-
- @Override
- public void onCanceled(Cursor cursor) {
- super.onCanceled(cursor);
-
- if (mObserver != null) {
- mContext.getContentResolver().unregisterContentObserver(mObserver);
- mObserver = null;
- }
-
- /** The load has been canceled, so we should release the resources associated with 'data'.*/
- releaseResources(cursor);
- }
-
- private void releaseResources(Cursor cursor) {
- if (cursor != null) {
- cursor.close();
- }
- }
-}
diff --git a/src/com/android/dialer/dialpad/SmartDialMap.java b/src/com/android/dialer/dialpad/SmartDialMap.java
deleted file mode 100644
index b51891a8c..000000000
--- a/src/com/android/dialer/dialpad/SmartDialMap.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.android.dialer.dialpad;
-
-/**
- * Note: These methods currently take characters as arguments. For future planned language support,
- * they will need to be changed to use codepoints instead of characters.
- *
- * http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#codePointAt(int)
- *
- * If/when this change is made, LatinSmartDialMap(which operates on chars) will continue to work
- * by simply casting from a codepoint to a character.
- */
-public interface SmartDialMap {
- /*
- * Returns true if the provided character can be mapped to a key on the dialpad
- */
- public boolean isValidDialpadCharacter(char ch);
-
- /*
- * Returns true if the provided character is a letter, and can be mapped to a key on the dialpad
- */
- public boolean isValidDialpadAlphabeticChar(char ch);
-
- /*
- * Returns true if the provided character is a digit, and can be mapped to a key on the dialpad
- */
- public boolean isValidDialpadNumericChar(char ch);
-
- /*
- * Get the index of the key on the dialpad which the character corresponds to
- */
- public byte getDialpadIndex(char ch);
-
- /*
- * Get the actual numeric character on the dialpad which the character corresponds to
- */
- public char getDialpadNumericCharacter(char ch);
-
- /*
- * Converts uppercase characters to lower case ones, and on a best effort basis, strips accents
- * from accented characters.
- */
- public char normalizeCharacter(char ch);
-}
diff --git a/src/com/android/dialer/dialpad/SmartDialMatchPosition.java b/src/com/android/dialer/dialpad/SmartDialMatchPosition.java
deleted file mode 100644
index bab2c50d8..000000000
--- a/src/com/android/dialer/dialpad/SmartDialMatchPosition.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2012 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.dialpad;
-
-import android.util.Log;
-
-import java.util.ArrayList;
-
-/**
- * Stores information about a range of characters matched in a display name The integers
- * start and end indicate that the range start to end (exclusive) correspond to some characters
- * in the query. Used to highlight certain parts of the contact's display name to indicate that
- * those ranges matched the user's query.
- */
-public class SmartDialMatchPosition {
- private static final String TAG = SmartDialMatchPosition.class.getSimpleName();
-
- public int start;
- public int end;
-
- public SmartDialMatchPosition(int start, int end) {
- this.start = start;
- this.end = end;
- }
-
- private void advance(int toAdvance) {
- this.start += toAdvance;
- this.end += toAdvance;
- }
-
- /**
- * Used by {@link SmartDialNameMatcher} to advance the positions of a match position found in
- * a sub query.
- *
- * @param inList ArrayList of SmartDialMatchPositions to modify.
- * @param toAdvance Offset to modify by.
- */
- public static void advanceMatchPositions(ArrayList<SmartDialMatchPosition> inList,
- int toAdvance) {
- for (int i = 0; i < inList.size(); i++) {
- inList.get(i).advance(toAdvance);
- }
- }
-
- /**
- * Used mainly for debug purposes. Displays contents of an ArrayList of SmartDialMatchPositions.
- *
- * @param list ArrayList of SmartDialMatchPositions to print out in a human readable fashion.
- */
- public static void print(ArrayList<SmartDialMatchPosition> list) {
- for (int i = 0; i < list.size(); i ++) {
- SmartDialMatchPosition m = list.get(i);
- Log.d(TAG, "[" + m.start + "," + m.end + "]");
- }
- }
-}
diff --git a/src/com/android/dialer/dialpad/SmartDialNameMatcher.java b/src/com/android/dialer/dialpad/SmartDialNameMatcher.java
deleted file mode 100644
index a54fe1618..000000000
--- a/src/com/android/dialer/dialpad/SmartDialNameMatcher.java
+++ /dev/null
@@ -1,439 +0,0 @@
-/*
- * Copyright (C) 2012 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.dialpad;
-
-import android.support.annotation.Nullable;
-import android.text.TextUtils;
-
-import com.android.dialer.dialpad.SmartDialPrefix.PhoneNumberTokens;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Lists;
-
-import java.util.ArrayList;
-
-/**
- * {@link #SmartDialNameMatcher} contains utility functions to remove accents from accented
- * characters and normalize a phone number. It also contains the matching logic that determines if
- * a contact's display name matches a numeric query. The boolean variable
- * {@link #ALLOW_INITIAL_MATCH} controls the behavior of the matching logic and determines
- * whether we allow matches like 57 - (J)ohn (S)mith.
- */
-public class SmartDialNameMatcher {
-
- private String mQuery;
-
- // Whether or not we allow matches like 57 - (J)ohn (S)mith
- private static final boolean ALLOW_INITIAL_MATCH = true;
-
- // The maximum length of the initial we will match - typically set to 1 to minimize false
- // positives
- private static final int INITIAL_LENGTH_LIMIT = 1;
-
- private final ArrayList<SmartDialMatchPosition> mMatchPositions = Lists.newArrayList();
-
- public static final SmartDialMap LATIN_SMART_DIAL_MAP = new LatinSmartDialMap();
-
- private final SmartDialMap mMap;
-
- private String mNameMatchMask = "";
- private String mPhoneNumberMatchMask = "";
-
- @VisibleForTesting
- public SmartDialNameMatcher(String query) {
- this(query, LATIN_SMART_DIAL_MAP);
- }
-
- public SmartDialNameMatcher(String query, SmartDialMap map) {
- mQuery = query;
- mMap = map;
- }
-
- /**
- * Constructs empty highlight mask. Bit 0 at a position means there is no match, Bit 1 means
- * there is a match and should be highlighted in the TextView.
- * @param builder StringBuilder object
- * @param length Length of the desired mask.
- */
- private void constructEmptyMask(StringBuilder builder, int length) {
- for (int i = 0; i < length; ++i) {
- builder.append("0");
- }
- }
-
- /**
- * Replaces the 0-bit at a position with 1-bit, indicating that there is a match.
- * @param builder StringBuilder object.
- * @param matchPos Match Positions to mask as 1.
- */
- private void replaceBitInMask(StringBuilder builder, SmartDialMatchPosition matchPos) {
- for (int i = matchPos.start; i < matchPos.end; ++i) {
- builder.replace(i, i + 1, "1");
- }
- }
-
- /**
- * Strips a phone number of unnecessary characters (spaces, dashes, etc.)
- *
- * @param number Phone number we want to normalize
- * @return Phone number consisting of digits from 0-9
- */
- public static String normalizeNumber(String number, SmartDialMap map) {
- return normalizeNumber(number, 0, map);
- }
-
- /**
- * Strips a phone number of unnecessary characters (spaces, dashes, etc.)
- *
- * @param number Phone number we want to normalize
- * @param offset Offset to start from
- * @return Phone number consisting of digits from 0-9
- */
- public static String normalizeNumber(String number, int offset, SmartDialMap map) {
- final StringBuilder s = new StringBuilder();
- for (int i = offset; i < number.length(); i++) {
- char ch = number.charAt(i);
- if (map.isValidDialpadNumericChar(ch)) {
- s.append(ch);
- }
- }
- return s.toString();
- }
-
- /**
- * Matches a phone number against a query. Let the test application overwrite the NANP setting.
- *
- * @param phoneNumber - Raw phone number
- * @param query - Normalized query (only contains numbers from 0-9)
- * @param useNanp - Overwriting nanp setting boolean, used for testing.
- * @return {@literal null} if the number and the query don't match, a valid
- * SmartDialMatchPosition with the matching positions otherwise
- */
- @VisibleForTesting
- @Nullable
- public SmartDialMatchPosition matchesNumber(String phoneNumber, String query, boolean useNanp) {
- if (TextUtils.isEmpty(phoneNumber)) {
- return null;
- }
- StringBuilder builder = new StringBuilder();
- constructEmptyMask(builder, phoneNumber.length());
- mPhoneNumberMatchMask = builder.toString();
-
- // Try matching the number as is
- SmartDialMatchPosition matchPos = matchesNumberWithOffset(phoneNumber, query, 0);
- if (matchPos == null) {
- final PhoneNumberTokens phoneNumberTokens =
- SmartDialPrefix.parsePhoneNumber(phoneNumber);
-
- if (phoneNumberTokens == null) {
- return matchPos;
- }
- if (phoneNumberTokens.countryCodeOffset != 0) {
- matchPos = matchesNumberWithOffset(phoneNumber, query,
- phoneNumberTokens.countryCodeOffset);
- }
- if (matchPos == null && phoneNumberTokens.nanpCodeOffset != 0 && useNanp) {
- matchPos = matchesNumberWithOffset(phoneNumber, query,
- phoneNumberTokens.nanpCodeOffset);
- }
- }
- if (matchPos != null) {
- replaceBitInMask(builder, matchPos);
- mPhoneNumberMatchMask = builder.toString();
- }
- return matchPos;
- }
-
- /**
- * Matches a phone number against the saved query, taking care of formatting characters and also
- * taking into account country code prefixes and special NANP number treatment.
- *
- * @param phoneNumber - Raw phone number
- * @return {@literal null} if the number and the query don't match, a valid
- * SmartDialMatchPosition with the matching positions otherwise
- */
- public SmartDialMatchPosition matchesNumber(String phoneNumber) {
- return matchesNumber(phoneNumber, mQuery, true);
- }
-
- /**
- * Matches a phone number against a query, taking care of formatting characters and also
- * taking into account country code prefixes and special NANP number treatment.
- *
- * @param phoneNumber - Raw phone number
- * @param query - Normalized query (only contains numbers from 0-9)
- * @return {@literal null} if the number and the query don't match, a valid
- * SmartDialMatchPosition with the matching positions otherwise
- */
- public SmartDialMatchPosition matchesNumber(String phoneNumber, String query) {
- return matchesNumber(phoneNumber, query, true);
- }
-
- /**
- * Matches a phone number against a query, taking care of formatting characters
- *
- * @param phoneNumber - Raw phone number
- * @param query - Normalized query (only contains numbers from 0-9)
- * @param offset - The position in the number to start the match against (used to ignore
- * leading prefixes/country codes)
- * @return {@literal null} if the number and the query don't match, a valid
- * SmartDialMatchPosition with the matching positions otherwise
- */
- private SmartDialMatchPosition matchesNumberWithOffset(String phoneNumber, String query,
- int offset) {
- if (TextUtils.isEmpty(phoneNumber) || TextUtils.isEmpty(query)) {
- return null;
- }
- int queryAt = 0;
- int numberAt = offset;
- for (int i = offset; i < phoneNumber.length(); i++) {
- if (queryAt == query.length()) {
- break;
- }
- char ch = phoneNumber.charAt(i);
- if (mMap.isValidDialpadNumericChar(ch)) {
- if (ch != query.charAt(queryAt)) {
- return null;
- }
- queryAt++;
- } else {
- if (queryAt == 0) {
- // Found a separator before any part of the query was matched, so advance the
- // offset to avoid prematurely highlighting separators before the rest of the
- // query.
- // E.g. don't highlight the first '-' if we're matching 1-510-111-1111 with
- // '510'.
- // However, if the current offset is 0, just include the beginning separators
- // anyway, otherwise the highlighting ends up looking weird.
- // E.g. if we're matching (510)-111-1111 with '510', we should include the
- // first '('.
- if (offset != 0) {
- offset++;
- }
- }
- }
- numberAt++;
- }
- return new SmartDialMatchPosition(0 + offset, numberAt);
- }
-
- /**
- * This function iterates through each token in the display name, trying to match the query
- * to the numeric equivalent of the token.
- *
- * A token is defined as a range in the display name delimited by characters that have no
- * latin alphabet equivalents (e.g. spaces - ' ', periods - ',', underscores - '_' or chinese
- * characters - '王'). Transliteration from non-latin characters to latin character will be
- * done on a best effort basis - e.g. 'Ü' - 'u'.
- *
- * For example,
- * the display name "Phillips Thomas Jr" contains three tokens: "phillips", "thomas", and "jr".
- *
- * A match must begin at the start of a token.
- * For example, typing 846(Tho) would match "Phillips Thomas", but 466(hom) would not.
- *
- * Also, a match can extend across tokens.
- * For example, typing 37337(FredS) would match (Fred S)mith.
- *
- * @param displayName The normalized(no accented characters) display name we intend to match
- * against.
- * @param query The string of digits that we want to match the display name to.
- * @param matchList An array list of {@link SmartDialMatchPosition}s that we add matched
- * positions to.
- * @return Returns true if a combination of the tokens in displayName match the query
- * string contained in query. If the function returns true, matchList will contain an
- * ArrayList of match positions (multiple matches correspond to initial matches).
- */
- @VisibleForTesting
- boolean matchesCombination(String displayName, String query,
- ArrayList<SmartDialMatchPosition> matchList) {
- StringBuilder builder = new StringBuilder();
- constructEmptyMask(builder, displayName.length());
- mNameMatchMask = builder.toString();
- final int nameLength = displayName.length();
- final int queryLength = query.length();
-
- if (nameLength < queryLength) {
- return false;
- }
-
- if (queryLength == 0) {
- return false;
- }
-
- // The current character index in displayName
- // E.g. 3 corresponds to 'd' in "Fred Smith"
- int nameStart = 0;
-
- // The current character in the query we are trying to match the displayName against
- int queryStart = 0;
-
- // The start position of the current token we are inspecting
- int tokenStart = 0;
-
- // The number of non-alphabetic characters we've encountered so far in the current match.
- // E.g. if we've currently matched 3733764849 to (Fred Smith W)illiam, then the
- // seperatorCount should be 2. This allows us to correctly calculate offsets for the match
- // positions
- int seperatorCount = 0;
-
- ArrayList<SmartDialMatchPosition> partial = new ArrayList<SmartDialMatchPosition>();
- // Keep going until we reach the end of displayName
- while (nameStart < nameLength && queryStart < queryLength) {
- char ch = displayName.charAt(nameStart);
- // Strip diacritics from accented characters if any
- ch = mMap.normalizeCharacter(ch);
- if (mMap.isValidDialpadCharacter(ch)) {
- if (mMap.isValidDialpadAlphabeticChar(ch)) {
- ch = mMap.getDialpadNumericCharacter(ch);
- }
- if (ch != query.charAt(queryStart)) {
- // Failed to match the current character in the query.
-
- // Case 1: Failed to match the first character in the query. Skip to the next
- // token since there is no chance of this token matching the query.
-
- // Case 2: Previous characters in the query matched, but the current character
- // failed to match. This happened in the middle of a token. Skip to the next
- // token since there is no chance of this token matching the query.
-
- // Case 3: Previous characters in the query matched, but the current character
- // failed to match. This happened right at the start of the current token. In
- // this case, we should restart the query and try again with the current token.
- // Otherwise, we would fail to match a query like "964"(yog) against a name
- // Yo-Yoghurt because the query match would fail on the 3rd character, and
- // then skip to the end of the "Yoghurt" token.
-
- if (queryStart == 0 || mMap.isValidDialpadCharacter(mMap.normalizeCharacter(
- displayName.charAt(nameStart - 1)))) {
- // skip to the next token, in the case of 1 or 2.
- while (nameStart < nameLength &&
- mMap.isValidDialpadCharacter(mMap.normalizeCharacter(
- displayName.charAt(nameStart)))) {
- nameStart++;
- }
- nameStart++;
- }
-
- // Restart the query and set the correct token position
- queryStart = 0;
- seperatorCount = 0;
- tokenStart = nameStart;
- } else {
- if (queryStart == queryLength - 1) {
-
- // As much as possible, we prioritize a full token match over a sub token
- // one so if we find a full token match, we can return right away
- matchList.add(new SmartDialMatchPosition(
- tokenStart, queryLength + tokenStart + seperatorCount));
- for (SmartDialMatchPosition match : matchList) {
- replaceBitInMask(builder, match);
- }
- mNameMatchMask = builder.toString();
- return true;
- } else if (ALLOW_INITIAL_MATCH && queryStart < INITIAL_LENGTH_LIMIT) {
- // we matched the first character.
- // branch off and see if we can find another match with the remaining
- // characters in the query string and the remaining tokens
- // find the next separator in the query string
- int j;
- for (j = nameStart; j < nameLength; j++) {
- if (!mMap.isValidDialpadCharacter(mMap.normalizeCharacter(
- displayName.charAt(j)))) {
- break;
- }
- }
- // this means there is at least one character left after the separator
- if (j < nameLength - 1) {
- final String remainder = displayName.substring(j + 1);
- final ArrayList<SmartDialMatchPosition> partialTemp =
- Lists.newArrayList();
- if (matchesCombination(
- remainder, query.substring(queryStart + 1), partialTemp)) {
-
- // store the list of possible match positions
- SmartDialMatchPosition.advanceMatchPositions(partialTemp, j + 1);
- partialTemp.add(0,
- new SmartDialMatchPosition(nameStart, nameStart + 1));
- // we found a partial token match, store the data in a
- // temp buffer and return it if we end up not finding a full
- // token match
- partial = partialTemp;
- }
- }
- }
- nameStart++;
- queryStart++;
- // we matched the current character in the name against one in the query,
- // continue and see if the rest of the characters match
- }
- } else {
- // found a separator, we skip this character and continue to the next one
- nameStart++;
- if (queryStart == 0) {
- // This means we found a separator before the start of a token,
- // so we should increment the token's start position to reflect its true
- // start position
- tokenStart = nameStart;
- } else {
- // Otherwise this separator was found in the middle of a token being matched,
- // so increase the separator count
- seperatorCount++;
- }
- }
- }
- // if we have no complete match at this point, then we attempt to fall back to the partial
- // token match(if any). If we don't allow initial matching (ALLOW_INITIAL_MATCH = false)
- // then partial will always be empty.
- if (!partial.isEmpty()) {
- matchList.addAll(partial);
- for (SmartDialMatchPosition match : matchList) {
- replaceBitInMask(builder, match);
- }
- mNameMatchMask = builder.toString();
- return true;
- }
- return false;
- }
-
- public boolean matches(String displayName) {
- mMatchPositions.clear();
- return matchesCombination(displayName, mQuery, mMatchPositions);
- }
-
- public ArrayList<SmartDialMatchPosition> getMatchPositions() {
- // Return a clone of mMatchPositions so that the caller can use it without
- // worrying about it changing
- return new ArrayList<SmartDialMatchPosition>(mMatchPositions);
- }
-
- public void setQuery(String query) {
- mQuery = query;
- }
-
- public String getNameMatchPositionsInString() {
- return mNameMatchMask;
- }
-
- public String getNumberMatchPositionsInString() {
- return mPhoneNumberMatchMask;
- }
-
- public String getQuery() {
- return mQuery;
- }
-}
diff --git a/src/com/android/dialer/dialpad/SmartDialPrefix.java b/src/com/android/dialer/dialpad/SmartDialPrefix.java
deleted file mode 100644
index a0b51ebb5..000000000
--- a/src/com/android/dialer/dialpad/SmartDialPrefix.java
+++ /dev/null
@@ -1,608 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialpad;
-
-import android.content.Context;
-
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Lists;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Smart Dial utility class to find prefixes of contacts. It contains both methods to find supported
- * prefix combinations for contact names, and also methods to find supported prefix combinations for
- * contacts' phone numbers. Each contact name is separated into several tokens, such as first name,
- * middle name, family name etc. Each phone number is also separated into country code, NANP area
- * code, and local number if such separation is possible.
- */
-public class SmartDialPrefix {
-
- /** The number of starting and ending tokens in a contact's name considered for initials.
- * For example, if both constants are set to 2, and a contact's name is
- * "Albert Ben Charles Daniel Ed Foster", the first two tokens "Albert" "Ben", and last two
- * tokens "Ed" "Foster" can be replaced by their initials in contact name matching.
- * Users can look up this contact by combinations of his initials such as "AF" "BF" "EF" "ABF"
- * "BEF" "ABEF" etc, but can not use combinations such as "CF" "DF" "ACF" "ADF" etc.
- */
- private static final int LAST_TOKENS_FOR_INITIALS = 2;
- private static final int FIRST_TOKENS_FOR_INITIALS = 2;
-
- /** The country code of the user's sim card obtained by calling getSimCountryIso*/
- private static final String PREF_USER_SIM_COUNTRY_CODE =
- "DialtactsActivity_user_sim_country_code";
- private static final String PREF_USER_SIM_COUNTRY_CODE_DEFAULT = null;
- private static String sUserSimCountryCode = PREF_USER_SIM_COUNTRY_CODE_DEFAULT;
-
- /** Indicates whether user is in NANP regions.*/
- private static boolean sUserInNanpRegion = false;
-
- /** Set of country names that use NANP code.*/
- private static Set<String> sNanpCountries = null;
-
- /** Set of supported country codes in front of the phone number. */
- private static Set<String> sCountryCodes = null;
-
- /** Dialpad mapping. */
- private static final SmartDialMap mMap = new LatinSmartDialMap();
-
- private static boolean sNanpInitialized = false;
-
- /** Initializes the Nanp settings, and finds out whether user is in a NANP region.*/
- public static void initializeNanpSettings(Context context){
- final TelephonyManager manager = (TelephonyManager) context.getSystemService(
- Context.TELEPHONY_SERVICE);
- if (manager != null) {
- sUserSimCountryCode = manager.getSimCountryIso();
- }
-
- final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
-
- if (sUserSimCountryCode != null) {
- /** Updates shared preferences with the latest country obtained from getSimCountryIso.*/
- prefs.edit().putString(PREF_USER_SIM_COUNTRY_CODE, sUserSimCountryCode).apply();
- } else {
- /** Uses previously stored country code if loading fails. */
- sUserSimCountryCode = prefs.getString(PREF_USER_SIM_COUNTRY_CODE,
- PREF_USER_SIM_COUNTRY_CODE_DEFAULT);
- }
- /** Queries the NANP country list to find out whether user is in a NANP region.*/
- sUserInNanpRegion = isCountryNanp(sUserSimCountryCode);
- sNanpInitialized = true;
- }
-
- /**
- * Explicitly setting the user Nanp to the given boolean
- */
- @VisibleForTesting
- public static void setUserInNanpRegion(boolean userInNanpRegion) {
- sUserInNanpRegion = userInNanpRegion;
- }
-
- /**
- * Class to record phone number parsing information.
- */
- public static class PhoneNumberTokens {
- /** Country code of the phone number. */
- final String countryCode;
-
- /** Offset of national number after the country code. */
- final int countryCodeOffset;
-
- /** Offset of local number after NANP area code.*/
- final int nanpCodeOffset;
-
- public PhoneNumberTokens(String countryCode, int countryCodeOffset, int nanpCodeOffset) {
- this.countryCode = countryCode;
- this.countryCodeOffset = countryCodeOffset;
- this.nanpCodeOffset = nanpCodeOffset;
- }
- }
-
- /**
- * Parses a contact's name into a list of separated tokens.
- *
- * @param contactName Contact's name stored in string.
- * @return A list of name tokens, for example separated first names, last name, etc.
- */
- public static ArrayList<String> parseToIndexTokens(String contactName) {
- final int length = contactName.length();
- final ArrayList<String> result = Lists.newArrayList();
- char c;
- final StringBuilder currentIndexToken = new StringBuilder();
- /**
- * Iterates through the whole name string. If the current character is a valid character,
- * append it to the current token. If the current character is not a valid character, for
- * example space " ", mark the current token as complete and add it to the list of tokens.
- */
- for (int i = 0; i < length; i++) {
- c = mMap.normalizeCharacter(contactName.charAt(i));
- if (mMap.isValidDialpadCharacter(c)) {
- /** Converts a character into the number on dialpad that represents the character.*/
- currentIndexToken.append(mMap.getDialpadIndex(c));
- } else {
- if (currentIndexToken.length() != 0) {
- result.add(currentIndexToken.toString());
- }
- currentIndexToken.delete(0, currentIndexToken.length());
- }
- }
-
- /** Adds the last token in case it has not been added.*/
- if (currentIndexToken.length() != 0) {
- result.add(currentIndexToken.toString());
- }
- return result;
- }
-
- /**
- * Generates a list of strings that any prefix of any string in the list can be used to look
- * up the contact's name.
- *
- * @param index The contact's name in string.
- * @return A List of strings, whose prefix can be used to look up the contact.
- */
- public static ArrayList<String> generateNamePrefixes(String index) {
- final ArrayList<String> result = Lists.newArrayList();
-
- /** Parses the name into a list of tokens.*/
- final ArrayList<String> indexTokens = parseToIndexTokens(index);
-
- if (indexTokens.size() > 0) {
- /** Adds the full token combinations to the list. For example, a contact with name
- * "Albert Ben Ed Foster" can be looked up by any prefix of the following strings
- * "Foster" "EdFoster" "BenEdFoster" and "AlbertBenEdFoster". This covers all cases of
- * look up that contains only one token, and that spans multiple continuous tokens.
- */
- final StringBuilder fullNameToken = new StringBuilder();
- for (int i = indexTokens.size() - 1; i >= 0; i--) {
- fullNameToken.insert(0, indexTokens.get(i));
- result.add(fullNameToken.toString());
- }
-
- /** Adds initial combinations to the list, with the number of initials restricted by
- * {@link #LAST_TOKENS_FOR_INITIALS} and {@link #FIRST_TOKENS_FOR_INITIALS}.
- * For example, a contact with name "Albert Ben Ed Foster" can be looked up by any
- * prefix of the following strings "EFoster" "BFoster" "BEFoster" "AFoster" "ABFoster"
- * "AEFoster" and "ABEFoster". This covers all cases of initial lookup.
- */
- ArrayList<String> fullNames = Lists.newArrayList();
- fullNames.add(indexTokens.get(indexTokens.size() - 1));
- final int recursiveNameStart = result.size();
- int recursiveNameEnd = result.size();
- String initial = "";
- for (int i = indexTokens.size() - 2; i >= 0; i--) {
- if ((i >= indexTokens.size() - LAST_TOKENS_FOR_INITIALS) ||
- (i < FIRST_TOKENS_FOR_INITIALS)) {
- initial = indexTokens.get(i).substring(0, 1);
-
- /** Recursively adds initial combinations to the list.*/
- for (int j = 0; j < fullNames.size(); ++j) {
- result.add(initial + fullNames.get(j));
- }
- for (int j = recursiveNameStart; j < recursiveNameEnd; ++j) {
- result.add(initial + result.get(j));
- }
- recursiveNameEnd = result.size();
- final String currentFullName = fullNames.get(fullNames.size() - 1);
- fullNames.add(indexTokens.get(i) + currentFullName);
- }
- }
- }
-
- return result;
- }
-
- /**
- * Computes a list of number strings based on tokens of a given phone number. Any prefix
- * of any string in the list can be used to look up the phone number. The list include the
- * full phone number, the national number if there is a country code in the phone number, and
- * the local number if there is an area code in the phone number following the NANP format.
- * For example, if a user has phone number +41 71 394 8392, the list will contain 41713948392
- * and 713948392. Any prefix to either of the strings can be used to look up the phone number.
- * If a user has a phone number +1 555-302-3029 (NANP format), the list will contain
- * 15553023029, 5553023029, and 3023029.
- *
- * @param number String of user's phone number.
- * @return A list of strings where any prefix of any entry can be used to look up the number.
- */
- public static ArrayList<String> parseToNumberTokens(String number) {
- final ArrayList<String> result = Lists.newArrayList();
- if (!TextUtils.isEmpty(number)) {
- /** Adds the full number to the list.*/
- result.add(SmartDialNameMatcher.normalizeNumber(number, mMap));
-
- final PhoneNumberTokens phoneNumberTokens = parsePhoneNumber(number);
- if (phoneNumberTokens == null) {
- return result;
- }
-
- if (phoneNumberTokens.countryCodeOffset != 0) {
- result.add(SmartDialNameMatcher.normalizeNumber(number,
- phoneNumberTokens.countryCodeOffset, mMap));
- }
-
- if (phoneNumberTokens.nanpCodeOffset != 0) {
- result.add(SmartDialNameMatcher.normalizeNumber(number,
- phoneNumberTokens.nanpCodeOffset, mMap));
- }
- }
- return result;
- }
-
- /**
- * Parses a phone number to find out whether it has country code and NANP area code.
- *
- * @param number Raw phone number.
- * @return a PhoneNumberToken instance with country code, NANP code information.
- */
- public static PhoneNumberTokens parsePhoneNumber(String number) {
- String countryCode = "";
- int countryCodeOffset = 0;
- int nanpNumberOffset = 0;
-
- if (!TextUtils.isEmpty(number)) {
- String normalizedNumber = SmartDialNameMatcher.normalizeNumber(number, mMap);
- if (number.charAt(0) == '+') {
- /** If the number starts with '+', tries to find valid country code. */
- for (int i = 1; i <= 1 + 3; i++) {
- if (number.length() <= i) {
- break;
- }
- countryCode = number.substring(1, i);
- if (isValidCountryCode(countryCode)) {
- countryCodeOffset = i;
- break;
- }
- }
- } else {
- /** If the number does not start with '+', finds out whether it is in NANP
- * format and has '1' preceding the number.
- */
- if ((normalizedNumber.length() == 11) && (normalizedNumber.charAt(0) == '1') &&
- (sUserInNanpRegion)) {
- countryCode = "1";
- countryCodeOffset = number.indexOf(normalizedNumber.charAt(1));
- if (countryCodeOffset == -1) {
- countryCodeOffset = 0;
- }
- }
- }
-
- /** If user is in NANP region, finds out whether a number is in NANP format.*/
- if (sUserInNanpRegion) {
- String areaCode = "";
- if (countryCode.equals("") && normalizedNumber.length() == 10){
- /** if the number has no country code but fits the NANP format, extracts the
- * NANP area code, and finds out offset of the local number.
- */
- areaCode = normalizedNumber.substring(0, 3);
- } else if (countryCode.equals("1") && normalizedNumber.length() == 11) {
- /** If the number has country code '1', finds out area code and offset of the
- * local number.
- */
- areaCode = normalizedNumber.substring(1, 4);
- }
- if (!areaCode.equals("")) {
- final int areaCodeIndex = number.indexOf(areaCode);
- if (areaCodeIndex != -1) {
- nanpNumberOffset = number.indexOf(areaCode) + 3;
- }
- }
- }
- }
- return new PhoneNumberTokens(countryCode, countryCodeOffset, nanpNumberOffset);
- }
-
- /**
- * Checkes whether a country code is valid.
- */
- private static boolean isValidCountryCode(String countryCode) {
- if (sCountryCodes == null) {
- sCountryCodes = initCountryCodes();
- }
- return sCountryCodes.contains(countryCode);
- }
-
- private static Set<String> initCountryCodes() {
- final HashSet<String> result = new HashSet<String>();
- result.add("1");
- result.add("7");
- result.add("20");
- result.add("27");
- result.add("30");
- result.add("31");
- result.add("32");
- result.add("33");
- result.add("34");
- result.add("36");
- result.add("39");
- result.add("40");
- result.add("41");
- result.add("43");
- result.add("44");
- result.add("45");
- result.add("46");
- result.add("47");
- result.add("48");
- result.add("49");
- result.add("51");
- result.add("52");
- result.add("53");
- result.add("54");
- result.add("55");
- result.add("56");
- result.add("57");
- result.add("58");
- result.add("60");
- result.add("61");
- result.add("62");
- result.add("63");
- result.add("64");
- result.add("65");
- result.add("66");
- result.add("81");
- result.add("82");
- result.add("84");
- result.add("86");
- result.add("90");
- result.add("91");
- result.add("92");
- result.add("93");
- result.add("94");
- result.add("95");
- result.add("98");
- result.add("211");
- result.add("212");
- result.add("213");
- result.add("216");
- result.add("218");
- result.add("220");
- result.add("221");
- result.add("222");
- result.add("223");
- result.add("224");
- result.add("225");
- result.add("226");
- result.add("227");
- result.add("228");
- result.add("229");
- result.add("230");
- result.add("231");
- result.add("232");
- result.add("233");
- result.add("234");
- result.add("235");
- result.add("236");
- result.add("237");
- result.add("238");
- result.add("239");
- result.add("240");
- result.add("241");
- result.add("242");
- result.add("243");
- result.add("244");
- result.add("245");
- result.add("246");
- result.add("247");
- result.add("248");
- result.add("249");
- result.add("250");
- result.add("251");
- result.add("252");
- result.add("253");
- result.add("254");
- result.add("255");
- result.add("256");
- result.add("257");
- result.add("258");
- result.add("260");
- result.add("261");
- result.add("262");
- result.add("263");
- result.add("264");
- result.add("265");
- result.add("266");
- result.add("267");
- result.add("268");
- result.add("269");
- result.add("290");
- result.add("291");
- result.add("297");
- result.add("298");
- result.add("299");
- result.add("350");
- result.add("351");
- result.add("352");
- result.add("353");
- result.add("354");
- result.add("355");
- result.add("356");
- result.add("357");
- result.add("358");
- result.add("359");
- result.add("370");
- result.add("371");
- result.add("372");
- result.add("373");
- result.add("374");
- result.add("375");
- result.add("376");
- result.add("377");
- result.add("378");
- result.add("379");
- result.add("380");
- result.add("381");
- result.add("382");
- result.add("385");
- result.add("386");
- result.add("387");
- result.add("389");
- result.add("420");
- result.add("421");
- result.add("423");
- result.add("500");
- result.add("501");
- result.add("502");
- result.add("503");
- result.add("504");
- result.add("505");
- result.add("506");
- result.add("507");
- result.add("508");
- result.add("509");
- result.add("590");
- result.add("591");
- result.add("592");
- result.add("593");
- result.add("594");
- result.add("595");
- result.add("596");
- result.add("597");
- result.add("598");
- result.add("599");
- result.add("670");
- result.add("672");
- result.add("673");
- result.add("674");
- result.add("675");
- result.add("676");
- result.add("677");
- result.add("678");
- result.add("679");
- result.add("680");
- result.add("681");
- result.add("682");
- result.add("683");
- result.add("685");
- result.add("686");
- result.add("687");
- result.add("688");
- result.add("689");
- result.add("690");
- result.add("691");
- result.add("692");
- result.add("800");
- result.add("808");
- result.add("850");
- result.add("852");
- result.add("853");
- result.add("855");
- result.add("856");
- result.add("870");
- result.add("878");
- result.add("880");
- result.add("881");
- result.add("882");
- result.add("883");
- result.add("886");
- result.add("888");
- result.add("960");
- result.add("961");
- result.add("962");
- result.add("963");
- result.add("964");
- result.add("965");
- result.add("966");
- result.add("967");
- result.add("968");
- result.add("970");
- result.add("971");
- result.add("972");
- result.add("973");
- result.add("974");
- result.add("975");
- result.add("976");
- result.add("977");
- result.add("979");
- result.add("992");
- result.add("993");
- result.add("994");
- result.add("995");
- result.add("996");
- result.add("998");
- return result;
- }
-
- public static SmartDialMap getMap() {
- return mMap;
- }
-
- /**
- * Indicates whether the given country uses NANP numbers
- * @see <a href="https://en.wikipedia.org/wiki/North_American_Numbering_Plan">
- * https://en.wikipedia.org/wiki/North_American_Numbering_Plan</a>
- *
- * @param country ISO 3166 country code (case doesn't matter)
- * @return True if country uses NANP numbers (e.g. US, Canada), false otherwise
- */
- @VisibleForTesting
- public static boolean isCountryNanp(String country) {
- if (TextUtils.isEmpty(country)) {
- return false;
- }
- if (sNanpCountries == null) {
- sNanpCountries = initNanpCountries();
- }
- return sNanpCountries.contains(country.toUpperCase());
- }
-
- private static Set<String> initNanpCountries() {
- final HashSet<String> result = new HashSet<String>();
- result.add("US"); // United States
- result.add("CA"); // Canada
- result.add("AS"); // American Samoa
- result.add("AI"); // Anguilla
- result.add("AG"); // Antigua and Barbuda
- result.add("BS"); // Bahamas
- result.add("BB"); // Barbados
- result.add("BM"); // Bermuda
- result.add("VG"); // British Virgin Islands
- result.add("KY"); // Cayman Islands
- result.add("DM"); // Dominica
- result.add("DO"); // Dominican Republic
- result.add("GD"); // Grenada
- result.add("GU"); // Guam
- result.add("JM"); // Jamaica
- result.add("PR"); // Puerto Rico
- result.add("MS"); // Montserrat
- result.add("MP"); // Northern Mariana Islands
- result.add("KN"); // Saint Kitts and Nevis
- result.add("LC"); // Saint Lucia
- result.add("VC"); // Saint Vincent and the Grenadines
- result.add("TT"); // Trinidad and Tobago
- result.add("TC"); // Turks and Caicos Islands
- result.add("VI"); // U.S. Virgin Islands
- return result;
- }
-
- /**
- * Returns whether the user is in a region that uses Nanp format based on the sim location.
- *
- * @return Whether user is in Nanp region.
- */
- public static boolean getUserInNanpRegion() {
- return sUserInNanpRegion;
- }
-}
diff --git a/src/com/android/dialer/dialpad/UnicodeDialerKeyListener.java b/src/com/android/dialer/dialpad/UnicodeDialerKeyListener.java
deleted file mode 100644
index 740b4566f..000000000
--- a/src/com/android/dialer/dialpad/UnicodeDialerKeyListener.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2012 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.dialpad;
-
-import android.telephony.PhoneNumberUtils;
-import android.text.Spanned;
-import android.text.method.DialerKeyListener;
-
-/**
- * {@link DialerKeyListener} with Unicode support. Converts any Unicode(e.g. Arabic) characters
- * that represent digits into digits before filtering the results so that we can support
- * pasted digits from Unicode languages.
- */
-public class UnicodeDialerKeyListener extends DialerKeyListener {
- public static final UnicodeDialerKeyListener INSTANCE = new UnicodeDialerKeyListener();
-
- @Override
- public CharSequence filter(CharSequence source, int start, int end,
- Spanned dest, int dstart, int dend) {
- final String converted = PhoneNumberUtils.convertKeypadLettersToDigits(
- PhoneNumberUtils.replaceUnicodeDigits(source.toString()));
- // PhoneNumberUtils.replaceUnicodeDigits performs a character for character replacement,
- // so we can assume that start and end positions should remain unchanged.
- CharSequence result = super.filter(converted, start, end, dest, dstart, dend);
- if (result == null) {
- if (source.equals(converted)) {
- // There was no conversion or filtering performed. Just return null according to
- // the behavior of DialerKeyListener.
- return null;
- } else {
- // filter returns null if the charsequence is to be returned unchanged/unfiltered.
- // But in this case we do want to return a modified character string (even if
- // none of the characters in the modified string are filtered). So if
- // result == null we return the unfiltered but converted numeric string instead.
- return converted.subSequence(start, end);
- }
- }
- return result;
- }
-}
diff --git a/src/com/android/dialer/filterednumber/BlockNumberDialogFragment.java b/src/com/android/dialer/filterednumber/BlockNumberDialogFragment.java
deleted file mode 100644
index 3c60a967b..000000000
--- a/src/com/android/dialer/filterednumber/BlockNumberDialogFragment.java
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * 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.filterednumber;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.FragmentManager;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.design.widget.Snackbar;
-import android.text.TextUtils;
-import android.view.View;
-import android.widget.Toast;
-
-import com.android.contacts.common.util.ContactDisplayUtils;
-import com.android.dialer.R;
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnBlockNumberListener;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnUnblockNumberListener;
-import com.android.dialer.voicemail.VisualVoicemailEnabledChecker;
-import com.android.dialer.logging.InteractionEvent;
-import com.android.dialer.logging.Logger;
-
-/**
- * Fragment for confirming and enacting blocking/unblocking a number. Also invokes snackbar
- * providing undo functionality.
- */
-public class BlockNumberDialogFragment extends DialogFragment {
-
- /**
- * Use a callback interface to update UI after success/undo. Favor this approach over other
- * more standard paradigms because of the variety of scenarios in which the DialogFragment
- * can be invoked (by an Activity, by a fragment, by an adapter, by an adapter list item).
- * Because of this, we do NOT support retaining state on rotation, and will dismiss the dialog
- * upon rotation instead.
- */
- public interface Callback {
- /**
- * Called when a number is successfully added to the set of filtered numbers
- */
- void onFilterNumberSuccess();
-
- /**
- * Called when a number is successfully removed from the set of filtered numbers
- */
- void onUnfilterNumberSuccess();
-
- /**
- * Called when the action of filtering or unfiltering a number is undone
- */
- void onChangeFilteredNumberUndo();
- }
-
- private static final String BLOCK_DIALOG_FRAGMENT = "BlockNumberDialog";
-
- private static final String ARG_BLOCK_ID = "argBlockId";
- private static final String ARG_NUMBER = "argNumber";
- private static final String ARG_COUNTRY_ISO = "argCountryIso";
- private static final String ARG_DISPLAY_NUMBER = "argDisplayNumber";
- private static final String ARG_PARENT_VIEW_ID = "parentViewId";
-
- private String mNumber;
- private String mDisplayNumber;
- private String mCountryIso;
-
- private FilteredNumberAsyncQueryHandler mHandler;
- private View mParentView;
- private VisualVoicemailEnabledChecker mVoicemailEnabledChecker;
- private Callback mCallback;
-
- public static void show(
- Integer blockId,
- String number,
- String countryIso,
- String displayNumber,
- Integer parentViewId,
- FragmentManager fragmentManager,
- Callback callback) {
- final BlockNumberDialogFragment newFragment = BlockNumberDialogFragment.newInstance(
- blockId, number, countryIso, displayNumber, parentViewId);
-
- newFragment.setCallback(callback);
- newFragment.show(fragmentManager, BlockNumberDialogFragment.BLOCK_DIALOG_FRAGMENT);
- }
-
- private static BlockNumberDialogFragment newInstance(
- Integer blockId,
- String number,
- String countryIso,
- String displayNumber,
- Integer parentViewId) {
- final BlockNumberDialogFragment fragment = new BlockNumberDialogFragment();
- final Bundle args = new Bundle();
- if (blockId != null) {
- args.putInt(ARG_BLOCK_ID, blockId.intValue());
- }
- if (parentViewId != null) {
- args.putInt(ARG_PARENT_VIEW_ID, parentViewId.intValue());
- }
- args.putString(ARG_NUMBER, number);
- args.putString(ARG_COUNTRY_ISO, countryIso);
- args.putString(ARG_DISPLAY_NUMBER, displayNumber);
- fragment.setArguments(args);
- return fragment;
- }
-
- @Override
- public Context getContext() {
- return getActivity();
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- super.onCreateDialog(savedInstanceState);
- final boolean isBlocked = getArguments().containsKey(ARG_BLOCK_ID);
-
- mNumber = getArguments().getString(ARG_NUMBER);
- mDisplayNumber = getArguments().getString(ARG_DISPLAY_NUMBER);
- mCountryIso = getArguments().getString(ARG_COUNTRY_ISO);
-
- if (TextUtils.isEmpty(mDisplayNumber)) {
- mDisplayNumber = mNumber;
- }
-
- mHandler = new FilteredNumberAsyncQueryHandler(getContext().getContentResolver());
- mVoicemailEnabledChecker = new VisualVoicemailEnabledChecker(getActivity(), null);
- /**
- * Choose not to update VoicemailEnabledChecker, as checks should already been done in
- * all current use cases.
- */
- mParentView = getActivity().findViewById(getArguments().getInt(ARG_PARENT_VIEW_ID));
-
- CharSequence title;
- String okText;
- String message;
- if (isBlocked) {
- title = null;
- okText = getString(R.string.unblock_number_ok);
- message = ContactDisplayUtils.getTtsSpannedPhoneNumber(getResources(),
- R.string.unblock_number_confirmation_title,
- mDisplayNumber).toString();
- } else {
- title = ContactDisplayUtils.getTtsSpannedPhoneNumber(getResources(),
- R.string.block_number_confirmation_title,
- mDisplayNumber);
- okText = getString(R.string.block_number_ok);
- if (FilteredNumberCompat.useNewFiltering()) {
- message = getString(R.string.block_number_confirmation_message_new_filtering);
- } else if (mVoicemailEnabledChecker.isVisualVoicemailEnabled()) {
- message = getString(R.string.block_number_confirmation_message_vvm);
- } else {
- message = getString(R.string.block_number_confirmation_message_no_vvm);
- }
- }
-
-
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
- .setTitle(title)
- .setMessage(message)
- .setPositiveButton(okText, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- if (isBlocked) {
- unblockNumber();
- } else {
- blockNumber();
- }
- }
- })
- .setNegativeButton(android.R.string.cancel, null);
- return builder.create();
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- if (!FilteredNumbersUtil.canBlockNumber(getActivity(), mNumber, mCountryIso)) {
- dismiss();
- Toast.makeText(getContext(),
- ContactDisplayUtils.getTtsSpannedPhoneNumber(
- getResources(), R.string.invalidNumber, mDisplayNumber),
- Toast.LENGTH_SHORT).show();
- }
- }
-
- @Override
- public void onPause() {
- // Dismiss on rotation.
- dismiss();
- mCallback = null;
-
- super.onPause();
- }
-
- public void setCallback(Callback callback) {
- mCallback = callback;
- }
-
- private CharSequence getBlockedMessage() {
- return ContactDisplayUtils.getTtsSpannedPhoneNumber(getResources(),
- R.string.snackbar_number_blocked, mDisplayNumber);
- }
-
- private CharSequence getUnblockedMessage() {
- return ContactDisplayUtils.getTtsSpannedPhoneNumber(getResources(),
- R.string.snackbar_number_unblocked, mDisplayNumber);
- }
-
- private int getActionTextColor() {
- return getContext().getResources().getColor(R.color.dialer_snackbar_action_text_color);
- }
-
- private void blockNumber() {
- final CharSequence message = getBlockedMessage();
- final CharSequence undoMessage = getUnblockedMessage();
- final Callback callback = mCallback;
- final int actionTextColor = getActionTextColor();
- final Context context = getContext();
-
- final OnUnblockNumberListener onUndoListener = new OnUnblockNumberListener() {
- @Override
- public void onUnblockComplete(int rows, ContentValues values) {
- Snackbar.make(mParentView, undoMessage, Snackbar.LENGTH_LONG).show();
- if (callback != null) {
- callback.onChangeFilteredNumberUndo();
- }
- }
- };
-
- final OnBlockNumberListener onBlockNumberListener = new OnBlockNumberListener() {
- @Override
- public void onBlockComplete(final Uri uri) {
- final View.OnClickListener undoListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- // Delete the newly created row on 'undo'.
- Logger.logInteraction(InteractionEvent.UNDO_BLOCK_NUMBER);
- mHandler.unblock(onUndoListener, uri);
- }
- };
-
- Snackbar.make(mParentView, message, Snackbar.LENGTH_LONG)
- .setAction(R.string.block_number_undo, undoListener)
- .setActionTextColor(actionTextColor)
- .show();
-
- if (callback != null) {
- callback.onFilterNumberSuccess();
- }
-
- if (context != null && FilteredNumbersUtil.hasRecentEmergencyCall(context)) {
- FilteredNumbersUtil.maybeNotifyCallBlockingDisabled(context);
- }
- }
- };
-
- mHandler.blockNumber(
- onBlockNumberListener,
- mNumber,
- mCountryIso);
- }
-
- private void unblockNumber() {
- final CharSequence message = getUnblockedMessage();
- final CharSequence undoMessage = getBlockedMessage();
- final Callback callback = mCallback;
- final int actionTextColor = getActionTextColor();
-
- final OnBlockNumberListener onUndoListener = new OnBlockNumberListener() {
- @Override
- public void onBlockComplete(final Uri uri) {
- Snackbar.make(mParentView, undoMessage, Snackbar.LENGTH_LONG).show();
- if (callback != null) {
- callback.onChangeFilteredNumberUndo();
- }
- }
- };
-
- mHandler.unblock(new OnUnblockNumberListener() {
- @Override
- public void onUnblockComplete(int rows, final ContentValues values) {
- final View.OnClickListener undoListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- // Re-insert the row on 'undo', with a new ID.
- Logger.logInteraction(InteractionEvent.UNDO_UNBLOCK_NUMBER);
- mHandler.blockNumber(onUndoListener, values);
- }
- };
-
- Snackbar.make(mParentView, message, Snackbar.LENGTH_LONG)
- .setAction(R.string.block_number_undo, undoListener)
- .setActionTextColor(actionTextColor)
- .show();
-
- if (callback != null) {
- callback.onUnfilterNumberSuccess();
- }
- }
- }, getArguments().getInt(ARG_BLOCK_ID));
- }
-}
diff --git a/src/com/android/dialer/filterednumber/BlockedNumbersAdapter.java b/src/com/android/dialer/filterednumber/BlockedNumbersAdapter.java
deleted file mode 100644
index 10a4f5abd..000000000
--- a/src/com/android/dialer/filterednumber/BlockedNumbersAdapter.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.filterednumber;
-
-import android.app.FragmentManager;
-import android.content.Context;
-import android.database.Cursor;
-import android.telephony.PhoneNumberUtils;
-import android.view.View;
-
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.GeoUtil;
-import com.android.dialer.R;
-import com.android.dialer.calllog.ContactInfoHelper;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
-import com.android.dialer.logging.InteractionEvent;
-import com.android.dialer.logging.Logger;
-
-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 String normalizedNumber = cursor.getString(cursor.getColumnIndex(
- FilteredNumberColumns.NORMALIZED_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.logInteraction(
- InteractionEvent.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/src/com/android/dialer/filterednumber/BlockedNumbersAutoMigrator.java b/src/com/android/dialer/filterednumber/BlockedNumbersAutoMigrator.java
deleted file mode 100644
index ed0faabbe..000000000
--- a/src/com/android/dialer/filterednumber/BlockedNumbersAutoMigrator.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2016 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.filterednumber;
-
-import com.google.common.base.Preconditions;
-
-import android.content.SharedPreferences;
-import android.util.Log;
-
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnHasBlockedNumbersListener;
-
-/**
- * Class responsible for checking if the user can be auto-migrated to {@link
- * android.provider.BlockedNumberContract} blocking. In order for this to happen, the user cannot
- * have any numbers that are blocked in the Dialer solution.
- */
-public class BlockedNumbersAutoMigrator {
-
- private static final String TAG = "BlockedNumbersAuto";
-
- private static final String HAS_CHECKED_AUTO_MIGRATE_KEY = "checkedAutoMigrate";
-
- private final SharedPreferences sharedPreferences;
- private final FilteredNumberAsyncQueryHandler queryHandler;
-
- /**
- * Constructs the BlockedNumbersAutoMigrator with the given {@link SharedPreferences} and {@link
- * FilteredNumberAsyncQueryHandler}.
- *
- * @param sharedPreferences The SharedPreferences used to persist information.
- * @param queryHandler The FilteredNumberAsyncQueryHandler used to determine if there are
- * blocked numbers.
- * @throws NullPointerException if sharedPreferences or queryHandler are null.
- */
- public BlockedNumbersAutoMigrator(SharedPreferences sharedPreferences,
- FilteredNumberAsyncQueryHandler queryHandler) {
- this.sharedPreferences = Preconditions.checkNotNull(sharedPreferences);
- this.queryHandler = Preconditions.checkNotNull(queryHandler);
- }
-
- /**
- * Attempts to perform the auto-migration. Auto-migration will only be attempted once and can be
- * performed only when the user has no blocked numbers. As a result of this method, the user
- * will be migrated to the framework blocking solution, as determined by {@link
- * FilteredNumberCompat#hasMigratedToNewBlocking()}.
- */
- public void autoMigrate() {
- if (!shouldAttemptAutoMigrate()) {
- return;
- }
-
- Log.i(TAG, "Attempting to auto-migrate.");
- queryHandler.hasBlockedNumbers(new OnHasBlockedNumbersListener() {
- @Override
- public void onHasBlockedNumbers(boolean hasBlockedNumbers) {
- if (hasBlockedNumbers) {
- Log.i(TAG, "Not auto-migrating: blocked numbers exist.");
- return;
- }
- Log.i(TAG, "Auto-migrating: no blocked numbers.");
- FilteredNumberCompat.setHasMigratedToNewBlocking(true);
- }
- });
- }
-
- private boolean shouldAttemptAutoMigrate() {
- if (sharedPreferences.contains(HAS_CHECKED_AUTO_MIGRATE_KEY)) {
- Log.d(TAG, "Not attempting auto-migrate: already checked once.");
- return false;
- }
- Log.i(TAG, "Updating state as already checked for auto-migrate.");
- sharedPreferences.edit().putBoolean(HAS_CHECKED_AUTO_MIGRATE_KEY, true).apply();
-
- if (!FilteredNumberCompat.canUseNewFiltering()) {
- Log.i(TAG, "Not attempting auto-migrate: not available.");
- return false;
- }
-
- if (FilteredNumberCompat.hasMigratedToNewBlocking()) {
- Log.i(TAG, "Not attempting auto-migrate: already migrated.");
- return false;
- }
- return true;
- }
-}
diff --git a/src/com/android/dialer/filterednumber/BlockedNumbersFragment.java b/src/com/android/dialer/filterednumber/BlockedNumbersFragment.java
deleted file mode 100644
index b64f18691..000000000
--- a/src/com/android/dialer/filterednumber/BlockedNumbersFragment.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * 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.filterednumber;
-
-import com.google.common.base.MoreObjects;
-
-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.contacts.common.lettertiles.LetterTileDrawable;
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.dialer.R;
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.database.FilteredNumberContract;
-import com.android.dialer.filterednumber.BlockedNumbersMigrator.Listener;
-import com.android.dialer.filterednumber.FilteredNumbersUtil.CheckForSendToVoicemailContactListener;
-import com.android.dialer.filterednumber.FilteredNumbersUtil.ImportSendToVoicemailContactsListener;
-import com.android.dialer.voicemail.VisualVoicemailEnabledChecker;
-
-public class BlockedNumbersFragment extends ListFragment
- implements LoaderManager.LoaderCallbacks<Cursor>, View.OnClickListener,
- VisualVoicemailEnabledChecker.Callback {
- private static final char ADD_BLOCKED_NUMBER_ICON_LETTER = '+';
-
- private BlockedNumbersMigrator blockedNumbersMigratorForTest;
- protected View migratePromoView;
- 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);
- MoreObjects.firstNonNull(blockedNumbersMigratorForTest,
- new BlockedNumbersMigrator(getContext().getContentResolver()))
- .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);
- }
- }
-
- @NeededForTesting
- void setBlockedNumbersMigratorForTest(BlockedNumbersMigrator blockedNumbersMigrator) {
- blockedNumbersMigratorForTest = blockedNumbersMigrator;
- }
-}
diff --git a/src/com/android/dialer/filterednumber/BlockedNumbersMigrator.java b/src/com/android/dialer/filterednumber/BlockedNumbersMigrator.java
deleted file mode 100644
index 373403046..000000000
--- a/src/com/android/dialer/filterednumber/BlockedNumbersMigrator.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * 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.filterednumber;
-
-import com.google.common.base.Preconditions;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.os.AsyncTask;
-
-import com.android.dialer.compat.BlockedNumbersSdkCompat;
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.database.FilteredNumberContract;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumber;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
-import com.android.incallui.Log;
-
-/**
- * Class which should be used to migrate numbers from {@link FilteredNumberContract} blocking to
- * {@link android.provider.BlockedNumberContract} blocking.
- */
-public class BlockedNumbersMigrator {
-
- private static final String TAG = "BlockedNumbersMigrator";
-
- /**
- * Listener for the operation to migrate from {@link FilteredNumberContract} blocking to
- * {@link android.provider.BlockedNumberContract} blocking.
- */
- public interface Listener {
-
- /**
- * Called when the migration operation is finished.
- */
- void onComplete();
- }
-
- private final ContentResolver mContentResolver;
-
- /**
- * Creates a new BlockedNumbersMigrate, using the given {@link ContentResolver} to perform
- * queries against the blocked numbers tables.
- *
- * @param contentResolver The ContentResolver
- * @throws NullPointerException if contentResolver is null
- */
- public BlockedNumbersMigrator(ContentResolver contentResolver) {
- mContentResolver = Preconditions.checkNotNull(contentResolver);
- }
-
- /**
- * Copies all of the numbers in the {@link FilteredNumberContract} block list to the
- * {@link android.provider.BlockedNumberContract} block list.
- *
- * @param listener {@link Listener} called once the migration is complete.
- * @return {@code true} if the migrate can be attempted, {@code false} otherwise.
- * @throws NullPointerException if listener is null
- */
- public boolean migrate(final Listener listener) {
- Log.i(TAG, "migrate - start");
- if (!FilteredNumberCompat.canUseNewFiltering()) {
- Log.i(TAG, "migrate - can't use new filtering");
- return false;
- }
- Preconditions.checkNotNull(listener);
- new AsyncTask<Void, Void, Boolean>() {
- @Override
- protected Boolean doInBackground(Void... params) {
- Log.i(TAG, "migrate - start background migration");
- return migrateToNewBlockingInBackground(mContentResolver);
- }
-
- @Override
- protected void onPostExecute(Boolean isSuccessful) {
- Log.i(TAG, "migrate - marking migration complete");
- FilteredNumberCompat.setHasMigratedToNewBlocking(isSuccessful);
- Log.i(TAG, "migrate - calling listener");
- listener.onComplete();
- }
- }.execute();
- return true;
- }
-
- private static boolean migrateToNewBlockingInBackground(ContentResolver resolver) {
- try (Cursor cursor = resolver.query(FilteredNumber.CONTENT_URI,
- new String[]{FilteredNumberColumns.NUMBER}, null, null, null)) {
- if (cursor == null) {
- Log.i(TAG, "migrate - cursor was null");
- return false;
- }
-
- Log.i(TAG, "migrate - attempting to migrate " + cursor.getCount() + "numbers");
-
- int numMigrated = 0;
- while (cursor.moveToNext()) {
- String originalNumber = cursor
- .getString(cursor.getColumnIndex(FilteredNumberColumns.NUMBER));
- if (isNumberInNewBlocking(resolver, originalNumber)) {
- Log.i(TAG, "migrate - number was already blocked in new blocking");
- continue;
- }
- ContentValues values = new ContentValues();
- values.put(BlockedNumbersSdkCompat.COLUMN_ORIGINAL_NUMBER, originalNumber);
- resolver.insert(BlockedNumbersSdkCompat.CONTENT_URI, values);
- ++numMigrated;
- }
- Log.i(TAG, "migrate - migration complete. " + numMigrated + " numbers migrated.");
- return true;
- }
- }
-
- private static boolean isNumberInNewBlocking(ContentResolver resolver, String originalNumber) {
- try (Cursor cursor = resolver.query(BlockedNumbersSdkCompat.CONTENT_URI,
- new String[]{BlockedNumbersSdkCompat._ID},
- BlockedNumbersSdkCompat.COLUMN_ORIGINAL_NUMBER + " = ?",
- new String[] {originalNumber}, null)) {
- return cursor != null && cursor.getCount() != 0;
- }
- }
-}
diff --git a/src/com/android/dialer/filterednumber/BlockedNumbersSettingsActivity.java b/src/com/android/dialer/filterednumber/BlockedNumbersSettingsActivity.java
deleted file mode 100644
index 5ce9d21f1..000000000
--- a/src/com/android/dialer/filterednumber/BlockedNumbersSettingsActivity.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * 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.filterednumber;
-
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
-import android.util.Log;
-import android.view.MenuItem;
-import android.widget.FrameLayout;
-import android.widget.FrameLayout.LayoutParams;
-import android.widget.Toast;
-
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.dialog.IndeterminateProgressDialog;
-import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
-import com.android.dialer.R;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.list.BlockedListSearchAdapter;
-import com.android.dialer.list.OnListFragmentScrolledListener;
-import com.android.dialer.list.BlockedListSearchFragment;
-import com.android.dialer.list.SearchFragment;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.logging.ScreenEvent;
-
-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.logScreenView(ScreenEvent.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.logScreenView(ScreenEvent.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 getActionBarHideOffset() {
- return 0;
- }
-
- @Override
- public int getActionBarHeight() {
- return 0;
- }
-}
diff --git a/src/com/android/dialer/filterednumber/FilteredNumbersUtil.java b/src/com/android/dialer/filterednumber/FilteredNumbersUtil.java
deleted file mode 100644
index 35d6f8d25..000000000
--- a/src/com/android/dialer/filterednumber/FilteredNumbersUtil.java
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- * 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.filterednumber;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.os.AsyncTask;
-import android.preference.PreferenceManager;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.Contacts;
-import android.provider.Settings;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.widget.Toast;
-
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.dialer.R;
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnHasBlockedNumbersListener;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumber;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
-import com.android.dialer.logging.InteractionEvent;
-import com.android.dialer.logging.Logger;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * Utility to help with tasks related to filtered numbers.
- */
-public class FilteredNumbersUtil {
-
- // Disable incoming call blocking if there was a call within the past 2 days.
- private static final long RECENT_EMERGENCY_CALL_THRESHOLD_MS = 1000 * 60 * 60 * 24 * 2;
-
- // Pref key for storing the time of end of the last emergency call in milliseconds after epoch.
- protected static final String LAST_EMERGENCY_CALL_MS_PREF_KEY = "last_emergency_call_ms";
-
- // Pref key for storing whether a notification has been dispatched to notify the user that call
- // blocking has been disabled because of a recent emergency call.
- protected static final String NOTIFIED_CALL_BLOCKING_DISABLED_BY_EMERGENCY_CALL_PREF_KEY =
- "notified_call_blocking_disabled_by_emergency_call";
-
- public static final String CALL_BLOCKING_NOTIFICATION_TAG = "call_blocking";
- public static final int CALL_BLOCKING_DISABLED_BY_EMERGENCY_CALL_NOTIFICATION_ID = 10;
-
- /**
- * Used for testing to specify that a custom threshold should be used instead of the default.
- * This custom threshold will only be used when setting this log tag to VERBOSE:
- *
- * adb shell setprop log.tag.DebugEmergencyCall VERBOSE
- *
- */
- @NeededForTesting
- private static final String DEBUG_EMERGENCY_CALL_TAG = "DebugEmergencyCall";
-
- /**
- * Used for testing to specify the custom threshold value, in milliseconds for whether an
- * emergency call is "recent". The default value will be used if this custom threshold is less
- * than zero. For example, to set this threshold to 60 seconds:
- *
- * adb shell settings put system dialer_emergency_call_threshold_ms 60000
- *
- */
- @NeededForTesting
- private static final String RECENT_EMERGENCY_CALL_THRESHOLD_SETTINGS_KEY =
- "dialer_emergency_call_threshold_ms";
-
- public interface CheckForSendToVoicemailContactListener {
- public void onComplete(boolean hasSendToVoicemailContact);
- }
-
- public interface ImportSendToVoicemailContactsListener {
- public void onImportComplete();
- }
-
- private static class ContactsQuery {
- static final String[] PROJECTION = {
- Contacts._ID
- };
-
- static final String SELECT_SEND_TO_VOICEMAIL_TRUE = Contacts.SEND_TO_VOICEMAIL + "=1";
-
- static final int ID_COLUMN_INDEX = 0;
- }
-
- public static class PhoneQuery {
- static final String[] PROJECTION = {
- Contacts._ID,
- Phone.NORMALIZED_NUMBER,
- Phone.NUMBER
- };
-
- static final int ID_COLUMN_INDEX = 0;
- static final int NORMALIZED_NUMBER_COLUMN_INDEX = 1;
- static final int NUMBER_COLUMN_INDEX = 2;
-
- static final String SELECT_SEND_TO_VOICEMAIL_TRUE = Contacts.SEND_TO_VOICEMAIL + "=1";
- }
-
- /**
- * Checks if there exists a contact with {@code Contacts.SEND_TO_VOICEMAIL} set to true.
- */
- public static void checkForSendToVoicemailContact(
- final Context context, final CheckForSendToVoicemailContactListener listener) {
- final AsyncTask task = new AsyncTask<Object, Void, Boolean>() {
- @Override
- public Boolean doInBackground(Object[] params) {
- if (context == null || !PermissionsUtil.hasContactsPermissions(context)) {
- return false;
- }
-
- final Cursor cursor = context.getContentResolver().query(
- Contacts.CONTENT_URI,
- ContactsQuery.PROJECTION,
- ContactsQuery.SELECT_SEND_TO_VOICEMAIL_TRUE,
- null,
- null);
-
- boolean hasSendToVoicemailContacts = false;
- if (cursor != null) {
- try {
- hasSendToVoicemailContacts = cursor.getCount() > 0;
- } finally {
- cursor.close();
- }
- }
-
- return hasSendToVoicemailContacts;
- }
-
- @Override
- public void onPostExecute(Boolean hasSendToVoicemailContact) {
- if (listener != null) {
- listener.onComplete(hasSendToVoicemailContact);
- }
- }
- };
- task.execute();
- }
-
- /**
- * Blocks all the phone numbers of any contacts marked as SEND_TO_VOICEMAIL, then clears the
- * SEND_TO_VOICEMAIL flag on those contacts.
- */
- public static void importSendToVoicemailContacts(
- final Context context, final ImportSendToVoicemailContactsListener listener) {
- Logger.logInteraction(InteractionEvent.IMPORT_SEND_TO_VOICEMAIL);
- final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler =
- new FilteredNumberAsyncQueryHandler(context.getContentResolver());
-
- final AsyncTask<Object, Void, Boolean> task = new AsyncTask<Object, Void, Boolean>() {
- @Override
- public Boolean doInBackground(Object[] params) {
- if (context == null) {
- return false;
- }
-
- // Get the phone number of contacts marked as SEND_TO_VOICEMAIL.
- final Cursor phoneCursor = context.getContentResolver().query(
- Phone.CONTENT_URI,
- PhoneQuery.PROJECTION,
- PhoneQuery.SELECT_SEND_TO_VOICEMAIL_TRUE,
- null,
- null);
-
- if (phoneCursor == null) {
- return false;
- }
-
- try {
- while (phoneCursor.moveToNext()) {
- final String normalizedNumber = phoneCursor.getString(
- PhoneQuery.NORMALIZED_NUMBER_COLUMN_INDEX);
- final String number = phoneCursor.getString(
- PhoneQuery.NUMBER_COLUMN_INDEX);
- if (normalizedNumber != null) {
- // Block the phone number of the contact.
- mFilteredNumberAsyncQueryHandler.blockNumber(
- null, normalizedNumber, number, null);
- }
- }
- } finally {
- phoneCursor.close();
- }
-
- // Clear SEND_TO_VOICEMAIL on all contacts. The setting has been imported to Dialer.
- ContentValues newValues = new ContentValues();
- newValues.put(Contacts.SEND_TO_VOICEMAIL, 0);
- context.getContentResolver().update(
- Contacts.CONTENT_URI,
- newValues,
- ContactsQuery.SELECT_SEND_TO_VOICEMAIL_TRUE,
- null);
-
- return true;
- }
-
- @Override
- public void onPostExecute(Boolean success) {
- if (success) {
- if (listener != null) {
- listener.onImportComplete();
- }
- } else if (context != null) {
- String toastStr = context.getString(R.string.send_to_voicemail_import_failed);
- Toast.makeText(context, toastStr, Toast.LENGTH_SHORT).show();
- }
- }
- };
- task.execute();
- }
-
- /**
- * WARNING: This method should NOT be executed on the UI thread.
- * Use {@code FilteredNumberAsyncQueryHandler} to asynchronously check if a number is blocked.
- */
- public static boolean shouldBlockVoicemail(
- Context context, String number, String countryIso, long voicemailDateMs) {
- final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
- if (TextUtils.isEmpty(normalizedNumber)) {
- return false;
- }
-
- if (hasRecentEmergencyCall(context)) {
- return false;
- }
-
- final Cursor cursor = context.getContentResolver().query(
- FilteredNumber.CONTENT_URI,
- new String[] {
- FilteredNumberColumns.CREATION_TIME
- },
- FilteredNumberColumns.NORMALIZED_NUMBER + "=?",
- new String[] { normalizedNumber },
- null);
- if (cursor == null) {
- return false;
- }
- try {
- /*
- * Block if number is found and it was added before this voicemail was received.
- * The VVM's date is reported with precision to the minute, even though its
- * magnitude is in milliseconds, so we perform the comparison in minutes.
- */
- return cursor.moveToFirst() &&
- TimeUnit.MINUTES.convert(voicemailDateMs, TimeUnit.MILLISECONDS) >=
- TimeUnit.MINUTES.convert(cursor.getLong(0), TimeUnit.MILLISECONDS);
- } finally {
- cursor.close();
- }
- }
-
- public static boolean hasRecentEmergencyCall(Context context) {
- if (context == null) {
- return false;
- }
-
- Long lastEmergencyCallTime = PreferenceManager.getDefaultSharedPreferences(context)
- .getLong(LAST_EMERGENCY_CALL_MS_PREF_KEY, 0);
- if (lastEmergencyCallTime == 0) {
- return false;
- }
-
- return (System.currentTimeMillis() - lastEmergencyCallTime)
- < getRecentEmergencyCallThresholdMs(context);
- }
-
- public static void recordLastEmergencyCallTime(Context context) {
- if (context == null) {
- return;
- }
-
- PreferenceManager.getDefaultSharedPreferences(context)
- .edit()
- .putLong(LAST_EMERGENCY_CALL_MS_PREF_KEY, System.currentTimeMillis())
- .putBoolean(NOTIFIED_CALL_BLOCKING_DISABLED_BY_EMERGENCY_CALL_PREF_KEY, false)
- .apply();
-
- maybeNotifyCallBlockingDisabled(context);
- }
-
- public static void maybeNotifyCallBlockingDisabled(final Context context) {
- // The Dialer is not responsible for this notification after migrating
- if (FilteredNumberCompat.useNewFiltering()) {
- return;
- }
- // Skip if the user has already received a notification for the most recent emergency call.
- if (PreferenceManager.getDefaultSharedPreferences(context)
- .getBoolean(NOTIFIED_CALL_BLOCKING_DISABLED_BY_EMERGENCY_CALL_PREF_KEY, false)) {
- return;
- }
-
- // If the user has blocked numbers, notify that call blocking is temporarily disabled.
- FilteredNumberAsyncQueryHandler queryHandler =
- new FilteredNumberAsyncQueryHandler(context.getContentResolver());
- queryHandler.hasBlockedNumbers(new OnHasBlockedNumbersListener() {
- @Override
- public void onHasBlockedNumbers(boolean hasBlockedNumbers) {
- if (context == null || !hasBlockedNumbers) {
- return;
- }
-
- NotificationManager notificationManager = (NotificationManager)
- context.getSystemService(Context.NOTIFICATION_SERVICE);
- Notification.Builder builder = new Notification.Builder(context)
- .setSmallIcon(R.drawable.ic_block_24dp)
- .setContentTitle(context.getString(
- R.string.call_blocking_disabled_notification_title))
- .setContentText(context.getString(
- R.string.call_blocking_disabled_notification_text))
- .setAutoCancel(true);
-
- final Intent contentIntent =
- new Intent(context, BlockedNumbersSettingsActivity.class);
- builder.setContentIntent(PendingIntent.getActivity(
- context, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT));
-
- notificationManager.notify(
- CALL_BLOCKING_NOTIFICATION_TAG,
- CALL_BLOCKING_DISABLED_BY_EMERGENCY_CALL_NOTIFICATION_ID,
- builder.build());
-
- // Record that the user has been notified for this emergency call.
- PreferenceManager.getDefaultSharedPreferences(context)
- .edit()
- .putBoolean(NOTIFIED_CALL_BLOCKING_DISABLED_BY_EMERGENCY_CALL_PREF_KEY, true)
- .apply();
- }
- });
- }
-
- public static boolean canBlockNumber(Context context, String number, String countryIso) {
- final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
- return !TextUtils.isEmpty(normalizedNumber)
- && !PhoneNumberUtils.isEmergencyNumber(normalizedNumber);
- }
-
- private static long getRecentEmergencyCallThresholdMs(Context context) {
- if (android.util.Log.isLoggable(
- DEBUG_EMERGENCY_CALL_TAG, android.util.Log.VERBOSE)) {
- long thresholdMs = Settings.System.getLong(
- context.getContentResolver(),
- RECENT_EMERGENCY_CALL_THRESHOLD_SETTINGS_KEY, 0);
- return thresholdMs > 0 ? thresholdMs : RECENT_EMERGENCY_CALL_THRESHOLD_MS;
- } else {
- return RECENT_EMERGENCY_CALL_THRESHOLD_MS;
- }
- }
-}
diff --git a/src/com/android/dialer/filterednumber/MigrateBlockedNumbersDialogFragment.java b/src/com/android/dialer/filterednumber/MigrateBlockedNumbersDialogFragment.java
deleted file mode 100644
index 209665292..000000000
--- a/src/com/android/dialer/filterednumber/MigrateBlockedNumbersDialogFragment.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2016 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.filterednumber;
-
-import com.google.common.base.Preconditions;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnShowListener;
-import android.os.Bundle;
-import android.view.View;
-import com.android.dialer.R;
-import com.android.dialer.filterednumber.BlockedNumbersMigrator.Listener;
-
-/**
- * Dialog fragment shown to users when they need to migrate to use
- * {@link android.provider.BlockedNumberContract} for blocking.
- */
-public class MigrateBlockedNumbersDialogFragment extends DialogFragment {
-
- private BlockedNumbersMigrator mBlockedNumbersMigrator;
- private BlockedNumbersMigrator.Listener mMigrationListener;
-
- /**
- * Creates a new MigrateBlockedNumbersDialogFragment.
- *
- * @param blockedNumbersMigrator The {@link BlockedNumbersMigrator} which will be used to
- * migrate the numbers.
- * @param migrationListener The {@link BlockedNumbersMigrator.Listener} to call when the
- * migration is complete.
- * @return The new MigrateBlockedNumbersDialogFragment.
- * @throws NullPointerException if blockedNumbersMigrator or migrationListener are {@code null}.
- */
- public static DialogFragment newInstance(BlockedNumbersMigrator blockedNumbersMigrator,
- BlockedNumbersMigrator.Listener migrationListener) {
- MigrateBlockedNumbersDialogFragment fragment = new MigrateBlockedNumbersDialogFragment();
- fragment.mBlockedNumbersMigrator = Preconditions.checkNotNull(blockedNumbersMigrator);
- fragment.mMigrationListener = Preconditions.checkNotNull(migrationListener);
- return fragment;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- super.onCreateDialog(savedInstanceState);
- AlertDialog dialog = new AlertDialog.Builder(getActivity())
- .setTitle(R.string.migrate_blocked_numbers_dialog_title)
- .setMessage(R.string.migrate_blocked_numbers_dialog_message)
- .setPositiveButton(R.string.migrate_blocked_numbers_dialog_allow_button, null)
- .setNegativeButton(R.string.migrate_blocked_numbers_dialog_cancel_button, null)
- .create();
- // The Dialog's buttons aren't available until show is called, so an OnShowListener
- // is used to set the positive button callback.
- dialog.setOnShowListener(new OnShowListener() {
- @Override
- public void onShow(DialogInterface dialog) {
- final AlertDialog alertDialog = (AlertDialog) dialog;
- alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
- .setOnClickListener(newPositiveButtonOnClickListener(alertDialog));
- }
- });
- return dialog;
- }
-
- /*
- * Creates a new View.OnClickListener to be used as the positive button in this dialog. The
- * OnClickListener will grey out the dialog's positive and negative buttons while the migration
- * is underway, and close the dialog once the migrate is complete.
- */
- private View.OnClickListener newPositiveButtonOnClickListener(final AlertDialog alertDialog) {
- return new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
- alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setEnabled(false);
- mBlockedNumbersMigrator.migrate(new Listener() {
- @Override
- public void onComplete() {
- alertDialog.dismiss();
- mMigrationListener.onComplete();
- }
- });
- }
- };
- }
-
- @Override
- public void onPause() {
- // The dialog is dismissed and state is cleaned up onPause, i.e. rotation.
- dismiss();
- mBlockedNumbersMigrator = null;
- mMigrationListener = null;
- super.onPause();
- }
-}
diff --git a/src/com/android/dialer/filterednumber/NumbersAdapter.java b/src/com/android/dialer/filterednumber/NumbersAdapter.java
deleted file mode 100644
index 17d5db343..000000000
--- a/src/com/android/dialer/filterednumber/NumbersAdapter.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * 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.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.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.util.UriUtils;
-import com.android.dialer.R;
-import com.android.dialer.calllog.ContactInfo;
-import com.android.dialer.calllog.ContactInfoHelper;
-import com.android.dialer.util.PhoneNumberUtil;
-
-public class NumbersAdapter extends SimpleCursorAdapter {
-
- private Context mContext;
- private FragmentManager mFragmentManager;
- private ContactInfoHelper mContactInfoHelper;
- private BidiFormatter mBidiFormatter = BidiFormatter.getInstance();
- private 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)
- ? ContactPhotoManager.TYPE_BUSINESS : ContactPhotoManager.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 PhoneNumberUtil.getGeoDescription(mContext, info.number);
- }
- }
-
- protected Context getContext() {
- return mContext;
- }
-
- protected FragmentManager getFragmentManager() {
- return mFragmentManager;
- }
-}
diff --git a/src/com/android/dialer/filterednumber/ViewNumbersToImportAdapter.java b/src/com/android/dialer/filterednumber/ViewNumbersToImportAdapter.java
deleted file mode 100644
index 58fe1d46c..000000000
--- a/src/com/android/dialer/filterednumber/ViewNumbersToImportAdapter.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.filterednumber;
-
-import android.app.FragmentManager;
-import android.database.Cursor;
-import android.content.Context;
-import android.view.View;
-
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.GeoUtil;
-import com.android.dialer.R;
-import com.android.dialer.calllog.ContactInfoHelper;
-
-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/src/com/android/dialer/filterednumber/ViewNumbersToImportFragment.java b/src/com/android/dialer/filterednumber/ViewNumbersToImportFragment.java
deleted file mode 100644
index 8b24c06da..000000000
--- a/src/com/android/dialer/filterednumber/ViewNumbersToImportFragment.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * 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.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.AsyncTask;
-import android.os.Bundle;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.Contacts;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.dialer.R;
-import com.android.dialer.database.FilteredNumberContract;
-import com.android.dialer.filterednumber.FilteredNumbersUtil.ImportSendToVoicemailContactsListener;
-
-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/src/com/android/dialer/interactions/PhoneNumberInteraction.java b/src/com/android/dialer/interactions/PhoneNumberInteraction.java
deleted file mode 100644
index 0c3ae510a..000000000
--- a/src/com/android/dialer/interactions/PhoneNumberInteraction.java
+++ /dev/null
@@ -1,516 +0,0 @@
-/*
- * Copyright (C) 2010 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.interactions;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.FragmentManager;
-import android.content.Context;
-import android.content.CursorLoader;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnDismissListener;
-import android.content.Intent;
-import android.content.Loader;
-import android.content.Loader.OnLoadCompleteListener;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.SipAddress;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.RawContacts;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.CheckBox;
-import android.widget.ListAdapter;
-import android.widget.TextView;
-
-import com.android.contacts.common.Collapser;
-import com.android.contacts.common.Collapser.Collapsible;
-import com.android.contacts.common.MoreContactUtils;
-import com.android.contacts.common.util.ContactDisplayUtils;
-import com.android.dialer.R;
-import com.android.dialer.TransactionSafeActivity;
-import com.android.dialer.contact.ContactUpdateService;
-import com.android.dialer.util.IntentUtil;
-import com.android.dialer.util.IntentUtil.CallIntentBuilder;
-import com.android.incallui.Call.LogState;
-import com.android.dialer.util.DialerUtils;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Initiates phone calls or a text message. If there are multiple candidates, this class shows a
- * dialog to pick one. Creating one of these interactions should be done through the static
- * factory methods.
- *
- * Note that this class initiates not only usual *phone* calls but also *SIP* calls.
- *
- * TODO: clean up code and documents since it is quite confusing to use "phone numbers" or
- * "phone calls" here while they can be SIP addresses or SIP calls (See also issue 5039627).
- */
-public class PhoneNumberInteraction implements OnLoadCompleteListener<Cursor> {
- private static final String TAG = PhoneNumberInteraction.class.getSimpleName();
-
- /**
- * A model object for capturing a phone number for a given contact.
- */
- @VisibleForTesting
- /* package */ static class PhoneItem implements Parcelable, Collapsible<PhoneItem> {
- long id;
- String phoneNumber;
- String accountType;
- String dataSet;
- long type;
- String label;
- /** {@link Phone#CONTENT_ITEM_TYPE} or {@link SipAddress#CONTENT_ITEM_TYPE}. */
- String mimeType;
-
- public PhoneItem() {
- }
-
- private PhoneItem(Parcel in) {
- this.id = in.readLong();
- this.phoneNumber = in.readString();
- this.accountType = in.readString();
- this.dataSet = in.readString();
- this.type = in.readLong();
- this.label = in.readString();
- this.mimeType = in.readString();
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeLong(id);
- dest.writeString(phoneNumber);
- dest.writeString(accountType);
- dest.writeString(dataSet);
- dest.writeLong(type);
- dest.writeString(label);
- dest.writeString(mimeType);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void collapseWith(PhoneItem phoneItem) {
- // Just keep the number and id we already have.
- }
-
- @Override
- public boolean shouldCollapseWith(PhoneItem phoneItem, Context context) {
- return MoreContactUtils.shouldCollapse(Phone.CONTENT_ITEM_TYPE, phoneNumber,
- Phone.CONTENT_ITEM_TYPE, phoneItem.phoneNumber);
- }
-
- @Override
- public String toString() {
- return phoneNumber;
- }
-
- public static final Parcelable.Creator<PhoneItem> CREATOR
- = new Parcelable.Creator<PhoneItem>() {
- @Override
- public PhoneItem createFromParcel(Parcel in) {
- return new PhoneItem(in);
- }
-
- @Override
- public PhoneItem[] newArray(int size) {
- return new PhoneItem[size];
- }
- };
- }
-
- /**
- * A list adapter that populates the list of contact's phone numbers.
- */
- private static class PhoneItemAdapter extends ArrayAdapter<PhoneItem> {
- private final int mInteractionType;
-
- public PhoneItemAdapter(Context context, List<PhoneItem> list,
- int interactionType) {
- super(context, R.layout.phone_disambig_item, android.R.id.text2, list);
- mInteractionType = interactionType;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final View view = super.getView(position, convertView, parent);
-
- final PhoneItem item = getItem(position);
- final TextView typeView = (TextView) view.findViewById(android.R.id.text1);
- CharSequence value = ContactDisplayUtils.getLabelForCallOrSms((int) item.type,
- item.label, mInteractionType, getContext());
-
- typeView.setText(value);
- return view;
- }
- }
-
- /**
- * {@link DialogFragment} used for displaying a dialog with a list of phone numbers of which
- * one will be chosen to make a call or initiate an sms message.
- *
- * It is recommended to use
- * {@link PhoneNumberInteraction#startInteractionForPhoneCall(TransactionSafeActivity, Uri)} or
- * {@link PhoneNumberInteraction#startInteractionForTextMessage(TransactionSafeActivity, Uri)}
- * instead of directly using this class, as those methods handle one or multiple data cases
- * appropriately.
- */
- /* Made public to let the system reach this class */
- public static class PhoneDisambiguationDialogFragment extends DialogFragment
- implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
-
- private static final String ARG_PHONE_LIST = "phoneList";
- private static final String ARG_INTERACTION_TYPE = "interactionType";
- private static final String ARG_CALL_INITIATION_TYPE = "callInitiation";
- private static final String ARG_IS_VIDEO_CALL = "is_video_call";
-
- private int mInteractionType;
- private ListAdapter mPhonesAdapter;
- private List<PhoneItem> mPhoneList;
- private int mCallInitiationType;
- private boolean mIsVideoCall;
-
- public static void show(FragmentManager fragmentManager, ArrayList<PhoneItem> phoneList,
- int interactionType, boolean isVideoCall, int callInitiationType) {
- PhoneDisambiguationDialogFragment fragment = new PhoneDisambiguationDialogFragment();
- Bundle bundle = new Bundle();
- bundle.putParcelableArrayList(ARG_PHONE_LIST, phoneList);
- bundle.putInt(ARG_INTERACTION_TYPE, interactionType);
- bundle.putInt(ARG_CALL_INITIATION_TYPE, callInitiationType);
- bundle.putBoolean(ARG_IS_VIDEO_CALL, isVideoCall);
- fragment.setArguments(bundle);
- fragment.show(fragmentManager, TAG);
- }
-
- public PhoneDisambiguationDialogFragment() {
- super();
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- final Activity activity = getActivity();
- mPhoneList = getArguments().getParcelableArrayList(ARG_PHONE_LIST);
- mInteractionType = getArguments().getInt(ARG_INTERACTION_TYPE);
- mCallInitiationType = getArguments().getInt(ARG_CALL_INITIATION_TYPE);
- mIsVideoCall = getArguments().getBoolean(ARG_IS_VIDEO_CALL);
-
- mPhonesAdapter = new PhoneItemAdapter(activity, mPhoneList, mInteractionType);
- final LayoutInflater inflater = activity.getLayoutInflater();
- final View setPrimaryView = inflater.inflate(R.layout.set_primary_checkbox, null);
- return new AlertDialog.Builder(activity)
- .setAdapter(mPhonesAdapter, this)
- .setTitle(mInteractionType == ContactDisplayUtils.INTERACTION_SMS
- ? R.string.sms_disambig_title : R.string.call_disambig_title)
- .setView(setPrimaryView)
- .create();
- }
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- final Activity activity = getActivity();
- if (activity == null) return;
- final AlertDialog alertDialog = (AlertDialog)dialog;
- if (mPhoneList.size() > which && which >= 0) {
- final PhoneItem phoneItem = mPhoneList.get(which);
- final CheckBox checkBox = (CheckBox)alertDialog.findViewById(R.id.setPrimary);
- if (checkBox.isChecked()) {
- // Request to mark the data as primary in the background.
- final Intent serviceIntent = ContactUpdateService.createSetSuperPrimaryIntent(
- activity, phoneItem.id);
- activity.startService(serviceIntent);
- }
-
- PhoneNumberInteraction.performAction(activity, phoneItem.phoneNumber,
- mInteractionType, mIsVideoCall, mCallInitiationType);
- } else {
- dialog.dismiss();
- }
- }
- }
-
- private static final String[] PHONE_NUMBER_PROJECTION = new String[] {
- Phone._ID, // 0
- Phone.NUMBER, // 1
- Phone.IS_SUPER_PRIMARY, // 2
- RawContacts.ACCOUNT_TYPE, // 3
- RawContacts.DATA_SET, // 4
- Phone.TYPE, // 5
- Phone.LABEL, // 6
- Phone.MIMETYPE, // 7
- Phone.CONTACT_ID // 8
- };
-
- private static final int _ID = 0;
- private static final int NUMBER = 1;
- private static final int IS_SUPER_PRIMARY = 2;
- private static final int ACCOUNT_TYPE = 3;
- private static final int DATA_SET = 4;
- private static final int TYPE = 5;
- private static final int LABEL = 6;
- private static final int MIMETYPE = 7;
- private static final int CONTACT_ID = 8;
-
- private static final String PHONE_NUMBER_SELECTION =
- Data.MIMETYPE + " IN ('"
- + Phone.CONTENT_ITEM_TYPE + "', "
- + "'" + SipAddress.CONTENT_ITEM_TYPE + "') AND "
- + Data.DATA1 + " NOT NULL";
-
- private final Context mContext;
- private final OnDismissListener mDismissListener;
- private final int mInteractionType;
-
- private final int mCallInitiationType;
- private boolean mUseDefault;
-
- private static final int UNKNOWN_CONTACT_ID = -1;
- private long mContactId = UNKNOWN_CONTACT_ID;
-
- private CursorLoader mLoader;
- private boolean mIsVideoCall;
-
- /**
- * Constructs a new {@link PhoneNumberInteraction}. The constructor takes in a {@link Context}
- * instead of a {@link TransactionSafeActivity} for testing purposes to verify the functionality
- * of this class. However, all factory methods for creating {@link PhoneNumberInteraction}s
- * require a {@link TransactionSafeActivity} (i.e. see {@link #startInteractionForPhoneCall}).
- */
- @VisibleForTesting
- /* package */ PhoneNumberInteraction(Context context, int interactionType,
- DialogInterface.OnDismissListener dismissListener) {
- this(context, interactionType, dismissListener, false /*isVideoCall*/,
- LogState.INITIATION_UNKNOWN);
- }
-
- private PhoneNumberInteraction(Context context, int interactionType,
- DialogInterface.OnDismissListener dismissListener, boolean isVideoCall,
- int callInitiationType) {
- mContext = context;
- mInteractionType = interactionType;
- mDismissListener = dismissListener;
- mCallInitiationType = callInitiationType;
- mIsVideoCall = isVideoCall;
- }
-
- private void performAction(String phoneNumber) {
- PhoneNumberInteraction.performAction(mContext, phoneNumber, mInteractionType, mIsVideoCall,
- mCallInitiationType);
- }
-
- private static void performAction(
- Context context, String phoneNumber, int interactionType,
- boolean isVideoCall, int callInitiationType) {
- Intent intent;
- switch (interactionType) {
- case ContactDisplayUtils.INTERACTION_SMS:
- intent = new Intent(
- Intent.ACTION_SENDTO, Uri.fromParts("sms", phoneNumber, null));
- break;
- default:
- intent = new CallIntentBuilder(phoneNumber)
- .setCallInitiationType(callInitiationType)
- .setIsVideoCall(isVideoCall)
- .build();
- break;
- }
- DialerUtils.startActivityWithErrorToast(context, intent);
- }
-
- /**
- * Initiates the interaction. This may result in a phone call or sms message started
- * or a disambiguation dialog to determine which phone number should be used. If there
- * is a primary phone number, it will be automatically used and a disambiguation dialog
- * will no be shown.
- */
- @VisibleForTesting
- /* package */ void startInteraction(Uri uri) {
- startInteraction(uri, true);
- }
-
- /**
- * Initiates the interaction to result in either a phone call or sms message for a contact.
- * @param uri Contact Uri
- * @param useDefault Whether or not to use the primary(default) phone number. If true, the
- * primary phone number will always be used by default if one is available. If false, a
- * disambiguation dialog will be shown regardless of whether or not a primary phone number
- * is available.
- */
- @VisibleForTesting
- /* package */ void startInteraction(Uri uri, boolean useDefault) {
- if (mLoader != null) {
- mLoader.reset();
- }
- mUseDefault = useDefault;
- final Uri queryUri;
- final String inputUriAsString = uri.toString();
- if (inputUriAsString.startsWith(Contacts.CONTENT_URI.toString())) {
- if (!inputUriAsString.endsWith(Contacts.Data.CONTENT_DIRECTORY)) {
- queryUri = Uri.withAppendedPath(uri, Contacts.Data.CONTENT_DIRECTORY);
- } else {
- queryUri = uri;
- }
- } else if (inputUriAsString.startsWith(Data.CONTENT_URI.toString())) {
- queryUri = uri;
- } else {
- throw new UnsupportedOperationException(
- "Input Uri must be contact Uri or data Uri (input: \"" + uri + "\")");
- }
-
- mLoader = new CursorLoader(mContext,
- queryUri,
- PHONE_NUMBER_PROJECTION,
- PHONE_NUMBER_SELECTION,
- null,
- null);
- mLoader.registerListener(0, this);
- mLoader.startLoading();
- }
-
- @Override
- public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
- if (cursor == null) {
- onDismiss();
- return;
- }
- try {
- ArrayList<PhoneItem> phoneList = new ArrayList<PhoneItem>();
- String primaryPhone = null;
- if (!isSafeToCommitTransactions()) {
- onDismiss();
- return;
- }
- while (cursor.moveToNext()) {
- if (mContactId == UNKNOWN_CONTACT_ID) {
- mContactId = cursor.getLong(CONTACT_ID);
- }
-
- if (mUseDefault && cursor.getInt(IS_SUPER_PRIMARY) != 0) {
- // Found super primary, call it.
- primaryPhone = cursor.getString(NUMBER);
- }
-
- PhoneItem item = new PhoneItem();
- item.id = cursor.getLong(_ID);
- item.phoneNumber = cursor.getString(NUMBER);
- item.accountType = cursor.getString(ACCOUNT_TYPE);
- item.dataSet = cursor.getString(DATA_SET);
- item.type = cursor.getInt(TYPE);
- item.label = cursor.getString(LABEL);
- item.mimeType = cursor.getString(MIMETYPE);
-
- phoneList.add(item);
- }
-
- if (mUseDefault && primaryPhone != null) {
- performAction(primaryPhone);
- onDismiss();
- return;
- }
-
- Collapser.collapseList(phoneList, mContext);
- if (phoneList.size() == 0) {
- onDismiss();
- } else if (phoneList.size() == 1) {
- PhoneItem item = phoneList.get(0);
- onDismiss();
- performAction(item.phoneNumber);
- } else {
- // There are multiple candidates. Let the user choose one.
- showDisambiguationDialog(phoneList);
- }
- } finally {
- cursor.close();
- }
- }
-
- private boolean isSafeToCommitTransactions() {
- return mContext instanceof TransactionSafeActivity ?
- ((TransactionSafeActivity) mContext).isSafeToCommitTransactions() : true;
- }
-
- private void onDismiss() {
- if (mDismissListener != null) {
- mDismissListener.onDismiss(null);
- }
- }
-
- /**
- * @param activity that is calling this interaction. This must be of type
- * {@link TransactionSafeActivity} because we need to check on the activity state after the
- * phone numbers have been queried for.
- * @param isVideoCall {@code true} if the call is a video call, {@code false} otherwise.
- * @param callInitiationType Indicates the UI affordance that was used to initiate the call.
- */
- public static void startInteractionForPhoneCall(TransactionSafeActivity activity, Uri uri,
- boolean isVideoCall, int callInitiationType) {
- (new PhoneNumberInteraction(activity, ContactDisplayUtils.INTERACTION_CALL, null,
- isVideoCall, callInitiationType)).startInteraction(uri, true);
- }
-
- /**
- * Start text messaging (a.k.a SMS) action using given contact Uri. If there are multiple
- * candidates for the phone call, dialog is automatically shown and the user is asked to choose
- * one.
- *
- * @param activity that is calling this interaction. This must be of type
- * {@link TransactionSafeActivity} because we need to check on the activity state after the
- * phone numbers have been queried for.
- * @param uri contact Uri (built from {@link Contacts#CONTENT_URI}) or data Uri
- * (built from {@link Data#CONTENT_URI}). Contact Uri may show the disambiguation dialog while
- * data Uri won't.
- */
- public static void startInteractionForTextMessage(TransactionSafeActivity activity, Uri uri) {
- (new PhoneNumberInteraction(activity, ContactDisplayUtils.INTERACTION_SMS, null))
- .startInteraction(uri, true);
- }
-
- @VisibleForTesting
- /* package */ CursorLoader getLoader() {
- return mLoader;
- }
-
- @VisibleForTesting
- /* package */ void showDisambiguationDialog(ArrayList<PhoneItem> phoneList) {
- final Activity activity = (Activity) mContext;
- if (activity.isDestroyed()) {
- // Check whether the activity is still running
- return;
- }
- try {
- PhoneDisambiguationDialogFragment.show(activity.getFragmentManager(),
- phoneList, mInteractionType, mIsVideoCall, mCallInitiationType);
- } catch (IllegalStateException e) {
- // ignore to be safe. Shouldn't happen because we checked the
- // activity wasn't destroyed, but to be safe.
- }
- }
-}
diff --git a/src/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java b/src/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java
deleted file mode 100644
index 172a4efef..000000000
--- a/src/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2013 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.interactions;
-
-import static android.Manifest.permission.READ_CONTACTS;
-import static android.Manifest.permission.WRITE_CONTACTS;
-
-import android.content.BroadcastReceiver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.PhoneLookup;
-import android.provider.ContactsContract.PinnedPositions;
-import android.text.TextUtils;
-
-import com.android.contacts.common.util.PermissionsUtil;
-
-/**
- * This broadcast receiver is used to listen to outgoing calls and undemote formerly demoted
- * contacts if a phone call is made to a phone number belonging to that contact.
- *
- * NOTE This doesn't work for corp contacts.
- */
-public class UndemoteOutgoingCallReceiver extends BroadcastReceiver {
-
- private static final long NO_CONTACT_FOUND = -1;
-
- @Override
- public void onReceive(final Context context, Intent intent) {
- if (!PermissionsUtil.hasPermission(context, READ_CONTACTS)
- || !PermissionsUtil.hasPermission(context, WRITE_CONTACTS)) {
- return;
- }
- if (intent != null && Intent.ACTION_NEW_OUTGOING_CALL.equals(intent.getAction())) {
- final String number = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
- if (TextUtils.isEmpty(number)) {
- return;
- }
- new Thread() {
- @Override
- public void run() {
- final long id = getContactIdFromPhoneNumber(context, number);
- if (id != NO_CONTACT_FOUND) {
- undemoteContactWithId(context, id);
- }
- }
- }.start();
- }
- }
-
- private void undemoteContactWithId(Context context, long id) {
- // If the contact is not demoted, this will not do anything. Otherwise, it will
- // restore it to an unpinned position. If it was a frequently called contact, it will
- // show up once again show up on the favorites screen.
- if (PermissionsUtil.hasPermission(context, WRITE_CONTACTS)) {
- try {
- PinnedPositions.undemote(context.getContentResolver(), id);
- } catch (SecurityException e) {
- // Just in case
- }
- }
- }
-
- private long getContactIdFromPhoneNumber(Context context, String number) {
- if (!PermissionsUtil.hasPermission(context, READ_CONTACTS)) {
- return NO_CONTACT_FOUND;
- }
- final Uri contactUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
- Uri.encode(number));
- final Cursor cursor;
- try {
- cursor = context.getContentResolver().query(contactUri, new String[] {
- PhoneLookup._ID}, null, null, null);
- } catch (SecurityException e) {
- // Just in case
- return NO_CONTACT_FOUND;
- }
- if (cursor == null) {
- return NO_CONTACT_FOUND;
- }
- try {
- if (cursor.moveToFirst()) {
- final long id = cursor.getLong(0);
- return id;
- } else {
- return NO_CONTACT_FOUND;
- }
- } finally {
- cursor.close();
- }
- }
-}
diff --git a/src/com/android/dialer/list/AllContactsFragment.java b/src/com/android/dialer/list/AllContactsFragment.java
deleted file mode 100644
index 7e76279d9..000000000
--- a/src/com/android/dialer/list/AllContactsFragment.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2013 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.list;
-
-import static android.Manifest.permission.READ_CONTACTS;
-
-import android.app.Activity;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.Loader;
-import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.QuickContact;
-import android.support.v13.app.FragmentCompat;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.list.ContactEntryListAdapter;
-import com.android.contacts.common.list.ContactEntryListFragment;
-import com.android.contacts.common.list.ContactListFilter;
-import com.android.contacts.common.list.DefaultContactListAdapter;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.contacts.common.util.ViewUtil;
-import com.android.dialer.R;
-import com.android.dialer.util.DialerUtils;
-import com.android.dialer.util.IntentUtil;
-import com.android.dialer.widget.EmptyContentView;
-import com.android.dialer.widget.EmptyContentView.OnEmptyViewActionButtonClickedListener;
-
-/**
- * Fragments to show all contacts with phone numbers.
- */
-public class AllContactsFragment extends ContactEntryListFragment<ContactEntryListAdapter>
- implements OnEmptyViewActionButtonClickedListener,
- FragmentCompat.OnRequestPermissionsResultCallback {
-
- private static final int READ_CONTACTS_PERMISSION_REQUEST_CODE = 1;
-
- private EmptyContentView mEmptyListView;
-
- /**
- * Listen to broadcast events about permissions in order to be notified if the READ_CONTACTS
- * permission is granted via the UI in another fragment.
- */
- private BroadcastReceiver mReadContactsPermissionGrantedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- reloadData();
- }
- };
-
- public AllContactsFragment() {
- setQuickContactEnabled(false);
- setAdjustSelectionBoundsEnabled(true);
- setPhotoLoaderEnabled(true);
- setSectionHeaderDisplayEnabled(true);
- setDarkTheme(false);
- setVisibleScrollbarEnabled(true);
- }
-
- @Override
- public void onViewCreated(View view, android.os.Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- mEmptyListView = (EmptyContentView) view.findViewById(R.id.empty_list_view);
- mEmptyListView.setImage(R.drawable.empty_contacts);
- mEmptyListView.setDescription(R.string.all_contacts_empty);
- mEmptyListView.setActionClickedListener(this);
- getListView().setEmptyView(mEmptyListView);
- mEmptyListView.setVisibility(View.GONE);
-
- ViewUtil.addBottomPaddingToListViewForFab(getListView(), getResources());
- }
-
- @Override
- public void onStart() {
- super.onStart();
- PermissionsUtil.registerPermissionReceiver(getActivity(),
- mReadContactsPermissionGrantedReceiver, READ_CONTACTS);
- }
-
- @Override
- public void onStop() {
- PermissionsUtil.unregisterPermissionReceiver(getActivity(),
- mReadContactsPermissionGrantedReceiver);
- super.onStop();
- }
-
- @Override
- protected void startLoading() {
- if (PermissionsUtil.hasPermission(getActivity(), READ_CONTACTS)) {
- super.startLoading();
- mEmptyListView.setDescription(R.string.all_contacts_empty);
- mEmptyListView.setActionLabel(R.string.all_contacts_empty_add_contact_action);
- } else {
- mEmptyListView.setDescription(R.string.permission_no_contacts);
- mEmptyListView.setActionLabel(R.string.permission_single_turn_on);
- mEmptyListView.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
- super.onLoadFinished(loader, data);
-
- if (data == null || data.getCount() == 0) {
- mEmptyListView.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- protected ContactEntryListAdapter createListAdapter() {
- final DefaultContactListAdapter adapter = new DefaultContactListAdapter(getActivity()) {
- @Override
- protected void bindView(View itemView, int partition, Cursor cursor, int position) {
- super.bindView(itemView, partition, cursor, position);
- itemView.setTag(this.getContactUri(partition, cursor));
- }
- };
- adapter.setDisplayPhotos(true);
- adapter.setFilter(ContactListFilter.createFilterWithType(
- ContactListFilter.FILTER_TYPE_DEFAULT));
- adapter.setSectionHeaderDisplayEnabled(isSectionHeaderDisplayEnabled());
- return adapter;
- }
-
- @Override
- protected View inflateView(LayoutInflater inflater, ViewGroup container) {
- return inflater.inflate(R.layout.all_contacts_fragment, null);
- }
-
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- final Uri uri = (Uri) view.getTag();
- if (uri != null) {
- if (CompatUtils.hasPrioritizedMimeType()) {
- QuickContact.showQuickContact(getContext(), view, uri, null,
- Phone.CONTENT_ITEM_TYPE);
- } else {
- QuickContact.showQuickContact(getActivity(), view, uri, QuickContact.MODE_LARGE,
- null);
- }
- }
- }
-
- @Override
- protected void onItemClick(int position, long id) {
- // Do nothing. Implemented to satisfy ContactEntryListFragment.
- }
-
- @Override
- public void onEmptyViewActionButtonClicked() {
- final Activity activity = getActivity();
- if (activity == null) {
- return;
- }
-
- if (!PermissionsUtil.hasPermission(activity, READ_CONTACTS)) {
- FragmentCompat.requestPermissions(this, new String[] {READ_CONTACTS},
- READ_CONTACTS_PERMISSION_REQUEST_CODE);
- } else {
- // Add new contact
- DialerUtils.startActivityWithErrorToast(activity, IntentUtil.getNewContactIntent(),
- R.string.add_contact_not_available);
- }
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, String[] permissions,
- int[] grantResults) {
- if (requestCode == READ_CONTACTS_PERMISSION_REQUEST_CODE) {
- if (grantResults.length >= 1 && PackageManager.PERMISSION_GRANTED == grantResults[0]) {
- // Force a refresh of the data since we were missing the permission before this.
- reloadData();
- }
- }
- }
-}
diff --git a/src/com/android/dialer/list/BlockedListSearchAdapter.java b/src/com/android/dialer/list/BlockedListSearchAdapter.java
deleted file mode 100644
index 1618826bd..000000000
--- a/src/com/android/dialer/list/BlockedListSearchAdapter.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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.list;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.graphics.Color;
-import android.view.View;
-
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.list.ContactListItemView;
-import com.android.dialer.R;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-
-/**
- * List adapter to display search results for adding a blocked number.
- */
-public class BlockedListSearchAdapter extends RegularSearchListAdapter {
-
- private Resources mResources;
- private FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
-
- public BlockedListSearchAdapter(Context context) {
- super(context);
- mResources = context.getResources();
- disableAllShortcuts();
- setShortcutEnabled(SHORTCUT_BLOCK_NUMBER, true);
-
- mFilteredNumberAsyncQueryHandler =
- new FilteredNumberAsyncQueryHandler(context.getContentResolver());
- }
-
- @Override
- protected boolean isChanged(boolean showNumberShortcuts) {
- return setShortcutEnabled(SHORTCUT_BLOCK_NUMBER, showNumberShortcuts || mIsQuerySipAddress);
- }
-
- public void setViewBlocked(ContactListItemView view, Integer id) {
- view.setTag(R.id.block_id, id);
- final int textColor = mResources.getColor(R.color.blocked_number_block_color);
- view.getDataView().setTextColor(textColor);
- view.getLabelView().setTextColor(textColor);
- //TODO: Add icon
- }
-
- public void setViewUnblocked(ContactListItemView view) {
- view.setTag(R.id.block_id, null);
- final int textColor = mResources.getColor(R.color.dialtacts_secondary_text_color);
- view.getDataView().setTextColor(textColor);
- view.getLabelView().setTextColor(textColor);
- //TODO: Remove icon
- }
-
- @Override
- protected void bindView(View itemView, int partition, Cursor cursor, int position) {
- super.bindView(itemView, partition, cursor, position);
-
- final ContactListItemView view = (ContactListItemView) itemView;
- // Reset view state to unblocked.
- setViewUnblocked(view);
-
- final String number = getPhoneNumber(position);
- final String countryIso = GeoUtil.getCurrentCountryIso(mContext);
- final FilteredNumberAsyncQueryHandler.OnCheckBlockedListener onCheckListener =
- new FilteredNumberAsyncQueryHandler.OnCheckBlockedListener() {
- @Override
- public void onCheckComplete(Integer id) {
- if (id != null) {
- setViewBlocked(view, id);
- }
- }
- };
- mFilteredNumberAsyncQueryHandler.isBlockedNumber(
- onCheckListener, number, countryIso);
- }
-}
diff --git a/src/com/android/dialer/list/BlockedListSearchFragment.java b/src/com/android/dialer/list/BlockedListSearchFragment.java
deleted file mode 100644
index da6b42820..000000000
--- a/src/com/android/dialer/list/BlockedListSearchFragment.java
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * 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.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.Log;
-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.GeoUtil;
-import com.android.contacts.common.list.ContactEntryListAdapter;
-import com.android.contacts.common.util.ContactDisplayUtils;
-import com.android.dialer.R;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnCheckBlockedListener;
-import com.android.dialer.filterednumber.BlockNumberDialogFragment;
-import com.android.dialer.logging.InteractionEvent;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.widget.SearchEditTextLayout;
-
-public class BlockedListSearchFragment extends RegularSearchFragment
- implements BlockNumberDialogFragment.Callback {
- private static final String TAG = BlockedListSearchFragment.class.getSimpleName();
-
- private static final String KEY_SEARCH_QUERY = "search_query";
-
- private FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
-
- private EditText mSearchView;
-
- 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(), false);
- }
-
- @Override
- public void afterTextChanged(Editable s) {}
- };
-
- private final SearchEditTextLayout.Callback mSearchLayoutCallback =
- new SearchEditTextLayout.Callback() {
- @Override
- public void onBackButtonClicked() {
- getActivity().onBackPressed();
- }
-
- @Override
- public void onSearchViewClicked() {
- }
- };
-
- @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(), false);
- mFilteredNumberAsyncQueryHandler = new FilteredNumberAsyncQueryHandler(
- getContext().getContentResolver());
- }
-
- @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:
- Log.w(TAG, "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 {
- Toast.makeText(getContext(),
- ContactDisplayUtils.getTtsSpannedPhoneNumber(getResources(),
- R.string.alreadyBlocked, number),
- Toast.LENGTH_SHORT).show();
- }
- }
- };
- final boolean success = mFilteredNumberAsyncQueryHandler.isBlockedNumber(
- onCheckListener, number, countryIso);
- if (!success) {
- Toast.makeText(getContext(),
- ContactDisplayUtils.getTtsSpannedPhoneNumber(
- getResources(), R.string.invalidNumber, number),
- Toast.LENGTH_SHORT).show();
- }
- }
-
- @Override
- public void onFilterNumberSuccess() {
- Logger.logInteraction(InteractionEvent.BLOCK_NUMBER_MANAGEMENT_SCREEN);
- goBack();
- }
-
- @Override
- public void onUnfilterNumberSuccess() {
- Log.wtf(TAG, "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/src/com/android/dialer/list/ContentChangedFilter.java b/src/com/android/dialer/list/ContentChangedFilter.java
deleted file mode 100644
index e552aa3f0..000000000
--- a/src/com/android/dialer/list/ContentChangedFilter.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.android.dialer.list;
-
-import android.view.View;
-import android.view.View.AccessibilityDelegate;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-
-/**
- * AccessibilityDelegate that will filter out TYPE_WINDOW_CONTENT_CHANGED
- * Used to suppress "Showing items x of y" from firing of ListView whenever it's content changes.
- * AccessibilityEvent can only be rejected at a view's parent once it is generated,
- * use addToParent() to add this delegate to the parent.
- */
-public class ContentChangedFilter extends AccessibilityDelegate {
- //the view we don't want TYPE_WINDOW_CONTENT_CHANGED to fire.
- private View mView;
-
- /**
- * Add this delegate to the parent of @param view to filter out TYPE_WINDOW_CONTENT_CHANGED
- */
- public static void addToParent(View view){
- View parent = (View) view.getParent();
- parent.setAccessibilityDelegate(new ContentChangedFilter(view));
- }
-
- private ContentChangedFilter(View view){
- super();
- mView = view;
- }
- @Override
- public boolean onRequestSendAccessibilityEvent (ViewGroup host, View child, AccessibilityEvent event){
- if(child == mView){
- if(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED){
- return false;
- }
- }
- return super.onRequestSendAccessibilityEvent(host,child,event);
- }
-
-}
diff --git a/src/com/android/dialer/list/DialerPhoneNumberListAdapter.java b/src/com/android/dialer/list/DialerPhoneNumberListAdapter.java
deleted file mode 100644
index 7164de2d7..000000000
--- a/src/com/android/dialer/list/DialerPhoneNumberListAdapter.java
+++ /dev/null
@@ -1,220 +0,0 @@
-package com.android.dialer.list;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.telephony.PhoneNumberUtils;
-import android.text.BidiFormatter;
-import android.text.TextDirectionHeuristics;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.contacts.common.CallUtil;
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.list.ContactListItemView;
-import com.android.contacts.common.list.PhoneNumberListAdapter;
-import com.android.contacts.common.util.ContactDisplayUtils;
-import com.android.dialer.R;
-
-/**
- * {@link PhoneNumberListAdapter} with the following added shortcuts, that are displayed as list
- * items:
- * 1) Directly calling the phone number query
- * 2) Adding the phone number query to a contact
- *
- * These shortcuts can be enabled or disabled to toggle whether or not they show up in the
- * list.
- */
-public class DialerPhoneNumberListAdapter extends PhoneNumberListAdapter {
-
- private String mFormattedQueryString;
- private String mCountryIso;
-
- public final static int SHORTCUT_INVALID = -1;
- public final static int SHORTCUT_DIRECT_CALL = 0;
- public final static int SHORTCUT_CREATE_NEW_CONTACT = 1;
- public final static int SHORTCUT_ADD_TO_EXISTING_CONTACT = 2;
- public final static int SHORTCUT_SEND_SMS_MESSAGE = 3;
- public final static int SHORTCUT_MAKE_VIDEO_CALL = 4;
- public final static int SHORTCUT_BLOCK_NUMBER = 5;
-
- public final static int SHORTCUT_COUNT = 6;
-
- private final boolean[] mShortcutEnabled = new boolean[SHORTCUT_COUNT];
-
- private final BidiFormatter mBidiFormatter = BidiFormatter.getInstance();
- private boolean mVideoCallingEnabled = false;
-
- public DialerPhoneNumberListAdapter(Context context) {
- super(context);
-
- mCountryIso = GeoUtil.getCurrentCountryIso(context);
- mVideoCallingEnabled = CallUtil.isVideoEnabled(context);
- }
-
- @Override
- public int getCount() {
- return super.getCount() + getShortcutCount();
- }
-
- /**
- * @return The number of enabled shortcuts. Ranges from 0 to a maximum of SHORTCUT_COUNT
- */
- public int getShortcutCount() {
- int count = 0;
- for (int i = 0; i < mShortcutEnabled.length; i++) {
- if (mShortcutEnabled[i]) count++;
- }
- return count;
- }
-
- public void disableAllShortcuts() {
- for (int i = 0; i < mShortcutEnabled.length; i++) {
- mShortcutEnabled[i] = false;
- }
- }
-
- @Override
- public int getItemViewType(int position) {
- final int shortcut = getShortcutTypeFromPosition(position);
- if (shortcut >= 0) {
- // shortcutPos should always range from 1 to SHORTCUT_COUNT
- return super.getViewTypeCount() + shortcut;
- } else {
- return super.getItemViewType(position);
- }
- }
-
- @Override
- public int getViewTypeCount() {
- // Number of item view types in the super implementation + 2 for the 2 new shortcuts
- return super.getViewTypeCount() + SHORTCUT_COUNT;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final int shortcutType = getShortcutTypeFromPosition(position);
- if (shortcutType >= 0) {
- if (convertView != null) {
- assignShortcutToView((ContactListItemView) convertView, shortcutType);
- return convertView;
- } else {
- final ContactListItemView v = new ContactListItemView(getContext(), null,
- mVideoCallingEnabled);
- assignShortcutToView(v, shortcutType);
- return v;
- }
- } else {
- return super.getView(position, convertView, parent);
- }
- }
-
- @Override
- protected ContactListItemView newView(
- Context context, int partition, Cursor cursor, int position, ViewGroup parent) {
- final ContactListItemView view = super.newView(context, partition, cursor, position,
- parent);
-
- view.setSupportVideoCallIcon(mVideoCallingEnabled);
- return view;
- }
-
- /**
- * @param position The position of the item
- * @return The enabled shortcut type matching the given position if the item is a
- * shortcut, -1 otherwise
- */
- public int getShortcutTypeFromPosition(int position) {
- int shortcutCount = position - super.getCount();
- if (shortcutCount >= 0) {
- // Iterate through the array of shortcuts, looking only for shortcuts where
- // mShortcutEnabled[i] is true
- for (int i = 0; shortcutCount >= 0 && i < mShortcutEnabled.length; i++) {
- if (mShortcutEnabled[i]) {
- shortcutCount--;
- if (shortcutCount < 0) return i;
- }
- }
- throw new IllegalArgumentException("Invalid position - greater than cursor count "
- + " but not a shortcut.");
- }
- return SHORTCUT_INVALID;
- }
-
- @Override
- public boolean isEmpty() {
- return getShortcutCount() == 0 && super.isEmpty();
- }
-
- @Override
- public boolean isEnabled(int position) {
- final int shortcutType = getShortcutTypeFromPosition(position);
- if (shortcutType >= 0) {
- return true;
- } else {
- return super.isEnabled(position);
- }
- }
-
- private void assignShortcutToView(ContactListItemView v, int shortcutType) {
- final CharSequence text;
- final int drawableId;
- final Resources resources = getContext().getResources();
- final String number = getFormattedQueryString();
- switch (shortcutType) {
- case SHORTCUT_DIRECT_CALL:
- text = ContactDisplayUtils.getTtsSpannedPhoneNumber(resources,
- R.string.search_shortcut_call_number,
- mBidiFormatter.unicodeWrap(number, TextDirectionHeuristics.LTR));
- drawableId = R.drawable.ic_search_phone;
- break;
- case SHORTCUT_CREATE_NEW_CONTACT:
- text = resources.getString(R.string.search_shortcut_create_new_contact);
- drawableId = R.drawable.ic_search_add_contact;
- break;
- case SHORTCUT_ADD_TO_EXISTING_CONTACT:
- text = resources.getString(R.string.search_shortcut_add_to_contact);
- drawableId = R.drawable.ic_person_24dp;
- break;
- case SHORTCUT_SEND_SMS_MESSAGE:
- text = resources.getString(R.string.search_shortcut_send_sms_message);
- drawableId = R.drawable.ic_message_24dp;
- break;
- case SHORTCUT_MAKE_VIDEO_CALL:
- text = resources.getString(R.string.search_shortcut_make_video_call);
- drawableId = R.drawable.ic_videocam;
- break;
- case SHORTCUT_BLOCK_NUMBER:
- text = resources.getString(R.string.search_shortcut_block_number);
- drawableId = R.drawable.ic_not_interested_googblue_24dp;
- break;
- default:
- throw new IllegalArgumentException("Invalid shortcut type");
- }
- v.setDrawableResource(drawableId);
- v.setDisplayName(text);
- v.setPhotoPosition(super.getPhotoPosition());
- v.setAdjustSelectionBoundsEnabled(false);
- }
-
- /**
- * @return True if the shortcut state (disabled vs enabled) was changed by this operation
- */
- public boolean setShortcutEnabled(int shortcutType, boolean visible) {
- final boolean changed = mShortcutEnabled[shortcutType] != visible;
- mShortcutEnabled[shortcutType] = visible;
- return changed;
- }
-
- public String getFormattedQueryString() {
- return mFormattedQueryString;
- }
-
- @Override
- public void setQueryString(String queryString) {
- mFormattedQueryString = PhoneNumberUtils.formatNumber(
- PhoneNumberUtils.normalizeNumber(queryString), mCountryIso);
- super.setQueryString(queryString);
- }
-}
diff --git a/src/com/android/dialer/list/DragDropController.java b/src/com/android/dialer/list/DragDropController.java
deleted file mode 100644
index 66ba513a8..000000000
--- a/src/com/android/dialer/list/DragDropController.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package com.android.dialer.list;
-
-import android.util.Log;
-import android.view.View;
-
-import com.android.contacts.common.compat.CompatUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Class that handles and combines drag events generated from multiple views, and then fires
- * off events to any OnDragDropListeners that have registered for callbacks.
- */
-public class DragDropController {
-
- private final List<OnDragDropListener> mOnDragDropListeners =
- new ArrayList<OnDragDropListener>();
- private final DragItemContainer mDragItemContainer;
- private final int[] mLocationOnScreen = new int[2];
-
- /**
- * Callback interface used to retrieve views based on the current touch coordinates of the
- * drag event. The {@link DragItemContainer} houses the draggable views that this
- * {@link DragDropController} controls.
- */
- public interface DragItemContainer {
- public PhoneFavoriteSquareTileView getViewForLocation(int x, int y);
- }
-
- public DragDropController(DragItemContainer dragItemContainer) {
- mDragItemContainer = dragItemContainer;
- }
-
- /**
- * @return True if the drag is started, false if the drag is cancelled for some reason.
- */
- boolean handleDragStarted(View v, int x, int y) {
- int screenX = x;
- int screenY = y;
- // The coordinates in dragEvent of DragEvent.ACTION_DRAG_STARTED before NYC is window-related.
- // This is fixed in NYC.
- if (CompatUtils.isNCompatible()) {
- v.getLocationOnScreen(mLocationOnScreen);
- screenX = x + mLocationOnScreen[0];
- screenY = y + mLocationOnScreen[1];
- }
- final PhoneFavoriteSquareTileView tileView = mDragItemContainer.getViewForLocation(
- screenX, screenY);
- if (tileView == null) {
- return false;
- }
- for (int i = 0; i < mOnDragDropListeners.size(); i++) {
- mOnDragDropListeners.get(i).onDragStarted(screenX, screenY, tileView);
- }
-
- return true;
- }
-
- public void handleDragHovered(View v, int x, int y) {
- v.getLocationOnScreen(mLocationOnScreen);
- final int screenX = x + mLocationOnScreen[0];
- final int screenY = y + mLocationOnScreen[1];
- final PhoneFavoriteSquareTileView view = mDragItemContainer.getViewForLocation(
- screenX, screenY);
- for (int i = 0; i < mOnDragDropListeners.size(); i++) {
- mOnDragDropListeners.get(i).onDragHovered(screenX, screenY, view);
- }
- }
-
- public void handleDragFinished(int x, int y, boolean isRemoveView) {
- if (isRemoveView) {
- for (int i = 0; i < mOnDragDropListeners.size(); i++) {
- mOnDragDropListeners.get(i).onDroppedOnRemove();
- }
- }
-
- for (int i = 0; i < mOnDragDropListeners.size(); i++) {
- mOnDragDropListeners.get(i).onDragFinished(x, y);
- }
- }
-
- public void addOnDragDropListener(OnDragDropListener listener) {
- if (!mOnDragDropListeners.contains(listener)) {
- mOnDragDropListeners.add(listener);
- }
- }
-
- public void removeOnDragDropListener(OnDragDropListener listener) {
- if (mOnDragDropListeners.contains(listener)) {
- mOnDragDropListeners.remove(listener);
- }
- }
-
-}
diff --git a/src/com/android/dialer/list/ListsFragment.java b/src/com/android/dialer/list/ListsFragment.java
deleted file mode 100644
index 52bf3cbb5..000000000
--- a/src/com/android/dialer/list/ListsFragment.java
+++ /dev/null
@@ -1,487 +0,0 @@
-/*
- * Copyright (C) 2013 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.list;
-
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.os.Bundle;
-import android.os.Trace;
-import android.preference.PreferenceManager;
-import android.provider.CallLog.Calls;
-import android.support.v13.app.FragmentPagerAdapter;
-import android.support.v4.view.ViewPager;
-import android.support.v4.view.ViewPager.OnPageChangeListener;
-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.contacts.common.list.ViewPagerTabs;
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.R;
-import com.android.dialer.calllog.CallLogFragment;
-import com.android.dialer.calllog.CallLogNotificationsHelper;
-import com.android.dialer.calllog.CallLogQueryHandler;
-import com.android.dialer.calllog.VisualVoicemailCallLogFragment;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.logging.ScreenEvent;
-import com.android.dialer.util.DialerUtils;
-import com.android.dialer.voicemail.VisualVoicemailEnabledChecker;
-import com.android.dialer.voicemail.VoicemailStatusHelper;
-import com.android.dialer.voicemail.VoicemailStatusHelperImpl;
-import com.android.dialer.widget.ActionBarController;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Fragment that is used as the main screen of the Dialer.
- *
- * Contains a ViewPager that contains various contact lists like the Speed Dial list and the
- * All Contacts list. This will also eventually contain the logic that allows sliding the
- * ViewPager containing the lists up above the search bar and pin it against the top of the
- * screen.
- */
-public class ListsFragment extends Fragment
- implements ViewPager.OnPageChangeListener, CallLogQueryHandler.Listener {
-
- private static final boolean DEBUG = DialtactsActivity.DEBUG;
- private static final String TAG = "ListsFragment";
-
- public static final int TAB_INDEX_SPEED_DIAL = 0;
- public static final int TAB_INDEX_HISTORY = 1;
- public static final int TAB_INDEX_ALL_CONTACTS = 2;
- public static final int TAB_INDEX_VOICEMAIL = 3;
-
- public static final int TAB_COUNT_DEFAULT = 3;
- public static final int TAB_COUNT_WITH_VOICEMAIL = 4;
-
- public interface HostInterface {
- public ActionBarController getActionBarController();
- }
-
- private ActionBar mActionBar;
- private ViewPager mViewPager;
- private ViewPagerTabs mViewPagerTabs;
- private ViewPagerAdapter mViewPagerAdapter;
- private RemoveView mRemoveView;
- private View mRemoveViewContent;
-
- private SpeedDialFragment mSpeedDialFragment;
- private CallLogFragment mHistoryFragment;
- private AllContactsFragment mAllContactsFragment;
- private CallLogFragment mVoicemailFragment;
-
- private SharedPreferences mPrefs;
- private boolean mHasActiveVoicemailProvider;
- private boolean mHasFetchedVoicemailStatus;
- private boolean mShowVoicemailTabAfterVoicemailStatusIsFetched;
-
- private VoicemailStatusHelper mVoicemailStatusHelper;
- private ArrayList<OnPageChangeListener> mOnPageChangeListeners =
- new ArrayList<OnPageChangeListener>();
-
- private String[] mTabTitles;
- private int[] mTabIcons;
-
- /**
- * The position of the currently selected tab.
- */
- private int mTabIndex = TAB_INDEX_SPEED_DIAL;
- private CallLogQueryHandler mCallLogQueryHandler;
-
- public class ViewPagerAdapter extends FragmentPagerAdapter {
- private final List<Fragment> mFragments = new ArrayList<>();
-
- public ViewPagerAdapter(FragmentManager fm) {
- super(fm);
- for (int i = 0; i < TAB_COUNT_WITH_VOICEMAIL; i++) {
- mFragments.add(null);
- }
- }
-
- @Override
- public long getItemId(int position) {
- return getRtlPosition(position);
- }
-
- @Override
- public Fragment getItem(int position) {
- switch (getRtlPosition(position)) {
- case TAB_INDEX_SPEED_DIAL:
- mSpeedDialFragment = new SpeedDialFragment();
- return mSpeedDialFragment;
- case TAB_INDEX_HISTORY:
- mHistoryFragment = new CallLogFragment(CallLogQueryHandler.CALL_TYPE_ALL);
- return mHistoryFragment;
- case TAB_INDEX_ALL_CONTACTS:
- mAllContactsFragment = new AllContactsFragment();
- return mAllContactsFragment;
- case TAB_INDEX_VOICEMAIL:
- mVoicemailFragment = new VisualVoicemailCallLogFragment();
- return mVoicemailFragment;
- }
- throw new IllegalStateException("No fragment at position " + position);
- }
-
- @Override
- public Fragment instantiateItem(ViewGroup container, int position) {
- // On rotation the FragmentManager handles rotation. Therefore getItem() isn't called.
- // Copy the fragments that the FragmentManager finds so that we can store them in
- // instance variables for later.
- final Fragment fragment =
- (Fragment) super.instantiateItem(container, position);
- if (fragment instanceof SpeedDialFragment) {
- mSpeedDialFragment = (SpeedDialFragment) fragment;
- } else if (fragment instanceof CallLogFragment && position == TAB_INDEX_HISTORY) {
- mHistoryFragment = (CallLogFragment) fragment;
- } else if (fragment instanceof AllContactsFragment) {
- mAllContactsFragment = (AllContactsFragment) fragment;
- } else if (fragment instanceof CallLogFragment && position == TAB_INDEX_VOICEMAIL) {
- mVoicemailFragment = (CallLogFragment) fragment;
- }
- mFragments.set(position, fragment);
- return fragment;
- }
-
- /**
- * When {@link android.support.v4.view.PagerAdapter#notifyDataSetChanged} is called,
- * this method is called on all pages to determine whether they need to be recreated.
- * When the voicemail tab is removed, the view needs to be recreated by returning
- * POSITION_NONE. If notifyDataSetChanged is called for some other reason, the voicemail
- * tab is recreated only if it is active. All other tabs do not need to be recreated
- * and POSITION_UNCHANGED is returned.
- */
- @Override
- public int getItemPosition(Object object) {
- return !mHasActiveVoicemailProvider &&
- mFragments.indexOf(object) == TAB_INDEX_VOICEMAIL ? POSITION_NONE :
- POSITION_UNCHANGED;
- }
-
- @Override
- public int getCount() {
- return mHasActiveVoicemailProvider ? TAB_COUNT_WITH_VOICEMAIL : TAB_COUNT_DEFAULT;
- }
-
- @Override
- public CharSequence getPageTitle(int position) {
- return mTabTitles[position];
- }
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- Trace.beginSection(TAG + " onCreate");
- super.onCreate(savedInstanceState);
-
- mVoicemailStatusHelper = new VoicemailStatusHelperImpl();
- mHasFetchedVoicemailStatus = false;
-
- mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
- mHasActiveVoicemailProvider = mPrefs.getBoolean(
- VisualVoicemailEnabledChecker.PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER, false);
-
- Trace.endSection();
- }
-
- @Override
- public void onResume() {
- Trace.beginSection(TAG + " onResume");
- super.onResume();
-
- mActionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
- if (getUserVisibleHint()) {
- sendScreenViewForCurrentPosition();
- }
-
- // Fetch voicemail status to determine if we should show the voicemail tab.
- mCallLogQueryHandler =
- new CallLogQueryHandler(getActivity(), getActivity().getContentResolver(), this);
- mCallLogQueryHandler.fetchVoicemailStatus();
- mCallLogQueryHandler.fetchMissedCallsUnreadCount();
- Trace.endSection();
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- Trace.beginSection(TAG + " onCreateView");
- Trace.beginSection(TAG + " inflate view");
- final View parentView = inflater.inflate(R.layout.lists_fragment, container, false);
- Trace.endSection();
- Trace.beginSection(TAG + " setup views");
- mViewPager = (ViewPager) parentView.findViewById(R.id.lists_pager);
- mViewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager());
- mViewPager.setAdapter(mViewPagerAdapter);
- mViewPager.setOffscreenPageLimit(TAB_COUNT_WITH_VOICEMAIL - 1);
- mViewPager.setOnPageChangeListener(this);
- showTab(TAB_INDEX_SPEED_DIAL);
-
- mTabTitles = new String[TAB_COUNT_WITH_VOICEMAIL];
- mTabTitles[TAB_INDEX_SPEED_DIAL] = getResources().getString(R.string.tab_speed_dial);
- mTabTitles[TAB_INDEX_HISTORY] = getResources().getString(R.string.tab_history);
- mTabTitles[TAB_INDEX_ALL_CONTACTS] = getResources().getString(R.string.tab_all_contacts);
- mTabTitles[TAB_INDEX_VOICEMAIL] = getResources().getString(R.string.tab_voicemail);
-
- mTabIcons = new int[TAB_COUNT_WITH_VOICEMAIL];
- mTabIcons[TAB_INDEX_SPEED_DIAL] = R.drawable.ic_grade_24dp;
- mTabIcons[TAB_INDEX_HISTORY] = R.drawable.ic_schedule_24dp;
- mTabIcons[TAB_INDEX_ALL_CONTACTS] = R.drawable.ic_people_24dp;
- mTabIcons[TAB_INDEX_VOICEMAIL] = R.drawable.ic_voicemail_24dp;
-
- mViewPagerTabs = (ViewPagerTabs) parentView.findViewById(R.id.lists_pager_header);
- mViewPagerTabs.configureTabIcons(mTabIcons);
- mViewPagerTabs.setViewPager(mViewPager);
- addOnPageChangeListener(mViewPagerTabs);
-
- mRemoveView = (RemoveView) parentView.findViewById(R.id.remove_view);
- mRemoveViewContent = parentView.findViewById(R.id.remove_view_content);
-
- Trace.endSection();
- Trace.endSection();
- return parentView;
- }
-
- public void addOnPageChangeListener(OnPageChangeListener onPageChangeListener) {
- if (!mOnPageChangeListeners.contains(onPageChangeListener)) {
- mOnPageChangeListeners.add(onPageChangeListener);
- }
- }
-
- /**
- * Shows the tab with the specified index. If the voicemail tab index is specified, but the
- * voicemail status hasn't been fetched, it will try to show the tab after the voicemail status
- * has been fetched.
- */
- public void showTab(int index) {
- if (index == TAB_INDEX_VOICEMAIL) {
- if (mHasActiveVoicemailProvider) {
- mViewPager.setCurrentItem(getRtlPosition(TAB_INDEX_VOICEMAIL));
- } else if (!mHasFetchedVoicemailStatus) {
- // Try to show the voicemail tab after the voicemail status returns.
- mShowVoicemailTabAfterVoicemailStatusIsFetched = true;
- }
- } else if (index < getTabCount()){
- mViewPager.setCurrentItem(getRtlPosition(index));
- }
- }
-
- @Override
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- mTabIndex = getRtlPosition(position);
-
- final int count = mOnPageChangeListeners.size();
- for (int i = 0; i < count; i++) {
- mOnPageChangeListeners.get(i).onPageScrolled(position, positionOffset,
- positionOffsetPixels);
- }
- }
-
- @Override
- public void onPageSelected(int position) {
- mTabIndex = getRtlPosition(position);
-
- // Show the tab which has been selected instead.
- mShowVoicemailTabAfterVoicemailStatusIsFetched = false;
-
- final int count = mOnPageChangeListeners.size();
- for (int i = 0; i < count; i++) {
- mOnPageChangeListeners.get(i).onPageSelected(position);
- }
- sendScreenViewForCurrentPosition();
- }
-
- @Override
- public void onPageScrollStateChanged(int state) {
- final int count = mOnPageChangeListeners.size();
- for (int i = 0; i < count; i++) {
- mOnPageChangeListeners.get(i).onPageScrollStateChanged(state);
- }
- }
-
- @Override
- public void onVoicemailStatusFetched(Cursor statusCursor) {
- mHasFetchedVoicemailStatus = true;
-
- if (getActivity() == null || getActivity().isFinishing()) {
- return;
- }
-
- // Update mHasActiveVoicemailProvider, which controls the number of tabs displayed.
- boolean hasActiveVoicemailProvider =
- mVoicemailStatusHelper.getNumberActivityVoicemailSources(statusCursor) > 0;
- if (hasActiveVoicemailProvider != mHasActiveVoicemailProvider) {
- mHasActiveVoicemailProvider = hasActiveVoicemailProvider;
- mViewPagerAdapter.notifyDataSetChanged();
-
- if (hasActiveVoicemailProvider) {
- mViewPagerTabs.updateTab(TAB_INDEX_VOICEMAIL);
- } else {
- mViewPagerTabs.removeTab(TAB_INDEX_VOICEMAIL);
- removeVoicemailFragment();
- }
-
- mPrefs.edit()
- .putBoolean(VisualVoicemailEnabledChecker.PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER,
- hasActiveVoicemailProvider)
- .commit();
- }
-
- if (hasActiveVoicemailProvider) {
- mCallLogQueryHandler.fetchVoicemailUnreadCount();
- }
-
- if (mHasActiveVoicemailProvider && mShowVoicemailTabAfterVoicemailStatusIsFetched) {
- mShowVoicemailTabAfterVoicemailStatusIsFetched = false;
- showTab(TAB_INDEX_VOICEMAIL);
- }
- }
-
- @Override
- public void onVoicemailUnreadCountFetched(Cursor cursor) {
- if (getActivity() == null || getActivity().isFinishing() || cursor == null) {
- return;
- }
-
- int count = 0;
- try {
- count = cursor.getCount();
- } finally {
- cursor.close();
- }
-
- mViewPagerTabs.setUnreadCount(count, TAB_INDEX_VOICEMAIL);
- mViewPagerTabs.updateTab(TAB_INDEX_VOICEMAIL);
- }
-
- @Override
- public void onMissedCallsUnreadCountFetched(Cursor cursor) {
- if (getActivity() == null || getActivity().isFinishing() || cursor == null) {
- return;
- }
-
- int count = 0;
- try {
- count = cursor.getCount();
- } finally {
- cursor.close();
- }
-
- mViewPagerTabs.setUnreadCount(count, TAB_INDEX_HISTORY);
- mViewPagerTabs.updateTab(TAB_INDEX_HISTORY);
- }
-
- @Override
- public boolean onCallsFetched(Cursor statusCursor) {
- // Return false; did not take ownership of cursor
- return false;
- }
-
- public int getCurrentTabIndex() {
- return mTabIndex;
- }
-
- /**
- * External method to update unread count because the unread count changes when the user
- * expands a voicemail in the call log or when the user expands an unread call in the call
- * history tab.
- */
- public void updateTabUnreadCounts() {
- if (mCallLogQueryHandler != null) {
- mCallLogQueryHandler.fetchMissedCallsUnreadCount();
- if (mHasActiveVoicemailProvider) {
- mCallLogQueryHandler.fetchVoicemailUnreadCount();
- }
- }
- }
-
- /**
- * External method to mark all missed calls as read.
- */
- public void markMissedCallsAsReadAndRemoveNotifications() {
- if (mCallLogQueryHandler != null) {
- mCallLogQueryHandler.markMissedCallsAsRead();
- CallLogNotificationsHelper.removeMissedCallNotifications(getActivity());
- }
- }
-
-
- public void showRemoveView(boolean show) {
- mRemoveViewContent.setVisibility(show ? View.VISIBLE : View.GONE);
- mRemoveView.setAlpha(show ? 0 : 1);
- mRemoveView.animate().alpha(show ? 1 : 0).start();
- }
-
- public boolean shouldShowActionBar() {
- // TODO: Update this based on scroll state.
- return mActionBar != null;
- }
-
- public SpeedDialFragment getSpeedDialFragment() {
- return mSpeedDialFragment;
- }
-
- public RemoveView getRemoveView() {
- return mRemoveView;
- }
-
- public int getTabCount() {
- return mViewPagerAdapter.getCount();
- }
-
- private int getRtlPosition(int position) {
- if (DialerUtils.isRtl()) {
- return mViewPagerAdapter.getCount() - 1 - position;
- }
- return position;
- }
-
- public void sendScreenViewForCurrentPosition() {
- if (!isResumed()) {
- return;
- }
-
- int screenType;
- switch (getCurrentTabIndex()) {
- case TAB_INDEX_SPEED_DIAL:
- screenType = ScreenEvent.SPEED_DIAL;
- break;
- case TAB_INDEX_HISTORY:
- screenType = ScreenEvent.CALL_LOG;
- break;
- case TAB_INDEX_ALL_CONTACTS:
- screenType = ScreenEvent.ALL_CONTACTS;
- break;
- case TAB_INDEX_VOICEMAIL:
- screenType = ScreenEvent.VOICEMAIL_LOG;
- default:
- return;
- }
- Logger.logScreenView(screenType, getActivity());
- }
-
- private void removeVoicemailFragment() {
- if (mVoicemailFragment != null) {
- getChildFragmentManager().beginTransaction().remove(mVoicemailFragment)
- .commitAllowingStateLoss();
- mVoicemailFragment = null;
- }
- }
-}
diff --git a/src/com/android/dialer/list/OnDragDropListener.java b/src/com/android/dialer/list/OnDragDropListener.java
deleted file mode 100644
index c9ef50b09..000000000
--- a/src/com/android/dialer/list/OnDragDropListener.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.android.dialer.list;
-
-
-/**
- * Classes that want to receive callbacks in response to drag events should implement this
- * interface.
- */
-public interface OnDragDropListener {
- /**
- * Called when a drag is started.
- * @param x X-coordinate of the drag event
- * @param y Y-coordinate of the drag event
- * @param view The contact tile which the drag was started on
- */
- public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView view);
-
- /**
- * Called when a drag is in progress and the user moves the dragged contact to a
- * location.
- *
- * @param x X-coordinate of the drag event
- * @param y Y-coordinate of the drag event
- * @param view Contact tile in the ListView which is currently being displaced
- * by the dragged contact
- */
- public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView view);
-
- /**
- * Called when a drag is completed (whether by dropping it somewhere or simply by dragging
- * the contact off the screen)
- * @param x X-coordinate of the drag event
- * @param y Y-coordinate of the drag event
- */
- public void onDragFinished(int x, int y);
-
- /**
- * Called when a contact has been dropped on the remove view, indicating that the user
- * wants to remove this contact.
- */
- public void onDroppedOnRemove();
-} \ No newline at end of file
diff --git a/src/com/android/dialer/list/OnListFragmentScrolledListener.java b/src/com/android/dialer/list/OnListFragmentScrolledListener.java
deleted file mode 100644
index 5ed3a6434..000000000
--- a/src/com/android/dialer/list/OnListFragmentScrolledListener.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2013 Google Inc.
- * Licensed to 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.list;
-
-/*
- * Interface to provide callback to activity when a child fragment is scrolled
- */
-public interface OnListFragmentScrolledListener {
- public void onListFragmentScrollStateChange(int scrollState);
- public void onListFragmentScroll(int firstVisibleItem, int visibleItemCount,
- int totalItemCount);
-}
diff --git a/src/com/android/dialer/list/PhoneFavoriteListView.java b/src/com/android/dialer/list/PhoneFavoriteListView.java
deleted file mode 100644
index aad8ad58f..000000000
--- a/src/com/android/dialer/list/PhoneFavoriteListView.java
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc.
- * Licensed to 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.list;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.os.Handler;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.DragEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.widget.GridView;
-import android.widget.ImageView;
-
-import com.android.dialer.R;
-import com.android.dialer.list.DragDropController.DragItemContainer;
-
-/**
- * Viewgroup that presents the user's speed dial contacts in a grid.
- */
-public class PhoneFavoriteListView extends GridView implements OnDragDropListener,
- DragItemContainer {
-
- public static final String LOG_TAG = PhoneFavoriteListView.class.getSimpleName();
-
- private float mTouchSlop;
-
- private int mTopScrollBound;
- private int mBottomScrollBound;
- private int mLastDragY;
-
- private Handler mScrollHandler;
- private final long SCROLL_HANDLER_DELAY_MILLIS = 5;
- private final int DRAG_SCROLL_PX_UNIT = 25;
-
- private boolean mIsDragScrollerRunning = false;
- private int mTouchDownForDragStartX;
- private int mTouchDownForDragStartY;
-
- private Bitmap mDragShadowBitmap;
- private ImageView mDragShadowOverlay;
- private View mDragShadowParent;
- private int mAnimationDuration;
-
- final int[] mLocationOnScreen = new int[2];
-
- // X and Y offsets inside the item from where the user grabbed to the
- // child's left coordinate. This is used to aid in the drawing of the drag shadow.
- private int mTouchOffsetToChildLeft;
- private int mTouchOffsetToChildTop;
-
- private int mDragShadowLeft;
- private int mDragShadowTop;
-
- private DragDropController mDragDropController = new DragDropController(this);
-
- private final float DRAG_SHADOW_ALPHA = 0.7f;
-
- /**
- * {@link #mTopScrollBound} and {@link mBottomScrollBound} will be
- * offseted to the top / bottom by {@link #getHeight} * {@link #BOUND_GAP_RATIO} pixels.
- */
- private final float BOUND_GAP_RATIO = 0.2f;
-
- private final Runnable mDragScroller = new Runnable() {
- @Override
- public void run() {
- if (mLastDragY <= mTopScrollBound) {
- smoothScrollBy(-DRAG_SCROLL_PX_UNIT, (int) SCROLL_HANDLER_DELAY_MILLIS);
- } else if (mLastDragY >= mBottomScrollBound) {
- smoothScrollBy(DRAG_SCROLL_PX_UNIT, (int) SCROLL_HANDLER_DELAY_MILLIS);
- }
- mScrollHandler.postDelayed(this, SCROLL_HANDLER_DELAY_MILLIS);
- }
- };
-
- private final AnimatorListenerAdapter mDragShadowOverAnimatorListener =
- new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mDragShadowBitmap != null) {
- mDragShadowBitmap.recycle();
- mDragShadowBitmap = null;
- }
- mDragShadowOverlay.setVisibility(GONE);
- mDragShadowOverlay.setImageBitmap(null);
- }
- };
-
- public PhoneFavoriteListView(Context context) {
- this(context, null);
- }
-
- public PhoneFavoriteListView(Context context, AttributeSet attrs) {
- this(context, attrs, -1);
- }
-
- public PhoneFavoriteListView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- mAnimationDuration = context.getResources().getInteger(R.integer.fade_duration);
- mTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop();
- mDragDropController.addOnDragDropListener(this);
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- mTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
- }
-
- /**
- * TODO: This is all swipe to remove code (nothing to do with drag to remove). This should
- * be cleaned up and removed once drag to remove becomes the only way to remove contacts.
- */
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mTouchDownForDragStartX = (int) ev.getX();
- mTouchDownForDragStartY = (int) ev.getY();
- }
-
- return super.onInterceptTouchEvent(ev);
- }
-
- @Override
- public boolean onDragEvent(DragEvent event) {
- final int action = event.getAction();
- final int eX = (int) event.getX();
- final int eY = (int) event.getY();
- switch (action) {
- case DragEvent.ACTION_DRAG_STARTED: {
- if (!PhoneFavoriteTileView.DRAG_PHONE_FAVORITE_TILE.equals(event.getLocalState())) {
- // Ignore any drag events that were not propagated by long pressing
- // on a {@link PhoneFavoriteTileView}
- return false;
- }
- if (!mDragDropController.handleDragStarted(this, eX, eY)) {
- return false;
- }
- break;
- }
- case DragEvent.ACTION_DRAG_LOCATION:
- mLastDragY = eY;
- mDragDropController.handleDragHovered(this, eX, eY);
- // Kick off {@link #mScrollHandler} if it's not started yet.
- if (!mIsDragScrollerRunning &&
- // And if the distance traveled while dragging exceeds the touch slop
- (Math.abs(mLastDragY - mTouchDownForDragStartY) >= 4 * mTouchSlop)) {
- mIsDragScrollerRunning = true;
- ensureScrollHandler();
- mScrollHandler.postDelayed(mDragScroller, SCROLL_HANDLER_DELAY_MILLIS);
- }
- break;
- case DragEvent.ACTION_DRAG_ENTERED:
- final int boundGap = (int) (getHeight() * BOUND_GAP_RATIO);
- mTopScrollBound = (getTop() + boundGap);
- mBottomScrollBound = (getBottom() - boundGap);
- break;
- case DragEvent.ACTION_DRAG_EXITED:
- case DragEvent.ACTION_DRAG_ENDED:
- case DragEvent.ACTION_DROP:
- ensureScrollHandler();
- mScrollHandler.removeCallbacks(mDragScroller);
- mIsDragScrollerRunning = false;
- // Either a successful drop or it's ended with out drop.
- if (action == DragEvent.ACTION_DROP || action == DragEvent.ACTION_DRAG_ENDED) {
- mDragDropController.handleDragFinished(eX, eY, false);
- }
- break;
- default:
- break;
- }
- // This ListView will consume the drag events on behalf of its children.
- return true;
- }
-
- public void setDragShadowOverlay(ImageView overlay) {
- mDragShadowOverlay = overlay;
- mDragShadowParent = (View) mDragShadowOverlay.getParent();
- }
-
- /**
- * Find the view under the pointer.
- */
- private View getViewAtPosition(int x, int y) {
- final int count = getChildCount();
- View child;
- for (int childIdx = 0; childIdx < count; childIdx++) {
- child = getChildAt(childIdx);
- if (y >= child.getTop() && y <= child.getBottom() && x >= child.getLeft()
- && x <= child.getRight()) {
- return child;
- }
- }
- return null;
- }
-
- private void ensureScrollHandler() {
- if (mScrollHandler == null) {
- mScrollHandler = getHandler();
- }
- }
-
- public DragDropController getDragDropController() {
- return mDragDropController;
- }
-
- @Override
- public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView tileView) {
- if (mDragShadowOverlay == null) {
- return;
- }
-
- mDragShadowOverlay.clearAnimation();
- mDragShadowBitmap = createDraggedChildBitmap(tileView);
- if (mDragShadowBitmap == null) {
- return;
- }
-
- tileView.getLocationOnScreen(mLocationOnScreen);
- mDragShadowLeft = mLocationOnScreen[0];
- mDragShadowTop = mLocationOnScreen[1];
-
- // x and y are the coordinates of the on-screen touch event. Using these
- // and the on-screen location of the tileView, calculate the difference between
- // the position of the user's finger and the position of the tileView. These will
- // be used to offset the location of the drag shadow so that it appears that the
- // tileView is positioned directly under the user's finger.
- mTouchOffsetToChildLeft = x - mDragShadowLeft;
- mTouchOffsetToChildTop = y - mDragShadowTop;
-
- mDragShadowParent.getLocationOnScreen(mLocationOnScreen);
- mDragShadowLeft -= mLocationOnScreen[0];
- mDragShadowTop -= mLocationOnScreen[1];
-
- mDragShadowOverlay.setImageBitmap(mDragShadowBitmap);
- mDragShadowOverlay.setVisibility(VISIBLE);
- mDragShadowOverlay.setAlpha(DRAG_SHADOW_ALPHA);
-
- mDragShadowOverlay.setX(mDragShadowLeft);
- mDragShadowOverlay.setY(mDragShadowTop);
- }
-
- @Override
- public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView tileView) {
- // Update the drag shadow location.
- mDragShadowParent.getLocationOnScreen(mLocationOnScreen);
- mDragShadowLeft = x - mTouchOffsetToChildLeft - mLocationOnScreen[0];
- mDragShadowTop = y - mTouchOffsetToChildTop - mLocationOnScreen[1];
- // Draw the drag shadow at its last known location if the drag shadow exists.
- if (mDragShadowOverlay != null) {
- mDragShadowOverlay.setX(mDragShadowLeft);
- mDragShadowOverlay.setY(mDragShadowTop);
- }
- }
-
- @Override
- public void onDragFinished(int x, int y) {
- if (mDragShadowOverlay != null) {
- mDragShadowOverlay.clearAnimation();
- mDragShadowOverlay.animate().alpha(0.0f)
- .setDuration(mAnimationDuration)
- .setListener(mDragShadowOverAnimatorListener)
- .start();
- }
- }
-
- @Override
- public void onDroppedOnRemove() {}
-
- private Bitmap createDraggedChildBitmap(View view) {
- view.setDrawingCacheEnabled(true);
- final Bitmap cache = view.getDrawingCache();
-
- Bitmap bitmap = null;
- if (cache != null) {
- try {
- bitmap = cache.copy(Bitmap.Config.ARGB_8888, false);
- } catch (final OutOfMemoryError e) {
- Log.w(LOG_TAG, "Failed to copy bitmap from Drawing cache", e);
- bitmap = null;
- }
- }
-
- view.destroyDrawingCache();
- view.setDrawingCacheEnabled(false);
-
- return bitmap;
- }
-
- @Override
- public PhoneFavoriteSquareTileView getViewForLocation(int x, int y) {
- getLocationOnScreen(mLocationOnScreen);
- // Calculate the X and Y coordinates of the drag event relative to the view
- final int viewX = x - mLocationOnScreen[0];
- final int viewY = y - mLocationOnScreen[1];
- final View child = getViewAtPosition(viewX, viewY);
-
- if (!(child instanceof PhoneFavoriteSquareTileView)) {
- return null;
- }
-
- return (PhoneFavoriteSquareTileView) child;
- }
-}
diff --git a/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java b/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java
deleted file mode 100644
index 69a230c8a..000000000
--- a/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
-
- * Copyright (C) 2011 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.list;
-
-import android.content.Context;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.QuickContact;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.TextView;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.list.ContactEntry;
-import com.android.dialer.R;
-
-/**
- * Displays the contact's picture overlaid with their name and number type in a tile.
- */
-public class PhoneFavoriteSquareTileView extends PhoneFavoriteTileView {
- private static final String TAG = PhoneFavoriteSquareTileView.class.getSimpleName();
-
- private final float mHeightToWidthRatio;
-
- private ImageButton mSecondaryButton;
-
- private ContactEntry mContactEntry;
-
- public PhoneFavoriteSquareTileView(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- mHeightToWidthRatio = getResources().getFraction(
- R.dimen.contact_tile_height_to_width_ratio, 1, 1);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- final TextView nameView = (TextView) findViewById(R.id.contact_tile_name);
- nameView.setElegantTextHeight(false);
- final TextView phoneTypeView = (TextView) findViewById(R.id.contact_tile_phone_type);
- phoneTypeView.setElegantTextHeight(false);
- mSecondaryButton = (ImageButton) findViewById(R.id.contact_tile_secondary_button);
- }
-
- @Override
- protected int getApproximateImageSize() {
- // The picture is the full size of the tile (minus some padding, but we can be generous)
- return getWidth();
- }
-
- private void launchQuickContact() {
- if (CompatUtils.hasPrioritizedMimeType()) {
- QuickContact.showQuickContact(getContext(), PhoneFavoriteSquareTileView.this,
- getLookupUri(), null, Phone.CONTENT_ITEM_TYPE);
- } else {
- QuickContact.showQuickContact(getContext(), PhoneFavoriteSquareTileView.this,
- getLookupUri(), QuickContact.MODE_LARGE, null);
- }
- }
-
- @Override
- public void loadFromContact(ContactEntry entry) {
- super.loadFromContact(entry);
- if (entry != null) {
- mSecondaryButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- launchQuickContact();
- }
- });
- }
- mContactEntry = entry;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int width = MeasureSpec.getSize(widthMeasureSpec);
- final int height = (int) (mHeightToWidthRatio * width);
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- getChildAt(i).measure(
- MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
- );
- }
- setMeasuredDimension(width, height);
- }
-
- @Override
- protected String getNameForView(ContactEntry contactEntry) {
- return contactEntry.getPreferredDisplayName();
- }
-
- public ContactEntry getContactEntry() {
- return mContactEntry;
- }
-}
diff --git a/src/com/android/dialer/list/PhoneFavoriteTileView.java b/src/com/android/dialer/list/PhoneFavoriteTileView.java
deleted file mode 100644
index 56d0b5d22..000000000
--- a/src/com/android/dialer/list/PhoneFavoriteTileView.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
-
- * Copyright (C) 2011 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.list;
-
-import android.content.ClipData;
-import android.content.Context;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ImageView;
-
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.MoreContactUtils;
-import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
-import com.android.contacts.common.list.ContactEntry;
-import com.android.contacts.common.list.ContactTileView;
-import com.android.dialer.R;
-
-/**
- * A light version of the {@link com.android.contacts.common.list.ContactTileView} that is used in
- * Dialtacts for frequently called contacts. Slightly different behavior from superclass when you
- * tap it, you want to call the frequently-called number for the contact, even if that is not the
- * default number for that contact. This abstract class is the super class to both the row and tile
- * view.
- */
-public abstract class PhoneFavoriteTileView extends ContactTileView {
-
- private static final String TAG = PhoneFavoriteTileView.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- // These parameters instruct the photo manager to display the default image/letter at 70% of
- // its normal size, and vertically offset upwards 12% towards the top of the letter tile, to
- // make room for the contact name and number label at the bottom of the image.
- private static final float DEFAULT_IMAGE_LETTER_OFFSET = -0.12f;
- private static final float DEFAULT_IMAGE_LETTER_SCALE = 0.70f;
-
- /** View that contains the transparent shadow that is overlaid on top of the contact image. */
- private View mShadowOverlay;
-
- /** Users' most frequent phone number. */
- private String mPhoneNumberString;
-
- // Dummy clip data object that is attached to drag shadows so that text views
- // don't crash with an NPE if the drag shadow is released in their bounds
- private static final ClipData EMPTY_CLIP_DATA = ClipData.newPlainText("", "");
-
- // Constant to pass to the drag event so that the drag action only happens when a phone favorite
- // tile is long pressed.
- static final String DRAG_PHONE_FAVORITE_TILE = "PHONE_FAVORITE_TILE";
-
- public PhoneFavoriteTileView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mShadowOverlay = findViewById(R.id.shadow_overlay);
-
- setOnLongClickListener(new OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- final PhoneFavoriteTileView view = (PhoneFavoriteTileView) v;
- // NOTE The drag shadow is handled in the ListView.
- view.startDrag(EMPTY_CLIP_DATA, new View.DragShadowBuilder(),
- DRAG_PHONE_FAVORITE_TILE, 0);
- return true;
- }
- });
- }
-
- @Override
- public void loadFromContact(ContactEntry entry) {
- super.loadFromContact(entry);
- // Set phone number to null in case we're reusing the view.
- mPhoneNumberString = null;
- if (entry != null) {
- // Grab the phone-number to call directly. See {@link onClick()}.
- mPhoneNumberString = entry.phoneNumber;
-
- // If this is a blank entry, don't show anything.
- // TODO krelease: Just hide the view for now. For this to truly look like an empty row
- // the entire ContactTileRow needs to be hidden.
- if (entry == ContactEntry.BLANK_ENTRY) {
- setVisibility(View.INVISIBLE);
- } else {
- final ImageView starIcon = (ImageView) findViewById(R.id.contact_star_icon);
- starIcon.setVisibility(entry.isFavorite ? View.VISIBLE : View.GONE);
- setVisibility(View.VISIBLE);
- }
- }
- }
-
- @Override
- protected boolean isDarkTheme() {
- return false;
- }
-
- @Override
- protected OnClickListener createClickListener() {
- return new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mListener == null) {
- return;
- }
- if (TextUtils.isEmpty(mPhoneNumberString)) {
- // Copy "superclass" implementation
- mListener.onContactSelected(getLookupUri(), MoreContactUtils
- .getTargetRectFromView(PhoneFavoriteTileView.this));
- } else {
- // When you tap a frequently-called contact, you want to
- // call them at the number that you usually talk to them
- // at (i.e. the one displayed in the UI), regardless of
- // whether that's their default number.
- mListener.onCallNumberDirectly(mPhoneNumberString);
- }
- }
- };
- }
-
- @Override
- protected DefaultImageRequest getDefaultImageRequest(String displayName, String lookupKey) {
- return new DefaultImageRequest(displayName, lookupKey, ContactPhotoManager.TYPE_DEFAULT,
- DEFAULT_IMAGE_LETTER_SCALE, DEFAULT_IMAGE_LETTER_OFFSET, false);
- }
-
- @Override
- protected void configureViewForImage(boolean isDefaultImage) {
- // Hide the shadow overlay if the image is a default image (i.e. colored letter tile)
- if (mShadowOverlay != null) {
- mShadowOverlay.setVisibility(isDefaultImage ? View.GONE : View.VISIBLE);
- }
- }
-
- @Override
- protected boolean isContactPhotoCircular() {
- // Unlike Contacts' tiles, the Dialer's favorites tiles are square.
- return false;
- }
-}
diff --git a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
deleted file mode 100644
index 77da7e937..000000000
--- a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
+++ /dev/null
@@ -1,696 +0,0 @@
-/*
- * Copyright (C) 2013 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.list;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ComparisonChain;
-import com.google.common.collect.Lists;
-
-import android.content.ContentProviderOperation;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.OperationApplicationException;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.PinnedPositions;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.LongSparseArray;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.ContactTileLoaderFactory;
-import com.android.contacts.common.list.ContactEntry;
-import com.android.contacts.common.list.ContactTileAdapter.DisplayType;
-import com.android.contacts.common.list.ContactTileView;
-import com.android.contacts.common.preference.ContactsPreferences;
-import com.android.dialer.R;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.PriorityQueue;
-
-/**
- * Also allows for a configurable number of columns as well as a maximum row of tiled contacts.
- */
-public class PhoneFavoritesTileAdapter extends BaseAdapter implements
- OnDragDropListener {
- private static final String TAG = PhoneFavoritesTileAdapter.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- public static final int NO_ROW_LIMIT = -1;
-
- public static final int ROW_LIMIT_DEFAULT = NO_ROW_LIMIT;
-
- private ContactTileView.Listener mListener;
- private OnDataSetChangedForAnimationListener mDataSetChangedListener;
-
- private Context mContext;
- private Resources mResources;
- private ContactsPreferences mContactsPreferences;
-
- /** Contact data stored in cache. This is used to populate the associated view. */
- protected ArrayList<ContactEntry> mContactEntries = null;
- /** Back up of the temporarily removed Contact during dragging. */
- private ContactEntry mDraggedEntry = null;
- /** Position of the temporarily removed contact in the cache. */
- private int mDraggedEntryIndex = -1;
- /** New position of the temporarily removed contact in the cache. */
- private int mDropEntryIndex = -1;
- /** New position of the temporarily entered contact in the cache. */
- private int mDragEnteredEntryIndex = -1;
-
- private boolean mAwaitingRemove = false;
- private boolean mDelayCursorUpdates = false;
-
- private ContactPhotoManager mPhotoManager;
- protected int mNumFrequents;
- protected int mNumStarred;
-
- protected int mIdIndex;
- protected int mLookupIndex;
- protected int mPhotoUriIndex;
- protected int mNamePrimaryIndex;
- protected int mNameAlternativeIndex;
- protected int mPresenceIndex;
- protected int mStatusIndex;
-
- private int mPhoneNumberIndex;
- private int mPhoneNumberTypeIndex;
- private int mPhoneNumberLabelIndex;
- private int mIsDefaultNumberIndex;
- private int mStarredIndex;
- protected int mPinnedIndex;
- protected int mContactIdIndex;
-
- /** Indicates whether a drag is in process. */
- private boolean mInDragging = false;
-
- // Pinned positions start from 1, so there are a total of 20 maximum pinned contacts
- public static final int PIN_LIMIT = 21;
-
- /**
- * The soft limit on how many contact tiles to show.
- * NOTE This soft limit would not restrict the number of starred contacts to show, rather
- * 1. If the count of starred contacts is less than this limit, show 20 tiles total.
- * 2. If the count of starred contacts is more than or equal to this limit,
- * show all starred tiles and no frequents.
- */
- private static final int TILES_SOFT_LIMIT = 20;
-
- final Comparator<ContactEntry> mContactEntryComparator = new Comparator<ContactEntry>() {
- @Override
- public int compare(ContactEntry lhs, ContactEntry rhs) {
- return ComparisonChain.start()
- .compare(lhs.pinned, rhs.pinned)
- .compare(getPreferredSortName(lhs), getPreferredSortName(rhs))
- .result();
- }
-
- private String getPreferredSortName(ContactEntry contactEntry) {
- if (mContactsPreferences.getSortOrder() == ContactsPreferences.SORT_ORDER_PRIMARY
- || TextUtils.isEmpty(contactEntry.nameAlternative)) {
- return contactEntry.namePrimary;
- }
- return contactEntry.nameAlternative;
- }
- };
-
- public interface OnDataSetChangedForAnimationListener {
- public void onDataSetChangedForAnimation(long... idsInPlace);
- public void cacheOffsetsForDatasetChange();
- };
-
- public PhoneFavoritesTileAdapter(Context context, ContactTileView.Listener listener,
- OnDataSetChangedForAnimationListener dataSetChangedListener) {
- mDataSetChangedListener = dataSetChangedListener;
- mListener = listener;
- mContext = context;
- mResources = context.getResources();
- mContactsPreferences = new ContactsPreferences(mContext);
- mNumFrequents = 0;
- mContactEntries = new ArrayList<ContactEntry>();
-
-
- bindColumnIndices();
- }
-
- public void setPhotoLoader(ContactPhotoManager photoLoader) {
- mPhotoManager = photoLoader;
- }
-
- /**
- * Indicates whether a drag is in process.
- *
- * @param inDragging Boolean variable indicating whether there is a drag in process.
- */
- public void setInDragging(boolean inDragging) {
- mDelayCursorUpdates = inDragging;
- mInDragging = inDragging;
- }
-
- /** Gets whether the drag is in process. */
- public boolean getInDragging() {
- return mInDragging;
- }
-
- /**
- * Sets the column indices for expected {@link Cursor}
- * based on {@link DisplayType}.
- */
- protected void bindColumnIndices() {
- mIdIndex = ContactTileLoaderFactory.CONTACT_ID;
- mNamePrimaryIndex = ContactTileLoaderFactory.DISPLAY_NAME;
- mNameAlternativeIndex = ContactTileLoaderFactory.DISPLAY_NAME_ALTERNATIVE;
- mStarredIndex = ContactTileLoaderFactory.STARRED;
- mPhotoUriIndex = ContactTileLoaderFactory.PHOTO_URI;
- mLookupIndex = ContactTileLoaderFactory.LOOKUP_KEY;
- mPhoneNumberIndex = ContactTileLoaderFactory.PHONE_NUMBER;
- mPhoneNumberTypeIndex = ContactTileLoaderFactory.PHONE_NUMBER_TYPE;
- mPhoneNumberLabelIndex = ContactTileLoaderFactory.PHONE_NUMBER_LABEL;
- mPinnedIndex = ContactTileLoaderFactory.PINNED;
- mContactIdIndex = ContactTileLoaderFactory.CONTACT_ID_FOR_DATA;
-
-
- mPresenceIndex = ContactTileLoaderFactory.CONTACT_PRESENCE;
- mStatusIndex = ContactTileLoaderFactory.CONTACT_STATUS;
- mIsDefaultNumberIndex = ContactTileLoaderFactory.IS_DEFAULT_NUMBER;
- }
-
- public void refreshContactsPreferences() {
- mContactsPreferences.refreshValue(ContactsPreferences.DISPLAY_ORDER_KEY);
- mContactsPreferences.refreshValue(ContactsPreferences.SORT_ORDER_KEY);
- }
-
- /**
- * Gets the number of frequents from the passed in cursor.
- *
- * This methods is needed so the GroupMemberTileAdapter can override this.
- *
- * @param cursor The cursor to get number of frequents from.
- */
- protected void saveNumFrequentsFromCursor(Cursor cursor) {
- mNumFrequents = cursor.getCount() - mNumStarred;
- }
-
- /**
- * Creates {@link ContactTileView}s for each item in {@link Cursor}.
- *
- * Else use {@link ContactTileLoaderFactory}
- */
- public void setContactCursor(Cursor cursor) {
- if (!mDelayCursorUpdates && cursor != null && !cursor.isClosed()) {
- mNumStarred = getNumStarredContacts(cursor);
- if (mAwaitingRemove) {
- mDataSetChangedListener.cacheOffsetsForDatasetChange();
- }
-
- saveNumFrequentsFromCursor(cursor);
- saveCursorToCache(cursor);
- // cause a refresh of any views that rely on this data
- notifyDataSetChanged();
- // about to start redraw
- mDataSetChangedListener.onDataSetChangedForAnimation();
- }
- }
-
- /**
- * Saves the cursor data to the cache, to speed up UI changes.
- *
- * @param cursor Returned cursor with data to populate the view.
- */
- private void saveCursorToCache(Cursor cursor) {
- mContactEntries.clear();
-
- cursor.moveToPosition(-1);
-
- final LongSparseArray<Object> duplicates = new LongSparseArray<Object>(cursor.getCount());
-
- // Track the length of {@link #mContactEntries} and compare to {@link #TILES_SOFT_LIMIT}.
- int counter = 0;
-
- while (cursor.moveToNext()) {
-
- final int starred = cursor.getInt(mStarredIndex);
- final long id;
-
- // We display a maximum of TILES_SOFT_LIMIT contacts, or the total number of starred
- // whichever is greater.
- if (starred < 1 && counter >= TILES_SOFT_LIMIT) {
- break;
- } else {
- id = cursor.getLong(mContactIdIndex);
- }
-
- final ContactEntry existing = (ContactEntry) duplicates.get(id);
- if (existing != null) {
- // Check if the existing number is a default number. If not, clear the phone number
- // and label fields so that the disambiguation dialog will show up.
- if (!existing.isDefaultNumber) {
- existing.phoneLabel = null;
- existing.phoneNumber = null;
- }
- continue;
- }
-
- final String photoUri = cursor.getString(mPhotoUriIndex);
- final String lookupKey = cursor.getString(mLookupIndex);
- final int pinned = cursor.getInt(mPinnedIndex);
- final String name = cursor.getString(mNamePrimaryIndex);
- final String nameAlternative = cursor.getString(mNameAlternativeIndex);
- final boolean isStarred = cursor.getInt(mStarredIndex) > 0;
- final boolean isDefaultNumber = cursor.getInt(mIsDefaultNumberIndex) > 0;
-
- final ContactEntry contact = new ContactEntry();
-
- contact.id = id;
- contact.namePrimary = (!TextUtils.isEmpty(name)) ? name :
- mResources.getString(R.string.missing_name);
- contact.nameAlternative = (!TextUtils.isEmpty(nameAlternative)) ? nameAlternative :
- mResources.getString(R.string.missing_name);
- contact.nameDisplayOrder = mContactsPreferences.getDisplayOrder();
- contact.photoUri = (photoUri != null ? Uri.parse(photoUri) : null);
- contact.lookupKey = lookupKey;
- contact.lookupUri = ContentUris.withAppendedId(
- Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), id);
- contact.isFavorite = isStarred;
- contact.isDefaultNumber = isDefaultNumber;
-
- // Set phone number and label
- final int phoneNumberType = cursor.getInt(mPhoneNumberTypeIndex);
- final String phoneNumberCustomLabel = cursor.getString(mPhoneNumberLabelIndex);
- contact.phoneLabel = (String) Phone.getTypeLabel(mResources, phoneNumberType,
- phoneNumberCustomLabel);
- contact.phoneNumber = cursor.getString(mPhoneNumberIndex);
-
- contact.pinned = pinned;
- mContactEntries.add(contact);
-
- duplicates.put(id, contact);
-
- counter++;
- }
-
- mAwaitingRemove = false;
-
- arrangeContactsByPinnedPosition(mContactEntries);
-
- notifyDataSetChanged();
- }
-
- /**
- * Iterates over the {@link Cursor}
- * Returns position of the first NON Starred Contact
- * Returns -1 if {@link DisplayType#STARRED_ONLY}
- * Returns 0 if {@link DisplayType#FREQUENT_ONLY}
- */
- protected int getNumStarredContacts(Cursor cursor) {
- cursor.moveToPosition(-1);
- while (cursor.moveToNext()) {
- if (cursor.getInt(mStarredIndex) == 0) {
- return cursor.getPosition();
- }
- }
-
- // There are not NON Starred contacts in cursor
- // Set divider positon to end
- return cursor.getCount();
- }
-
- /**
- * Returns the number of frequents that will be displayed in the list.
- */
- public int getNumFrequents() {
- return mNumFrequents;
- }
-
- @Override
- public int getCount() {
- if (mContactEntries == null) {
- return 0;
- }
-
- return mContactEntries.size();
- }
-
- /**
- * Returns an ArrayList of the {@link ContactEntry}s that are to appear
- * on the row for the given position.
- */
- @Override
- public ContactEntry getItem(int position) {
- return mContactEntries.get(position);
- }
-
- /**
- * For the top row of tiled contacts, the item id is the position of the row of
- * contacts.
- * For frequent contacts, the item id is the maximum number of rows of tiled contacts +
- * the actual contact id. Since contact ids are always greater than 0, this guarantees that
- * all items within this adapter will always have unique ids.
- */
- @Override
- public long getItemId(int position) {
- return getItem(position).id;
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- @Override
- public boolean areAllItemsEnabled() {
- return true;
- }
-
- @Override
- public boolean isEnabled(int position) {
- return getCount() > 0;
- }
-
- @Override
- public void notifyDataSetChanged() {
- if (DEBUG) {
- Log.v(TAG, "notifyDataSetChanged");
- }
- super.notifyDataSetChanged();
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (DEBUG) {
- Log.v(TAG, "get view for " + String.valueOf(position));
- }
-
- int itemViewType = getItemViewType(position);
-
- PhoneFavoriteTileView tileView = null;
-
- if (convertView instanceof PhoneFavoriteTileView) {
- tileView = (PhoneFavoriteTileView) convertView;
- }
-
- if (tileView == null) {
- tileView = (PhoneFavoriteTileView) View.inflate(mContext,
- R.layout.phone_favorite_tile_view, null);
- }
- tileView.setPhotoManager(mPhotoManager);
- tileView.setListener(mListener);
- tileView.loadFromContact(getItem(position));
- return tileView;
- }
-
- @Override
- public int getViewTypeCount() {
- return ViewTypes.COUNT;
- }
-
- @Override
- public int getItemViewType(int position) {
- return ViewTypes.TILE;
- }
-
- /**
- * Temporarily removes a contact from the list for UI refresh. Stores data for this contact
- * in the back-up variable.
- *
- * @param index Position of the contact to be removed.
- */
- public void popContactEntry(int index) {
- if (isIndexInBound(index)) {
- mDraggedEntry = mContactEntries.get(index);
- mDraggedEntryIndex = index;
- mDragEnteredEntryIndex = index;
- markDropArea(mDragEnteredEntryIndex);
- }
- }
-
- /**
- * @param itemIndex Position of the contact in {@link #mContactEntries}.
- * @return True if the given index is valid for {@link #mContactEntries}.
- */
- public boolean isIndexInBound(int itemIndex) {
- return itemIndex >= 0 && itemIndex < mContactEntries.size();
- }
-
- /**
- * Mark the tile as drop area by given the item index in {@link #mContactEntries}.
- *
- * @param itemIndex Position of the contact in {@link #mContactEntries}.
- */
- private void markDropArea(int itemIndex) {
- if (mDraggedEntry != null && isIndexInBound(mDragEnteredEntryIndex) &&
- isIndexInBound(itemIndex)) {
- mDataSetChangedListener.cacheOffsetsForDatasetChange();
- // Remove the old placeholder item and place the new placeholder item.
- final int oldIndex = mDragEnteredEntryIndex;
- mContactEntries.remove(mDragEnteredEntryIndex);
- mDragEnteredEntryIndex = itemIndex;
- mContactEntries.add(mDragEnteredEntryIndex, ContactEntry.BLANK_ENTRY);
- ContactEntry.BLANK_ENTRY.id = mDraggedEntry.id;
- mDataSetChangedListener.onDataSetChangedForAnimation();
- notifyDataSetChanged();
- }
- }
-
- /**
- * Drops the temporarily removed contact to the desired location in the list.
- */
- public void handleDrop() {
- boolean changed = false;
- if (mDraggedEntry != null) {
- if (isIndexInBound(mDragEnteredEntryIndex) &&
- mDragEnteredEntryIndex != mDraggedEntryIndex) {
- // Don't add the ContactEntry here (to prevent a double animation from occuring).
- // When we receive a new cursor the list of contact entries will automatically be
- // populated with the dragged ContactEntry at the correct spot.
- mDropEntryIndex = mDragEnteredEntryIndex;
- mContactEntries.set(mDropEntryIndex, mDraggedEntry);
- mDataSetChangedListener.cacheOffsetsForDatasetChange();
- changed = true;
- } else if (isIndexInBound(mDraggedEntryIndex)) {
- // If {@link #mDragEnteredEntryIndex} is invalid,
- // falls back to the original position of the contact.
- mContactEntries.remove(mDragEnteredEntryIndex);
- mContactEntries.add(mDraggedEntryIndex, mDraggedEntry);
- mDropEntryIndex = mDraggedEntryIndex;
- notifyDataSetChanged();
- }
-
- if (changed && mDropEntryIndex < PIN_LIMIT) {
- final ArrayList<ContentProviderOperation> operations =
- getReflowedPinningOperations(mContactEntries, mDraggedEntryIndex,
- mDropEntryIndex);
- if (!operations.isEmpty()) {
- // update the database here with the new pinned positions
- try {
- mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY,
- operations);
- } catch (RemoteException | OperationApplicationException e) {
- Log.e(TAG, "Exception thrown when pinning contacts", e);
- }
- }
- }
- mDraggedEntry = null;
- }
- }
-
- /**
- * Invoked when the dragged item is dropped to unsupported location. We will then move the
- * contact back to where it was dragged from.
- */
- public void dropToUnsupportedView() {
- if (isIndexInBound(mDragEnteredEntryIndex)) {
- mContactEntries.remove(mDragEnteredEntryIndex);
- mContactEntries.add(mDraggedEntryIndex, mDraggedEntry);
- notifyDataSetChanged();
- }
- }
-
- /**
- * Clears all temporary variables at a new interaction.
- */
- public void cleanTempVariables() {
- mDraggedEntryIndex = -1;
- mDropEntryIndex = -1;
- mDragEnteredEntryIndex = -1;
- mDraggedEntry = null;
- }
-
- /**
- * Used when a contact is removed from speeddial. This will both unstar and set pinned position
- * of the contact to PinnedPosition.DEMOTED so that it doesn't show up anymore in the favorites
- * list.
- */
- private void unstarAndUnpinContact(Uri contactUri) {
- final ContentValues values = new ContentValues(2);
- values.put(Contacts.STARRED, false);
- values.put(Contacts.PINNED, PinnedPositions.DEMOTED);
- mContext.getContentResolver().update(contactUri, values, null, null);
- }
-
- /**
- * Given a list of contacts that each have pinned positions, rearrange the list (destructive)
- * such that all pinned contacts are in their defined pinned positions, and unpinned contacts
- * take the spaces between those pinned contacts. Demoted contacts should not appear in the
- * resulting list.
- *
- * This method also updates the pinned positions of pinned contacts so that they are all
- * unique positive integers within range from 0 to toArrange.size() - 1. This is because
- * when the contact entries are read from the database, it is possible for them to have
- * overlapping pin positions due to sync or modifications by third party apps.
- */
- @VisibleForTesting
- /* package */ void arrangeContactsByPinnedPosition(ArrayList<ContactEntry> toArrange) {
- final PriorityQueue<ContactEntry> pinnedQueue =
- new PriorityQueue<ContactEntry>(PIN_LIMIT, mContactEntryComparator);
-
- final List<ContactEntry> unpinnedContacts = new LinkedList<ContactEntry>();
-
- final int length = toArrange.size();
- for (int i = 0; i < length; i++) {
- final ContactEntry contact = toArrange.get(i);
- // Decide whether the contact is hidden(demoted), pinned, or unpinned
- if (contact.pinned > PIN_LIMIT || contact.pinned == PinnedPositions.UNPINNED) {
- unpinnedContacts.add(contact);
- } else if (contact.pinned > PinnedPositions.DEMOTED) {
- // Demoted or contacts with negative pinned positions are ignored.
- // Pinned contacts go into a priority queue where they are ranked by pinned
- // position. This is required because the contacts provider does not return
- // contacts ordered by pinned position.
- pinnedQueue.add(contact);
- }
- }
-
- final int maxToPin = Math.min(PIN_LIMIT, pinnedQueue.size() + unpinnedContacts.size());
-
- toArrange.clear();
- for (int i = 1; i < maxToPin + 1; i++) {
- if (!pinnedQueue.isEmpty() && pinnedQueue.peek().pinned <= i) {
- final ContactEntry toPin = pinnedQueue.poll();
- toPin.pinned = i;
- toArrange.add(toPin);
- } else if (!unpinnedContacts.isEmpty()) {
- toArrange.add(unpinnedContacts.remove(0));
- }
- }
-
- // If there are still contacts in pinnedContacts at this point, it means that the pinned
- // positions of these pinned contacts exceed the actual number of contacts in the list.
- // For example, the user had 10 frequents, starred and pinned one of them at the last spot,
- // and then cleared frequents. Contacts in this situation should become unpinned.
- while (!pinnedQueue.isEmpty()) {
- final ContactEntry entry = pinnedQueue.poll();
- entry.pinned = PinnedPositions.UNPINNED;
- toArrange.add(entry);
- }
-
- // Any remaining unpinned contacts that weren't in the gaps between the pinned contacts
- // now just get appended to the end of the list.
- toArrange.addAll(unpinnedContacts);
- }
-
- /**
- * Given an existing list of contact entries and a single entry that is to be pinned at a
- * particular position, return a list of {@link ContentProviderOperation}s that contains new
- * pinned positions for all contacts that are forced to be pinned at new positions, trying as
- * much as possible to keep pinned contacts at their original location.
- *
- * At this point in time the pinned position of each contact in the list has already been
- * updated by {@link #arrangeContactsByPinnedPosition}, so we can assume that all pinned
- * positions(within {@link #PIN_LIMIT} are unique positive integers.
- */
- @VisibleForTesting
- /* package */ ArrayList<ContentProviderOperation> getReflowedPinningOperations(
- ArrayList<ContactEntry> list, int oldPos, int newPinPos) {
- final ArrayList<ContentProviderOperation> positions = Lists.newArrayList();
- final int lowerBound = Math.min(oldPos, newPinPos);
- final int upperBound = Math.max(oldPos, newPinPos);
- for (int i = lowerBound; i <= upperBound; i++) {
- final ContactEntry entry = list.get(i);
-
- // Pinned positions in the database start from 1 instead of being zero-indexed like
- // arrays, so offset by 1.
- final int databasePinnedPosition = i + 1;
- if (entry.pinned == databasePinnedPosition) continue;
-
- final Uri uri = Uri.withAppendedPath(Contacts.CONTENT_URI, String.valueOf(entry.id));
- final ContentValues values = new ContentValues();
- values.put(Contacts.PINNED, databasePinnedPosition);
- positions.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
- }
- return positions;
- }
-
- protected static class ViewTypes {
- public static final int TILE = 0;
- public static final int COUNT = 1;
- }
-
- @Override
- public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView view) {
- setInDragging(true);
- final int itemIndex = mContactEntries.indexOf(view.getContactEntry());
- popContactEntry(itemIndex);
- }
-
- @Override
- public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView view) {
- if (view == null) {
- // The user is hovering over a view that is not a contact tile, no need to do
- // anything here.
- return;
- }
- final int itemIndex = mContactEntries.indexOf(view.getContactEntry());
- if (mInDragging &&
- mDragEnteredEntryIndex != itemIndex &&
- isIndexInBound(itemIndex) &&
- itemIndex < PIN_LIMIT &&
- itemIndex >= 0) {
- markDropArea(itemIndex);
- }
- }
-
- @Override
- public void onDragFinished(int x, int y) {
- setInDragging(false);
- // A contact has been dragged to the RemoveView in order to be unstarred, so simply wait
- // for the new contact cursor which will cause the UI to be refreshed without the unstarred
- // contact.
- if (!mAwaitingRemove) {
- handleDrop();
- }
- }
-
- @Override
- public void onDroppedOnRemove() {
- if (mDraggedEntry != null) {
- unstarAndUnpinContact(mDraggedEntry.lookupUri);
- mAwaitingRemove = true;
- }
- }
-}
diff --git a/src/com/android/dialer/list/RegularSearchFragment.java b/src/com/android/dialer/list/RegularSearchFragment.java
deleted file mode 100644
index df18af044..000000000
--- a/src/com/android/dialer/list/RegularSearchFragment.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2013 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.list;
-
-import static android.Manifest.permission.READ_CONTACTS;
-
-import android.app.Activity;
-import android.content.pm.PackageManager;
-import android.support.v13.app.FragmentCompat;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-
-import com.android.contacts.common.list.ContactEntryListAdapter;
-import com.android.contacts.common.list.PinnedHeaderListView;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.contacts.commonbind.analytics.AnalyticsUtil;
-import com.android.dialerbind.ObjectFactory;
-import com.android.incallui.Call.LogState;
-
-import com.android.dialer.R;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.logging.ScreenEvent;
-import com.android.dialer.service.CachedNumberLookupService;
-import com.android.dialer.widget.EmptyContentView;
-import com.android.dialer.widget.EmptyContentView.OnEmptyViewActionButtonClickedListener;
-
-public class RegularSearchFragment extends SearchFragment
- implements OnEmptyViewActionButtonClickedListener,
- FragmentCompat.OnRequestPermissionsResultCallback {
-
- public static final int PERMISSION_REQUEST_CODE = 1;
-
- private static final int SEARCH_DIRECTORY_RESULT_LIMIT = 5;
-
- private static final CachedNumberLookupService mCachedNumberLookupService =
- ObjectFactory.newCachedNumberLookupService();
-
- public interface CapabilityChecker {
- public boolean isNearbyPlacesSearchEnabled();
- }
-
- protected String mPermissionToRequest;
-
- public RegularSearchFragment() {
- configureDirectorySearch();
- }
-
- public void configureDirectorySearch() {
- setDirectorySearchEnabled(true);
- setDirectoryResultLimit(SEARCH_DIRECTORY_RESULT_LIMIT);
- }
-
- @Override
- protected void onCreateView(LayoutInflater inflater, ViewGroup container) {
- super.onCreateView(inflater, container);
- ((PinnedHeaderListView) getListView()).setScrollToSectionOnHeaderTouch(true);
- }
-
- @Override
- protected ContactEntryListAdapter createListAdapter() {
- RegularSearchListAdapter adapter = new RegularSearchListAdapter(getActivity());
- adapter.setDisplayPhotos(true);
- adapter.setUseCallableUri(usesCallableUri());
- adapter.setListener(this);
- return adapter;
- }
-
- @Override
- protected void cacheContactInfo(int position) {
- if (mCachedNumberLookupService != null) {
- final RegularSearchListAdapter adapter =
- (RegularSearchListAdapter) getAdapter();
- mCachedNumberLookupService.addContact(getContext(),
- adapter.getContactInfo(mCachedNumberLookupService, position));
- }
- }
-
- @Override
- protected void setupEmptyView() {
- if (mEmptyView != null && getActivity() != null) {
- final int imageResource;
- final int actionLabelResource;
- final int descriptionResource;
- final OnEmptyViewActionButtonClickedListener listener;
- if (!PermissionsUtil.hasPermission(getActivity(), READ_CONTACTS)) {
- imageResource = R.drawable.empty_contacts;
- actionLabelResource = R.string.permission_single_turn_on;
- descriptionResource = R.string.permission_no_search;
- listener = this;
- mPermissionToRequest = READ_CONTACTS;
- } else {
- imageResource = EmptyContentView.NO_IMAGE;
- actionLabelResource = EmptyContentView.NO_LABEL;
- descriptionResource = EmptyContentView.NO_LABEL;
- listener = null;
- mPermissionToRequest = null;
- }
-
- mEmptyView.setImage(imageResource);
- mEmptyView.setActionLabel(actionLabelResource);
- mEmptyView.setDescription(descriptionResource);
- if (listener != null) {
- mEmptyView.setActionClickedListener(listener);
- }
- }
- }
-
- @Override
- public void onEmptyViewActionButtonClicked() {
- final Activity activity = getActivity();
- if (activity == null) {
- return;
- }
-
- if (READ_CONTACTS.equals(mPermissionToRequest)) {
- FragmentCompat.requestPermissions(this, new String[] {mPermissionToRequest},
- PERMISSION_REQUEST_CODE);
- }
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, String[] permissions,
- int[] grantResults) {
- if (requestCode == PERMISSION_REQUEST_CODE) {
- setupEmptyView();
- if (grantResults != null && grantResults.length == 1
- && PackageManager.PERMISSION_GRANTED == grantResults[0]) {
- PermissionsUtil.notifyPermissionGranted(getActivity(), mPermissionToRequest);
- }
- }
- }
-
- @Override
- protected int getCallInitiationType(boolean isRemoteDirectory) {
- return isRemoteDirectory ? LogState.INITIATION_REMOTE_DIRECTORY
- : LogState.INITIATION_REGULAR_SEARCH;
- }
-}
diff --git a/src/com/android/dialer/list/RegularSearchListAdapter.java b/src/com/android/dialer/list/RegularSearchListAdapter.java
deleted file mode 100644
index afc621cf5..000000000
--- a/src/com/android/dialer/list/RegularSearchListAdapter.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2013 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.list;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.text.TextUtils;
-
-import com.android.contacts.common.CallUtil;
-import com.android.contacts.common.ContactsUtils;
-import com.android.contacts.common.compat.DirectoryCompat;
-import com.android.contacts.common.list.DirectoryPartition;
-import com.android.contacts.common.util.PhoneNumberHelper;
-import com.android.dialer.calllog.ContactInfo;
-import com.android.dialer.service.CachedNumberLookupService;
-import com.android.dialer.service.CachedNumberLookupService.CachedContactInfo;
-
-/**
- * List adapter to display regular search results.
- */
-public class RegularSearchListAdapter extends DialerPhoneNumberListAdapter {
- protected boolean mIsQuerySipAddress;
-
- public RegularSearchListAdapter(Context context) {
- super(context);
- setShortcutEnabled(SHORTCUT_CREATE_NEW_CONTACT, false);
- setShortcutEnabled(SHORTCUT_ADD_TO_EXISTING_CONTACT, false);
- }
-
- public CachedContactInfo getContactInfo(
- CachedNumberLookupService lookupService, int position) {
- ContactInfo info = new ContactInfo();
- CachedContactInfo cacheInfo = lookupService.buildCachedContactInfo(info);
- final Cursor item = (Cursor) getItem(position);
- if (item != null) {
- final DirectoryPartition partition =
- (DirectoryPartition) getPartition(getPartitionForPosition(position));
- final long directoryId = partition.getDirectoryId();
- final boolean isExtendedDirectory = isExtendedDirectory(directoryId);
-
- info.name = item.getString(PhoneQuery.DISPLAY_NAME);
- info.type = item.getInt(PhoneQuery.PHONE_TYPE);
- info.label = item.getString(PhoneQuery.PHONE_LABEL);
- info.number = item.getString(PhoneQuery.PHONE_NUMBER);
- final String photoUriStr = item.getString(PhoneQuery.PHOTO_URI);
- info.photoUri = photoUriStr == null ? null : Uri.parse(photoUriStr);
- /*
- * An extended directory is custom directory in the app, but not a directory provided by
- * framework. So it can't be USER_TYPE_WORK.
- *
- * When a search result is selected, RegularSearchFragment calls getContactInfo and
- * cache the resulting @{link ContactInfo} into local db. Set usertype to USER_TYPE_WORK
- * only if it's NOT extended directory id and is enterprise directory.
- */
- info.userType = !isExtendedDirectory
- && DirectoryCompat.isEnterpriseDirectoryId(directoryId)
- ? ContactsUtils.USER_TYPE_WORK : ContactsUtils.USER_TYPE_CURRENT;
-
- cacheInfo.setLookupKey(item.getString(PhoneQuery.LOOKUP_KEY));
-
- final String sourceName = partition.getLabel();
- if (isExtendedDirectory) {
- cacheInfo.setExtendedSource(sourceName, directoryId);
- } else {
- cacheInfo.setDirectorySource(sourceName, directoryId);
- }
- }
- return cacheInfo;
- }
-
- @Override
- public String getFormattedQueryString() {
- if (mIsQuerySipAddress) {
- // Return unnormalized SIP address
- return getQueryString();
- }
- return super.getFormattedQueryString();
- }
-
- @Override
- public void setQueryString(String queryString) {
- // Don't show actions if the query string contains a letter.
- final boolean showNumberShortcuts = !TextUtils.isEmpty(getFormattedQueryString())
- && hasDigitsInQueryString();
- mIsQuerySipAddress = PhoneNumberHelper.isUriNumber(queryString);
-
- if (isChanged(showNumberShortcuts)) {
- notifyDataSetChanged();
- }
- super.setQueryString(queryString);
- }
-
- protected boolean isChanged(boolean showNumberShortcuts) {
- boolean changed = false;
- changed |= setShortcutEnabled(SHORTCUT_DIRECT_CALL,
- showNumberShortcuts || mIsQuerySipAddress);
- changed |= setShortcutEnabled(SHORTCUT_SEND_SMS_MESSAGE, showNumberShortcuts);
- changed |= setShortcutEnabled(SHORTCUT_MAKE_VIDEO_CALL,
- showNumberShortcuts && CallUtil.isVideoEnabled(getContext()));
- return changed;
- }
-
- /**
- * Whether there is at least one digit in the query string.
- */
- private boolean hasDigitsInQueryString() {
- String queryString = getQueryString();
- int length = queryString.length();
- for (int i = 0; i < length; i++) {
- if (Character.isDigit(queryString.charAt(i))) {
- return true;
- }
- }
- return false;
- }
-}
diff --git a/src/com/android/dialer/list/RemoveView.java b/src/com/android/dialer/list/RemoveView.java
deleted file mode 100644
index 41f41752e..000000000
--- a/src/com/android/dialer/list/RemoveView.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package com.android.dialer.list;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.DragEvent;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.dialer.R;
-
-public class RemoveView extends FrameLayout {
-
- DragDropController mDragDropController;
- TextView mRemoveText;
- ImageView mRemoveIcon;
- int mUnhighlightedColor;
- int mHighlightedColor;
- Drawable mRemoveDrawable;
-
- public RemoveView(Context context) {
- super(context);
- }
-
- public RemoveView(Context context, AttributeSet attrs) {
- this(context, attrs, -1);
- }
-
- public RemoveView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- protected void onFinishInflate() {
- mRemoveText = (TextView) findViewById(R.id.remove_view_text);
- mRemoveIcon = (ImageView) findViewById(R.id.remove_view_icon);
- final Resources r = getResources();
- mUnhighlightedColor = r.getColor(R.color.remove_text_color);
- mHighlightedColor = r.getColor(R.color.remove_highlighted_text_color);
- mRemoveDrawable = r.getDrawable(R.drawable.ic_remove);
- }
-
- public void setDragDropController(DragDropController controller) {
- mDragDropController = controller;
- }
-
- @Override
- public boolean onDragEvent(DragEvent event) {
- final int action = event.getAction();
- switch (action) {
- case DragEvent.ACTION_DRAG_ENTERED:
- // TODO: This is temporary solution and should be removed once accessibility for
- // drag and drop is supported by framework(b/26871588).
- sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT);
- setAppearanceHighlighted();
- break;
- case DragEvent.ACTION_DRAG_EXITED:
- setAppearanceNormal();
- break;
- case DragEvent.ACTION_DRAG_LOCATION:
- if (mDragDropController != null) {
- mDragDropController.handleDragHovered(this, (int) event.getX(),
- (int) event.getY());
- }
- break;
- case DragEvent.ACTION_DROP:
- sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT);
- if (mDragDropController != null) {
- mDragDropController.handleDragFinished((int) event.getX(), (int) event.getY(),
- true);
- }
- setAppearanceNormal();
- break;
- }
- return true;
- }
-
- private void setAppearanceNormal() {
- mRemoveText.setTextColor(mUnhighlightedColor);
- mRemoveIcon.setColorFilter(mUnhighlightedColor);
- invalidate();
- }
-
- private void setAppearanceHighlighted() {
- mRemoveText.setTextColor(mHighlightedColor);
- mRemoveIcon.setColorFilter(mHighlightedColor);
- invalidate();
- }
-}
diff --git a/src/com/android/dialer/list/SearchFragment.java b/src/com/android/dialer/list/SearchFragment.java
deleted file mode 100644
index 82395b6f8..000000000
--- a/src/com/android/dialer/list/SearchFragment.java
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
- * Copyright (C) 2013 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.list;
-
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.animation.AnimatorListenerAdapter;
-import android.app.Activity;
-import android.app.DialogFragment;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.Interpolator;
-import android.widget.AbsListView;
-import android.widget.AbsListView.OnScrollListener;
-import android.widget.LinearLayout;
-import android.widget.ListView;
-import android.widget.Space;
-
-import com.android.contacts.common.list.ContactEntryListAdapter;
-import com.android.contacts.common.list.ContactListItemView;
-import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
-import com.android.contacts.common.list.PhoneNumberPickerFragment;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.contacts.common.util.ViewUtil;
-import com.android.dialer.R;
-import com.android.dialer.dialpad.DialpadFragment.ErrorDialogFragment;
-import com.android.dialer.util.DialerUtils;
-import com.android.dialer.util.IntentUtil;
-import com.android.dialer.widget.EmptyContentView;
-import com.android.phone.common.animation.AnimUtils;
-
-public class SearchFragment extends PhoneNumberPickerFragment {
- private static final String TAG = SearchFragment.class.getSimpleName();
-
- private OnListFragmentScrolledListener mActivityScrollListener;
- private View.OnTouchListener mActivityOnTouchListener;
-
- /*
- * Stores the untouched user-entered string that is used to populate the add to contacts
- * intent.
- */
- private String mAddToContactNumber;
- private int mActionBarHeight;
- private int mShadowHeight;
- private int mPaddingTop;
- private int mShowDialpadDuration;
- private int mHideDialpadDuration;
-
- /**
- * Used to resize the list view containing search results so that it fits the available space
- * above the dialpad. Does not have a user-visible effect in regular touch usage (since the
- * dialpad hides that portion of the ListView anyway), but improves usability in accessibility
- * mode.
- */
- private Space mSpacer;
-
- private HostInterface mActivity;
-
- protected EmptyContentView mEmptyView;
-
- public interface HostInterface {
- public boolean isActionBarShowing();
- public boolean isDialpadShown();
- public int getDialpadHeight();
- public int getActionBarHideOffset();
- public int getActionBarHeight();
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
-
- setQuickContactEnabled(true);
- setAdjustSelectionBoundsEnabled(false);
- setDarkTheme(false);
- setPhotoPosition(ContactListItemView.getDefaultPhotoPosition(false /* opposite */));
- setUseCallableUri(true);
-
- try {
- mActivityScrollListener = (OnListFragmentScrolledListener) activity;
- } catch (ClassCastException e) {
- Log.d(TAG, activity.toString() + " doesn't implement OnListFragmentScrolledListener. " +
- "Ignoring.");
- }
- }
-
- @Override
- public void onStart() {
- super.onStart();
- if (isSearchMode()) {
- getAdapter().setHasHeader(0, false);
- }
-
- mActivity = (HostInterface) getActivity();
-
- final Resources res = getResources();
- mActionBarHeight = mActivity.getActionBarHeight();
- mShadowHeight = res.getDrawable(R.drawable.search_shadow).getIntrinsicHeight();
- mPaddingTop = res.getDimensionPixelSize(R.dimen.search_list_padding_top);
- mShowDialpadDuration = res.getInteger(R.integer.dialpad_slide_in_duration);
- mHideDialpadDuration = res.getInteger(R.integer.dialpad_slide_out_duration);
-
- final View parentView = getView();
-
- final ListView listView = getListView();
-
- if (mEmptyView == null) {
- mEmptyView = new EmptyContentView(getActivity());
- ((ViewGroup) getListView().getParent()).addView(mEmptyView);
- getListView().setEmptyView(mEmptyView);
- setupEmptyView();
- }
-
- listView.setBackgroundColor(res.getColor(R.color.background_dialer_results));
- listView.setClipToPadding(false);
- setVisibleScrollbarEnabled(false);
-
- //Turn of accessibility live region as the list constantly update itself and spam messages.
- listView.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_NONE);
- ContentChangedFilter.addToParent(listView);
-
- listView.setOnScrollListener(new OnScrollListener() {
- @Override
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- if (mActivityScrollListener != null) {
- mActivityScrollListener.onListFragmentScrollStateChange(scrollState);
- }
- }
-
- @Override
- public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
- int totalItemCount) {
- }
- });
- if (mActivityOnTouchListener != null) {
- listView.setOnTouchListener(mActivityOnTouchListener);
- }
-
- updatePosition(false /* animate */);
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- ViewUtil.addBottomPaddingToListViewForFab(getListView(), getResources());
- }
-
- @Override
- public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
- Animator animator = null;
- if (nextAnim != 0) {
- animator = AnimatorInflater.loadAnimator(getActivity(), nextAnim);
- }
- if (animator != null) {
- final View view = getView();
- final int oldLayerType = view.getLayerType();
- view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- view.setLayerType(oldLayerType, null);
- }
- });
- }
- return animator;
- }
-
- @Override
- protected void setSearchMode(boolean flag) {
- super.setSearchMode(flag);
- // This hides the "All contacts with phone numbers" header in the search fragment
- final ContactEntryListAdapter adapter = getAdapter();
- if (adapter != null) {
- adapter.setHasHeader(0, false);
- }
- }
-
- public void setAddToContactNumber(String addToContactNumber) {
- mAddToContactNumber = addToContactNumber;
- }
-
- /**
- * Return true if phone number is prohibited by a value -
- * (R.string.config_prohibited_phone_number_regexp) in the config files. False otherwise.
- */
- public boolean checkForProhibitedPhoneNumber(String number) {
- // Regular expression prohibiting manual phone call. Can be empty i.e. "no rule".
- String prohibitedPhoneNumberRegexp = getResources().getString(
- R.string.config_prohibited_phone_number_regexp);
-
- // "persist.radio.otaspdial" is a temporary hack needed for one carrier's automated
- // test equipment.
- if (number != null
- && !TextUtils.isEmpty(prohibitedPhoneNumberRegexp)
- && number.matches(prohibitedPhoneNumberRegexp)) {
- Log.d(TAG, "The phone number is prohibited explicitly by a rule.");
- if (getActivity() != null) {
- DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
- R.string.dialog_phone_call_prohibited_message);
- dialogFragment.show(getFragmentManager(), "phone_prohibited_dialog");
- }
-
- return true;
- }
- return false;
- }
-
- @Override
- protected ContactEntryListAdapter createListAdapter() {
- DialerPhoneNumberListAdapter adapter = new DialerPhoneNumberListAdapter(getActivity());
- adapter.setDisplayPhotos(true);
- adapter.setUseCallableUri(super.usesCallableUri());
- adapter.setListener(this);
- return adapter;
- }
-
- @Override
- protected void onItemClick(int position, long id) {
- final DialerPhoneNumberListAdapter adapter = (DialerPhoneNumberListAdapter) getAdapter();
- final int shortcutType = adapter.getShortcutTypeFromPosition(position);
- final OnPhoneNumberPickerActionListener listener;
- final Intent intent;
- final String number;
-
- Log.i(TAG, "onItemClick: shortcutType=" + shortcutType);
-
- switch (shortcutType) {
- case DialerPhoneNumberListAdapter.SHORTCUT_INVALID:
- super.onItemClick(position, id);
- break;
- case DialerPhoneNumberListAdapter.SHORTCUT_DIRECT_CALL:
- number = adapter.getQueryString();
- listener = getOnPhoneNumberPickerListener();
- if (listener != null && !checkForProhibitedPhoneNumber(number)) {
- listener.onPickPhoneNumber(number, false /* isVideoCall */,
- getCallInitiationType(false /* isRemoteDirectory */));
- }
- break;
- case DialerPhoneNumberListAdapter.SHORTCUT_CREATE_NEW_CONTACT:
- number = TextUtils.isEmpty(mAddToContactNumber) ?
- adapter.getFormattedQueryString() : mAddToContactNumber;
- intent = IntentUtil.getNewContactIntent(number);
- DialerUtils.startActivityWithErrorToast(getActivity(), intent);
- break;
- case DialerPhoneNumberListAdapter.SHORTCUT_ADD_TO_EXISTING_CONTACT:
- number = TextUtils.isEmpty(mAddToContactNumber) ?
- adapter.getFormattedQueryString() : mAddToContactNumber;
- intent = IntentUtil.getAddToExistingContactIntent(number);
- DialerUtils.startActivityWithErrorToast(getActivity(), intent,
- R.string.add_contact_not_available);
- break;
- case DialerPhoneNumberListAdapter.SHORTCUT_SEND_SMS_MESSAGE:
- number = adapter.getFormattedQueryString();
- intent = IntentUtil.getSendSmsIntent(number);
- DialerUtils.startActivityWithErrorToast(getActivity(), intent);
- break;
- case DialerPhoneNumberListAdapter.SHORTCUT_MAKE_VIDEO_CALL:
- number = TextUtils.isEmpty(mAddToContactNumber) ?
- adapter.getQueryString() : mAddToContactNumber;
- listener = getOnPhoneNumberPickerListener();
- if (listener != null && !checkForProhibitedPhoneNumber(number)) {
- listener.onPickPhoneNumber(number, true /* isVideoCall */,
- getCallInitiationType(false /* isRemoteDirectory */));
- }
- break;
- }
- }
-
- /**
- * Updates the position and padding of the search fragment, depending on whether the dialpad is
- * shown. This can be optionally animated.
- * @param animate
- */
- public void updatePosition(boolean animate) {
- // Use negative shadow height instead of 0 to account for the 9-patch's shadow.
- int startTranslationValue =
- mActivity.isDialpadShown() ? mActionBarHeight - mShadowHeight : -mShadowHeight;
- int endTranslationValue = 0;
- // Prevents ListView from being translated down after a rotation when the ActionBar is up.
- if (animate || mActivity.isActionBarShowing()) {
- endTranslationValue =
- mActivity.isDialpadShown() ? 0 : mActionBarHeight - mShadowHeight;
- }
- if (animate) {
- // If the dialpad will be shown, then this animation involves sliding the list up.
- final boolean slideUp = mActivity.isDialpadShown();
-
- Interpolator interpolator = slideUp ? AnimUtils.EASE_IN : AnimUtils.EASE_OUT ;
- int duration = slideUp ? mShowDialpadDuration : mHideDialpadDuration;
- getView().setTranslationY(startTranslationValue);
- getView().animate()
- .translationY(endTranslationValue)
- .setInterpolator(interpolator)
- .setDuration(duration)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- if (!slideUp) {
- resizeListView();
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (slideUp) {
- resizeListView();
- }
- }
- });
-
- } else {
- getView().setTranslationY(endTranslationValue);
- resizeListView();
- }
-
- // There is padding which should only be applied when the dialpad is not shown.
- int paddingTop = mActivity.isDialpadShown() ? 0 : mPaddingTop;
- final ListView listView = getListView();
- listView.setPaddingRelative(
- listView.getPaddingStart(),
- paddingTop,
- listView.getPaddingEnd(),
- listView.getPaddingBottom());
- }
-
- public void resizeListView() {
- if (mSpacer == null) {
- return;
- }
- int spacerHeight = mActivity.isDialpadShown() ? mActivity.getDialpadHeight() : 0;
- if (spacerHeight != mSpacer.getHeight()) {
- final LinearLayout.LayoutParams lp =
- (LinearLayout.LayoutParams) mSpacer.getLayoutParams();
- lp.height = spacerHeight;
- mSpacer.setLayoutParams(lp);
- }
- }
-
- @Override
- protected void startLoading() {
- if (getActivity() == null) {
- return;
- }
-
- if (PermissionsUtil.hasContactsPermissions(getActivity())) {
- super.startLoading();
- } else if (TextUtils.isEmpty(getQueryString())) {
- // Clear out any existing call shortcuts.
- final DialerPhoneNumberListAdapter adapter =
- (DialerPhoneNumberListAdapter) getAdapter();
- adapter.disableAllShortcuts();
- } else {
- // The contact list is not going to change (we have no results since permissions are
- // denied), but the shortcuts might because of the different query, so update the
- // list.
- getAdapter().notifyDataSetChanged();
- }
-
- setupEmptyView();
- }
-
- public void setOnTouchListener(View.OnTouchListener onTouchListener) {
- mActivityOnTouchListener = onTouchListener;
- }
-
- @Override
- protected View inflateView(LayoutInflater inflater, ViewGroup container) {
- final LinearLayout parent = (LinearLayout) super.inflateView(inflater, container);
- final int orientation = getResources().getConfiguration().orientation;
- if (orientation == Configuration.ORIENTATION_PORTRAIT) {
- mSpacer = new Space(getActivity());
- parent.addView(mSpacer,
- new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0));
- }
- return parent;
- }
-
- protected void setupEmptyView() {}
-}
diff --git a/src/com/android/dialer/list/SmartDialNumberListAdapter.java b/src/com/android/dialer/list/SmartDialNumberListAdapter.java
deleted file mode 100644
index fe27a25ab..000000000
--- a/src/com/android/dialer/list/SmartDialNumberListAdapter.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2013 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.list;
-
-import android.content.ContentUris;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.ContactsContract;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.common.CallUtil;
-import com.android.contacts.common.list.ContactListItemView;
-import com.android.dialer.dialpad.SmartDialCursorLoader;
-import com.android.dialer.dialpad.SmartDialNameMatcher;
-import com.android.dialer.dialpad.SmartDialPrefix;
-import com.android.dialer.dialpad.SmartDialMatchPosition;
-
-import java.util.ArrayList;
-
-/**
- * List adapter to display the SmartDial search results.
- */
-public class SmartDialNumberListAdapter extends DialerPhoneNumberListAdapter {
-
- private static final String TAG = SmartDialNumberListAdapter.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- private SmartDialNameMatcher mNameMatcher;
-
- public SmartDialNumberListAdapter(Context context) {
- super(context);
- mNameMatcher = new SmartDialNameMatcher("", SmartDialPrefix.getMap());
- setShortcutEnabled(SmartDialNumberListAdapter.SHORTCUT_DIRECT_CALL, false);
-
- if (DEBUG) {
- Log.v(TAG, "Constructing List Adapter");
- }
- }
-
- /**
- * Sets query for the SmartDialCursorLoader.
- */
- public void configureLoader(SmartDialCursorLoader loader) {
- if (DEBUG) {
- Log.v(TAG, "Configure Loader with query" + getQueryString());
- }
-
- if (getQueryString() == null) {
- loader.configureQuery("");
- mNameMatcher.setQuery("");
- } else {
- loader.configureQuery(getQueryString());
- mNameMatcher.setQuery(PhoneNumberUtils.normalizeNumber(getQueryString()));
- }
- }
-
- /**
- * Sets highlight options for a List item in the SmartDial search results.
- * @param view ContactListItemView where the result will be displayed.
- * @param cursor Object containing information of the associated List item.
- */
- @Override
- protected void setHighlight(ContactListItemView view, Cursor cursor) {
- view.clearHighlightSequences();
-
- if (mNameMatcher.matches(cursor.getString(PhoneQuery.DISPLAY_NAME))) {
- final ArrayList<SmartDialMatchPosition> nameMatches = mNameMatcher.getMatchPositions();
- for (SmartDialMatchPosition match:nameMatches) {
- view.addNameHighlightSequence(match.start, match.end);
- if (DEBUG) {
- Log.v(TAG, cursor.getString(PhoneQuery.DISPLAY_NAME) + " " +
- mNameMatcher.getQuery() + " " + String.valueOf(match.start));
- }
- }
- }
-
- final SmartDialMatchPosition numberMatch = mNameMatcher.matchesNumber(cursor.getString(
- PhoneQuery.PHONE_NUMBER));
- if (numberMatch != null) {
- view.addNumberHighlightSequence(numberMatch.start, numberMatch.end);
- }
- }
-
- /**
- * Gets Uri for the list item at the given position.
- * @param position Location of the data of interest.
- * @return Data Uri of the entry.
- */
- public Uri getDataUri(int position) {
- Cursor cursor = ((Cursor)getItem(position));
- if (cursor != null) {
- long id = cursor.getLong(PhoneQuery.PHONE_ID);
- return ContentUris.withAppendedId(ContactsContract.Data.CONTENT_URI, id);
- } else {
- Log.w(TAG, "Cursor was null in getDataUri() call. Returning null instead.");
- return null;
- }
- }
-
- @Override
- public void setQueryString(String queryString) {
- final boolean showNumberShortcuts = !TextUtils.isEmpty(getFormattedQueryString());
- boolean changed = false;
- changed |= setShortcutEnabled(SHORTCUT_CREATE_NEW_CONTACT, showNumberShortcuts);
- changed |= setShortcutEnabled(SHORTCUT_ADD_TO_EXISTING_CONTACT, showNumberShortcuts);
- changed |= setShortcutEnabled(SHORTCUT_SEND_SMS_MESSAGE, showNumberShortcuts);
- changed |= setShortcutEnabled(SHORTCUT_MAKE_VIDEO_CALL,
- showNumberShortcuts && CallUtil.isVideoEnabled(getContext()));
- if (changed) {
- notifyDataSetChanged();
- }
- super.setQueryString(queryString);
- }
-}
diff --git a/src/com/android/dialer/list/SmartDialSearchFragment.java b/src/com/android/dialer/list/SmartDialSearchFragment.java
deleted file mode 100644
index fcb61ffe0..000000000
--- a/src/com/android/dialer/list/SmartDialSearchFragment.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2013 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.list;
-
-import static android.Manifest.permission.CALL_PHONE;
-
-import android.app.Activity;
-import android.content.Loader;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.v13.app.FragmentCompat;
-import android.util.Log;
-import android.view.View;
-
-import com.android.contacts.common.list.ContactEntryListAdapter;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.dialer.dialpad.SmartDialCursorLoader;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.logging.ScreenEvent;
-import com.android.dialer.R;
-import com.android.dialer.widget.EmptyContentView;
-import com.android.incallui.Call.LogState;
-
-import java.util.ArrayList;
-
-/**
- * Implements a fragment to load and display SmartDial search results.
- */
-public class SmartDialSearchFragment extends SearchFragment
- implements EmptyContentView.OnEmptyViewActionButtonClickedListener,
- FragmentCompat.OnRequestPermissionsResultCallback {
- private static final String TAG = SmartDialSearchFragment.class.getSimpleName();
-
- private static final int CALL_PHONE_PERMISSION_REQUEST_CODE = 1;
-
- /**
- * Creates a SmartDialListAdapter to display and operate on search results.
- */
- @Override
- protected ContactEntryListAdapter createListAdapter() {
- SmartDialNumberListAdapter adapter = new SmartDialNumberListAdapter(getActivity());
- adapter.setUseCallableUri(super.usesCallableUri());
- adapter.setQuickContactEnabled(true);
- // Set adapter's query string to restore previous instance state.
- adapter.setQueryString(getQueryString());
- adapter.setListener(this);
- return adapter;
- }
-
- /**
- * Creates a SmartDialCursorLoader object to load query results.
- */
- @Override
- public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- // Smart dialing does not support Directory Load, falls back to normal search instead.
- if (id == getDirectoryLoaderId()) {
- return super.onCreateLoader(id, args);
- } else {
- final SmartDialNumberListAdapter adapter = (SmartDialNumberListAdapter) getAdapter();
- SmartDialCursorLoader loader = new SmartDialCursorLoader(super.getContext());
- adapter.configureLoader(loader);
- return loader;
- }
- }
-
- /**
- * Gets the Phone Uri of an entry for calling.
- * @param position Location of the data of interest.
- * @return Phone Uri to establish a phone call.
- */
- @Override
- protected Uri getPhoneUri(int position) {
- final SmartDialNumberListAdapter adapter = (SmartDialNumberListAdapter) getAdapter();
- return adapter.getDataUri(position);
- }
-
- @Override
- protected void setupEmptyView() {
- if (mEmptyView != null && getActivity() != null) {
- if (!PermissionsUtil.hasPermission(getActivity(), CALL_PHONE)) {
- mEmptyView.setImage(R.drawable.empty_contacts);
- mEmptyView.setActionLabel(R.string.permission_single_turn_on);
- mEmptyView.setDescription(R.string.permission_place_call);
- mEmptyView.setActionClickedListener(this);
- } else {
- mEmptyView.setImage(EmptyContentView.NO_IMAGE);
- mEmptyView.setActionLabel(EmptyContentView.NO_LABEL);
- mEmptyView.setDescription(EmptyContentView.NO_LABEL);
- }
- }
- }
-
- @Override
- public void onEmptyViewActionButtonClicked() {
- final Activity activity = getActivity();
- if (activity == null) {
- return;
- }
-
- FragmentCompat.requestPermissions(this, new String[] {CALL_PHONE},
- CALL_PHONE_PERMISSION_REQUEST_CODE);
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, String[] permissions,
- int[] grantResults) {
- if (requestCode == CALL_PHONE_PERMISSION_REQUEST_CODE) {
- setupEmptyView();
- }
- }
-
- @Override
- protected int getCallInitiationType(boolean isRemoteDirectory) {
- return LogState.INITIATION_SMART_DIAL;
- }
-
- public boolean isShowingPermissionRequest() {
- return mEmptyView != null && mEmptyView.isShowingContent();
- }
-}
diff --git a/src/com/android/dialer/list/SpeedDialFragment.java b/src/com/android/dialer/list/SpeedDialFragment.java
deleted file mode 100644
index 7e10297d0..000000000
--- a/src/com/android/dialer/list/SpeedDialFragment.java
+++ /dev/null
@@ -1,504 +0,0 @@
-/*
- * Copyright (C) 2013 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.list;
-
-import static android.Manifest.permission.READ_CONTACTS;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.LoaderManager;
-import android.content.CursorLoader;
-import android.content.Loader;
-import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Trace;
-import android.support.v13.app.FragmentCompat;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.animation.AnimationUtils;
-import android.view.animation.LayoutAnimationController;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.FrameLayout;
-import android.widget.FrameLayout.LayoutParams;
-import android.widget.ImageView;
-import android.widget.ListView;
-
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.ContactTileLoaderFactory;
-import com.android.contacts.common.list.ContactTileView;
-import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.dialer.R;
-import com.android.dialer.widget.EmptyContentView;
-import com.android.incallui.Call.LogState;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-
-/**
- * This fragment displays the user's favorite/frequent contacts in a grid.
- */
-public class SpeedDialFragment extends Fragment implements OnItemClickListener,
- PhoneFavoritesTileAdapter.OnDataSetChangedForAnimationListener,
- EmptyContentView.OnEmptyViewActionButtonClickedListener,
- FragmentCompat.OnRequestPermissionsResultCallback {
-
- private static final int READ_CONTACTS_PERMISSION_REQUEST_CODE = 1;
-
- /**
- * By default, the animation code assumes that all items in a list view are of the same height
- * when animating new list items into view (e.g. from the bottom of the screen into view).
- * This can cause incorrect translation offsets when a item that is larger or smaller than
- * other list item is removed from the list. This key is used to provide the actual height
- * of the removed object so that the actual translation appears correct to the user.
- */
- private static final long KEY_REMOVED_ITEM_HEIGHT = Long.MAX_VALUE;
-
- private static final String TAG = "SpeedDialFragment";
- private static final boolean DEBUG = false;
-
- private int mAnimationDuration;
-
- /**
- * Used with LoaderManager.
- */
- private static int LOADER_ID_CONTACT_TILE = 1;
-
- public interface HostInterface {
- public void setDragDropController(DragDropController controller);
- public void showAllContactsTab();
- }
-
- private class ContactTileLoaderListener implements LoaderManager.LoaderCallbacks<Cursor> {
- @Override
- public CursorLoader onCreateLoader(int id, Bundle args) {
- if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onCreateLoader.");
- return ContactTileLoaderFactory.createStrequentPhoneOnlyLoader(getActivity());
- }
-
- @Override
- public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
- if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onLoadFinished");
- mContactTileAdapter.setContactCursor(data);
- setEmptyViewVisibility(mContactTileAdapter.getCount() == 0);
- }
-
- @Override
- public void onLoaderReset(Loader<Cursor> loader) {
- if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onLoaderReset. ");
- }
- }
-
- private class ContactTileAdapterListener implements ContactTileView.Listener {
- @Override
- public void onContactSelected(Uri contactUri, Rect targetRect) {
- if (mPhoneNumberPickerActionListener != null) {
- mPhoneNumberPickerActionListener.onPickDataUri(contactUri,
- false /* isVideoCall */, LogState.INITIATION_SPEED_DIAL);
- }
- }
-
- @Override
- public void onCallNumberDirectly(String phoneNumber) {
- if (mPhoneNumberPickerActionListener != null) {
- mPhoneNumberPickerActionListener.onPickPhoneNumber(phoneNumber,
- false /* isVideoCall */, LogState.INITIATION_SPEED_DIAL);
- }
- }
-
- @Override
- public int getApproximateTileWidth() {
- return getView().getWidth();
- }
- }
-
- private class ScrollListener implements ListView.OnScrollListener {
- @Override
- public void onScroll(AbsListView view,
- int firstVisibleItem, int visibleItemCount, int totalItemCount) {
- if (mActivityScrollListener != null) {
- mActivityScrollListener.onListFragmentScroll(firstVisibleItem, visibleItemCount,
- totalItemCount);
- }
- }
-
- @Override
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- mActivityScrollListener.onListFragmentScrollStateChange(scrollState);
- }
- }
-
- private OnPhoneNumberPickerActionListener mPhoneNumberPickerActionListener;
-
- private OnListFragmentScrolledListener mActivityScrollListener;
- private PhoneFavoritesTileAdapter mContactTileAdapter;
-
- private View mParentView;
-
- private PhoneFavoriteListView mListView;
-
- private View mContactTileFrame;
-
- private final HashMap<Long, Integer> mItemIdTopMap = new HashMap<Long, Integer>();
- private final HashMap<Long, Integer> mItemIdLeftMap = new HashMap<Long, Integer>();
-
- /**
- * Layout used when there are no favorites.
- */
- private EmptyContentView mEmptyView;
-
- private final ContactTileView.Listener mContactTileAdapterListener =
- new ContactTileAdapterListener();
- private final LoaderManager.LoaderCallbacks<Cursor> mContactTileLoaderListener =
- new ContactTileLoaderListener();
- private final ScrollListener mScrollListener = new ScrollListener();
-
- @Override
- public void onAttach(Activity activity) {
- if (DEBUG) Log.d(TAG, "onAttach()");
- super.onAttach(activity);
-
- // Construct two base adapters which will become part of PhoneFavoriteMergedAdapter.
- // We don't construct the resultant adapter at this moment since it requires LayoutInflater
- // that will be available on onCreateView().
- mContactTileAdapter = new PhoneFavoritesTileAdapter(activity, mContactTileAdapterListener,
- this);
- mContactTileAdapter.setPhotoLoader(ContactPhotoManager.getInstance(activity));
- }
-
- @Override
- public void onCreate(Bundle savedState) {
- if (DEBUG) Log.d(TAG, "onCreate()");
- Trace.beginSection(TAG + " onCreate");
- super.onCreate(savedState);
-
- mAnimationDuration = getResources().getInteger(R.integer.fade_duration);
- Trace.endSection();
- }
-
- @Override
- public void onResume() {
- Trace.beginSection(TAG + " onResume");
- super.onResume();
- if (mContactTileAdapter != null) {
- mContactTileAdapter.refreshContactsPreferences();
- }
- if (PermissionsUtil.hasContactsPermissions(getActivity())) {
- if (getLoaderManager().getLoader(LOADER_ID_CONTACT_TILE) == null) {
- getLoaderManager().initLoader(LOADER_ID_CONTACT_TILE, null,
- mContactTileLoaderListener);
-
- } else {
- getLoaderManager().getLoader(LOADER_ID_CONTACT_TILE).forceLoad();
- }
-
- mEmptyView.setDescription(R.string.speed_dial_empty);
- mEmptyView.setActionLabel(R.string.speed_dial_empty_add_favorite_action);
- } else {
- mEmptyView.setDescription(R.string.permission_no_speeddial);
- mEmptyView.setActionLabel(R.string.permission_single_turn_on);
- }
- Trace.endSection();
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- Trace.beginSection(TAG + " onCreateView");
- mParentView = inflater.inflate(R.layout.speed_dial_fragment, container, false);
-
- mListView = (PhoneFavoriteListView) mParentView.findViewById(R.id.contact_tile_list);
- mListView.setOnItemClickListener(this);
- mListView.setVerticalScrollBarEnabled(false);
- mListView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
- mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
- mListView.getDragDropController().addOnDragDropListener(mContactTileAdapter);
-
- final ImageView dragShadowOverlay =
- (ImageView) getActivity().findViewById(R.id.contact_tile_drag_shadow_overlay);
- mListView.setDragShadowOverlay(dragShadowOverlay);
-
- mEmptyView = (EmptyContentView) mParentView.findViewById(R.id.empty_list_view);
- mEmptyView.setImage(R.drawable.empty_speed_dial);
- mEmptyView.setActionClickedListener(this);
-
- mContactTileFrame = mParentView.findViewById(R.id.contact_tile_frame);
-
- final LayoutAnimationController controller = new LayoutAnimationController(
- AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_in));
- controller.setDelay(0);
- mListView.setLayoutAnimation(controller);
- mListView.setAdapter(mContactTileAdapter);
-
- mListView.setOnScrollListener(mScrollListener);
- mListView.setFastScrollEnabled(false);
- mListView.setFastScrollAlwaysVisible(false);
-
- //prevent content changes of the list from firing accessibility events.
- mListView.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_NONE);
- ContentChangedFilter.addToParent(mListView);
-
- Trace.endSection();
- return mParentView;
- }
-
- public boolean hasFrequents() {
- if (mContactTileAdapter == null) return false;
- return mContactTileAdapter.getNumFrequents() > 0;
- }
-
- /* package */ void setEmptyViewVisibility(final boolean visible) {
- final int previousVisibility = mEmptyView.getVisibility();
- final int emptyViewVisibility = visible ? View.VISIBLE : View.GONE;
- final int listViewVisibility = visible ? View.GONE : View.VISIBLE;
-
- if (previousVisibility != emptyViewVisibility) {
- final FrameLayout.LayoutParams params = (LayoutParams) mContactTileFrame
- .getLayoutParams();
- params.height = visible ? LayoutParams.WRAP_CONTENT : LayoutParams.MATCH_PARENT;
- mContactTileFrame.setLayoutParams(params);
- mEmptyView.setVisibility(emptyViewVisibility);
- mListView.setVisibility(listViewVisibility);
- }
- }
-
- @Override
- public void onStart() {
- super.onStart();
-
- final Activity activity = getActivity();
-
- try {
- mActivityScrollListener = (OnListFragmentScrolledListener) activity;
- } catch (ClassCastException e) {
- throw new ClassCastException(activity.toString()
- + " must implement OnListFragmentScrolledListener");
- }
-
- try {
- OnDragDropListener listener = (OnDragDropListener) activity;
- mListView.getDragDropController().addOnDragDropListener(listener);
- ((HostInterface) activity).setDragDropController(mListView.getDragDropController());
- } catch (ClassCastException e) {
- throw new ClassCastException(activity.toString()
- + " must implement OnDragDropListener and HostInterface");
- }
-
- try {
- mPhoneNumberPickerActionListener = (OnPhoneNumberPickerActionListener) activity;
- } catch (ClassCastException e) {
- throw new ClassCastException(activity.toString()
- + " must implement PhoneFavoritesFragment.listener");
- }
-
- // Use initLoader() instead of restartLoader() to refraining unnecessary reload.
- // This method call implicitly assures ContactTileLoaderListener's onLoadFinished() will
- // be called, on which we'll check if "all" contacts should be reloaded again or not.
- if (PermissionsUtil.hasContactsPermissions(activity)) {
- getLoaderManager().initLoader(LOADER_ID_CONTACT_TILE, null, mContactTileLoaderListener);
- } else {
- setEmptyViewVisibility(true);
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * This is only effective for elements provided by {@link #mContactTileAdapter}.
- * {@link #mContactTileAdapter} has its own logic for click events.
- */
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- final int contactTileAdapterCount = mContactTileAdapter.getCount();
- if (position <= contactTileAdapterCount) {
- Log.e(TAG, "onItemClick() event for unexpected position. "
- + "The position " + position + " is before \"all\" section. Ignored.");
- }
- }
-
- /**
- * Cache the current view offsets into memory. Once a relayout of views in the ListView
- * has happened due to a dataset change, the cached offsets are used to create animations
- * that slide views from their previous positions to their new ones, to give the appearance
- * that the views are sliding into their new positions.
- */
- private void saveOffsets(int removedItemHeight) {
- final int firstVisiblePosition = mListView.getFirstVisiblePosition();
- if (DEBUG) {
- Log.d(TAG, "Child count : " + mListView.getChildCount());
- }
- for (int i = 0; i < mListView.getChildCount(); i++) {
- final View child = mListView.getChildAt(i);
- final int position = firstVisiblePosition + i;
- // Since we are getting the position from mListView and then querying
- // mContactTileAdapter, its very possible that things are out of sync
- // and we might index out of bounds. Let's make sure that this doesn't happen.
- if (!mContactTileAdapter.isIndexInBound(position)) {
- continue;
- }
- final long itemId = mContactTileAdapter.getItemId(position);
- if (DEBUG) {
- Log.d(TAG, "Saving itemId: " + itemId + " for listview child " + i + " Top: "
- + child.getTop());
- }
- mItemIdTopMap.put(itemId, child.getTop());
- mItemIdLeftMap.put(itemId, child.getLeft());
- }
- mItemIdTopMap.put(KEY_REMOVED_ITEM_HEIGHT, removedItemHeight);
- }
-
- /*
- * Performs animations for the gridView
- */
- private void animateGridView(final long... idsInPlace) {
- if (mItemIdTopMap.isEmpty()) {
- // Don't do animations if the database is being queried for the first time and
- // the previous item offsets have not been cached, or the user hasn't done anything
- // (dragging, swiping etc) that requires an animation.
- return;
- }
-
- final ViewTreeObserver observer = mListView.getViewTreeObserver();
- observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- @SuppressWarnings("unchecked")
- @Override
- public boolean onPreDraw() {
- observer.removeOnPreDrawListener(this);
- final int firstVisiblePosition = mListView.getFirstVisiblePosition();
- final AnimatorSet animSet = new AnimatorSet();
- final ArrayList<Animator> animators = new ArrayList<Animator>();
- for (int i = 0; i < mListView.getChildCount(); i++) {
- final View child = mListView.getChildAt(i);
- int position = firstVisiblePosition + i;
-
- // Since we are getting the position from mListView and then querying
- // mContactTileAdapter, its very possible that things are out of sync
- // and we might index out of bounds. Let's make sure that this doesn't happen.
- if (!mContactTileAdapter.isIndexInBound(position)) {
- continue;
- }
-
- final long itemId = mContactTileAdapter.getItemId(position);
-
- if (containsId(idsInPlace, itemId)) {
- animators.add(ObjectAnimator.ofFloat(
- child, "alpha", 0.0f, 1.0f));
- break;
- } else {
- Integer startTop = mItemIdTopMap.get(itemId);
- Integer startLeft = mItemIdLeftMap.get(itemId);
- final int top = child.getTop();
- final int left = child.getLeft();
- int deltaX = 0;
- int deltaY = 0;
-
- if (startLeft != null) {
- if (startLeft != left) {
- deltaX = startLeft - left;
- animators.add(ObjectAnimator.ofFloat(
- child, "translationX", deltaX, 0.0f));
- }
- }
-
- if (startTop != null) {
- if (startTop != top) {
- deltaY = startTop - top;
- animators.add(ObjectAnimator.ofFloat(
- child, "translationY", deltaY, 0.0f));
- }
- }
-
- if (DEBUG) {
- Log.d(TAG, "Found itemId: " + itemId + " for listview child " + i +
- " Top: " + top +
- " Delta: " + deltaY);
- }
- }
- }
-
- if (animators.size() > 0) {
- animSet.setDuration(mAnimationDuration).playTogether(animators);
- animSet.start();
- }
-
- mItemIdTopMap.clear();
- mItemIdLeftMap.clear();
- return true;
- }
- });
- }
-
- private boolean containsId(long[] ids, long target) {
- // Linear search on array is fine because this is typically only 0-1 elements long
- for (int i = 0; i < ids.length; i++) {
- if (ids[i] == target) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public void onDataSetChangedForAnimation(long... idsInPlace) {
- animateGridView(idsInPlace);
- }
-
- @Override
- public void cacheOffsetsForDatasetChange() {
- saveOffsets(0);
- }
-
- public AbsListView getListView() {
- return mListView;
- }
-
- @Override
- public void onEmptyViewActionButtonClicked() {
- final Activity activity = getActivity();
- if (activity == null) {
- return;
- }
-
- if (!PermissionsUtil.hasPermission(activity, READ_CONTACTS)) {
- FragmentCompat.requestPermissions(this, new String[] {READ_CONTACTS},
- READ_CONTACTS_PERMISSION_REQUEST_CODE);
- } else {
- // Switch tabs
- ((HostInterface) activity).showAllContactsTab();
- }
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, String[] permissions,
- int[] grantResults) {
- if (requestCode == READ_CONTACTS_PERMISSION_REQUEST_CODE) {
- if (grantResults.length == 1 && PackageManager.PERMISSION_GRANTED == grantResults[0]) {
- PermissionsUtil.notifyPermissionGranted(getActivity(), READ_CONTACTS);
- }
- }
- }
-}
diff --git a/src/com/android/dialer/logging/InteractionEvent.java b/src/com/android/dialer/logging/InteractionEvent.java
deleted file mode 100644
index 88518b47c..000000000
--- a/src/com/android/dialer/logging/InteractionEvent.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.logging;
-
-/**
- * Class holding constants for Dialer interactions
- */
-public class InteractionEvent {
-
- public static final int UNKNOWN = 0;
-
- /**
- * An incoming call was blocked
- */
- public static final int CALL_BLOCKED = 15;
-
- /**
- * The user blocked a number from the Call Log screen
- */
- public static final int BLOCK_NUMBER_CALL_LOG = 16;
-
- /**
- * The user blocked a number from the Call details screen
- */
- public static final int BLOCK_NUMBER_CALL_DETAIL = 17;
-
- /**
- * The user blocked a number from the Management screen
- */
- public static final int BLOCK_NUMBER_MANAGEMENT_SCREEN = 18;
-
- /**
- * The user unblocked a number from the Call Log screen
- */
- public static final int UNBLOCK_NUMBER_CALL_LOG = 19;
-
- /**
- * The user unblocked a number from the Call details screen
- */
- public static final int UNBLOCK_NUMBER_CALL_DETAIL = 20;
-
- /**
- * The user unblocked a number from the Management screen
- */
- public static final int UNBLOCK_NUMBER_MANAGEMENT_SCREEN = 21;
-
- /**
- * The user blocked numbers from contacts marked as send to voicemail
- */
- public static final int IMPORT_SEND_TO_VOICEMAIL = 22;
-
- /**
- * The user blocked a number then undid the block
- */
- public static final int UNDO_BLOCK_NUMBER = 23;
-
- /**
- * The user unblocked a number then undid the unblock
- */
- public static final int UNDO_UNBLOCK_NUMBER = 24;
-
-}
diff --git a/src/com/android/dialer/logging/Logger.java b/src/com/android/dialer/logging/Logger.java
deleted file mode 100644
index 25b7268ad..000000000
--- a/src/com/android/dialer/logging/Logger.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.logging;
-
-import android.app.Activity;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.commonbind.analytics.AnalyticsUtil;
-import com.android.dialerbind.ObjectFactory;
-import com.android.incallui.Call;
-
-/**
- * Single entry point for all logging/analytics-related work for all user interactions.
- */
-public abstract class Logger {
- public static final String TAG = "Logger";
-
- public static Logger getInstance() {
- return ObjectFactory.getLoggerInstance();
- }
-
- /**
- * Logs a call event. PII like the call's number or caller details should never be logged.
- *
- * @param call to log.
- */
- public static void logCall(Call call) {
- final Logger logger = getInstance();
- if (logger != null) {
- logger.logCallImpl(call);
- }
- }
-
- /**
- * Logs an event indicating that a screen was displayed.
- *
- * @param screenType integer identifier of the displayed screen
- * @param activity Parent activity of the displayed screen.
- */
- public static void logScreenView(int screenType, Activity activity) {
- final Logger logger = getInstance();
- if (logger != null) {
- logger.logScreenViewImpl(screenType);
- }
-
- final String screenName = ScreenEvent.getScreenName(screenType);
- if (!TextUtils.isEmpty(screenName)) {
- AnalyticsUtil.sendScreenView(screenName, activity, null);
- } else {
- Log.w(TAG, "Unknown screenType: " + screenType);
- }
- }
-
- /**
- * Logs an interaction that occurred
- *
- * @param interaction an integer representing what interaction occurred.
- * {@see com.android.dialer.logging.InteractionEvent}
- */
- public static void logInteraction(int interaction) {
- final Logger logger = getInstance();
- if (logger != null) {
- logger.logInteractionImpl(interaction);
- }
- }
-
- public abstract void logCallImpl(Call call);
- public abstract void logScreenViewImpl(int screenType);
- public abstract void logInteractionImpl(int dialerInteraction);
-}
diff --git a/src/com/android/dialer/logging/ScreenEvent.java b/src/com/android/dialer/logging/ScreenEvent.java
deleted file mode 100644
index e0d7b0026..000000000
--- a/src/com/android/dialer/logging/ScreenEvent.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * 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.logging;
-
-import android.text.TextUtils;
-
-import com.android.contacts.common.dialog.ClearFrequentsDialog;
-import com.android.contacts.common.interactions.ImportExportDialogFragment;
-import com.android.dialer.calllog.CallLogFragment;
-import com.android.dialer.dialpad.DialpadFragment;
-import com.android.dialer.filterednumber.BlockedNumbersFragment;
-import com.android.dialer.list.AllContactsFragment;
-import com.android.dialer.list.BlockedListSearchFragment;
-import com.android.dialer.list.RegularSearchFragment;
-import com.android.dialer.list.SmartDialSearchFragment;
-import com.android.dialer.list.SpeedDialFragment;
-import com.android.dialer.settings.DialerSettingsActivity;
-import com.android.incallui.AnswerFragment;
-import com.android.incallui.CallCardFragment;
-import com.android.incallui.ConferenceManagerFragment;
-import com.android.incallui.InCallActivity;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Stores constants identifying individual screens/dialogs/fragments in the application, and also
- * provides a mapping of integer id -> screen name mappings for analytics purposes.
- */
-public class ScreenEvent {
- private static final Map<Integer, String> sScreenNameMap = new HashMap<>();
-
- public static final String FRAGMENT_TAG_SEPARATOR = "#";
-
- public static final int UNKNOWN = 0;
-
- // The dialpad in the main Dialer activity
- public static final int DIALPAD = 1;
-
- // The speed dial tab in the main Dialer activity
- public static final int SPEED_DIAL = 2;
-
- // The recents tab in the main Dialer activity
- public static final int CALL_LOG = 3;
-
- // The voicemail tab in the main Dialer activity
- public static final int VOICEMAIL_LOG = 4;
-
- // The all contacts tab in the main Dialer activity
- public static final int ALL_CONTACTS = 5;
-
- // List of search results returned by typing into the search box.
- public static final int REGULAR_SEARCH = 6;
-
- // List of search results returned by typing into the dialpad.
- public static final int SMART_DIAL_SEARCH = 7;
-
- // The All and Missed call log tabs in CallLogActivity
- public static final int CALL_LOG_FILTER = 8;
-
- // Dialer settings screen.
- public static final int SETTINGS = 9;
-
- // The "Import/export contacts" dialog launched via the overflow menu.
- public static final int IMPORT_EXPORT_CONTACTS = 10;
-
- // The "Clear frequents" dialog launched via the overflow menu.
- public static final int CLEAR_FREQUENTS = 11;
-
- // The "Send feedback" dialog launched via the overflow menu.
- public static final int SEND_FEEDBACK = 12;
-
- // The main in call screen that displays caller details and contact photos
- public static final int INCALL = 13;
-
- // The screen that displays the glowpad widget (slide right to answer,
- // slide left to dismiss).
- public static final int INCOMING_CALL = 14;
-
- // Conference management fragment displayed for conferences that support
- // management of individual calls within the conference.
- public static final int CONFERENCE_MANAGEMENT = 15;
-
- // The dialpad displayed in-call that is used to send dtmf tones.
- public static final int INCALL_DIALPAD = 16;
-
- // Menu options displayed when long pressing on a call log entry.
- public static final int CALL_LOG_CONTEXT_MENU = 17;
-
- // Screen displayed to allow the user to see an overview of all blocked
- // numbers.
- public static final int BLOCKED_NUMBER_MANAGEMENT = 18;
-
- // Screen displayed to allow the user to add a new blocked number.
- public static final int BLOCKED_NUMBER_ADD_NUMBER = 19;
-
- static {
- sScreenNameMap.put(ScreenEvent.DIALPAD,
- getScreenNameWithTag(DialpadFragment.class.getSimpleName(), "Dialer"));
- sScreenNameMap.put(ScreenEvent.SPEED_DIAL, SpeedDialFragment.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.CALL_LOG,
- getScreenNameWithTag(CallLogFragment.class.getSimpleName(), "History"));
- sScreenNameMap.put(ScreenEvent.VOICEMAIL_LOG,
- getScreenNameWithTag(CallLogFragment.class.getSimpleName(), "Voicemail"));
- sScreenNameMap.put(ScreenEvent.ALL_CONTACTS, AllContactsFragment.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.REGULAR_SEARCH,
- RegularSearchFragment.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.SMART_DIAL_SEARCH,
- SmartDialSearchFragment.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.CALL_LOG_FILTER,
- getScreenNameWithTag(CallLogFragment.class.getSimpleName(), "Filtered"));
- sScreenNameMap.put(ScreenEvent.SETTINGS,
- DialerSettingsActivity.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.IMPORT_EXPORT_CONTACTS,
- ImportExportDialogFragment.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.CLEAR_FREQUENTS,
- ClearFrequentsDialog.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.SEND_FEEDBACK, "SendFeedback");
- sScreenNameMap.put(ScreenEvent.INCALL, InCallActivity.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.INCOMING_CALL, AnswerFragment.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.CONFERENCE_MANAGEMENT,
- ConferenceManagerFragment.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.INCALL_DIALPAD,
- getScreenNameWithTag(DialpadFragment.class.getSimpleName(), "InCall"));
- sScreenNameMap.put(ScreenEvent.CALL_LOG_CONTEXT_MENU, "CallLogContextMenu");
- sScreenNameMap.put(ScreenEvent.BLOCKED_NUMBER_MANAGEMENT,
- BlockedNumbersFragment.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.BLOCKED_NUMBER_ADD_NUMBER,
- BlockedListSearchFragment.class.getSimpleName());
- }
-
- /**
- * For a given screen type, returns the actual screen name that is used for logging/analytics
- * purposes.
- *
- * @param screenType unique ID of a type of screen
- *
- * @return the tagged version of the screen name corresponding to the provided screenType,
- * or {@null} if the provided screenType is unknown.
- */
- public static String getScreenName(int screenType) {
- return sScreenNameMap.get(screenType);
- }
-
- /**
- * Build a tagged version of the provided screenName if the tag is non-empty.
- *
- * @param screenName Name of the screen.
- * @param tag Optional tag describing the screen.
- * @return the unchanged screenName if the tag is {@code null} or empty, the tagged version of
- * the screenName otherwise.
- */
- public static String getScreenNameWithTag(String screenName, String tag) {
- if (TextUtils.isEmpty(tag)) {
- return screenName;
- }
- return screenName + FRAGMENT_TAG_SEPARATOR + tag;
- }
-}
diff --git a/src/com/android/dialer/service/CachedNumberLookupService.java b/src/com/android/dialer/service/CachedNumberLookupService.java
deleted file mode 100644
index 018ada93f..000000000
--- a/src/com/android/dialer/service/CachedNumberLookupService.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.android.dialer.service;
-
-import android.content.Context;
-import android.net.Uri;
-import android.support.annotation.Nullable;
-
-import com.android.dialer.calllog.ContactInfo;
-
-import java.io.InputStream;
-
-public interface CachedNumberLookupService {
-
- public interface CachedContactInfo {
- public static final int SOURCE_TYPE_DIRECTORY = 1;
- public static final int SOURCE_TYPE_EXTENDED = 2;
- public static final int SOURCE_TYPE_PLACES = 3;
- public static final int SOURCE_TYPE_PROFILE = 4;
- public static final int SOURCE_TYPE_CNAP = 5;
-
- public ContactInfo getContactInfo();
-
- public void setSource(int sourceType, String name, long directoryId);
- public void setDirectorySource(String name, long directoryId);
- public void setExtendedSource(String name, long directoryId);
- public void setLookupKey(String lookupKey);
- }
-
- public CachedContactInfo buildCachedContactInfo(ContactInfo info);
-
- /**
- * Perform a lookup using the cached number lookup service to return contact
- * information stored in the cache that corresponds to the given number.
- *
- * @param context Valid context
- * @param number Phone number to lookup the cache for
- * @return A {@link CachedContactInfo} containing the contact information if the phone
- * number is found in the cache, {@link ContactInfo#EMPTY} if the phone number was
- * not found in the cache, and null if there was an error when querying the cache.
- */
- public CachedContactInfo lookupCachedContactFromNumber(Context context, String number);
-
- public void addContact(Context context, CachedContactInfo info);
-
- public boolean isCacheUri(String uri);
-
- public boolean isBusiness(int sourceType);
- public boolean canReportAsInvalid(int sourceType, String objectId);
-
- /**
- * @return return {@link Uri} to the photo or return {@code null} when failing to add photo
- */
- public @Nullable Uri addPhoto(Context context, String number, InputStream in);
-
- /**
- * Remove all cached phone number entries from the cache, regardless of how old they
- * are.
- *
- * @param context Valid context
- */
- public void clearAllCacheEntries(Context context);
-}
diff --git a/src/com/android/dialer/service/ExtendedCallInfoService.java b/src/com/android/dialer/service/ExtendedCallInfoService.java
deleted file mode 100644
index 5481cc90d..000000000
--- a/src/com/android/dialer/service/ExtendedCallInfoService.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2016 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.service;
-
-import android.support.annotation.IntDef;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Interface of service to get extended call information.
- */
-public interface ExtendedCallInfoService {
- /**
- * All the possible locations that a user can report a number as spam or not spam.
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({REPORTING_LOCATION_UNKNOWN, REPORTING_LOCATION_CALL_LOG_HISTORY,
- REPORTING_LOCATION_FEEDBACK_PROMPT})
- @interface ReportingLocation {}
- int REPORTING_LOCATION_UNKNOWN = 0;
- int REPORTING_LOCATION_CALL_LOG_HISTORY = 1;
- int REPORTING_LOCATION_FEEDBACK_PROMPT = 2;
-
- /**
- * Interface for a callback to be invoked when data is fetched.
- */
- interface Listener {
- /**
- * Called when data is fetched.
- * @param isSpam True if the call is spam.
- */
- void onComplete(boolean isSpam);
- }
-
- /**
- * Gets extended call information.
- * @param number The phone number of the call.
- * @param countryIso The country ISO of the call.
- * @param listener The callback to be invoked after {@code Info} is fetched.
- */
- void getExtendedCallInfo(String number, String countryIso, Listener listener);
-
- /**
- * Reports number as spam.
- * @param number The number to be reported.
- * @param countryIso The country ISO of the number.
- * @param callType Whether the type of call is missed, voicemail, etc. Example of this is
- * {@link android.provider.CallLog.Calls#VOICEMAIL_TYPE}.
- * @param from Where in the dialer this was reported from.
- * Must be one of {@link ReportingLocation}.
- */
- void reportSpam(String number, String countryIso, int callType, @ReportingLocation int from);
-
- /**
- * Reports number as not spam.
- * @param number The number to be reported.
- * @param countryIso The country ISO of the number.
- * @param callType Whether the type of call is missed, voicemail, etc. Example of this is
- * {@link android.provider.CallLog.Calls#VOICEMAIL_TYPE}.
- * @param from Where in the dialer this was reported from.
- * Must be one of {@link ReportingLocation}.
- */
- void reportNotSpam(String number, String countryIso, int callType, @ReportingLocation int from);
-}
diff --git a/src/com/android/dialer/settings/AppCompatPreferenceActivity.java b/src/com/android/dialer/settings/AppCompatPreferenceActivity.java
deleted file mode 100644
index 4e5d9c90e..000000000
--- a/src/com/android/dialer/settings/AppCompatPreferenceActivity.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * 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.settings;
-
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.preference.PreferenceActivity;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatDelegate;
-import android.support.v7.widget.Toolbar;
-import android.view.MenuInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls
- * to be used with AppCompat.
- */
-public class AppCompatPreferenceActivity extends PreferenceActivity {
- private AppCompatDelegate mDelegate;
-
- private boolean mIsSafeToCommitTransactions;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- getDelegate().installViewFactory();
- getDelegate().onCreate(savedInstanceState);
- super.onCreate(savedInstanceState);
- mIsSafeToCommitTransactions = true;
- }
-
- @Override
- protected void onPostCreate(Bundle savedInstanceState) {
- super.onPostCreate(savedInstanceState);
- getDelegate().onPostCreate(savedInstanceState);
- }
-
- public ActionBar getSupportActionBar() {
- return getDelegate().getSupportActionBar();
- }
-
- public void setSupportActionBar(Toolbar toolbar) {
- getDelegate().setSupportActionBar(toolbar);
- }
-
- @Override
- public MenuInflater getMenuInflater() {
- return getDelegate().getMenuInflater();
- }
-
- @Override
- public void setContentView(int layoutResID) {
- getDelegate().setContentView(layoutResID);
- }
-
- @Override
- public void setContentView(View view) {
- getDelegate().setContentView(view);
- }
-
- @Override
- public void setContentView(View view, ViewGroup.LayoutParams params) {
- getDelegate().setContentView(view, params);
- }
-
- @Override
- public void addContentView(View view, ViewGroup.LayoutParams params) {
- getDelegate().addContentView(view, params);
- }
-
- @Override
- protected void onPostResume() {
- super.onPostResume();
- getDelegate().onPostResume();
- }
-
- @Override
- protected void onTitleChanged(CharSequence title, int color) {
- super.onTitleChanged(title, color);
- getDelegate().setTitle(title);
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- getDelegate().onConfigurationChanged(newConfig);
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- getDelegate().onStop();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- getDelegate().onDestroy();
- }
-
- @Override
- public void invalidateOptionsMenu() {
- getDelegate().invalidateOptionsMenu();
- }
-
- private AppCompatDelegate getDelegate() {
- if (mDelegate == null) {
- mDelegate = AppCompatDelegate.create(this, null);
- }
- return mDelegate;
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- mIsSafeToCommitTransactions = true;
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mIsSafeToCommitTransactions = true;
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mIsSafeToCommitTransactions = false;
- }
-
- /**
- * Returns true if it is safe to commit {@link FragmentTransaction}s at this time, based on
- * whether {@link Activity#onSaveInstanceState} has been called or not.
- *
- * Make sure that the current activity calls into
- * {@link super.onSaveInstanceState(Bundle outState)} (if that method is overridden),
- * so the flag is properly set.
- */
- public boolean isSafeToCommitTransactions() {
- return mIsSafeToCommitTransactions;
- }
-}
diff --git a/src/com/android/dialer/settings/DefaultRingtonePreference.java b/src/com/android/dialer/settings/DefaultRingtonePreference.java
deleted file mode 100644
index a8a23fddf..000000000
--- a/src/com/android/dialer/settings/DefaultRingtonePreference.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2014 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.settings;
-
-import android.content.Context;
-import android.content.Intent;
-import android.media.RingtoneManager;
-import android.net.Uri;
-import android.preference.RingtonePreference;
-import android.util.AttributeSet;
-import android.widget.Toast;
-
-import com.android.dialer.R;
-import com.android.dialer.compat.SettingsCompat;
-
-/**
- * RingtonePreference which doesn't show default ringtone setting.
- */
-public class DefaultRingtonePreference extends RingtonePreference {
- public DefaultRingtonePreference(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onPrepareRingtonePickerIntent(Intent ringtonePickerIntent) {
- super.onPrepareRingtonePickerIntent(ringtonePickerIntent);
-
- /*
- * Since this preference is for choosing the default ringtone, it
- * doesn't make sense to show a 'Default' item.
- */
- ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false);
- }
-
- @Override
- protected void onSaveRingtone(Uri ringtoneUri) {
- if (!SettingsCompat.System.canWrite(getContext())) {
- Toast.makeText(
- getContext(),
- getContext().getResources().getString(R.string.toast_cannot_write_system_settings),
- Toast.LENGTH_SHORT).show();
- return;
- }
- RingtoneManager.setActualDefaultRingtoneUri(getContext(), getRingtoneType(), ringtoneUri);
- }
-
- @Override
- protected Uri onRestoreRingtone() {
- return RingtoneManager.getActualDefaultRingtoneUri(getContext(), getRingtoneType());
- }
-}
diff --git a/src/com/android/dialer/settings/DialerSettingsActivity.java b/src/com/android/dialer/settings/DialerSettingsActivity.java
deleted file mode 100644
index dc1e21457..000000000
--- a/src/com/android/dialer/settings/DialerSettingsActivity.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2013 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.settings;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.os.UserManager;
-import android.preference.PreferenceManager;
-import android.provider.Settings;
-import android.support.v4.os.BuildCompat;
-import android.telecom.TelecomManager;
-import android.telephony.TelephonyManager;
-import android.view.MenuItem;
-import android.widget.Toast;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.TelephonyManagerCompat;
-import com.android.dialer.R;
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.compat.SettingsCompat;
-import com.android.dialer.compat.UserManagerCompat;
-
-import java.util.List;
-
-public class DialerSettingsActivity extends AppCompatPreferenceActivity {
- protected SharedPreferences mPreferences;
- private boolean migrationStatusOnBuildHeaders;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- /*
- * The headers need to be recreated if the migration status changed between when the headers
- * were created and now.
- */
- if (migrationStatusOnBuildHeaders != FilteredNumberCompat.hasMigratedToNewBlocking()) {
- invalidateHeaders();
- }
- }
-
- @Override
- public void onBuildHeaders(List<Header> target) {
- if (showDisplayOptions()) {
- Header displayOptionsHeader = new Header();
- displayOptionsHeader.titleRes = R.string.display_options_title;
- displayOptionsHeader.fragment = DisplayOptionsSettingsFragment.class.getName();
- target.add(displayOptionsHeader);
- }
-
- Header soundSettingsHeader = new Header();
- soundSettingsHeader.titleRes = R.string.sounds_and_vibration_title;
- soundSettingsHeader.fragment = SoundSettingsFragment.class.getName();
- soundSettingsHeader.id = R.id.settings_header_sounds_and_vibration;
- target.add(soundSettingsHeader);
-
- if (CompatUtils.isMarshmallowCompatible()) {
- Header quickResponseSettingsHeader = new Header();
- Intent quickResponseSettingsIntent =
- new Intent(TelecomManager.ACTION_SHOW_RESPOND_VIA_SMS_SETTINGS);
- quickResponseSettingsHeader.titleRes = R.string.respond_via_sms_setting_title;
- quickResponseSettingsHeader.intent = quickResponseSettingsIntent;
- target.add(quickResponseSettingsHeader);
- }
-
- TelephonyManager telephonyManager =
- (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
-
- // "Call Settings" (full settings) is shown if the current user is primary user and there
- // is only one SIM. Before N, "Calling accounts" setting is shown if the current user is
- // primary user and there are multiple SIMs. In N+, "Calling accounts" is shown whenever
- // "Call Settings" is not shown.
- boolean isPrimaryUser = isPrimaryUser();
- if (isPrimaryUser
- && TelephonyManagerCompat.getPhoneCount(telephonyManager) <= 1) {
- Header callSettingsHeader = new Header();
- Intent callSettingsIntent = new Intent(TelecomManager.ACTION_SHOW_CALL_SETTINGS);
- callSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-
- callSettingsHeader.titleRes = R.string.call_settings_label;
- callSettingsHeader.intent = callSettingsIntent;
- target.add(callSettingsHeader);
- } else if (BuildCompat.isAtLeastN() || isPrimaryUser) {
- Header phoneAccountSettingsHeader = new Header();
- Intent phoneAccountSettingsIntent =
- new Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS);
- phoneAccountSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-
- phoneAccountSettingsHeader.titleRes = R.string.phone_account_settings_label;
- phoneAccountSettingsHeader.intent = phoneAccountSettingsIntent;
- target.add(phoneAccountSettingsHeader);
- }
- if (FilteredNumberCompat.canCurrentUserOpenBlockSettings(this)) {
- Header blockedCallsHeader = new Header();
- blockedCallsHeader.titleRes = R.string.manage_blocked_numbers_label;
- blockedCallsHeader.intent = FilteredNumberCompat.createManageBlockedNumbersIntent(this);
- target.add(blockedCallsHeader);
- migrationStatusOnBuildHeaders = FilteredNumberCompat.hasMigratedToNewBlocking();
- }
- if (isPrimaryUser
- && (TelephonyManagerCompat.isTtyModeSupported(telephonyManager)
- || TelephonyManagerCompat.isHearingAidCompatibilitySupported(telephonyManager))) {
- Header accessibilitySettingsHeader = new Header();
- Intent accessibilitySettingsIntent =
- new Intent(TelecomManager.ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS);
- accessibilitySettingsHeader.titleRes = R.string.accessibility_settings_title;
- accessibilitySettingsHeader.intent = accessibilitySettingsIntent;
- target.add(accessibilitySettingsHeader);
- }
- }
-
- /**
- * Returns {@code true} or {@code false} based on whether the display options setting should be
- * shown. For languages such as Chinese, Japanese, or Korean, display options aren't useful
- * since contacts are sorted and displayed family name first by default.
- *
- * @return {@code true} if the display options should be shown, {@code false} otherwise.
- */
- private boolean showDisplayOptions() {
- return getResources().getBoolean(R.bool.config_display_order_user_changeable)
- && getResources().getBoolean(R.bool.config_sort_order_user_changeable);
- }
-
- @Override
- public void onHeaderClick(Header header, int position) {
- if (header.id == R.id.settings_header_sounds_and_vibration) {
- // If we don't have the permission to write to system settings, go to system sound
- // settings instead. Otherwise, perform the super implementation (which launches our
- // own preference fragment.
- if (!SettingsCompat.System.canWrite(this)) {
- Toast.makeText(
- this,
- getResources().getString(R.string.toast_cannot_write_system_settings),
- Toast.LENGTH_SHORT).show();
- startActivity(new Intent(Settings.ACTION_SOUND_SETTINGS));
- return;
- }
- }
- super.onHeaderClick(header, position);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == android.R.id.home) {
- onBackPressed();
- return true;
- }
- return false;
- }
-
- @Override
- public void onBackPressed() {
- if (!isSafeToCommitTransactions()) {
- return;
- }
- super.onBackPressed();
- }
-
- @Override
- protected boolean isValidFragment(String fragmentName) {
- return true;
- }
-
- /**
- * @return Whether the current user is the primary user.
- */
- private boolean isPrimaryUser() {
- return UserManagerCompat.isSystemUser((UserManager) getSystemService(Context.USER_SERVICE));
- }
-}
diff --git a/src/com/android/dialer/settings/DisplayOptionsSettingsFragment.java b/src/com/android/dialer/settings/DisplayOptionsSettingsFragment.java
deleted file mode 100644
index 4b2c8f6db..000000000
--- a/src/com/android/dialer/settings/DisplayOptionsSettingsFragment.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.settings;
-
-import android.os.Bundle;
-import android.preference.PreferenceFragment;
-
-import com.android.dialer.R;
-
-public class DisplayOptionsSettingsFragment extends PreferenceFragment {
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- addPreferencesFromResource(R.xml.display_options_settings);
- }
-}
diff --git a/src/com/android/dialer/settings/SoundSettingsFragment.java b/src/com/android/dialer/settings/SoundSettingsFragment.java
deleted file mode 100644
index 59f8798c3..000000000
--- a/src/com/android/dialer/settings/SoundSettingsFragment.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (C) 2014 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.settings;
-
-import android.content.Context;
-import android.media.RingtoneManager;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Vibrator;
-import android.preference.CheckBoxPreference;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.PreferenceFragment;
-import android.preference.PreferenceScreen;
-import android.provider.Settings;
-import android.telephony.CarrierConfigManager;
-import android.telephony.TelephonyManager;
-import android.widget.Toast;
-
-import com.android.contacts.common.compat.SdkVersionOverride;
-import com.android.dialer.R;
-import com.android.dialer.compat.SettingsCompat;
-import com.android.phone.common.util.SettingsUtil;
-
-public class SoundSettingsFragment extends PreferenceFragment
- implements Preference.OnPreferenceChangeListener {
-
- private static final int NO_DTMF_TONE = 0;
- private static final int PLAY_DTMF_TONE = 1;
-
- private static final int NO_VIBRATION_FOR_CALLS = 0;
- private static final int DO_VIBRATION_FOR_CALLS = 1;
-
-
- private static final int DTMF_TONE_TYPE_NORMAL = 0;
-
- private static final int SHOW_CARRIER_SETTINGS = 0;
- private static final int HIDE_CARRIER_SETTINGS = 1;
-
- private static final int MSG_UPDATE_RINGTONE_SUMMARY = 1;
-
- private Preference mRingtonePreference;
- private CheckBoxPreference mVibrateWhenRinging;
- private CheckBoxPreference mPlayDtmfTone;
- private ListPreference mDtmfToneLength;
-
- private final Runnable mRingtoneLookupRunnable = new Runnable() {
- @Override
- public void run() {
- updateRingtonePreferenceSummary();
- }
- };
-
- private final Handler mRingtoneLookupComplete = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_UPDATE_RINGTONE_SUMMARY:
- mRingtonePreference.setSummary((CharSequence) msg.obj);
- break;
- }
- }
- };
-
- @Override
- public Context getContext() {
- return getActivity();
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- addPreferencesFromResource(R.xml.sound_settings);
-
- Context context = getActivity();
-
- mRingtonePreference = findPreference(context.getString(R.string.ringtone_preference_key));
- mVibrateWhenRinging = (CheckBoxPreference) findPreference(
- context.getString(R.string.vibrate_on_preference_key));
- mPlayDtmfTone = (CheckBoxPreference) findPreference(
- context.getString(R.string.play_dtmf_preference_key));
- mDtmfToneLength = (ListPreference) findPreference(
- context.getString(R.string.dtmf_tone_length_preference_key));
-
- if (hasVibrator()) {
- mVibrateWhenRinging.setOnPreferenceChangeListener(this);
- } else {
- getPreferenceScreen().removePreference(mVibrateWhenRinging);
- mVibrateWhenRinging = null;
- }
-
- mPlayDtmfTone.setOnPreferenceChangeListener(this);
- mPlayDtmfTone.setChecked(shouldPlayDtmfTone());
-
- TelephonyManager telephonyManager =
- (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
- if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M
- && telephonyManager.canChangeDtmfToneLength()
- && (telephonyManager.isWorldPhone() || !shouldHideCarrierSettings())) {
- mDtmfToneLength.setOnPreferenceChangeListener(this);
- mDtmfToneLength.setValueIndex(
- Settings.System.getInt(context.getContentResolver(),
- Settings.System.DTMF_TONE_TYPE_WHEN_DIALING,
- DTMF_TONE_TYPE_NORMAL));
- } else {
- getPreferenceScreen().removePreference(mDtmfToneLength);
- mDtmfToneLength = null;
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- if (!SettingsCompat.System.canWrite(getContext())) {
- // If the user launches this setting fragment, then toggles the WRITE_SYSTEM_SETTINGS
- // AppOp, then close the fragment since there is nothing useful to do.
- getActivity().onBackPressed();
- return;
- }
-
- if (mVibrateWhenRinging != null) {
- mVibrateWhenRinging.setChecked(shouldVibrateWhenRinging());
- }
-
- // Lookup the ringtone name asynchronously.
- new Thread(mRingtoneLookupRunnable).start();
- }
-
- /**
- * Supports onPreferenceChangeListener to look for preference changes.
- *
- * @param preference The preference to be changed
- * @param objValue The value of the selection, NOT its localized display value.
- */
- @Override
- public boolean onPreferenceChange(Preference preference, Object objValue) {
- if (!SettingsCompat.System.canWrite(getContext())) {
- // A user shouldn't be able to get here, but this protects against monkey crashes.
- Toast.makeText(
- getContext(),
- getResources().getString(R.string.toast_cannot_write_system_settings),
- Toast.LENGTH_SHORT).show();
- return true;
- }
- if (preference == mVibrateWhenRinging) {
- boolean doVibrate = (Boolean) objValue;
- Settings.System.putInt(getActivity().getContentResolver(),
- Settings.System.VIBRATE_WHEN_RINGING,
- doVibrate ? DO_VIBRATION_FOR_CALLS : NO_VIBRATION_FOR_CALLS);
- } else if (preference == mDtmfToneLength) {
- int index = mDtmfToneLength.findIndexOfValue((String) objValue);
- Settings.System.putInt(getActivity().getContentResolver(),
- Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, index);
- }
- return true;
- }
-
- /**
- * Click listener for toggle events.
- */
- @Override
- public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
- if (!SettingsCompat.System.canWrite(getContext())) {
- Toast.makeText(
- getContext(),
- getResources().getString(R.string.toast_cannot_write_system_settings),
- Toast.LENGTH_SHORT).show();
- return true;
- }
- if (preference == mPlayDtmfTone) {
- Settings.System.putInt(getActivity().getContentResolver(),
- Settings.System.DTMF_TONE_WHEN_DIALING,
- mPlayDtmfTone.isChecked() ? PLAY_DTMF_TONE : NO_DTMF_TONE);
- }
- return true;
- }
-
- /**
- * Updates the summary text on the ringtone preference with the name of the ringtone.
- */
- private void updateRingtonePreferenceSummary() {
- SettingsUtil.updateRingtoneName(
- getActivity(),
- mRingtoneLookupComplete,
- RingtoneManager.TYPE_RINGTONE,
- mRingtonePreference.getKey(),
- MSG_UPDATE_RINGTONE_SUMMARY);
- }
-
- /**
- * Obtain the value for "vibrate when ringing" setting. The default value is false.
- *
- * Watch out: if the setting is missing in the device, this will try obtaining the old
- * "vibrate on ring" setting from AudioManager, and save the previous setting to the new one.
- */
- private boolean shouldVibrateWhenRinging() {
- int vibrateWhenRingingSetting = Settings.System.getInt(getActivity().getContentResolver(),
- Settings.System.VIBRATE_WHEN_RINGING,
- NO_VIBRATION_FOR_CALLS);
- return hasVibrator() && (vibrateWhenRingingSetting == DO_VIBRATION_FOR_CALLS);
- }
-
- /**
- * Obtains the value for dialpad/DTMF tones. The default value is true.
- */
- private boolean shouldPlayDtmfTone() {
- int dtmfToneSetting = Settings.System.getInt(getActivity().getContentResolver(),
- Settings.System.DTMF_TONE_WHEN_DIALING,
- PLAY_DTMF_TONE);
- return dtmfToneSetting == PLAY_DTMF_TONE;
- }
-
- /**
- * Whether the device hardware has a vibrator.
- */
- private boolean hasVibrator() {
- Vibrator vibrator = (Vibrator) getActivity().getSystemService(Context.VIBRATOR_SERVICE);
- return vibrator != null && vibrator.hasVibrator();
- }
-
- private boolean shouldHideCarrierSettings() {
- CarrierConfigManager configManager = (CarrierConfigManager) getActivity().getSystemService(
- Context.CARRIER_CONFIG_SERVICE);
- return configManager.getConfig().getBoolean(
- CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL);
- }
-}
diff --git a/src/com/android/dialer/util/AppCompatConstants.java b/src/com/android/dialer/util/AppCompatConstants.java
deleted file mode 100644
index 1d52eee1d..000000000
--- a/src/com/android/dialer/util/AppCompatConstants.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.util;
-
-import android.provider.CallLog.Calls;
-
-public final class AppCompatConstants {
-
- public static final int CALLS_INCOMING_TYPE = Calls.INCOMING_TYPE;
- public static final int CALLS_OUTGOING_TYPE = Calls.OUTGOING_TYPE;
- public static final int CALLS_MISSED_TYPE = Calls.MISSED_TYPE;
- public static final int CALLS_VOICEMAIL_TYPE = Calls.VOICEMAIL_TYPE;
- // Added to android.provider.CallLog.Calls in N+.
- public static final int CALLS_REJECTED_TYPE = 5;
- // Added to android.provider.CallLog.Calls in N+.
- public static final int CALLS_BLOCKED_TYPE = 6;
-}
diff --git a/src/com/android/dialer/util/Assert.java b/src/com/android/dialer/util/Assert.java
deleted file mode 100644
index ec0a6ccb6..000000000
--- a/src/com/android/dialer/util/Assert.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2016 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.util;
-
-import android.os.Looper;
-
-public class Assert {
- public static void assertNotUiThread(String msg) {
- if (Looper.myLooper() == Looper.getMainLooper()) {
- throw new AssertionError(msg);
- }
- }
-
- public static void assertNotNull(Object object, String msg) {
- if (object == null) {
- throw new AssertionError(object);
- }
- }
-
- public static void assertNotNull(Object object) {
- assertNotNull(object, null);
- }
-}
diff --git a/src/com/android/dialer/util/AsyncTaskExecutor.java b/src/com/android/dialer/util/AsyncTaskExecutor.java
deleted file mode 100644
index ca09f0878..000000000
--- a/src/com/android/dialer/util/AsyncTaskExecutor.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2011 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.util;
-
-import android.os.AsyncTask;
-
-import java.util.concurrent.Executor;
-
-/**
- * Interface used to submit {@link AsyncTask} objects to run in the background.
- * <p>
- * This interface has a direct parallel with the {@link Executor} interface. It exists to decouple
- * the mechanics of AsyncTask submission from the description of how that AsyncTask will execute.
- * <p>
- * One immediate benefit of this approach is that testing becomes much easier, since it is easy to
- * introduce a mock or fake AsyncTaskExecutor in unit/integration tests, and thus inspect which
- * tasks have been submitted and control their execution in an orderly manner.
- * <p>
- * Another benefit in due course will be the management of the submitted tasks. An extension to this
- * interface is planned to allow Activities to easily cancel all the submitted tasks that are still
- * pending in the onDestroy() method of the Activity.
- */
-public interface AsyncTaskExecutor {
- /**
- * Executes the given AsyncTask with the default Executor.
- * <p>
- * This method <b>must only be called from the ui thread</b>.
- * <p>
- * The identifier supplied is any Object that can be used to identify the task later. Most
- * commonly this will be an enum which the tests can also refer to. {@code null} is also
- * accepted, though of course this won't help in identifying the task later.
- */
- <T> AsyncTask<T, ?, ?> submit(Object identifier, AsyncTask<T, ?, ?> task, T... params);
-}
diff --git a/src/com/android/dialer/util/AsyncTaskExecutors.java b/src/com/android/dialer/util/AsyncTaskExecutors.java
deleted file mode 100644
index a59af3e41..000000000
--- a/src/com/android/dialer/util/AsyncTaskExecutors.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2011 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.util;
-
-import android.os.AsyncTask;
-import android.os.Looper;
-
-import com.android.contacts.common.testing.NeededForTesting;
-import com.google.common.base.Preconditions;
-
-import java.util.concurrent.Executor;
-
-/**
- * Factory methods for creating AsyncTaskExecutors.
- * <p>
- * All of the factory methods on this class check first to see if you have set a static
- * {@link AsyncTaskExecutorFactory} set through the
- * {@link #setFactoryForTest(AsyncTaskExecutorFactory)} method, and if so delegate to that instead,
- * which is one way of injecting dependencies for testing classes whose construction cannot be
- * controlled such as {@link android.app.Activity}.
- */
-public final class AsyncTaskExecutors {
- /**
- * A single instance of the {@link AsyncTaskExecutorFactory}, to which we delegate if it is
- * non-null, for injecting when testing.
- */
- private static AsyncTaskExecutorFactory mInjectedAsyncTaskExecutorFactory = null;
-
- /**
- * Creates an AsyncTaskExecutor that submits tasks to run with
- * {@link AsyncTask#SERIAL_EXECUTOR}.
- */
- public static AsyncTaskExecutor createAsyncTaskExecutor() {
- synchronized (AsyncTaskExecutors.class) {
- if (mInjectedAsyncTaskExecutorFactory != null) {
- return mInjectedAsyncTaskExecutorFactory.createAsyncTaskExeuctor();
- }
- return new SimpleAsyncTaskExecutor(AsyncTask.SERIAL_EXECUTOR);
- }
- }
-
- /**
- * Creates an AsyncTaskExecutor that submits tasks to run with
- * {@link AsyncTask#THREAD_POOL_EXECUTOR}.
- */
- public static AsyncTaskExecutor createThreadPoolExecutor() {
- synchronized (AsyncTaskExecutors.class) {
- if (mInjectedAsyncTaskExecutorFactory != null) {
- return mInjectedAsyncTaskExecutorFactory.createAsyncTaskExeuctor();
- }
- return new SimpleAsyncTaskExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
- }
-
- /** Interface for creating AsyncTaskExecutor objects. */
- public interface AsyncTaskExecutorFactory {
- AsyncTaskExecutor createAsyncTaskExeuctor();
- }
-
- @NeededForTesting
- public static void setFactoryForTest(AsyncTaskExecutorFactory factory) {
- synchronized (AsyncTaskExecutors.class) {
- mInjectedAsyncTaskExecutorFactory = factory;
- }
- }
-
- public static void checkCalledFromUiThread() {
- Preconditions.checkState(Thread.currentThread() == Looper.getMainLooper().getThread(),
- "submit method must be called from ui thread, was: " + Thread.currentThread());
- }
-
- private static class SimpleAsyncTaskExecutor implements AsyncTaskExecutor {
- private final Executor mExecutor;
-
- public SimpleAsyncTaskExecutor(Executor executor) {
- mExecutor = executor;
- }
-
- @Override
- public <T> AsyncTask<T, ?, ?> submit(Object identifer, AsyncTask<T, ?, ?> task,
- T... params) {
- checkCalledFromUiThread();
- return task.executeOnExecutor(mExecutor, params);
- }
- }
-}
diff --git a/src/com/android/dialer/util/BlockReportSpamDialogs.java b/src/com/android/dialer/util/BlockReportSpamDialogs.java
deleted file mode 100644
index 45b2f4228..000000000
--- a/src/com/android/dialer/util/BlockReportSpamDialogs.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2016 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.util;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.view.View;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-
-import com.android.dialer.R;
-
-/**
- * Helper class for creating block/report dialog fragments.
- */
-public class BlockReportSpamDialogs {
- public static final String BLOCK_REPORT_SPAM_DIALOG_TAG = "BlockReportSpamDialog";
- public static final String BLOCK_DIALOG_TAG = "BlockDialog";
- public static final String UNBLOCK_DIALOG_TAG = "UnblockDialog";
- public static final String NOT_SPAM_DIALOG_TAG = "NotSpamDialog";
-
- /**
- * Listener passed to block/report spam dialog for positive click in
- * {@link BlockReportSpamDialogFragment}.
- */
- public interface OnSpamDialogClickListener {
- /**
- * Called when user clicks on positive button in block/report spam dialog.
- * @param isSpamChecked Whether the spam checkbox is checked.
- */
- void onClick(boolean isSpamChecked);
- }
-
- /**
- * Listener passed to all dialogs except the block/report spam dialog for positive click.
- */
- public interface OnConfirmListener {
- /**
- * Called when user clicks on positive button in the dialog.
- */
- void onClick();
- }
-
- /**
- * Contains the common attributes between all block/unblock/report dialog fragments.
- */
- private static class CommonDialogsFragment extends DialogFragment {
- /**
- * The number to display in the dialog title.
- */
- protected String mDisplayNumber;
-
- /**
- * Called when dialog positive button is pressed.
- */
- protected OnConfirmListener mPositiveListener;
-
- /**
- * Called when dialog is dismissed.
- */
- @Nullable
- protected DialogInterface.OnDismissListener mDismissListener;
-
- @Override
- public void onDismiss(DialogInterface dialog) {
- if (mDismissListener != null) {
- mDismissListener.onDismiss(dialog);
- }
- super.onDismiss(dialog);
- }
-
- @Override
- public void onPause() {
- // The dialog is dismissed onPause, i.e. rotation.
- dismiss();
- mDismissListener = null;
- mPositiveListener = null;
- mDisplayNumber = null;
- super.onPause();
- }
- }
-
- /**
- * Dialog for block/report spam with the mark as spam checkbox.
- */
- public static class BlockReportSpamDialogFragment extends CommonDialogsFragment {
- /**
- * Called when dialog positive button is pressed.
- */
- private OnSpamDialogClickListener mPositiveListener;
-
- /**
- * Whether the mark as spam checkbox is checked before displaying the dialog.
- */
- private boolean mSpamChecked;
-
- public static DialogFragment newInstance(String displayNumber,
- boolean spamChecked,
- OnSpamDialogClickListener positiveListener,
- @Nullable DialogInterface.OnDismissListener
- dismissListener) {
- BlockReportSpamDialogFragment fragment = new BlockReportSpamDialogFragment();
- fragment.mSpamChecked = spamChecked;
- fragment.mDisplayNumber = displayNumber;
- fragment.mPositiveListener = positiveListener;
- fragment.mDismissListener = dismissListener;
- return fragment;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- super.onCreateDialog(savedInstanceState);
- View dialogView = View.inflate(getActivity(), R.layout.block_report_spam_dialog, null);
- final CheckBox isSpamCheckbox =
- (CheckBox) dialogView.findViewById(R.id.report_number_as_spam_action);
- // Listen for changes on the checkbox and update if orientation changes
- isSpamCheckbox.setChecked(mSpamChecked);
- isSpamCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- mSpamChecked = isChecked;
- }
- });
-
- AlertDialog.Builder alertDialogBuilder = createDialogBuilder(getActivity(), this);
- Dialog dialog = alertDialogBuilder
- .setView(dialogView)
- .setTitle(getString(R.string.block_report_number_alert_title, mDisplayNumber))
- .setPositiveButton(R.string.block_number_ok,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dismiss();
- mPositiveListener.onClick(isSpamCheckbox.isChecked());
- }
- }).create();
- dialog.setCanceledOnTouchOutside(true);
- return dialog;
- }
- }
-
- /**
- * Dialog for blocking a number.
- */
- public static class BlockDialogFragment extends CommonDialogsFragment {
- public static DialogFragment newInstance(String displayNumber,
- OnConfirmListener positiveListener,
- @Nullable DialogInterface.OnDismissListener
- dismissListener) {
- BlockDialogFragment fragment = new BlockDialogFragment();
- fragment.mDisplayNumber = displayNumber;
- fragment.mPositiveListener = positiveListener;
- fragment.mDismissListener = dismissListener;
- return fragment;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- super.onCreateDialog(savedInstanceState);
- // Return the newly created dialog
- AlertDialog.Builder alertDialogBuilder = createDialogBuilder(getActivity(), this);
- Dialog dialog = alertDialogBuilder
- .setTitle(getString(R.string.block_report_number_alert_title, mDisplayNumber))
- .setPositiveButton(R.string.block_number_ok,
- createGenericOnClickListener(this, mPositiveListener))
- .create();
- dialog.setCanceledOnTouchOutside(true);
- return dialog;
- }
- }
-
- /**
- * Dialog for unblocking a number.
- */
- public static class UnblockDialogFragment extends CommonDialogsFragment {
- /**
- * Whether or not the number is spam.
- */
- private boolean mIsSpam;
-
- public static DialogFragment newInstance(String displayNumber,
- boolean isSpam,
- OnConfirmListener positiveListener,
- @Nullable DialogInterface.OnDismissListener
- dismissListener) {
- UnblockDialogFragment fragment = new UnblockDialogFragment();
- fragment.mDisplayNumber = displayNumber;
- fragment.mIsSpam = isSpam;
- fragment.mPositiveListener = positiveListener;
- fragment.mDismissListener = dismissListener;
- return fragment;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- super.onCreateDialog(savedInstanceState);
- // Return the newly created dialog
- AlertDialog.Builder alertDialogBuilder = createDialogBuilder(getActivity(), this);
- if (mIsSpam) {
- alertDialogBuilder.setMessage(R.string.unblock_number_alert_details);
- }
- Dialog dialog = alertDialogBuilder
- .setTitle(getString(R.string.unblock_report_number_alert_title, mDisplayNumber))
- .setPositiveButton(R.string.unblock_number_ok,
- createGenericOnClickListener(this, mPositiveListener))
- .create();
- dialog.setCanceledOnTouchOutside(true);
- return dialog;
- }
- }
-
- /**
- * Dialog for reporting a number as not spam.
- */
- public static class ReportNotSpamDialogFragment extends CommonDialogsFragment {
- public static DialogFragment newInstance(String displayNumber,
- OnConfirmListener positiveListener,
- @Nullable DialogInterface.OnDismissListener
- dismissListener) {
- ReportNotSpamDialogFragment fragment = new ReportNotSpamDialogFragment();
- fragment.mDisplayNumber = displayNumber;
- fragment.mPositiveListener = positiveListener;
- fragment.mDismissListener = dismissListener;
- return fragment;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- super.onCreateDialog(savedInstanceState);
- // Return the newly created dialog
- AlertDialog.Builder alertDialogBuilder = createDialogBuilder(getActivity(), this);
- Dialog dialog = alertDialogBuilder
- .setTitle(getString(R.string.report_not_spam_alert_title, mDisplayNumber))
- .setMessage(R.string.report_not_spam_alert_details)
- .setPositiveButton(R.string.report_not_spam_alert_button,
- createGenericOnClickListener(this, mPositiveListener))
- .create();
- dialog.setCanceledOnTouchOutside(true);
- return dialog;
- }
- }
-
- /**
- * Creates a dialog with the default cancel button listener (dismisses dialog).
- */
- private static AlertDialog.Builder createDialogBuilder(Activity activity,
- final DialogFragment fragment) {
- return new AlertDialog.Builder(activity)
- .setCancelable(true)
- .setNegativeButton(android.R.string.cancel,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- fragment.dismiss();
- }
- });
- }
-
- /**
- * Creates a generic click listener which dismisses the fragment and then calls the actual
- * listener.
- */
- private static DialogInterface.OnClickListener createGenericOnClickListener(
- final DialogFragment fragment,
- final OnConfirmListener listener) {
- return new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- fragment.dismiss();
- listener.onClick();
- }
- };
- }
-}
diff --git a/src/com/android/dialer/util/DialerUtils.java b/src/com/android/dialer/util/DialerUtils.java
deleted file mode 100644
index 95d6a81b6..000000000
--- a/src/com/android/dialer/util/DialerUtils.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2014 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.util;
-
-import android.app.Activity;
-import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.Telephony;
-import android.telecom.TelecomManager;
-import android.text.BidiFormatter;
-import android.text.TextDirectionHeuristics;
-import android.text.TextUtils;
-import android.view.View;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.Toast;
-
-import com.android.contacts.common.ContactsUtils;
-import com.android.contacts.common.interactions.TouchPointManager;
-import com.android.dialer.R;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * General purpose utility methods for the Dialer.
- */
-public class DialerUtils {
-
- /**
- * Attempts to start an activity and displays a toast with the default error message if the
- * activity is not found, instead of throwing an exception.
- *
- * @param context to start the activity with.
- * @param intent to start the activity with.
- */
- public static void startActivityWithErrorToast(Context context, Intent intent) {
- startActivityWithErrorToast(context, intent, R.string.activity_not_available);
- }
-
- /**
- * Attempts to start an activity and displays a toast with a provided error message if the
- * activity is not found, instead of throwing an exception.
- *
- * @param context to start the activity with.
- * @param intent to start the activity with.
- * @param msgId Resource ID of the string to display in an error message if the activity is
- * not found.
- */
- public static void startActivityWithErrorToast(Context context, Intent intent, int msgId) {
- try {
- if ((IntentUtil.CALL_ACTION.equals(intent.getAction())
- && context instanceof Activity)) {
- // All dialer-initiated calls should pass the touch point to the InCallUI
- Point touchPoint = TouchPointManager.getInstance().getPoint();
- if (touchPoint.x != 0 || touchPoint.y != 0) {
- Bundle extras;
- // Make sure to not accidentally clobber any existing extras
- if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) {
- extras = intent.getParcelableExtra(
- TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
- } else {
- extras = new Bundle();
- }
- extras.putParcelable(TouchPointManager.TOUCH_POINT, touchPoint);
- intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
- }
-
- final boolean hasCallPermission = TelecomUtil.placeCall((Activity) context, intent);
- if (!hasCallPermission) {
- // TODO: Make calling activity show request permission dialog and handle
- // callback results appropriately.
- Toast.makeText(context, "Cannot place call without Phone permission",
- Toast.LENGTH_SHORT);
- }
- } else {
- context.startActivity(intent);
- }
- } catch (ActivityNotFoundException e) {
- Toast.makeText(context, msgId, Toast.LENGTH_SHORT).show();
- }
- }
-
- /**
- * Returns the component name to use in order to send an SMS using the default SMS application,
- * or null if none exists.
- */
- public static ComponentName getSmsComponent(Context context) {
- String smsPackage = Telephony.Sms.getDefaultSmsPackage(context);
- if (smsPackage != null) {
- final PackageManager packageManager = context.getPackageManager();
- final Intent intent = new Intent(Intent.ACTION_SENDTO,
- Uri.fromParts(ContactsUtils.SCHEME_SMSTO, "", null));
- final List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(intent, 0);
- for (ResolveInfo resolveInfo : resolveInfos) {
- if (smsPackage.equals(resolveInfo.activityInfo.packageName)) {
- return new ComponentName(smsPackage, resolveInfo.activityInfo.name);
- }
- }
- }
- return null;
- }
-
- /**
- * Closes an {@link AutoCloseable}, silently ignoring any checked exceptions. Does nothing if
- * null.
- *
- * @param closeable to close.
- */
- public static void closeQuietly(AutoCloseable closeable) {
- if (closeable != null) {
- try {
- closeable.close();
- } catch (RuntimeException rethrown) {
- throw rethrown;
- } catch (Exception ignored) {
- }
- }
- }
-
- /**
- * Joins a list of {@link CharSequence} into a single {@link CharSequence} seperated by a
- * localized delimiter such as ", ".
- *
- * @param resources Resources used to get list delimiter.
- * @param list List of char sequences to join.
- * @return Joined char sequences.
- */
- public static CharSequence join(Resources resources, Iterable<CharSequence> list) {
- StringBuilder sb = new StringBuilder();
- final BidiFormatter formatter = BidiFormatter.getInstance();
- final CharSequence separator = resources.getString(R.string.list_delimeter);
-
- Iterator<CharSequence> itr = list.iterator();
- boolean firstTime = true;
- while (itr.hasNext()) {
- if (firstTime) {
- firstTime = false;
- } else {
- sb.append(separator);
- }
- // Unicode wrap the elements of the list to respect RTL for individual strings.
- sb.append(formatter.unicodeWrap(
- itr.next().toString(), TextDirectionHeuristics.FIRSTSTRONG_LTR));
- }
-
- // Unicode wrap the joined value, to respect locale's RTL ordering for the whole list.
- return formatter.unicodeWrap(sb.toString());
- }
-
- /**
- * @return True if the application is currently in RTL mode.
- */
- public static boolean isRtl() {
- return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) ==
- View.LAYOUT_DIRECTION_RTL;
- }
-
- public static void showInputMethod(View view) {
- final InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(
- Context.INPUT_METHOD_SERVICE);
- if (imm != null) {
- imm.showSoftInput(view, 0);
- }
- }
-
- public static void hideInputMethod(View view) {
- final InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(
- Context.INPUT_METHOD_SERVICE);
- if (imm != null) {
- imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
- }
- }
-}
diff --git a/src/com/android/dialer/util/EmptyLoader.java b/src/com/android/dialer/util/EmptyLoader.java
deleted file mode 100644
index dd4c0a330..000000000
--- a/src/com/android/dialer/util/EmptyLoader.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2011 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.util;
-
-import android.app.LoaderManager.LoaderCallbacks;
-import android.content.Context;
-import android.content.Loader;
-import android.os.Bundle;
-
-/**
- * A {@link Loader} only used to make use of the {@link android.app.Fragment#setStartDeferred}
- * feature from an old-style fragment which doesn't use {@link Loader}s to load data.
- *
- * This loader never delivers results. A caller fragment must destroy it when deferred fragments
- * should be started.
- */
-public class EmptyLoader extends Loader<Object> {
- public EmptyLoader(Context context) {
- super(context);
- }
-
- /**
- * {@link LoaderCallbacks} which just generates {@link EmptyLoader}. {@link #onLoadFinished}
- * and {@link #onLoaderReset} are no-op.
- */
- public static class Callback implements LoaderCallbacks<Object> {
- private final Context mContext;
-
- public Callback(Context context) {
- mContext = context.getApplicationContext();
- }
-
- @Override
- public Loader<Object> onCreateLoader(int id, Bundle args) {
- return new EmptyLoader(mContext);
- }
-
- @Override
- public void onLoadFinished(Loader<Object> loader, Object data) {
- }
-
- @Override
- public void onLoaderReset(Loader<Object> loader) {
- }
- }
-}
diff --git a/src/com/android/dialer/util/ExpirableCache.java b/src/com/android/dialer/util/ExpirableCache.java
deleted file mode 100644
index 00ebd1607..000000000
--- a/src/com/android/dialer/util/ExpirableCache.java
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * Copyright (C) 2011 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.util;
-
-import android.util.LruCache;
-
-import com.android.contacts.common.testing.NeededForTesting;
-
-import java.util.concurrent.atomic.AtomicInteger;
-
-import javax.annotation.concurrent.Immutable;
-import javax.annotation.concurrent.ThreadSafe;
-
-/**
- * An LRU cache in which all items can be marked as expired at a given time and it is possible to
- * query whether a particular cached value is expired or not.
- * <p>
- * A typical use case for this is caching of values which are expensive to compute but which are
- * still useful when out of date.
- * <p>
- * Consider a cache for contact information:
- * <pre>{@code
- * private ExpirableCache<String, Contact> mContactCache;}</pre>
- * which stores the contact information for a given phone number.
- * <p>
- * When we need to store contact information for a given phone number, we can look up the info in
- * the cache:
- * <pre>{@code
- * CachedValue<Contact> cachedContact = mContactCache.getCachedValue(phoneNumber);
- * }</pre>
- * We might also want to fetch the contact information again if the item is expired.
- * <pre>
- * if (cachedContact.isExpired()) {
- * fetchContactForNumber(phoneNumber,
- * new FetchListener() {
- * &#64;Override
- * public void onFetched(Contact contact) {
- * mContactCache.put(phoneNumber, contact);
- * }
- * });
- * }</pre>
- * and insert it back into the cache when the fetch completes.
- * <p>
- * At a certain point we want to expire the content of the cache because we know the content may
- * no longer be up-to-date, for instance, when resuming the activity this is shown into:
- * <pre>
- * &#64;Override
- * protected onResume() {
- * // We were paused for some time, the cached value might no longer be up to date.
- * mContactCache.expireAll();
- * super.onResume();
- * }
- * </pre>
- * The values will be still available from the cache, but they will be expired.
- * <p>
- * If interested only in the value itself, not whether it is expired or not, one should use the
- * {@link #getPossiblyExpired(Object)} method. If interested only in non-expired values, one should
- * use the {@link #get(Object)} method instead.
- * <p>
- * This class wraps around an {@link LruCache} instance: it follows the {@link LruCache} behavior
- * for evicting items when the cache is full. It is possible to supply your own subclass of LruCache
- * by using the {@link #create(LruCache)} method, which can define a custom expiration policy.
- * Since the underlying cache maps keys to cached values it can determine which items are expired
- * and which are not, allowing for an implementation that evicts expired items before non expired
- * ones.
- * <p>
- * This class is thread-safe.
- *
- * @param <K> the type of the keys
- * @param <V> the type of the values
- */
-@ThreadSafe
-public class ExpirableCache<K, V> {
- /**
- * A cached value stored inside the cache.
- * <p>
- * It provides access to the value stored in the cache but also allows to check whether the
- * value is expired.
- *
- * @param <V> the type of value stored in the cache
- */
- public interface CachedValue<V> {
- /** Returns the value stored in the cache for a given key. */
- public V getValue();
-
- /**
- * Checks whether the value, while still being present in the cache, is expired.
- *
- * @return true if the value is expired
- */
- public boolean isExpired();
- }
-
- /**
- * Cached values storing the generation at which they were added.
- */
- @Immutable
- private static class GenerationalCachedValue<V> implements ExpirableCache.CachedValue<V> {
- /** The value stored in the cache. */
- public final V mValue;
- /** The generation at which the value was added to the cache. */
- private final int mGeneration;
- /** The atomic integer storing the current generation of the cache it belongs to. */
- private final AtomicInteger mCacheGeneration;
-
- /**
- * @param cacheGeneration the atomic integer storing the generation of the cache in which
- * this value will be stored
- */
- public GenerationalCachedValue(V value, AtomicInteger cacheGeneration) {
- mValue = value;
- mCacheGeneration = cacheGeneration;
- // Snapshot the current generation.
- mGeneration = mCacheGeneration.get();
- }
-
- @Override
- public V getValue() {
- return mValue;
- }
-
- @Override
- public boolean isExpired() {
- return mGeneration != mCacheGeneration.get();
- }
- }
-
- /** The underlying cache used to stored the cached values. */
- private LruCache<K, CachedValue<V>> mCache;
-
- /**
- * The current generation of items added to the cache.
- * <p>
- * Items in the cache can belong to a previous generation, but in that case they would be
- * expired.
- *
- * @see ExpirableCache.CachedValue#isExpired()
- */
- private final AtomicInteger mGeneration;
-
- private ExpirableCache(LruCache<K, CachedValue<V>> cache) {
- mCache = cache;
- mGeneration = new AtomicInteger(0);
- }
-
- /**
- * Returns the cached value for the given key, or null if no value exists.
- * <p>
- * The cached value gives access both to the value associated with the key and whether it is
- * expired or not.
- * <p>
- * If not interested in whether the value is expired, use {@link #getPossiblyExpired(Object)}
- * instead.
- * <p>
- * If only wants values that are not expired, use {@link #get(Object)} instead.
- *
- * @param key the key to look up
- */
- public CachedValue<V> getCachedValue(K key) {
- return mCache.get(key);
- }
-
- /**
- * Returns the value for the given key, or null if no value exists.
- * <p>
- * When using this method, it is not possible to determine whether the value is expired or not.
- * Use {@link #getCachedValue(Object)} to achieve that instead. However, if using
- * {@link #getCachedValue(Object)} to determine if an item is expired, one should use the item
- * within the {@link CachedValue} and not call {@link #getPossiblyExpired(Object)} to get the
- * value afterwards, since that is not guaranteed to return the same value or that the newly
- * returned value is in the same state.
- *
- * @param key the key to look up
- */
- public V getPossiblyExpired(K key) {
- CachedValue<V> cachedValue = getCachedValue(key);
- return cachedValue == null ? null : cachedValue.getValue();
- }
-
- /**
- * Returns the value for the given key only if it is not expired, or null if no value exists or
- * is expired.
- * <p>
- * This method will return null if either there is no value associated with this key or if the
- * associated value is expired.
- *
- * @param key the key to look up
- */
- @NeededForTesting
- public V get(K key) {
- CachedValue<V> cachedValue = getCachedValue(key);
- return cachedValue == null || cachedValue.isExpired() ? null : cachedValue.getValue();
- }
-
- /**
- * Puts an item in the cache.
- * <p>
- * Newly added item will not be expired until {@link #expireAll()} is next called.
- *
- * @param key the key to look up
- * @param value the value to associate with the key
- */
- public void put(K key, V value) {
- mCache.put(key, newCachedValue(value));
- }
-
- /**
- * Mark all items currently in the cache as expired.
- * <p>
- * Newly added items after this call will be marked as not expired.
- * <p>
- * Expiring the items in the cache does not imply they will be evicted.
- */
- public void expireAll() {
- mGeneration.incrementAndGet();
- }
-
- /**
- * Creates a new {@link CachedValue} instance to be stored in this cache.
- * <p>
- * Implementation of {@link LruCache#create(K)} can use this method to create a new entry.
- */
- public CachedValue<V> newCachedValue(V value) {
- return new GenerationalCachedValue<V>(value, mGeneration);
- }
-
- /**
- * Creates a new {@link ExpirableCache} that wraps the given {@link LruCache}.
- * <p>
- * The created cache takes ownership of the cache passed in as an argument.
- *
- * @param <K> the type of the keys
- * @param <V> the type of the values
- * @param cache the cache to store the value in
- * @return the newly created expirable cache
- * @throws IllegalArgumentException if the cache is not empty
- */
- public static <K, V> ExpirableCache<K, V> create(LruCache<K, CachedValue<V>> cache) {
- return new ExpirableCache<K, V>(cache);
- }
-
- /**
- * Creates a new {@link ExpirableCache} with the given maximum size.
- *
- * @param <K> the type of the keys
- * @param <V> the type of the values
- * @return the newly created expirable cache
- */
- public static <K, V> ExpirableCache<K, V> create(int maxSize) {
- return create(new LruCache<K, CachedValue<V>>(maxSize));
- }
-}
diff --git a/src/com/android/dialer/util/IntentUtil.java b/src/com/android/dialer/util/IntentUtil.java
deleted file mode 100644
index 581e10da4..000000000
--- a/src/com/android/dialer/util/IntentUtil.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (C) 2012 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.util;
-
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.SystemClock;
-import android.provider.ContactsContract;
-import android.telecom.PhoneAccountHandle;
-import android.telecom.TelecomManager;
-import android.telecom.VideoProfile;
-
-import com.android.contacts.common.CallUtil;
-
-/**
- * Utilities for creation of intents in Dialer, such as {@link Intent#ACTION_CALL}.
- */
-public class IntentUtil {
-
- public static final String CALL_ACTION = Intent.ACTION_CALL;
- private static final String SMS_URI_PREFIX = "sms:";
- private static final int NO_PHONE_TYPE = -1;
-
- public static final String EXTRA_CALL_INITIATION_TYPE
- = "com.android.dialer.EXTRA_CALL_INITIATION_TYPE";
- public static final String EXTRA_CALL_CREATED_TIME_MILLIS =
- "android.telecom.extra.CALL_CREATED_TIME_MILLIS";
-
- public static class CallIntentBuilder {
- private Uri mUri;
- private int mCallInitiationType;
- private PhoneAccountHandle mPhoneAccountHandle;
- private boolean mIsVideoCall = false;
-
- public CallIntentBuilder(Uri uri) {
- mUri = uri;
- }
-
- public CallIntentBuilder(String number) {
- this(CallUtil.getCallUri(number));
- }
-
- public CallIntentBuilder setCallInitiationType(int initiationType) {
- mCallInitiationType = initiationType;
- return this;
- }
-
- public CallIntentBuilder setPhoneAccountHandle(PhoneAccountHandle accountHandle) {
- mPhoneAccountHandle = accountHandle;
- return this;
- }
-
- public CallIntentBuilder setIsVideoCall(boolean isVideoCall) {
- mIsVideoCall = isVideoCall;
- return this;
- }
-
- public Intent build() {
- return getCallIntent(
- mUri,
- mPhoneAccountHandle,
- mIsVideoCall ? VideoProfile.STATE_BIDIRECTIONAL : VideoProfile.STATE_AUDIO_ONLY,
- mCallInitiationType);
- }
- }
-
- /**
- * Create a call intent that can be used to place a call.
- *
- * @param uri Address to place the call to.
- * @param accountHandle {@link PhoneAccountHandle} to place the call with.
- * @param videoState Initial video state of the call.
- * @param callIntiationType The UI affordance the call was initiated by.
- * @return Call intent with provided extras and data.
- */
- public static Intent getCallIntent(
- Uri uri, PhoneAccountHandle accountHandle, int videoState, int callIntiationType) {
- final Intent intent = new Intent(CALL_ACTION, uri);
- intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
-
- final Bundle b = new Bundle();
- b.putLong(EXTRA_CALL_CREATED_TIME_MILLIS, SystemClock.elapsedRealtime());
- b.putInt(EXTRA_CALL_INITIATION_TYPE, callIntiationType);
- intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, b);
-
- if (accountHandle != null) {
- intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
- }
-
- return intent;
- }
-
- public static Intent getSendSmsIntent(CharSequence phoneNumber) {
- return new Intent(Intent.ACTION_SENDTO, Uri.parse(SMS_URI_PREFIX + phoneNumber));
- }
-
- public static Intent getNewContactIntent() {
- return new Intent(Intent.ACTION_INSERT, ContactsContract.Contacts.CONTENT_URI);
- }
-
- public static Intent getNewContactIntent(CharSequence phoneNumber) {
- return getNewContactIntent(
- null /* name */,
- phoneNumber /* phoneNumber */,
- NO_PHONE_TYPE);
- }
-
- public static Intent getNewContactIntent(
- CharSequence name, CharSequence phoneNumber, int phoneNumberType) {
- Intent intent = getNewContactIntent();
- populateContactIntent(intent, name, phoneNumber, phoneNumberType);
- return intent;
- }
-
- public static Intent getAddToExistingContactIntent() {
- Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
- intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
- return intent;
- }
-
- public static Intent getAddToExistingContactIntent(CharSequence phoneNumber) {
- return getAddToExistingContactIntent(
- null /* name */,
- phoneNumber /* phoneNumber */,
- NO_PHONE_TYPE);
- }
-
- public static Intent getAddToExistingContactIntent(
- CharSequence name, CharSequence phoneNumber, int phoneNumberType) {
- Intent intent = getAddToExistingContactIntent();
- populateContactIntent(intent, name, phoneNumber, phoneNumberType);
- return intent;
- }
-
- private static void populateContactIntent(
- Intent intent, CharSequence name, CharSequence phoneNumber, int phoneNumberType) {
- if (phoneNumber != null) {
- intent.putExtra(ContactsContract.Intents.Insert.PHONE, phoneNumber);
- }
- if (name != null) {
- intent.putExtra(ContactsContract.Intents.Insert.NAME, name);
- }
- if (phoneNumberType != NO_PHONE_TYPE) {
- intent.putExtra(ContactsContract.Intents.Insert.PHONE_TYPE, phoneNumberType);
- }
- }
-}
diff --git a/src/com/android/dialer/util/MoreStrings.java b/src/com/android/dialer/util/MoreStrings.java
deleted file mode 100644
index 68956f25c..000000000
--- a/src/com/android/dialer/util/MoreStrings.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2013 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.util;
-
-/**
- * Static utility methods for Strings.
- */
-public class MoreStrings {
-
- public static String toSafeString(String value) {
- if (value == null) {
- return null;
- }
-
- // Do exactly same thing as Uri#toSafeString() does, which will enable us to compare
- // sanitized phone numbers.
- final StringBuilder builder = new StringBuilder();
- for (int i = 0; i < value.length(); i++) {
- final char c = value.charAt(i);
- if (c == '-' || c == '@' || c == '.') {
- builder.append(c);
- } else {
- builder.append('x');
- }
- }
- return builder.toString();
- }
-
-}
diff --git a/src/com/android/dialer/util/OrientationUtil.java b/src/com/android/dialer/util/OrientationUtil.java
deleted file mode 100644
index 2eb2af3ff..000000000
--- a/src/com/android/dialer/util/OrientationUtil.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2012 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.util;
-
-import android.content.Context;
-import android.content.res.Configuration;
-
-/**
- * Static methods related to device orientation.
- */
-public class OrientationUtil {
-
- /**
- * @return if the context is in landscape orientation.
- */
- public static boolean isLandscape(Context context) {
- return context.getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE;
- }
-}
diff --git a/src/com/android/dialer/util/PhoneLookupUtil.java b/src/com/android/dialer/util/PhoneLookupUtil.java
deleted file mode 100644
index 1a7239642..000000000
--- a/src/com/android/dialer/util/PhoneLookupUtil.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2016 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.util;
-
-import android.net.Uri;
-import android.provider.ContactsContract;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.PhoneLookupSdkCompat;
-
-public final class PhoneLookupUtil {
- /**
- * @return the column name that stores contact id for phone lookup query.
- */
- public static String getContactIdColumnNameForUri(Uri phoneLookupUri) {
- if (CompatUtils.isNCompatible()) {
- return PhoneLookupSdkCompat.CONTACT_ID;
- }
- // In pre-N, contact id is stored in {@link PhoneLookup#_ID} in non-sip query.
- boolean isSip = phoneLookupUri.getBooleanQueryParameter(
- ContactsContract.PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, false);
- return (isSip) ? PhoneLookupSdkCompat.CONTACT_ID : ContactsContract.PhoneLookup._ID;
- }
-
- private PhoneLookupUtil() {}
-}
diff --git a/src/com/android/dialer/util/PhoneNumberUtil.java b/src/com/android/dialer/util/PhoneNumberUtil.java
deleted file mode 100644
index 33f987359..000000000
--- a/src/com/android/dialer/util/PhoneNumberUtil.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2013 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.util;
-
-import android.content.Context;
-import android.provider.CallLog;
-import android.telecom.PhoneAccountHandle;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Pair;
-
-import com.android.contacts.common.util.PhoneNumberHelper;
-import com.android.contacts.common.util.TelephonyManagerUtils;
-import com.google.common.collect.Sets;
-import com.google.i18n.phonenumbers.NumberParseException;
-import com.google.i18n.phonenumbers.Phonenumber;
-import com.google.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
-
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
-public class PhoneNumberUtil {
- private static final String TAG = "PhoneNumberUtil";
- private static final Set<String> LEGACY_UNKNOWN_NUMBERS = Sets.newHashSet("-1", "-2", "-3");
-
- /** Returns true if it is possible to place a call to the given number. */
- public static boolean canPlaceCallsTo(CharSequence number, int presentation) {
- return presentation == CallLog.Calls.PRESENTATION_ALLOWED
- && !TextUtils.isEmpty(number) && !isLegacyUnknownNumbers(number);
- }
-
- /**
- * Returns true if the given number is the number of the configured voicemail. To be able to
- * mock-out this, it is not a static method.
- */
- public static boolean isVoicemailNumber(
- Context context, PhoneAccountHandle accountHandle, CharSequence number) {
- if (TextUtils.isEmpty(number)) {
- return false;
- }
- return TelecomUtil.isVoicemailNumber(context, accountHandle, number.toString());
- }
-
- /**
- * Returns true if the given number is a SIP address. To be able to mock-out this, it is not a
- * static method.
- */
- public static boolean isSipNumber(CharSequence number) {
- return number != null && PhoneNumberHelper.isUriNumber(number.toString());
- }
-
- public static boolean isUnknownNumberThatCanBeLookedUp(
- Context context,
- PhoneAccountHandle accountHandle,
- CharSequence number,
- int presentation) {
- if (presentation == CallLog.Calls.PRESENTATION_UNKNOWN) {
- return false;
- }
- if (presentation == CallLog.Calls.PRESENTATION_RESTRICTED) {
- return false;
- }
- if (presentation == CallLog.Calls.PRESENTATION_PAYPHONE) {
- return false;
- }
- if (TextUtils.isEmpty(number)) {
- return false;
- }
- if (isVoicemailNumber(context, accountHandle, number)) {
- return false;
- }
- if (isLegacyUnknownNumbers(number)) {
- return false;
- }
- return true;
- }
-
- public static boolean isLegacyUnknownNumbers(CharSequence number) {
- return number != null && LEGACY_UNKNOWN_NUMBERS.contains(number.toString());
- }
-
- /**
- * @return a geographical description string for the specified number.
- * @see com.android.i18n.phonenumbers.PhoneNumberOfflineGeocoder
- */
- public static String getGeoDescription(Context context, String number) {
- Log.v(TAG, "getGeoDescription('" + pii(number) + "')...");
-
- if (TextUtils.isEmpty(number)) {
- return null;
- }
-
- com.google.i18n.phonenumbers.PhoneNumberUtil util =
- com.google.i18n.phonenumbers.PhoneNumberUtil.getInstance();
- PhoneNumberOfflineGeocoder geocoder = PhoneNumberOfflineGeocoder.getInstance();
-
- Locale locale = context.getResources().getConfiguration().locale;
- String countryIso = TelephonyManagerUtils.getCurrentCountryIso(context, locale);
- Phonenumber.PhoneNumber pn = null;
- try {
- Log.v(TAG, "parsing '" + pii(number)
- + "' for countryIso '" + countryIso + "'...");
- pn = util.parse(number, countryIso);
- Log.v(TAG, "- parsed number: " + pii(pn));
- } catch (NumberParseException e) {
- Log.v(TAG, "getGeoDescription: NumberParseException for incoming number '" +
- pii(number) + "'");
- }
-
- if (pn != null) {
- String description = geocoder.getDescriptionForNumber(pn, locale);
- Log.v(TAG, "- got description: '" + description + "'");
- return description;
- }
-
- return null;
- }
-
- private static String pii(Object pii) {
- return com.android.incallui.Log.pii(pii);
- }
-}
diff --git a/src/com/android/dialer/util/TelecomUtil.java b/src/com/android/dialer/util/TelecomUtil.java
deleted file mode 100644
index 69c7334b9..000000000
--- a/src/com/android/dialer/util/TelecomUtil.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * 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.util;
-
-import android.Manifest;
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.net.Uri;
-import android.provider.CallLog.Calls;
-import android.support.annotation.Nullable;
-import android.support.v4.content.ContextCompat;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.telecom.TelecomManager;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.telecom.TelecomManagerCompat;
-import com.android.dialer.compat.DialerCompatUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Performs permission checks before calling into TelecomManager. Each method is self-explanatory -
- * perform the required check and return the fallback default if the permission is missing,
- * otherwise return the value from TelecomManager.
- *
- */
-public class TelecomUtil {
- private static final String TAG = "TelecomUtil";
- private static boolean sWarningLogged = false;
-
- public static void showInCallScreen(Context context, boolean showDialpad) {
- if (hasReadPhoneStatePermission(context)) {
- try {
- getTelecomManager(context).showInCallScreen(showDialpad);
- } catch (SecurityException e) {
- // Just in case
- Log.w(TAG, "TelecomManager.showInCallScreen called without permission.");
- }
- }
- }
-
- public static void silenceRinger(Context context) {
- if (hasModifyPhoneStatePermission(context)) {
- try {
- TelecomManagerCompat.silenceRinger(getTelecomManager(context));
- } catch (SecurityException e) {
- // Just in case
- Log.w(TAG, "TelecomManager.silenceRinger called without permission.");
- }
- }
- }
-
- public static void cancelMissedCallsNotification(Context context) {
- if (hasModifyPhoneStatePermission(context)) {
- try {
- getTelecomManager(context).cancelMissedCallsNotification();
- } catch (SecurityException e) {
- Log.w(TAG, "TelecomManager.cancelMissedCalls called without permission.");
- }
- }
- }
-
- public static Uri getAdnUriForPhoneAccount(Context context, PhoneAccountHandle handle) {
- if (hasModifyPhoneStatePermission(context)) {
- try {
- return TelecomManagerCompat.getAdnUriForPhoneAccount(
- getTelecomManager(context), handle);
- } catch (SecurityException e) {
- Log.w(TAG, "TelecomManager.getAdnUriForPhoneAccount called without permission.");
- }
- }
- return null;
- }
-
- public static boolean handleMmi(Context context, String dialString,
- @Nullable PhoneAccountHandle handle) {
- if (hasModifyPhoneStatePermission(context)) {
- try {
- if (handle == null) {
- return getTelecomManager(context).handleMmi(dialString);
- } else {
- return getTelecomManager(context).handleMmi(dialString, handle);
- }
- } catch (SecurityException e) {
- Log.w(TAG, "TelecomManager.handleMmi called without permission.");
- }
- }
- return false;
- }
-
- @Nullable
- public static PhoneAccountHandle getDefaultOutgoingPhoneAccount(Context context,
- String uriScheme) {
- if (hasReadPhoneStatePermission(context)) {
- return TelecomManagerCompat.getDefaultOutgoingPhoneAccount(
- getTelecomManager(context), uriScheme);
- }
- return null;
- }
-
- public static PhoneAccount getPhoneAccount(Context context, PhoneAccountHandle handle) {
- return TelecomManagerCompat.getPhoneAccount(getTelecomManager(context), handle);
- }
-
- public static List<PhoneAccountHandle> getCallCapablePhoneAccounts(Context context) {
- if (hasReadPhoneStatePermission(context)) {
- return TelecomManagerCompat.getCallCapablePhoneAccounts(getTelecomManager(context));
- }
- return new ArrayList<>();
- }
-
- public static boolean isInCall(Context context) {
- if (hasReadPhoneStatePermission(context)) {
- return getTelecomManager(context).isInCall();
- }
- return false;
- }
-
- public static boolean isVoicemailNumber(Context context, PhoneAccountHandle accountHandle,
- String number) {
- if (hasReadPhoneStatePermission(context)) {
- return TelecomManagerCompat.isVoiceMailNumber(getTelecomManager(context),
- accountHandle, number);
- }
- return false;
- }
-
- @Nullable
- public static String getVoicemailNumber(Context context, PhoneAccountHandle accountHandle) {
- if (hasReadPhoneStatePermission(context)) {
- return TelecomManagerCompat.getVoiceMailNumber(getTelecomManager(context),
- getTelephonyManager(context), accountHandle);
- }
- return null;
- }
-
- /**
- * Tries to place a call using the {@link TelecomManager}.
- *
- * @param activity a valid activity.
- * @param intent the call intent.
- *
- * @return {@code true} if we successfully attempted to place the call, {@code false} if it
- * failed due to a permission check.
- */
- public static boolean placeCall(Activity activity, Intent intent) {
- if (hasCallPhonePermission(activity)) {
- TelecomManagerCompat.placeCall(activity, getTelecomManager(activity), intent);
- return true;
- }
- return false;
- }
-
- public static Uri getCallLogUri(Context context) {
- return hasReadWriteVoicemailPermissions(context) ? Calls.CONTENT_URI_WITH_VOICEMAIL
- : Calls.CONTENT_URI;
- }
-
- public static boolean hasReadWriteVoicemailPermissions(Context context) {
- return isDefaultDialer(context)
- || (hasPermission(context, Manifest.permission.READ_VOICEMAIL)
- && hasPermission(context, Manifest.permission.WRITE_VOICEMAIL));
- }
-
- public static boolean hasModifyPhoneStatePermission(Context context) {
- return isDefaultDialer(context)
- || hasPermission(context, Manifest.permission.MODIFY_PHONE_STATE);
- }
-
- public static boolean hasReadPhoneStatePermission(Context context) {
- return isDefaultDialer(context)
- || hasPermission(context, Manifest.permission.READ_PHONE_STATE);
- }
-
- public static boolean hasCallPhonePermission(Context context) {
- return isDefaultDialer(context)
- || hasPermission(context, Manifest.permission.CALL_PHONE);
- }
-
- private static boolean hasPermission(Context context, String permission) {
- return ContextCompat.checkSelfPermission(context, permission)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- public static boolean isDefaultDialer(Context context) {
- final boolean result = TextUtils.equals(context.getPackageName(),
- TelecomManagerCompat.getDefaultDialerPackage(getTelecomManager(context)));
- if (result) {
- sWarningLogged = false;
- } else {
- if (!sWarningLogged) {
- // Log only once to prevent spam.
- Log.w(TAG, "Dialer is not currently set to be default dialer");
- sWarningLogged = true;
- }
- }
- return result;
- }
-
- private static TelecomManager getTelecomManager(Context context) {
- return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
- }
-
- private static TelephonyManager getTelephonyManager(Context context) {
- return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
- }
-}
diff --git a/src/com/android/dialer/voicemail/VisualVoicemailEnabledChecker.java b/src/com/android/dialer/voicemail/VisualVoicemailEnabledChecker.java
deleted file mode 100644
index 80a0368bd..000000000
--- a/src/com/android/dialer/voicemail/VisualVoicemailEnabledChecker.java
+++ /dev/null
@@ -1,103 +0,0 @@
-package com.android.dialer.voicemail;
-
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.preference.PreferenceManager;
-import android.support.annotation.Nullable;
-
-import com.android.dialer.calllog.CallLogQueryHandler;
-
-/**
- * Helper class to check whether visual voicemail is enabled.
- *
- * Call isVisualVoicemailEnabled() to retrieve the result.
- *
- * The result is cached and saved in a SharedPreferences, stored as a boolean in
- * PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER. Every time a new instance is created, it will try to
- * restore the cached result from the SharedPreferences.
- *
- * Call asyncUpdate() to make a CallLogQuery to check the actual status. This is a async call so
- * isVisualVoicemailEnabled() will not be affected immediately.
- *
- * If the status has changed as a result of asyncUpdate(),
- * Callback.onVisualVoicemailEnabledStatusChanged() will be called with the new value.
- */
-public class VisualVoicemailEnabledChecker implements CallLogQueryHandler.Listener {
-
- public static final String PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER =
- "has_active_voicemail_provider";
- private SharedPreferences mPrefs;
- private boolean mHasActiveVoicemailProvider;
- private CallLogQueryHandler mCallLogQueryHandler;
- private VoicemailStatusHelper mVoicemailStatusHelper;
- private Context mContext;
-
- public interface Callback {
-
- /**
- * Callback to notify enabled status has changed to the @param newValue
- */
- void onVisualVoicemailEnabledStatusChanged(boolean newValue);
- }
-
- private Callback mCallback;
-
- public VisualVoicemailEnabledChecker(Context context, @Nullable Callback callback) {
- mContext = context;
- mCallback = callback;
- mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
- mVoicemailStatusHelper = new VoicemailStatusHelperImpl();
- mHasActiveVoicemailProvider = mPrefs.getBoolean(PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER,
- false);
- }
-
- /**
- * @return whether visual voicemail is enabled. Result is cached, call asyncUpdate() to
- * update the result.
- */
- public boolean isVisualVoicemailEnabled() {
- return mHasActiveVoicemailProvider;
- }
-
- /**
- * Perform an async query into the system to check the status of visual voicemail.
- * If the status has changed, Callback.onVisualVoicemailEnabledStatusChanged() will be called.
- */
- public void asyncUpdate() {
- mCallLogQueryHandler =
- new CallLogQueryHandler(mContext, mContext.getContentResolver(), this);
- mCallLogQueryHandler.fetchVoicemailStatus();
- }
-
- @Override
- public void onVoicemailStatusFetched(Cursor statusCursor) {
- boolean hasActiveVoicemailProvider =
- mVoicemailStatusHelper.getNumberActivityVoicemailSources(statusCursor) > 0;
- if (hasActiveVoicemailProvider != mHasActiveVoicemailProvider) {
- mHasActiveVoicemailProvider = hasActiveVoicemailProvider;
- mPrefs.edit().putBoolean(PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER,
- mHasActiveVoicemailProvider);
- if (mCallback != null) {
- mCallback.onVisualVoicemailEnabledStatusChanged(mHasActiveVoicemailProvider);
- }
- }
- }
-
- @Override
- public void onVoicemailUnreadCountFetched(Cursor cursor) {
- // Do nothing
- }
-
- @Override
- public void onMissedCallsUnreadCountFetched(Cursor cursor) {
- // Do nothing
- }
-
- @Override
- public boolean onCallsFetched(Cursor combinedCursor) {
- // Do nothing
- return false;
- }
-}
diff --git a/src/com/android/dialer/voicemail/VoicemailArchiveActivity.java b/src/com/android/dialer/voicemail/VoicemailArchiveActivity.java
deleted file mode 100644
index 16b947cd3..000000000
--- a/src/com/android/dialer/voicemail/VoicemailArchiveActivity.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright (C) 2016 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.voicemail;
-
-import android.content.Intent;
-import android.database.Cursor;
-import android.os.Bundle;
-import android.support.v7.app.ActionBar;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.view.MenuItem;
-import android.view.View;
-
-import com.android.contacts.common.GeoUtil;
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.R;
-import com.android.dialer.TransactionSafeActivity;
-import com.android.dialer.calllog.CallLogAdapter;
-import com.android.dialer.calllog.CallLogQueryHandler;
-import com.android.dialer.calllog.ContactInfoHelper;
-import com.android.dialer.widget.EmptyContentView;
-import com.android.dialerbind.ObjectFactory;
-
-/**
- * This activity manages all the voicemails archived by the user.
- */
-public class VoicemailArchiveActivity extends TransactionSafeActivity
- implements CallLogAdapter.CallFetcher, CallLogQueryHandler.Listener {
- private RecyclerView mRecyclerView;
- private LinearLayoutManager mLayoutManager;
- private EmptyContentView mEmptyListView;
- private CallLogAdapter mAdapter;
- private VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
- private CallLogQueryHandler mCallLogQueryHandler;
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (!isSafeToCommitTransactions()) {
- return true;
- }
-
- switch (item.getItemId()) {
- case android.R.id.home:
- Intent intent = new Intent(this, DialtactsActivity.class);
- // Clears any activities between VoicemailArchiveActivity and DialtactsActivity
- // on the activity stack and reuses the existing instance of DialtactsActivity
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.call_log_fragment);
-
- // Make window opaque to reduce overdraw
- getWindow().setBackgroundDrawable(null);
-
- ActionBar actionBar = getSupportActionBar();
- actionBar.setDisplayShowHomeEnabled(true);
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowTitleEnabled(true);
- actionBar.setElevation(0);
-
- mCallLogQueryHandler = new CallLogQueryHandler(this, getContentResolver(), this);
- mVoicemailPlaybackPresenter = VoicemailArchivePlaybackPresenter
- .getInstance(this, savedInstanceState);
-
- mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
- mRecyclerView.setHasFixedSize(true);
- mLayoutManager = new LinearLayoutManager(this);
- mRecyclerView.setLayoutManager(mLayoutManager);
- mEmptyListView = (EmptyContentView) findViewById(R.id.empty_list_view);
- mEmptyListView.setDescription(R.string.voicemail_archive_empty);
- mEmptyListView.setImage(R.drawable.empty_call_log);
-
- mAdapter = ObjectFactory.newCallLogAdapter(
- this,
- this,
- new ContactInfoHelper(this, GeoUtil.getCurrentCountryIso(this)),
- mVoicemailPlaybackPresenter,
- CallLogAdapter.ACTIVITY_TYPE_ARCHIVE);
- mRecyclerView.setAdapter(mAdapter);
- fetchCalls();
- }
-
- @Override
- protected void onPause() {
- mVoicemailPlaybackPresenter.onPause();
- mAdapter.onPause();
- super.onPause();
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mAdapter.onResume();
- mVoicemailPlaybackPresenter.onResume();
- }
-
- @Override
- public void onDestroy() {
- mVoicemailPlaybackPresenter.onDestroy();
- mAdapter.changeCursor(null);
- super.onDestroy();
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mVoicemailPlaybackPresenter.onSaveInstanceState(outState);
- }
-
- @Override
- public void fetchCalls() {
- mCallLogQueryHandler.fetchVoicemailArchive();
- }
-
- @Override
- public void onVoicemailStatusFetched(Cursor statusCursor) {
- // Do nothing
- }
-
- @Override
- public void onVoicemailUnreadCountFetched(Cursor cursor) {
- // Do nothing
- }
-
- @Override
- public void onMissedCallsUnreadCountFetched(Cursor cursor) {
- // Do nothing
- }
-
- @Override
- public boolean onCallsFetched(Cursor cursor) {
- mAdapter.changeCursorVoicemail(cursor);
- boolean showListView = cursor != null && cursor.getCount() > 0;
- mRecyclerView.setVisibility(showListView ? View.VISIBLE : View.GONE);
- mEmptyListView.setVisibility(!showListView ? View.VISIBLE : View.GONE);
- return true;
- }
-}
diff --git a/src/com/android/dialer/voicemail/VoicemailArchivePlaybackPresenter.java b/src/com/android/dialer/voicemail/VoicemailArchivePlaybackPresenter.java
deleted file mode 100644
index 5f73d1689..000000000
--- a/src/com/android/dialer/voicemail/VoicemailArchivePlaybackPresenter.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2016 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.voicemail;
-
-import android.app.Activity;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.util.Log;
-import com.android.dialer.calllog.CallLogAsyncTaskUtil;
-import com.android.dialer.database.VoicemailArchiveContract;
-import java.io.FileNotFoundException;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Similar to the {@link VoicemailPlaybackPresenter}, but for the archive voicemail tab. It checks
- * whether the voicemail file exists locally before preparing it.
- */
-public class VoicemailArchivePlaybackPresenter extends VoicemailPlaybackPresenter {
- private static final String TAG = "VMPlaybackPresenter";
- private static VoicemailPlaybackPresenter sInstance;
-
- public VoicemailArchivePlaybackPresenter(Activity activity) {
- super(activity);
- }
-
- public static VoicemailPlaybackPresenter getInstance(
- Activity activity, Bundle savedInstanceState) {
- if (sInstance == null) {
- sInstance = new VoicemailArchivePlaybackPresenter(activity);
- }
-
- sInstance.init(activity, savedInstanceState);
- return sInstance;
- }
-
- @Override
- protected void checkForContent(final OnContentCheckedListener callback) {
- mAsyncTaskExecutor.submit(Tasks.CHECK_FOR_CONTENT, new AsyncTask<Void, Void, Boolean>() {
- @Override
- public Boolean doInBackground(Void... params) {
- try {
- // Check if the _data column of the archived voicemail is valid
- if (mVoicemailUri != null) {
- mContext.getContentResolver().openInputStream(mVoicemailUri);
- return true;
- }
- } catch (FileNotFoundException e) {
- Log.d(TAG, "Voicemail file not found for " + mVoicemailUri);
- }
- return false;
- }
-
- @Override
- public void onPostExecute(Boolean hasContent) {
- callback.onContentChecked(hasContent);
- }
- });
- }
-
- @Override
- protected void startArchiveVoicemailTask(final Uri voicemailUri, final boolean archivedByUser) {
- // If a user wants to share an archived voicemail, no need for archiving, just go straight
- // to share intent.
- if (!archivedByUser) {
- sendShareIntent(voicemailUri);
- }
- }
-
- @Override
- protected boolean requestContent(int code) {
- handleError(new FileNotFoundException("Voicemail archive file does not exist"));
- return false; // No way for archive tab to request content
- }
-}
diff --git a/src/com/android/dialer/voicemail/VoicemailAsyncTaskUtil.java b/src/com/android/dialer/voicemail/VoicemailAsyncTaskUtil.java
deleted file mode 100644
index 7abf9a72c..000000000
--- a/src/com/android/dialer/voicemail/VoicemailAsyncTaskUtil.java
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * Copyright (C) 2016 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.voicemail;
-
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.dialer.calllog.CallLogQuery;
-import com.android.dialer.database.VoicemailArchiveContract;
-import com.android.dialer.util.AsyncTaskExecutor;
-import com.android.dialer.util.AsyncTaskExecutors;
-import com.google.common.base.Preconditions;
-import com.google.common.io.ByteStreams;
-
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.provider.CallLog;
-import android.provider.VoicemailContract;
-import android.util.Log;
-import com.android.common.io.MoreCloseables;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import javax.annotation.Nullable;
-
-/**
- * Class containing asynchronous tasks for voicemails.
- */
-@NeededForTesting
-public class VoicemailAsyncTaskUtil {
- private static final String TAG = "VoicemailAsyncTaskUtil";
-
- /** The enumeration of {@link AsyncTask} objects we use in this class. */
- public enum Tasks {
- GET_VOICEMAIL_FILE_PATH,
- SET_VOICEMAIL_ARCHIVE_STATUS,
- ARCHIVE_VOICEMAIL_CONTENT
- }
-
- @NeededForTesting
- public interface OnArchiveVoicemailListener {
- /**
- * Called after the voicemail has been archived.
- *
- * @param archivedVoicemailUri the URI of the archived voicemail
- */
- void onArchiveVoicemail(@Nullable Uri archivedVoicemailUri);
- }
-
- @NeededForTesting
- public interface OnSetVoicemailArchiveStatusListener {
- /**
- * Called after the voicemail archived_by_user column is updated.
- *
- * @param success whether the update was successful or not
- */
- void onSetVoicemailArchiveStatus(boolean success);
- }
-
- @NeededForTesting
- public interface OnGetArchivedVoicemailFilePathListener {
- /**
- * Called after the voicemail file path is obtained.
- *
- * @param filePath the file path of the archived voicemail
- */
- void onGetArchivedVoicemailFilePath(@Nullable String filePath);
- }
-
- private final ContentResolver mResolver;
- private final AsyncTaskExecutor mAsyncTaskExecutor;
-
- @NeededForTesting
- public VoicemailAsyncTaskUtil(ContentResolver contentResolver) {
- mResolver = Preconditions.checkNotNull(contentResolver);
- mAsyncTaskExecutor = AsyncTaskExecutors.createThreadPoolExecutor();
- }
-
- /**
- * Returns the archived voicemail file path.
- */
- @NeededForTesting
- public void getVoicemailFilePath(
- final OnGetArchivedVoicemailFilePathListener listener,
- final Uri voicemailUri) {
- Preconditions.checkNotNull(listener);
- Preconditions.checkNotNull(voicemailUri);
- mAsyncTaskExecutor.submit(Tasks.GET_VOICEMAIL_FILE_PATH,
- new AsyncTask<Void, Void, String>() {
- @Nullable
- @Override
- protected String doInBackground(Void... params) {
- try (Cursor cursor = mResolver.query(voicemailUri,
- new String[]{VoicemailArchiveContract.VoicemailArchive._DATA},
- null, null, null)) {
- if (hasContent(cursor)) {
- return cursor.getString(cursor.getColumnIndex(
- VoicemailArchiveContract.VoicemailArchive._DATA));
- }
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(String filePath) {
- listener.onGetArchivedVoicemailFilePath(filePath);
- }
- });
- }
-
- /**
- * Updates the archived_by_user flag of the archived voicemail.
- */
- @NeededForTesting
- public void setVoicemailArchiveStatus(
- final OnSetVoicemailArchiveStatusListener listener,
- final Uri voicemailUri,
- final boolean archivedByUser) {
- Preconditions.checkNotNull(listener);
- Preconditions.checkNotNull(voicemailUri);
- mAsyncTaskExecutor.submit(Tasks.SET_VOICEMAIL_ARCHIVE_STATUS,
- new AsyncTask<Void, Void, Boolean>() {
- @Override
- protected Boolean doInBackground(Void... params) {
- ContentValues values = new ContentValues(1);
- values.put(VoicemailArchiveContract.VoicemailArchive.ARCHIVED,
- archivedByUser);
- return mResolver.update(voicemailUri, values, null, null) > 0;
- }
-
- @Override
- protected void onPostExecute(Boolean success) {
- listener.onSetVoicemailArchiveStatus(success);
- }
- });
- }
-
- /**
- * Checks if a voicemail has already been archived, if so, return the previously archived URI.
- * Otherwise, copy the voicemail information to the local dialer database. If archive was
- * successful, archived voicemail URI is returned to listener, otherwise null.
- */
- @NeededForTesting
- public void archiveVoicemailContent(
- final OnArchiveVoicemailListener listener,
- final Uri voicemailUri) {
- Preconditions.checkNotNull(listener);
- Preconditions.checkNotNull(voicemailUri);
- mAsyncTaskExecutor.submit(Tasks.ARCHIVE_VOICEMAIL_CONTENT,
- new AsyncTask<Void, Void, Uri>() {
- @Nullable
- @Override
- protected Uri doInBackground(Void... params) {
- Uri archivedVoicemailUri = getArchivedVoicemailUri(voicemailUri);
-
- // If previously archived, return uri, otherwise archive everything.
- if (archivedVoicemailUri != null) {
- return archivedVoicemailUri;
- }
-
- // Combine call log and voicemail content info.
- ContentValues values = getVoicemailContentValues(voicemailUri);
- if (values == null) {
- return null;
- }
-
- Uri insertedVoicemailUri = mResolver.insert(
- VoicemailArchiveContract.VoicemailArchive.CONTENT_URI, values);
- if (insertedVoicemailUri == null) {
- return null;
- }
-
- // Copy voicemail content to a new file.
- boolean copiedFile = false;
- try (InputStream inputStream = mResolver.openInputStream(voicemailUri);
- OutputStream outputStream =
- mResolver.openOutputStream(insertedVoicemailUri)) {
- if (inputStream != null && outputStream != null) {
- ByteStreams.copy(inputStream, outputStream);
- copiedFile = true;
- return insertedVoicemailUri;
- }
- } catch (IOException e) {
- Log.w(TAG, "Failed to copy voicemail content to new file: "
- + e.toString());
- } finally {
- if (!copiedFile) {
- // Roll back insert if the voicemail content was not copied.
- mResolver.delete(insertedVoicemailUri, null, null);
- }
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(Uri archivedVoicemailUri) {
- listener.onArchiveVoicemail(archivedVoicemailUri);
- }
- });
- }
-
- /**
- * Helper method to get the archived URI of a voicemail.
- *
- * @param voicemailUri a {@link android.provider.VoicemailContract.Voicemails#CONTENT_URI} URI.
- * @return the URI of the archived voicemail or {@code null}
- */
- @Nullable
- private Uri getArchivedVoicemailUri(Uri voicemailUri) {
- try (Cursor cursor = getArchiveExistsCursor(voicemailUri)) {
- if (hasContent(cursor)) {
- return VoicemailArchiveContract.VoicemailArchive
- .buildWithId(cursor.getInt(cursor.getColumnIndex(
- VoicemailArchiveContract.VoicemailArchive._ID)));
- }
- }
- return null;
- }
-
- /**
- * Helper method to make a copy of all the values needed to display a voicemail.
- *
- * @param voicemailUri a {@link VoicemailContract.Voicemails#CONTENT_URI} URI.
- * @return the combined call log and voicemail values for the given URI, or {@code null}
- */
- @Nullable
- private ContentValues getVoicemailContentValues(Uri voicemailUri) {
- try (Cursor callLogInfo = getCallLogInfoCursor(voicemailUri);
- Cursor contentInfo = getContentInfoCursor(voicemailUri)) {
-
- if (hasContent(callLogInfo) && hasContent(contentInfo)) {
- // Create values to insert into database.
- ContentValues values = new ContentValues();
-
- // Insert voicemail call log info.
- values.put(VoicemailArchiveContract.VoicemailArchive.COUNTRY_ISO,
- callLogInfo.getString(CallLogQuery.COUNTRY_ISO));
- values.put(VoicemailArchiveContract.VoicemailArchive.GEOCODED_LOCATION,
- callLogInfo.getString(CallLogQuery.GEOCODED_LOCATION));
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_NAME,
- callLogInfo.getString(CallLogQuery.CACHED_NAME));
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_NUMBER_TYPE,
- callLogInfo.getInt(CallLogQuery.CACHED_NUMBER_TYPE));
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_NUMBER_LABEL,
- callLogInfo.getString(CallLogQuery.CACHED_NUMBER_LABEL));
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_LOOKUP_URI,
- callLogInfo.getString(CallLogQuery.CACHED_LOOKUP_URI));
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_MATCHED_NUMBER,
- callLogInfo.getString(CallLogQuery.CACHED_MATCHED_NUMBER));
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_NORMALIZED_NUMBER,
- callLogInfo.getString(CallLogQuery.CACHED_NORMALIZED_NUMBER));
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_FORMATTED_NUMBER,
- callLogInfo.getString(CallLogQuery.CACHED_FORMATTED_NUMBER));
- values.put(VoicemailArchiveContract.VoicemailArchive.NUMBER_PRESENTATION,
- callLogInfo.getInt(CallLogQuery.NUMBER_PRESENTATION));
- values.put(VoicemailArchiveContract.VoicemailArchive.ACCOUNT_COMPONENT_NAME,
- callLogInfo.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME));
- values.put(VoicemailArchiveContract.VoicemailArchive.ACCOUNT_ID,
- callLogInfo.getString(CallLogQuery.ACCOUNT_ID));
- values.put(VoicemailArchiveContract.VoicemailArchive.FEATURES,
- callLogInfo.getInt(CallLogQuery.FEATURES));
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_PHOTO_URI,
- callLogInfo.getString(CallLogQuery.CACHED_PHOTO_URI));
-
- // Insert voicemail content info.
- values.put(VoicemailArchiveContract.VoicemailArchive.SERVER_ID,
- contentInfo.getInt(contentInfo.getColumnIndex(
- VoicemailContract.Voicemails._ID)));
- values.put(VoicemailArchiveContract.VoicemailArchive.NUMBER,
- contentInfo.getString(contentInfo.getColumnIndex(
- VoicemailContract.Voicemails.NUMBER)));
- values.put(VoicemailArchiveContract.VoicemailArchive.DATE,
- contentInfo.getLong(contentInfo.getColumnIndex(
- VoicemailContract.Voicemails.DATE)));
- values.put(VoicemailArchiveContract.VoicemailArchive.DURATION,
- contentInfo.getLong(contentInfo.getColumnIndex(
- VoicemailContract.Voicemails.DURATION)));
- values.put(VoicemailArchiveContract.VoicemailArchive.MIME_TYPE,
- contentInfo.getString(contentInfo.getColumnIndex(
- VoicemailContract.Voicemails.MIME_TYPE)));
- values.put(VoicemailArchiveContract.VoicemailArchive.TRANSCRIPTION,
- contentInfo.getString(contentInfo.getColumnIndex(
- VoicemailContract.Voicemails.TRANSCRIPTION)));
-
- // Achived is false by default because it is updated after insertion.
- values.put(VoicemailArchiveContract.VoicemailArchive.ARCHIVED, false);
-
- return values;
- }
- }
- return null;
- }
-
- private boolean hasContent(@Nullable Cursor cursor) {
- return cursor != null && cursor.moveToFirst();
- }
-
- @Nullable
- private Cursor getCallLogInfoCursor(Uri voicemailUri) {
- return mResolver.query(
- ContentUris.withAppendedId(CallLog.Calls.CONTENT_URI_WITH_VOICEMAIL,
- ContentUris.parseId(voicemailUri)),
- CallLogQuery._PROJECTION, null, null, null);
- }
-
- @Nullable
- private Cursor getContentInfoCursor(Uri voicemailUri) {
- return mResolver.query(voicemailUri,
- new String[] {
- VoicemailContract.Voicemails._ID,
- VoicemailContract.Voicemails.NUMBER,
- VoicemailContract.Voicemails.DATE,
- VoicemailContract.Voicemails.DURATION,
- VoicemailContract.Voicemails.MIME_TYPE,
- VoicemailContract.Voicemails.TRANSCRIPTION,
- }, null, null, null);
- }
-
- @Nullable
- private Cursor getArchiveExistsCursor(Uri voicemailUri) {
- return mResolver.query(VoicemailArchiveContract.VoicemailArchive.CONTENT_URI,
- new String[] {VoicemailArchiveContract.VoicemailArchive._ID},
- VoicemailArchiveContract.VoicemailArchive.SERVER_ID + "="
- + ContentUris.parseId(voicemailUri),
- null,
- null);
- }
-}
diff --git a/src/com/android/dialer/voicemail/VoicemailAudioManager.java b/src/com/android/dialer/voicemail/VoicemailAudioManager.java
deleted file mode 100644
index fe6cf5f45..000000000
--- a/src/com/android/dialer/voicemail/VoicemailAudioManager.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * 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.voicemail;
-
-import android.content.Context;
-import android.media.AudioManager;
-import android.media.AudioManager.OnAudioFocusChangeListener;
-import android.telecom.CallAudioState;
-import android.util.Log;
-
-import java.util.concurrent.RejectedExecutionException;
-
-/**
- * This class manages all audio changes for voicemail playback.
- */
-final class VoicemailAudioManager implements OnAudioFocusChangeListener,
- WiredHeadsetManager.Listener {
- private static final String TAG = VoicemailAudioManager.class.getSimpleName();
-
- public static final int PLAYBACK_STREAM = AudioManager.STREAM_VOICE_CALL;
-
- private AudioManager mAudioManager;
- private VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
- private WiredHeadsetManager mWiredHeadsetManager;
- private boolean mWasSpeakerOn;
- private CallAudioState mCallAudioState;
-
- public VoicemailAudioManager(Context context,
- VoicemailPlaybackPresenter voicemailPlaybackPresenter) {
- mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- mVoicemailPlaybackPresenter = voicemailPlaybackPresenter;
- mWiredHeadsetManager = new WiredHeadsetManager(context);
- mWiredHeadsetManager.setListener(this);
-
- mCallAudioState = getInitialAudioState();
- Log.i(TAG, "Initial audioState = " + mCallAudioState);
- }
-
- public void requestAudioFocus() {
- int result = mAudioManager.requestAudioFocus(
- this,
- PLAYBACK_STREAM,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
- if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
- throw new RejectedExecutionException("Could not capture audio focus.");
- }
- }
-
- public void abandonAudioFocus() {
- mAudioManager.abandonAudioFocus(this);
- }
-
- @Override
- public void onAudioFocusChange(int focusChange) {
- Log.d(TAG, "onAudioFocusChange: focusChange=" + focusChange);
- mVoicemailPlaybackPresenter.onAudioFocusChange(focusChange == AudioManager.AUDIOFOCUS_GAIN);
- }
-
- @Override
- public void onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn) {
- Log.i(TAG, "wired headset was plugged in changed: " + oldIsPluggedIn
- + " -> "+ newIsPluggedIn);
-
- if (oldIsPluggedIn == newIsPluggedIn) {
- return;
- }
-
- int newRoute = mCallAudioState.getRoute(); // start out with existing route
- if (newIsPluggedIn) {
- newRoute = CallAudioState.ROUTE_WIRED_HEADSET;
- } else {
- if (mWasSpeakerOn) {
- newRoute = CallAudioState.ROUTE_SPEAKER;
- } else {
- newRoute = CallAudioState.ROUTE_EARPIECE;
- }
- }
-
- mVoicemailPlaybackPresenter.setSpeakerphoneOn(newRoute == CallAudioState.ROUTE_SPEAKER);
-
- // We need to call this every time even if we do not change the route because the supported
- // routes changed either to include or not include WIRED_HEADSET.
- setSystemAudioState(
- new CallAudioState(false /* muted */, newRoute, calculateSupportedRoutes()));
- }
-
- public void setSpeakerphoneOn(boolean on) {
- setAudioRoute(on ? CallAudioState.ROUTE_SPEAKER : CallAudioState.ROUTE_WIRED_OR_EARPIECE);
- }
-
- public boolean isWiredHeadsetPluggedIn() {
- return mWiredHeadsetManager.isPluggedIn();
- }
-
- public void registerReceivers() {
- // Receivers is plural because we expect to add bluetooth support.
- mWiredHeadsetManager.registerReceiver();
- }
-
- public void unregisterReceivers() {
- mWiredHeadsetManager.unregisterReceiver();
- }
-
- /**
- * Change the audio route, for example from earpiece to speakerphone.
- *
- * @param route The new audio route to use. See {@link CallAudioState}.
- */
- void setAudioRoute(int route) {
- Log.v(TAG, "setAudioRoute, route: " + CallAudioState.audioRouteToString(route));
-
- // Change ROUTE_WIRED_OR_EARPIECE to a single entry.
- int newRoute = selectWiredOrEarpiece(route, mCallAudioState.getSupportedRouteMask());
-
- // If route is unsupported, do nothing.
- if ((mCallAudioState.getSupportedRouteMask() | newRoute) == 0) {
- Log.w(TAG, "Asking to set to a route that is unsupported: " + newRoute);
- return;
- }
-
- if (mCallAudioState.getRoute() != newRoute) {
- // Remember the new speaker state so it can be restored when the user plugs and unplugs
- // a headset.
- mWasSpeakerOn = newRoute == CallAudioState.ROUTE_SPEAKER;
- setSystemAudioState(new CallAudioState(false /* muted */, newRoute,
- mCallAudioState.getSupportedRouteMask()));
- }
- }
-
- private CallAudioState getInitialAudioState() {
- int supportedRouteMask = calculateSupportedRoutes();
- int route = selectWiredOrEarpiece(CallAudioState.ROUTE_WIRED_OR_EARPIECE,
- supportedRouteMask);
- return new CallAudioState(false /* muted */, route, supportedRouteMask);
- }
-
- private int calculateSupportedRoutes() {
- int routeMask = CallAudioState.ROUTE_SPEAKER;
- if (mWiredHeadsetManager.isPluggedIn()) {
- routeMask |= CallAudioState.ROUTE_WIRED_HEADSET;
- } else {
- routeMask |= CallAudioState.ROUTE_EARPIECE;
- }
- return routeMask;
- }
-
- private int selectWiredOrEarpiece(int route, int supportedRouteMask) {
- // Since they are mutually exclusive and one is ALWAYS valid, we allow a special input of
- // ROUTE_WIRED_OR_EARPIECE so that callers don't have to make a call to check which is
- // supported before calling setAudioRoute.
- if (route == CallAudioState.ROUTE_WIRED_OR_EARPIECE) {
- route = CallAudioState.ROUTE_WIRED_OR_EARPIECE & supportedRouteMask;
- if (route == 0) {
- Log.wtf(TAG, "One of wired headset or earpiece should always be valid.");
- // assume earpiece in this case.
- route = CallAudioState.ROUTE_EARPIECE;
- }
- }
- return route;
- }
-
- private void setSystemAudioState(CallAudioState callAudioState) {
- CallAudioState oldAudioState = mCallAudioState;
- mCallAudioState = callAudioState;
-
- Log.i(TAG, "setSystemAudioState: changing from " + oldAudioState + " to "
- + mCallAudioState);
-
- // Audio route.
- if (mCallAudioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
- turnOnSpeaker(true);
- } else if (mCallAudioState.getRoute() == CallAudioState.ROUTE_EARPIECE ||
- mCallAudioState.getRoute() == CallAudioState.ROUTE_WIRED_HEADSET) {
- // Just handle turning off the speaker, the system will handle switching between wired
- // headset and earpiece.
- turnOnSpeaker(false);
- }
- }
-
- private void turnOnSpeaker(boolean on) {
- if (mAudioManager.isSpeakerphoneOn() != on) {
- Log.i(TAG, "turning speaker phone on: " + on);
- mAudioManager.setSpeakerphoneOn(on);
- }
- }
-}
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
deleted file mode 100644
index 7d6fe78d1..000000000
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
+++ /dev/null
@@ -1,638 +0,0 @@
-/*
- * Copyright (C) 2011 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.voicemail;
-
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Handler;
-import android.util.AttributeSet;
-import android.support.design.widget.Snackbar;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.SeekBar;
-import android.widget.SeekBar.OnSeekBarChangeListener;
-import android.widget.Space;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.common.io.MoreCloseables;
-import com.android.dialer.PhoneCallDetails;
-import com.android.dialer.R;
-import com.android.dialer.calllog.CallLogAsyncTaskUtil;
-
-import com.android.dialer.database.VoicemailArchiveContract;
-import com.android.dialer.database.VoicemailArchiveContract.VoicemailArchive;
-import com.android.dialer.util.AsyncTaskExecutor;
-import com.android.dialer.util.AsyncTaskExecutors;
-import com.android.dialerbind.ObjectFactory;
-import com.google.common.annotations.VisibleForTesting;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Objects;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.ScheduledExecutorService;
-
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.GuardedBy;
-import javax.annotation.concurrent.NotThreadSafe;
-import javax.annotation.concurrent.ThreadSafe;
-
-/**
- * Displays and plays a single voicemail. See {@link VoicemailPlaybackPresenter} for
- * details on the voicemail playback implementation.
- *
- * This class is not thread-safe, it is thread-confined. All calls to all public
- * methods on this class are expected to come from the main ui thread.
- */
-@NotThreadSafe
-public class VoicemailPlaybackLayout extends LinearLayout
- implements VoicemailPlaybackPresenter.PlaybackView,
- CallLogAsyncTaskUtil.CallLogAsyncTaskListener {
- private static final String TAG = VoicemailPlaybackLayout.class.getSimpleName();
- private static final int VOICEMAIL_DELETE_DELAY_MS = 3000;
- private static final int VOICEMAIL_ARCHIVE_DELAY_MS = 3000;
-
- /** The enumeration of {@link AsyncTask} objects we use in this class. */
- public enum Tasks {
- QUERY_ARCHIVED_STATUS
- }
-
- /**
- * Controls the animation of the playback slider.
- */
- @ThreadSafe
- private final class PositionUpdater implements Runnable {
-
- /** Update rate for the slider, 30fps. */
- private static final int SLIDER_UPDATE_PERIOD_MILLIS = 1000 / 30;
-
- private int mDurationMs;
- private final ScheduledExecutorService mExecutorService;
- private final Object mLock = new Object();
- @GuardedBy("mLock") private ScheduledFuture<?> mScheduledFuture;
-
- private Runnable mUpdateClipPositionRunnable = new Runnable() {
- @Override
- public void run() {
- int currentPositionMs = 0;
- synchronized (mLock) {
- if (mScheduledFuture == null || mPresenter == null) {
- // This task has been canceled. Just stop now.
- return;
- }
- currentPositionMs = mPresenter.getMediaPlayerPosition();
- }
- setClipPosition(currentPositionMs, mDurationMs);
- }
- };
-
- public PositionUpdater(int durationMs, ScheduledExecutorService executorService) {
- mDurationMs = durationMs;
- mExecutorService = executorService;
- }
-
- @Override
- public void run() {
- post(mUpdateClipPositionRunnable);
- }
-
- public void startUpdating() {
- synchronized (mLock) {
- cancelPendingRunnables();
- mScheduledFuture = mExecutorService.scheduleAtFixedRate(
- this, 0, SLIDER_UPDATE_PERIOD_MILLIS, TimeUnit.MILLISECONDS);
- }
- }
-
- public void stopUpdating() {
- synchronized (mLock) {
- cancelPendingRunnables();
- }
- }
-
- @GuardedBy("mLock")
- private void cancelPendingRunnables() {
- if (mScheduledFuture != null) {
- mScheduledFuture.cancel(true);
- mScheduledFuture = null;
- }
- removeCallbacks(mUpdateClipPositionRunnable);
- }
- }
-
- /**
- * Handle state changes when the user manipulates the seek bar.
- */
- private final OnSeekBarChangeListener mSeekBarChangeListener = new OnSeekBarChangeListener() {
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- if (mPresenter != null) {
- mPresenter.pausePlaybackForSeeking();
- }
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- if (mPresenter != null) {
- mPresenter.resumePlaybackAfterSeeking(seekBar.getProgress());
- }
- }
-
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- setClipPosition(progress, seekBar.getMax());
- // Update the seek position if user manually changed it. This makes sure position gets
- // updated when user use volume button to seek playback in talkback mode.
- if (fromUser) {
- mPresenter.seek(progress);
- }
- }
- };
-
- /**
- * Click listener to toggle speakerphone.
- */
- private final View.OnClickListener mSpeakerphoneListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mPresenter != null) {
- mPresenter.toggleSpeakerphone();
- }
- }
- };
-
- /**
- * Click listener to play or pause voicemail playback.
- */
- private final View.OnClickListener mStartStopButtonListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mPresenter == null) {
- return;
- }
-
- if (mIsPlaying) {
- mPresenter.pausePlayback();
- } else {
- mPresenter.resumePlayback();
- }
- }
- };
-
- private final View.OnClickListener mDeleteButtonListener = new View.OnClickListener() {
- @Override
- public void onClick(View view ) {
- if (mPresenter == null) {
- return;
- }
- mPresenter.pausePlayback();
- mPresenter.onVoicemailDeleted();
-
- final Uri deleteUri = mVoicemailUri;
- final Runnable deleteCallback = new Runnable() {
- @Override
- public void run() {
- if (Objects.equals(deleteUri, mVoicemailUri)) {
- CallLogAsyncTaskUtil.deleteVoicemail(mContext, deleteUri,
- VoicemailPlaybackLayout.this);
- }
- }
- };
-
- final Handler handler = new Handler();
- // Add a little buffer time in case the user clicked "undo" at the end of the delay
- // window.
- handler.postDelayed(deleteCallback, VOICEMAIL_DELETE_DELAY_MS + 50);
-
- Snackbar.make(VoicemailPlaybackLayout.this, R.string.snackbar_voicemail_deleted,
- Snackbar.LENGTH_LONG)
- .setDuration(VOICEMAIL_DELETE_DELAY_MS)
- .setAction(R.string.snackbar_voicemail_deleted_undo,
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mPresenter.onVoicemailDeleteUndo();
- handler.removeCallbacks(deleteCallback);
- }
- })
- .setActionTextColor(
- mContext.getResources().getColor(
- R.color.dialer_snackbar_action_text_color))
- .show();
- }
- };
-
- private final View.OnClickListener mArchiveButtonListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mPresenter == null || isArchiving(mVoicemailUri)) {
- return;
- }
- mIsArchiving.add(mVoicemailUri);
- mPresenter.pausePlayback();
- updateArchiveUI(mVoicemailUri);
- disableUiElements();
- mPresenter.archiveContent(mVoicemailUri, true);
- }
- };
-
- private final View.OnClickListener mShareButtonListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mPresenter == null || isArchiving(mVoicemailUri)) {
- return;
- }
- disableUiElements();
- mPresenter.archiveContent(mVoicemailUri, false);
- }
- };
-
- private Context mContext;
- private VoicemailPlaybackPresenter mPresenter;
- private Uri mVoicemailUri;
- private final AsyncTaskExecutor mAsyncTaskExecutor =
- AsyncTaskExecutors.createAsyncTaskExecutor();
- private boolean mIsPlaying = false;
- /**
- * Keeps track of which voicemails are currently being archived in order to update the voicemail
- * card UI every time a user opens a new card.
- */
- private static final ArrayList<Uri> mIsArchiving = new ArrayList<>();
-
- private SeekBar mPlaybackSeek;
- private ImageButton mStartStopButton;
- private ImageButton mPlaybackSpeakerphone;
- private ImageButton mDeleteButton;
- private ImageButton mArchiveButton;
- private ImageButton mShareButton;
-
- private Space mArchiveSpace;
- private Space mShareSpace;
-
- private TextView mStateText;
- private TextView mPositionText;
- private TextView mTotalDurationText;
-
- private PositionUpdater mPositionUpdater;
- private Drawable mVoicemailSeekHandleEnabled;
- private Drawable mVoicemailSeekHandleDisabled;
-
- public VoicemailPlaybackLayout(Context context) {
- this(context, null);
- }
-
- public VoicemailPlaybackLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- mContext = context;
- LayoutInflater inflater =
- (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- inflater.inflate(R.layout.voicemail_playback_layout, this);
- }
-
- @Override
- public void setPresenter(VoicemailPlaybackPresenter presenter, Uri voicemailUri) {
- mPresenter = presenter;
- mVoicemailUri = voicemailUri;
- if (ObjectFactory.isVoicemailArchiveEnabled(mContext)) {
- updateArchiveUI(mVoicemailUri);
- updateArchiveButton(mVoicemailUri);
- }
-
- if (ObjectFactory.isVoicemailShareEnabled(mContext)) {
- // Show share button and space before it
- mShareSpace.setVisibility(View.VISIBLE);
- mShareButton.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mPlaybackSeek = (SeekBar) findViewById(R.id.playback_seek);
- mStartStopButton = (ImageButton) findViewById(R.id.playback_start_stop);
- mPlaybackSpeakerphone = (ImageButton) findViewById(R.id.playback_speakerphone);
- mDeleteButton = (ImageButton) findViewById(R.id.delete_voicemail);
- mArchiveButton =(ImageButton) findViewById(R.id.archive_voicemail);
- mShareButton = (ImageButton) findViewById(R.id.share_voicemail);
-
- mArchiveSpace = (Space) findViewById(R.id.space_before_archive_voicemail);
- mShareSpace = (Space) findViewById(R.id.space_before_share_voicemail);
-
- mStateText = (TextView) findViewById(R.id.playback_state_text);
- mPositionText = (TextView) findViewById(R.id.playback_position_text);
- mTotalDurationText = (TextView) findViewById(R.id.total_duration_text);
-
- mPlaybackSeek.setOnSeekBarChangeListener(mSeekBarChangeListener);
- mStartStopButton.setOnClickListener(mStartStopButtonListener);
- mPlaybackSpeakerphone.setOnClickListener(mSpeakerphoneListener);
- mDeleteButton.setOnClickListener(mDeleteButtonListener);
- mArchiveButton.setOnClickListener(mArchiveButtonListener);
- mShareButton.setOnClickListener(mShareButtonListener);
-
- mPositionText.setText(formatAsMinutesAndSeconds(0));
- mTotalDurationText.setText(formatAsMinutesAndSeconds(0));
-
- mVoicemailSeekHandleEnabled = getResources().getDrawable(
- R.drawable.ic_voicemail_seek_handle, mContext.getTheme());
- mVoicemailSeekHandleDisabled = getResources().getDrawable(
- R.drawable.ic_voicemail_seek_handle_disabled, mContext.getTheme());
- }
-
- @Override
- public void onPlaybackStarted(int duration, ScheduledExecutorService executorService) {
- mIsPlaying = true;
-
- mStartStopButton.setImageResource(R.drawable.ic_pause);
-
- if (mPositionUpdater != null) {
- mPositionUpdater.stopUpdating();
- mPositionUpdater = null;
- }
- mPositionUpdater = new PositionUpdater(duration, executorService);
- mPositionUpdater.startUpdating();
- }
-
- @Override
- public void onPlaybackStopped() {
- mIsPlaying = false;
-
- mStartStopButton.setImageResource(R.drawable.ic_play_arrow);
-
- if (mPositionUpdater != null) {
- mPositionUpdater.stopUpdating();
- mPositionUpdater = null;
- }
- }
-
- @Override
- public void onPlaybackError() {
- if (mPositionUpdater != null) {
- mPositionUpdater.stopUpdating();
- }
-
- disableUiElements();
- mStateText.setText(getString(R.string.voicemail_playback_error));
- }
-
- @Override
- public void onSpeakerphoneOn(boolean on) {
- if (on) {
- mPlaybackSpeakerphone.setImageResource(R.drawable.ic_volume_up_24dp);
- // Speaker is now on, tapping button will turn it off.
- mPlaybackSpeakerphone.setContentDescription(
- mContext.getString(R.string.voicemail_speaker_off));
- } else {
- mPlaybackSpeakerphone.setImageResource(R.drawable.ic_volume_down_24dp);
- // Speaker is now off, tapping button will turn it on.
- mPlaybackSpeakerphone.setContentDescription(
- mContext.getString(R.string.voicemail_speaker_on));
- }
- }
-
- @Override
- public void setClipPosition(int positionMs, int durationMs) {
- int seekBarPositionMs = Math.max(0, positionMs);
- int seekBarMax = Math.max(seekBarPositionMs, durationMs);
- if (mPlaybackSeek.getMax() != seekBarMax) {
- mPlaybackSeek.setMax(seekBarMax);
- }
-
- mPlaybackSeek.setProgress(seekBarPositionMs);
-
- mPositionText.setText(formatAsMinutesAndSeconds(seekBarPositionMs));
- mTotalDurationText.setText(formatAsMinutesAndSeconds(durationMs));
- }
-
- @Override
- public void setSuccess() {
- mStateText.setText(null);
- }
-
- @Override
- public void setIsFetchingContent() {
- disableUiElements();
- mStateText.setText(getString(R.string.voicemail_fetching_content));
- }
-
- @Override
- public void setFetchContentTimeout() {
- mStartStopButton.setEnabled(true);
- mStateText.setText(getString(R.string.voicemail_fetching_timout));
- }
-
- @Override
- public int getDesiredClipPosition() {
- return mPlaybackSeek.getProgress();
- }
-
- @Override
- public void disableUiElements() {
- mStartStopButton.setEnabled(false);
- resetSeekBar();
- }
-
- @Override
- public void enableUiElements() {
- mDeleteButton.setEnabled(true);
- mStartStopButton.setEnabled(true);
- mPlaybackSeek.setEnabled(true);
- mPlaybackSeek.setThumb(mVoicemailSeekHandleEnabled);
- }
-
- @Override
- public void resetSeekBar() {
- mPlaybackSeek.setProgress(0);
- mPlaybackSeek.setEnabled(false);
- mPlaybackSeek.setThumb(mVoicemailSeekHandleDisabled);
- }
-
- @Override
- public void onDeleteCall() {}
-
- @Override
- public void onDeleteVoicemail() {
- mPresenter.onVoicemailDeletedInDatabase();
- }
-
- @Override
- public void onGetCallDetails(PhoneCallDetails[] details) {}
-
- private String getString(int resId) {
- return mContext.getString(resId);
- }
-
- /**
- * Formats a number of milliseconds as something that looks like {@code 00:05}.
- * <p>
- * We always use four digits, two for minutes two for seconds. In the very unlikely event
- * that the voicemail duration exceeds 99 minutes, the display is capped at 99 minutes.
- */
- private String formatAsMinutesAndSeconds(int millis) {
- int seconds = millis / 1000;
- int minutes = seconds / 60;
- seconds -= minutes * 60;
- if (minutes > 99) {
- minutes = 99;
- }
- return String.format("%02d:%02d", minutes, seconds);
- }
-
- /**
- * Called when a voicemail archive succeeded. If the expanded voicemail was being
- * archived, update the card UI. Either way, display a snackbar linking user to archive.
- */
- @Override
- public void onVoicemailArchiveSucceded(Uri voicemailUri) {
- if (isArchiving(voicemailUri)) {
- mIsArchiving.remove(voicemailUri);
- if (Objects.equals(voicemailUri, mVoicemailUri)) {
- onVoicemailArchiveResult();
- hideArchiveButton();
- }
- }
-
- Snackbar.make(this, R.string.snackbar_voicemail_archived,
- Snackbar.LENGTH_LONG)
- .setDuration(VOICEMAIL_ARCHIVE_DELAY_MS)
- .setAction(R.string.snackbar_voicemail_archived_goto,
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Intent intent = new Intent(mContext,
- VoicemailArchiveActivity.class);
- mContext.startActivity(intent);
- }
- })
- .setActionTextColor(
- mContext.getResources().getColor(R.color.dialer_snackbar_action_text_color))
- .show();
- }
-
- /**
- * If a voicemail archive failed, and the expanded card was being archived, update the card UI.
- * Either way, display a toast saying the voicemail archive failed.
- */
- @Override
- public void onVoicemailArchiveFailed(Uri voicemailUri) {
- if (isArchiving(voicemailUri)) {
- mIsArchiving.remove(voicemailUri);
- if (Objects.equals(voicemailUri, mVoicemailUri)) {
- onVoicemailArchiveResult();
- }
- }
- String toastStr = mContext.getString(R.string.voicemail_archive_failed);
- Toast.makeText(mContext, toastStr, Toast.LENGTH_SHORT).show();
- }
-
- public void hideArchiveButton() {
- mArchiveSpace.setVisibility(View.GONE);
- mArchiveButton.setVisibility(View.GONE);
- mArchiveButton.setClickable(false);
- mArchiveButton.setEnabled(false);
- }
-
- /**
- * Whenever a voicemail archive succeeds or fails, clear the text displayed in the voicemail
- * card.
- */
- private void onVoicemailArchiveResult() {
- enableUiElements();
- mStateText.setText(null);
- mArchiveButton.setColorFilter(null);
- }
-
- /**
- * Whether or not the voicemail with the given uri is being archived.
- */
- private boolean isArchiving(@Nullable Uri uri) {
- return uri != null && mIsArchiving.contains(uri);
- }
-
- /**
- * Show the proper text and hide the archive button if the voicemail is still being archived.
- */
- private void updateArchiveUI(@Nullable Uri voicemailUri) {
- if (!Objects.equals(voicemailUri, mVoicemailUri)) {
- return;
- }
- if (isArchiving(voicemailUri)) {
- // If expanded card was in the middle of archiving, disable buttons and display message
- disableUiElements();
- mDeleteButton.setEnabled(false);
- mArchiveButton.setColorFilter(getResources().getColor(R.color.setting_disabled_color));
- mStateText.setText(getString(R.string.voicemail_archiving_content));
- } else {
- onVoicemailArchiveResult();
- }
- }
-
- /**
- * Hides the archive button if the voicemail has already been archived, shows otherwise.
- * @param voicemailUri the URI of the voicemail for which the archive button needs to be updated
- */
- private void updateArchiveButton(@Nullable final Uri voicemailUri) {
- if (voicemailUri == null ||
- !Objects.equals(voicemailUri, mVoicemailUri) || isArchiving(voicemailUri) ||
- Objects.equals(voicemailUri.getAuthority(),VoicemailArchiveContract.AUTHORITY)) {
- return;
- }
- mAsyncTaskExecutor.submit(Tasks.QUERY_ARCHIVED_STATUS,
- new AsyncTask<Void, Void, Boolean>() {
- @Override
- public Boolean doInBackground(Void... params) {
- Cursor cursor = mContext.getContentResolver().query(VoicemailArchive.CONTENT_URI,
- null, VoicemailArchive.SERVER_ID + "=" + ContentUris.parseId(mVoicemailUri)
- + " AND " + VoicemailArchive.ARCHIVED + "= 1", null, null);
- boolean archived = cursor != null && cursor.getCount() > 0;
- cursor.close();
- return archived;
- }
-
- @Override
- public void onPostExecute(Boolean archived) {
- if (!Objects.equals(voicemailUri, mVoicemailUri)) {
- return;
- }
-
- if (archived) {
- hideArchiveButton();
- } else {
- mArchiveSpace.setVisibility(View.VISIBLE);
- mArchiveButton.setVisibility(View.VISIBLE);
- mArchiveButton.setClickable(true);
- mArchiveButton.setEnabled(true);
- }
-
- }
- });
- }
-
- @VisibleForTesting
- public String getStateText() {
- return mStateText.getText().toString();
- }
-}
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
deleted file mode 100644
index e224ddc2a..000000000
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
+++ /dev/null
@@ -1,1010 +0,0 @@
-/*
- * Copyright (C) 2011 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.voicemail;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.ContentResolver;
-import android.content.Intent;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.media.MediaPlayer;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.provider.VoicemailContract;
-import android.support.v4.content.FileProvider;
-import android.util.Log;
-import android.view.WindowManager.LayoutParams;
-
-import com.android.dialer.R;
-import com.android.dialer.calllog.CallLogAsyncTaskUtil;
-import com.android.dialer.util.AsyncTaskExecutor;
-import com.android.dialer.util.AsyncTaskExecutors;
-import com.android.common.io.MoreCloseables;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Executors;
-import java.util.concurrent.RejectedExecutionException;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import javax.annotation.concurrent.NotThreadSafe;
-import javax.annotation.concurrent.ThreadSafe;
-
-/**
- * Contains the controlling logic for a voicemail playback in the call log. It is closely coupled
- * to assumptions about the behaviors and lifecycle of the call log, in particular in the
- * {@link CallLogFragment} and {@link CallLogAdapter}.
- * <p>
- * This controls a single {@link com.android.dialer.voicemail.VoicemailPlaybackLayout}. A single
- * instance can be reused for different such layouts, using {@link #setPlaybackView}. This
- * is to facilitate reuse across different voicemail call log entries.
- * <p>
- * This class is not thread safe. The thread policy for this class is thread-confinement, all calls
- * into this class from outside must be done from the main UI thread.
- */
-@NotThreadSafe
-@VisibleForTesting
-public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListener,
- MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener {
-
- private static final String TAG = "VmPlaybackPresenter";
-
- /** Contract describing the behaviour we need from the ui we are controlling. */
- public interface PlaybackView {
- int getDesiredClipPosition();
- void disableUiElements();
- void enableUiElements();
- void onPlaybackError();
- void onPlaybackStarted(int duration, ScheduledExecutorService executorService);
- void onPlaybackStopped();
- void onSpeakerphoneOn(boolean on);
- void setClipPosition(int clipPositionInMillis, int clipLengthInMillis);
- void setSuccess();
- void setFetchContentTimeout();
- void setIsFetchingContent();
- void onVoicemailArchiveSucceded(Uri voicemailUri);
- void onVoicemailArchiveFailed(Uri voicemailUri);
- void setPresenter(VoicemailPlaybackPresenter presenter, Uri voicemailUri);
- void resetSeekBar();
- }
-
- public interface OnVoicemailDeletedListener {
- void onVoicemailDeleted(Uri uri);
- void onVoicemailDeleteUndo();
- void onVoicemailDeletedInDatabase();
- }
-
- /** The enumeration of {@link AsyncTask} objects we use in this class. */
- public enum Tasks {
- CHECK_FOR_CONTENT,
- CHECK_CONTENT_AFTER_CHANGE,
- ARCHIVE_VOICEMAIL
- }
-
- protected interface OnContentCheckedListener {
- void onContentChecked(boolean hasContent);
- }
-
- private static final String[] HAS_CONTENT_PROJECTION = new String[] {
- VoicemailContract.Voicemails.HAS_CONTENT,
- VoicemailContract.Voicemails.DURATION
- };
-
- private static final int NUMBER_OF_THREADS_IN_POOL = 2;
- // Time to wait for content to be fetched before timing out.
- private static final long FETCH_CONTENT_TIMEOUT_MS = 20000;
-
- private static final String VOICEMAIL_URI_KEY =
- VoicemailPlaybackPresenter.class.getName() + ".VOICEMAIL_URI";
- private static final String IS_PREPARED_KEY =
- VoicemailPlaybackPresenter.class.getName() + ".IS_PREPARED";
- // If present in the saved instance bundle, we should not resume playback on create.
- private static final String IS_PLAYING_STATE_KEY =
- VoicemailPlaybackPresenter.class.getName() + ".IS_PLAYING_STATE_KEY";
- // If present in the saved instance bundle, indicates where to set the playback slider.
- private static final String CLIP_POSITION_KEY =
- VoicemailPlaybackPresenter.class.getName() + ".CLIP_POSITION_KEY";
- private static final String IS_SPEAKERPHONE_ON_KEY =
- VoicemailPlaybackPresenter.class.getName() + ".IS_SPEAKER_PHONE_ON";
- public static final int PLAYBACK_REQUEST = 0;
- public static final int ARCHIVE_REQUEST = 1;
- public static final int SHARE_REQUEST = 2;
-
- /**
- * The most recently cached duration. We cache this since we don't want to keep requesting it
- * from the player, as this can easily lead to throwing {@link IllegalStateException} (any time
- * the player is released, it's illegal to ask for the duration).
- */
- private final AtomicInteger mDuration = new AtomicInteger(0);
-
- private static VoicemailPlaybackPresenter sInstance;
-
- private Activity mActivity;
- protected Context mContext;
- private PlaybackView mView;
- protected Uri mVoicemailUri;
-
- protected MediaPlayer mMediaPlayer;
- private int mPosition;
- private boolean mIsPlaying;
- // MediaPlayer crashes on some method calls if not prepared but does not have a method which
- // exposes its prepared state. Store this locally, so we can check and prevent crashes.
- private boolean mIsPrepared;
- private boolean mIsSpeakerphoneOn;
-
- private boolean mShouldResumePlaybackAfterSeeking;
- private int mInitialOrientation;
-
- // Used to run async tasks that need to interact with the UI.
- protected AsyncTaskExecutor mAsyncTaskExecutor;
- private static ScheduledExecutorService mScheduledExecutorService;
- /**
- * Used to handle the result of a successful or time-out fetch result.
- * <p>
- * This variable is thread-contained, accessed only on the ui thread.
- */
- private FetchResultHandler mFetchResultHandler;
- private final List<FetchResultHandler> mArchiveResultHandlers = new ArrayList<>();
- private Handler mHandler = new Handler();
- private PowerManager.WakeLock mProximityWakeLock;
- private VoicemailAudioManager mVoicemailAudioManager;
-
- private OnVoicemailDeletedListener mOnVoicemailDeletedListener;
- private final VoicemailAsyncTaskUtil mVoicemailAsyncTaskUtil;
-
- /**
- * Obtain singleton instance of this class. Use a single instance to provide a consistent
- * listener to the AudioManager when requesting and abandoning audio focus.
- *
- * Otherwise, after rotation the previous listener will still be active but a new listener
- * will be provided to calls to the AudioManager, which is bad. For example, abandoning
- * audio focus with the new listeners results in an AUDIO_FOCUS_GAIN callback to the
- * previous listener, which is the opposite of the intended behavior.
- */
- public static VoicemailPlaybackPresenter getInstance(
- Activity activity, Bundle savedInstanceState) {
- if (sInstance == null) {
- sInstance = new VoicemailPlaybackPresenter(activity);
- }
-
- sInstance.init(activity, savedInstanceState);
- return sInstance;
- }
-
- /**
- * Initialize variables which are activity-independent and state-independent.
- */
- protected VoicemailPlaybackPresenter(Activity activity) {
- Context context = activity.getApplicationContext();
- mAsyncTaskExecutor = AsyncTaskExecutors.createAsyncTaskExecutor();
- mVoicemailAudioManager = new VoicemailAudioManager(context, this);
- mVoicemailAsyncTaskUtil = new VoicemailAsyncTaskUtil(context.getContentResolver());
- PowerManager powerManager =
- (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- if (powerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
- mProximityWakeLock = powerManager.newWakeLock(
- PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG);
- }
- }
-
- /**
- * Update variables which are activity-dependent or state-dependent.
- */
- protected void init(Activity activity, Bundle savedInstanceState) {
- mActivity = activity;
- mContext = activity;
-
- mInitialOrientation = mContext.getResources().getConfiguration().orientation;
- mActivity.setVolumeControlStream(VoicemailAudioManager.PLAYBACK_STREAM);
-
- if (savedInstanceState != null) {
- // Restores playback state when activity is recreated, such as after rotation.
- mVoicemailUri = (Uri) savedInstanceState.getParcelable(VOICEMAIL_URI_KEY);
- mIsPrepared = savedInstanceState.getBoolean(IS_PREPARED_KEY);
- mPosition = savedInstanceState.getInt(CLIP_POSITION_KEY, 0);
- mIsPlaying = savedInstanceState.getBoolean(IS_PLAYING_STATE_KEY, false);
- mIsSpeakerphoneOn = savedInstanceState.getBoolean(IS_SPEAKERPHONE_ON_KEY, false);
- }
-
- if (mMediaPlayer == null) {
- mIsPrepared = false;
- mIsPlaying = false;
- }
- }
-
- /**
- * Must be invoked when the parent Activity is saving it state.
- */
- public void onSaveInstanceState(Bundle outState) {
- if (mView != null) {
- outState.putParcelable(VOICEMAIL_URI_KEY, mVoicemailUri);
- outState.putBoolean(IS_PREPARED_KEY, mIsPrepared);
- outState.putInt(CLIP_POSITION_KEY, mView.getDesiredClipPosition());
- outState.putBoolean(IS_PLAYING_STATE_KEY, mIsPlaying);
- outState.putBoolean(IS_SPEAKERPHONE_ON_KEY, mIsSpeakerphoneOn);
- }
- }
-
- /**
- * Specify the view which this presenter controls and the voicemail to prepare to play.
- */
- public void setPlaybackView(
- PlaybackView view, Uri voicemailUri, boolean startPlayingImmediately) {
- mView = view;
- mView.setPresenter(this, voicemailUri);
-
- // Handles cases where the same entry is binded again when scrolling in list, or where
- // the MediaPlayer was retained after an orientation change.
- if (mMediaPlayer != null && mIsPrepared && voicemailUri.equals(mVoicemailUri)) {
- // If the voicemail card was rebinded, we need to set the position to the appropriate
- // point. Since we retain the media player, we can just set it to the position of the
- // media player.
- mPosition = mMediaPlayer.getCurrentPosition();
- onPrepared(mMediaPlayer);
- } else {
- if (!voicemailUri.equals(mVoicemailUri)) {
- mVoicemailUri = voicemailUri;
- mPosition = 0;
- // Default to earpiece.
- setSpeakerphoneOn(false);
- mVoicemailAudioManager.setSpeakerphoneOn(false);
- } else {
- // Update the view to the current speakerphone state.
- mView.onSpeakerphoneOn(mIsSpeakerphoneOn);
- }
- /*
- * Check to see if the content field in the DB is set. If set, we proceed to
- * prepareContent() method. We get the duration of the voicemail from the query and set
- * it if the content is not available.
- */
- checkForContent(new OnContentCheckedListener() {
- @Override
- public void onContentChecked(boolean hasContent) {
- if (hasContent) {
- prepareContent();
- } else if (mView != null) {
- mView.resetSeekBar();
- mView.setClipPosition(0, mDuration.get());
- }
- }
- });
-
- if (startPlayingImmediately) {
- // Since setPlaybackView can get called during the view binding process, we don't
- // want to reset mIsPlaying to false if the user is currently playing the
- // voicemail and the view is rebound.
- mIsPlaying = startPlayingImmediately;
- }
- }
- }
-
- /**
- * Reset the presenter for playback back to its original state.
- */
- public void resetAll() {
- pausePresenter(true);
-
- mView = null;
- mVoicemailUri = null;
- }
-
- /**
- * When navigating away from voicemail playback, we need to release the media player,
- * pause the UI and save the position.
- *
- * @param reset {@code true} if we want to reset the position of the playback, {@code false} if
- * we want to retain the current position (in case we return to the voicemail).
- */
- public void pausePresenter(boolean reset) {
- if (mMediaPlayer != null) {
- mMediaPlayer.release();
- mMediaPlayer = null;
- }
-
- disableProximitySensor(false /* waitForFarState */);
-
- mIsPrepared = false;
- mIsPlaying = false;
-
- if (reset) {
- // We want to reset the position whether or not the view is valid.
- mPosition = 0;
- }
-
- if (mView != null) {
- mView.onPlaybackStopped();
- if (reset) {
- mView.setClipPosition(0, mDuration.get());
- } else {
- mPosition = mView.getDesiredClipPosition();
- }
- }
- }
-
- /**
- * Must be invoked when the parent activity is resumed.
- */
- public void onResume() {
- mVoicemailAudioManager.registerReceivers();
- }
-
- /**
- * Must be invoked when the parent activity is paused.
- */
- public void onPause() {
- mVoicemailAudioManager.unregisterReceivers();
-
- if (mContext != null && mIsPrepared
- && mInitialOrientation != mContext.getResources().getConfiguration().orientation) {
- // If an orientation change triggers the pause, retain the MediaPlayer.
- Log.d(TAG, "onPause: Orientation changed.");
- return;
- }
-
- // Release the media player, otherwise there may be failures.
- pausePresenter(false);
-
- if (mActivity != null) {
- mActivity.getWindow().clearFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
-
- }
-
- /**
- * Must be invoked when the parent activity is destroyed.
- */
- public void onDestroy() {
- // Clear references to avoid leaks from the singleton instance.
- mActivity = null;
- mContext = null;
-
- if (mScheduledExecutorService != null) {
- mScheduledExecutorService.shutdown();
- mScheduledExecutorService = null;
- }
-
- if (!mArchiveResultHandlers.isEmpty()) {
- for (FetchResultHandler fetchResultHandler : mArchiveResultHandlers) {
- fetchResultHandler.destroy();
- }
- mArchiveResultHandlers.clear();
- }
-
- if (mFetchResultHandler != null) {
- mFetchResultHandler.destroy();
- mFetchResultHandler = null;
- }
- }
-
- /**
- * Checks to see if we have content available for this voicemail.
- */
- protected void checkForContent(final OnContentCheckedListener callback) {
- mAsyncTaskExecutor.submit(Tasks.CHECK_FOR_CONTENT, new AsyncTask<Void, Void, Boolean>() {
- @Override
- public Boolean doInBackground(Void... params) {
- return queryHasContent(mVoicemailUri);
- }
-
- @Override
- public void onPostExecute(Boolean hasContent) {
- callback.onContentChecked(hasContent);
- }
- });
- }
-
- private boolean queryHasContent(Uri voicemailUri) {
- if (voicemailUri == null || mContext == null) {
- return false;
- }
-
- ContentResolver contentResolver = mContext.getContentResolver();
- Cursor cursor = contentResolver.query(
- voicemailUri, null, null, null, null);
- try {
- if (cursor != null && cursor.moveToNext()) {
- int duration = cursor.getInt(cursor.getColumnIndex(
- VoicemailContract.Voicemails.DURATION));
- // Convert database duration (seconds) into mDuration (milliseconds)
- mDuration.set(duration > 0 ? duration * 1000 : 0);
- return cursor.getInt(cursor.getColumnIndex(
- VoicemailContract.Voicemails.HAS_CONTENT)) == 1;
- }
- } finally {
- MoreCloseables.closeQuietly(cursor);
- }
- return false;
- }
-
- /**
- * Makes a broadcast request to ask that a voicemail source fetch this content.
- * <p>
- * This method <b>must be called on the ui thread</b>.
- * <p>
- * This method will be called when we realise that we don't have content for this voicemail. It
- * will trigger a broadcast to request that the content be downloaded. It will add a listener to
- * the content resolver so that it will be notified when the has_content field changes. It will
- * also set a timer. If the has_content field changes to true within the allowed time, we will
- * proceed to {@link #prepareContent()}. If the has_content field does not
- * become true within the allowed time, we will update the ui to reflect the fact that content
- * was not available.
- *
- * @return whether issued request to fetch content
- */
- protected boolean requestContent(int code) {
- if (mContext == null || mVoicemailUri == null) {
- return false;
- }
-
- FetchResultHandler tempFetchResultHandler =
- new FetchResultHandler(new Handler(), mVoicemailUri, code);
-
- switch (code) {
- case ARCHIVE_REQUEST:
- mArchiveResultHandlers.add(tempFetchResultHandler);
- break;
- default:
- if (mFetchResultHandler != null) {
- mFetchResultHandler.destroy();
- }
- mView.setIsFetchingContent();
- mFetchResultHandler = tempFetchResultHandler;
- break;
- }
-
- // Send voicemail fetch request.
- Intent intent = new Intent(VoicemailContract.ACTION_FETCH_VOICEMAIL, mVoicemailUri);
- mContext.sendBroadcast(intent);
- return true;
- }
-
- @ThreadSafe
- private class FetchResultHandler extends ContentObserver implements Runnable {
- private AtomicBoolean mIsWaitingForResult = new AtomicBoolean(true);
- private final Handler mFetchResultHandler;
- private final Uri mVoicemailUri;
- private final int mRequestCode;
-
- public FetchResultHandler(Handler handler, Uri uri, int code) {
- super(handler);
- mFetchResultHandler = handler;
- mRequestCode = code;
- mVoicemailUri = uri;
- if (mContext != null) {
- mContext.getContentResolver().registerContentObserver(
- mVoicemailUri, false, this);
- mFetchResultHandler.postDelayed(this, FETCH_CONTENT_TIMEOUT_MS);
- }
- }
-
- /**
- * Stop waiting for content and notify UI if {@link FETCH_CONTENT_TIMEOUT_MS} has elapsed.
- */
- @Override
- public void run() {
- if (mIsWaitingForResult.getAndSet(false) && mContext != null) {
- mContext.getContentResolver().unregisterContentObserver(this);
- if (mView != null) {
- mView.setFetchContentTimeout();
- }
- }
- }
-
- public void destroy() {
- if (mIsWaitingForResult.getAndSet(false) && mContext != null) {
- mContext.getContentResolver().unregisterContentObserver(this);
- mFetchResultHandler.removeCallbacks(this);
- }
- }
-
- @Override
- public void onChange(boolean selfChange) {
- mAsyncTaskExecutor.submit(Tasks.CHECK_CONTENT_AFTER_CHANGE,
- new AsyncTask<Void, Void, Boolean>() {
-
- @Override
- public Boolean doInBackground(Void... params) {
- return queryHasContent(mVoicemailUri);
- }
-
- @Override
- public void onPostExecute(Boolean hasContent) {
- if (hasContent && mContext != null && mIsWaitingForResult.getAndSet(false)) {
- mContext.getContentResolver().unregisterContentObserver(
- FetchResultHandler.this);
- prepareContent();
- if (mRequestCode == ARCHIVE_REQUEST) {
- startArchiveVoicemailTask(mVoicemailUri, true /* archivedByUser */);
- } else if (mRequestCode == SHARE_REQUEST) {
- startArchiveVoicemailTask(mVoicemailUri, false /* archivedByUser */);
- }
- }
- }
- });
- }
- }
-
- /**
- * Prepares the voicemail content for playback.
- * <p>
- * This method will be called once we know that our voicemail has content (according to the
- * content provider). this method asynchronously tries to prepare the data source through the
- * media player. If preparation is successful, the media player will {@link #onPrepared()},
- * and it will call {@link #onError()} otherwise.
- */
- protected void prepareContent() {
- if (mView == null) {
- return;
- }
- Log.d(TAG, "prepareContent");
-
- // Release the previous media player, otherwise there may be failures.
- if (mMediaPlayer != null) {
- mMediaPlayer.release();
- mMediaPlayer = null;
- }
-
- mView.disableUiElements();
- mIsPrepared = false;
-
- try {
- mMediaPlayer = new MediaPlayer();
- mMediaPlayer.setOnPreparedListener(this);
- mMediaPlayer.setOnErrorListener(this);
- mMediaPlayer.setOnCompletionListener(this);
-
- mMediaPlayer.reset();
- mMediaPlayer.setDataSource(mContext, mVoicemailUri);
- mMediaPlayer.setAudioStreamType(VoicemailAudioManager.PLAYBACK_STREAM);
- mMediaPlayer.prepareAsync();
- } catch (IOException e) {
- handleError(e);
- }
- }
-
- /**
- * Once the media player is prepared, enables the UI and adopts the appropriate playback state.
- */
- @Override
- public void onPrepared(MediaPlayer mp) {
- if (mView == null) {
- return;
- }
- Log.d(TAG, "onPrepared");
- mIsPrepared = true;
-
- // Update the duration in the database if it was not previously retrieved
- CallLogAsyncTaskUtil.updateVoicemailDuration(mContext, mVoicemailUri,
- TimeUnit.MILLISECONDS.toSeconds(mMediaPlayer.getDuration()));
-
- mDuration.set(mMediaPlayer.getDuration());
-
- Log.d(TAG, "onPrepared: mPosition=" + mPosition);
- mView.setClipPosition(mPosition, mDuration.get());
- mView.enableUiElements();
- mView.setSuccess();
- mMediaPlayer.seekTo(mPosition);
-
- if (mIsPlaying) {
- resumePlayback();
- } else {
- pausePlayback();
- }
- }
-
- /**
- * Invoked if preparing the media player fails, for example, if file is missing or the voicemail
- * is an unknown file format that can't be played.
- */
- @Override
- public boolean onError(MediaPlayer mp, int what, int extra) {
- handleError(new IllegalStateException("MediaPlayer error listener invoked: " + extra));
- return true;
- }
-
- protected void handleError(Exception e) {
- Log.d(TAG, "handleError: Could not play voicemail " + e);
-
- if (mIsPrepared) {
- mMediaPlayer.release();
- mMediaPlayer = null;
- mIsPrepared = false;
- }
-
- if (mView != null) {
- mView.onPlaybackError();
- }
-
- mPosition = 0;
- mIsPlaying = false;
- }
-
- /**
- * After done playing the voicemail clip, reset the clip position to the start.
- */
- @Override
- public void onCompletion(MediaPlayer mediaPlayer) {
- pausePlayback();
-
- // Reset the seekbar position to the beginning.
- mPosition = 0;
- if (mView != null) {
- mView.setClipPosition(0, mDuration.get());
- }
- }
-
- /**
- * Only play voicemail when audio focus is granted. When it is lost (usually by another
- * application requesting focus), pause playback.
- *
- * @param gainedFocus {@code true} if the audio focus was gained, {@code} false otherwise.
- */
- public void onAudioFocusChange(boolean gainedFocus) {
- if (mIsPlaying == gainedFocus) {
- // Nothing new here, just exit.
- return;
- }
-
- if (!mIsPlaying) {
- resumePlayback();
- } else {
- pausePlayback();
- }
- }
-
- /**
- * Resumes voicemail playback at the clip position stored by the presenter. Null-op if already
- * playing.
- */
- public void resumePlayback() {
- if (mView == null) {
- return;
- }
-
- if (!mIsPrepared) {
- /*
- * Check content before requesting content to avoid duplicated requests. It is possible
- * that the UI doesn't know content has arrived if the fetch took too long causing a
- * timeout, but succeeded.
- */
- checkForContent(new OnContentCheckedListener() {
- @Override
- public void onContentChecked(boolean hasContent) {
- if (!hasContent) {
- // No local content, download from server. Queue playing if the request was
- // issued,
- mIsPlaying = requestContent(PLAYBACK_REQUEST);
- } else {
- // Queue playing once the media play loaded the content.
- mIsPlaying = true;
- prepareContent();
- }
- }
- });
- return;
- }
-
- mIsPlaying = true;
-
- if (mMediaPlayer != null && !mMediaPlayer.isPlaying()) {
- // Clamp the start position between 0 and the duration.
- mPosition = Math.max(0, Math.min(mPosition, mDuration.get()));
-
- mMediaPlayer.seekTo(mPosition);
-
- try {
- // Grab audio focus.
- // Can throw RejectedExecutionException.
- mVoicemailAudioManager.requestAudioFocus();
- mMediaPlayer.start();
- setSpeakerphoneOn(mIsSpeakerphoneOn);
- } catch (RejectedExecutionException e) {
- handleError(e);
- }
- }
-
- Log.d(TAG, "Resumed playback at " + mPosition + ".");
- mView.onPlaybackStarted(mDuration.get(), getScheduledExecutorServiceInstance());
- }
-
- /**
- * Pauses voicemail playback at the current position. Null-op if already paused.
- */
- public void pausePlayback() {
- if (!mIsPrepared) {
- return;
- }
-
- mIsPlaying = false;
-
- if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
- mMediaPlayer.pause();
- }
-
- mPosition = mMediaPlayer == null ? 0 : mMediaPlayer.getCurrentPosition();
-
- Log.d(TAG, "Paused playback at " + mPosition + ".");
-
- if (mView != null) {
- mView.onPlaybackStopped();
- }
-
- mVoicemailAudioManager.abandonAudioFocus();
-
- if (mActivity != null) {
- mActivity.getWindow().clearFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
- disableProximitySensor(true /* waitForFarState */);
- }
-
- /**
- * Pauses playback when the user starts seeking the position, and notes whether the voicemail is
- * playing to know whether to resume playback once the user selects a new position.
- */
- public void pausePlaybackForSeeking() {
- if (mMediaPlayer != null) {
- mShouldResumePlaybackAfterSeeking = mMediaPlayer.isPlaying();
- }
- pausePlayback();
- }
-
- public void resumePlaybackAfterSeeking(int desiredPosition) {
- mPosition = desiredPosition;
- if (mShouldResumePlaybackAfterSeeking) {
- mShouldResumePlaybackAfterSeeking = false;
- resumePlayback();
- }
- }
-
- /**
- * Seek to position. This is called when user manually seek the playback. It could be either
- * by touch or volume button while in talkback mode.
- * @param position
- */
- public void seek(int position) {
- mPosition = position;
- }
-
- private void enableProximitySensor() {
- if (mProximityWakeLock == null || mIsSpeakerphoneOn || !mIsPrepared
- || mMediaPlayer == null || !mMediaPlayer.isPlaying()) {
- return;
- }
-
- if (!mProximityWakeLock.isHeld()) {
- Log.i(TAG, "Acquiring proximity wake lock");
- mProximityWakeLock.acquire();
- } else {
- Log.i(TAG, "Proximity wake lock already acquired");
- }
- }
-
- private void disableProximitySensor(boolean waitForFarState) {
- if (mProximityWakeLock == null) {
- return;
- }
- if (mProximityWakeLock.isHeld()) {
- Log.i(TAG, "Releasing proximity wake lock");
- int flags = waitForFarState ? PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY : 0;
- mProximityWakeLock.release(flags);
- } else {
- Log.i(TAG, "Proximity wake lock already released");
- }
- }
-
- /**
- * This is for use by UI interactions only. It simplifies UI logic.
- */
- public void toggleSpeakerphone() {
- mVoicemailAudioManager.setSpeakerphoneOn(!mIsSpeakerphoneOn);
- setSpeakerphoneOn(!mIsSpeakerphoneOn);
- }
-
- /**
- * This method only handles app-level changes to the speakerphone. Audio layer changes should
- * be handled separately. This is so that the VoicemailAudioManager can trigger changes to
- * the presenter without the presenter triggering the audio manager and duplicating actions.
- */
- public void setSpeakerphoneOn(boolean on) {
- if (mView == null) {
- return;
- }
-
- mView.onSpeakerphoneOn(on);
-
- mIsSpeakerphoneOn = on;
-
- // This should run even if speakerphone is not being toggled because we may be switching
- // from earpiece to headphone and vise versa. Also upon initial setup the default audio
- // source is the earpiece, so we want to trigger the proximity sensor.
- if (mIsPlaying) {
- if (on || mVoicemailAudioManager.isWiredHeadsetPluggedIn()) {
- disableProximitySensor(false /* waitForFarState */);
- if (mIsPrepared && mMediaPlayer != null && mMediaPlayer.isPlaying()) {
- mActivity.getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
- } else {
- enableProximitySensor();
- if (mActivity != null) {
- mActivity.getWindow().clearFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
- }
- }
- }
-
- public void setOnVoicemailDeletedListener(OnVoicemailDeletedListener listener) {
- mOnVoicemailDeletedListener = listener;
- }
-
- public int getMediaPlayerPosition() {
- return mIsPrepared && mMediaPlayer != null ? mMediaPlayer.getCurrentPosition() : 0;
- }
-
- public void notifyUiOfArchiveResult(Uri voicemailUri, boolean archived) {
- if (mView == null) {
- return;
- }
- if (archived) {
- mView.onVoicemailArchiveSucceded(voicemailUri);
- } else {
- mView.onVoicemailArchiveFailed(voicemailUri);
- }
- }
-
- /* package */ void onVoicemailDeleted() {
- // Trampoline the event notification to the interested listener.
- if (mOnVoicemailDeletedListener != null) {
- mOnVoicemailDeletedListener.onVoicemailDeleted(mVoicemailUri);
- }
- }
-
- /* package */ void onVoicemailDeleteUndo() {
- // Trampoline the event notification to the interested listener.
- if (mOnVoicemailDeletedListener != null) {
- mOnVoicemailDeletedListener.onVoicemailDeleteUndo();
- }
- }
-
- /* package */ void onVoicemailDeletedInDatabase() {
- // Trampoline the event notification to the interested listener.
- if (mOnVoicemailDeletedListener != null) {
- mOnVoicemailDeletedListener.onVoicemailDeletedInDatabase();
- }
- }
-
- private static synchronized ScheduledExecutorService getScheduledExecutorServiceInstance() {
- if (mScheduledExecutorService == null) {
- mScheduledExecutorService = Executors.newScheduledThreadPool(NUMBER_OF_THREADS_IN_POOL);
- }
- return mScheduledExecutorService;
- }
-
- /**
- * If voicemail has already been downloaded, go straight to archiving. Otherwise, request
- * the voicemail content first.
- */
- public void archiveContent(final Uri voicemailUri, final boolean archivedByUser) {
- checkForContent(new OnContentCheckedListener() {
- @Override
- public void onContentChecked(boolean hasContent) {
- if (!hasContent) {
- requestContent(archivedByUser ? ARCHIVE_REQUEST : SHARE_REQUEST);
- } else {
- startArchiveVoicemailTask(voicemailUri, archivedByUser);
- }
- }
- });
- }
-
- /**
- * Asynchronous task used to archive a voicemail given its uri.
- */
- protected void startArchiveVoicemailTask(final Uri voicemailUri, final boolean archivedByUser) {
- mVoicemailAsyncTaskUtil.archiveVoicemailContent(
- new VoicemailAsyncTaskUtil.OnArchiveVoicemailListener() {
- @Override
- public void onArchiveVoicemail(final Uri archivedVoicemailUri) {
- if (archivedVoicemailUri == null) {
- notifyUiOfArchiveResult(voicemailUri, false);
- return;
- }
-
- if (archivedByUser) {
- setArchivedVoicemailStatusAndUpdateUI(voicemailUri,
- archivedVoicemailUri, true);
- } else {
- sendShareIntent(archivedVoicemailUri);
- }
- }
- }, voicemailUri);
- }
-
- /**
- * Sends the intent for sharing the voicemail file.
- */
- protected void sendShareIntent(final Uri voicemailUri) {
- mVoicemailAsyncTaskUtil.getVoicemailFilePath(
- new VoicemailAsyncTaskUtil.OnGetArchivedVoicemailFilePathListener() {
- @Override
- public void onGetArchivedVoicemailFilePath(String filePath) {
- mView.enableUiElements();
- if (filePath == null) {
- mView.setFetchContentTimeout();
- return;
- }
- Uri voicemailFileUri = FileProvider.getUriForFile(
- mContext,
- mContext.getString(R.string.contacts_file_provider_authority),
- new File(filePath));
- mContext.startActivity(Intent.createChooser(
- getShareIntent(voicemailFileUri),
- mContext.getResources().getText(
- R.string.call_log_share_voicemail)));
- }
- }, voicemailUri);
- }
-
- /** Sets archived_by_user field to the given boolean and updates the URI. */
- private void setArchivedVoicemailStatusAndUpdateUI(
- final Uri voicemailUri,
- final Uri archivedVoicemailUri,
- boolean status) {
- mVoicemailAsyncTaskUtil.setVoicemailArchiveStatus(
- new VoicemailAsyncTaskUtil.OnSetVoicemailArchiveStatusListener() {
- @Override
- public void onSetVoicemailArchiveStatus(boolean success) {
- notifyUiOfArchiveResult(voicemailUri, success);
- }
- }, archivedVoicemailUri, status);
- }
-
- private Intent getShareIntent(Uri voicemailFileUri) {
- Intent shareIntent = new Intent();
- shareIntent.setAction(Intent.ACTION_SEND);
- shareIntent.putExtra(Intent.EXTRA_STREAM, voicemailFileUri);
- shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- shareIntent.setType(mContext.getContentResolver()
- .getType(voicemailFileUri));
- return shareIntent;
- }
-
- @VisibleForTesting
- public boolean isPlaying() {
- return mIsPlaying;
- }
-
- @VisibleForTesting
- public boolean isSpeakerphoneOn() {
- return mIsSpeakerphoneOn;
- }
-
- @VisibleForTesting
- public void clearInstance() {
- sInstance = null;
- }
-}
diff --git a/src/com/android/dialer/voicemail/VoicemailStatusHelper.java b/src/com/android/dialer/voicemail/VoicemailStatusHelper.java
deleted file mode 100644
index d790b7764..000000000
--- a/src/com/android/dialer/voicemail/VoicemailStatusHelper.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2011 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.voicemail;
-
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.VoicemailContract.Status;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import java.util.List;
-
-/**
- * Interface used by the call log UI to determine what user message, if any, related to voicemail
- * source status needs to be shown. The messages are returned in the order of importance.
- * <p>
- * The implementation of this interface interacts with the voicemail content provider to fetch
- * statuses of all the registered voicemail sources and determines if any status message needs to
- * be shown. The user of this interface must observe/listen to provider changes and invoke
- * this class to check if any message needs to be shown.
- */
-public interface VoicemailStatusHelper {
- @VisibleForTesting
- public class StatusMessage {
- /** Package of the source on behalf of which this message has to be shown.*/
- public final String sourcePackage;
- /**
- * The string resource id of the status message that should be shown in the call log
- * page. Set to -1, if this message is not to be shown in call log.
- */
- public final int callLogMessageId;
- /**
- * The string resource id of the status message that should be shown in the call details
- * page. Set to -1, if this message is not to be shown in call details page.
- */
- public final int callDetailsMessageId;
- /** The string resource id of the action message that should be shown. */
- public final int actionMessageId;
- /** URI for the corrective action, where applicable. Null if no action URI is available. */
- public final Uri actionUri;
-
- public StatusMessage(String sourcePackage, int callLogMessageId, int callDetailsMessageId,
- int actionMessageId, Uri actionUri) {
- this.sourcePackage = sourcePackage;
- this.callLogMessageId = callLogMessageId;
- this.callDetailsMessageId = callDetailsMessageId;
- this.actionMessageId = actionMessageId;
- this.actionUri = actionUri;
- }
-
- /** Whether this message should be shown in the call log page. */
- public boolean showInCallLog() {
- return callLogMessageId != -1;
- }
-
- /** Whether this message should be shown in the call details page. */
- public boolean showInCallDetails() {
- return callDetailsMessageId != -1;
- }
- }
-
- /**
- * Returns a list of messages, in the order or priority that should be shown to the user. An
- * empty list is returned if no message needs to be shown.
- * @param cursor The cursor pointing to the query on {@link Status#CONTENT_URI}. The projection
- * to be used is defined by the implementation class of this interface.
- */
- @VisibleForTesting
- public List<StatusMessage> getStatusMessages(Cursor cursor);
-
- /**
- * Returns the number of active voicemail sources installed.
- * <p>
- * The number of sources is counted by querying the voicemail status table.
- */
- public int getNumberActivityVoicemailSources(Cursor cursor);
-}
diff --git a/src/com/android/dialer/voicemail/VoicemailStatusHelperImpl.java b/src/com/android/dialer/voicemail/VoicemailStatusHelperImpl.java
deleted file mode 100644
index ff1786862..000000000
--- a/src/com/android/dialer/voicemail/VoicemailStatusHelperImpl.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (C) 2011 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.voicemail;
-
-import static android.provider.VoicemailContract.Status.CONFIGURATION_STATE_CAN_BE_CONFIGURED;
-import static android.provider.VoicemailContract.Status.CONFIGURATION_STATE_OK;
-import static android.provider.VoicemailContract.Status.DATA_CHANNEL_STATE_NO_CONNECTION;
-import static android.provider.VoicemailContract.Status.DATA_CHANNEL_STATE_OK;
-import static android.provider.VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING;
-import static android.provider.VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION;
-import static android.provider.VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_OK;
-
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.VoicemailContract.Status;
-
-import com.android.contacts.common.util.UriUtils;
-import com.android.dialer.R;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-/** Implementation of {@link VoicemailStatusHelper}. */
-public class VoicemailStatusHelperImpl implements VoicemailStatusHelper {
- private static final int SOURCE_PACKAGE_INDEX = 0;
- private static final int CONFIGURATION_STATE_INDEX = 1;
- private static final int DATA_CHANNEL_STATE_INDEX = 2;
- private static final int NOTIFICATION_CHANNEL_STATE_INDEX = 3;
- private static final int SETTINGS_URI_INDEX = 4;
- private static final int VOICEMAIL_ACCESS_URI_INDEX = 5;
- private static final int NUM_COLUMNS = 6;
- /** Projection on the voicemail_status table used by this class. */
- public static final String[] PROJECTION = new String[NUM_COLUMNS];
- static {
- PROJECTION[SOURCE_PACKAGE_INDEX] = Status.SOURCE_PACKAGE;
- PROJECTION[CONFIGURATION_STATE_INDEX] = Status.CONFIGURATION_STATE;
- PROJECTION[DATA_CHANNEL_STATE_INDEX] = Status.DATA_CHANNEL_STATE;
- PROJECTION[NOTIFICATION_CHANNEL_STATE_INDEX] = Status.NOTIFICATION_CHANNEL_STATE;
- PROJECTION[SETTINGS_URI_INDEX] = Status.SETTINGS_URI;
- PROJECTION[VOICEMAIL_ACCESS_URI_INDEX] = Status.VOICEMAIL_ACCESS_URI;
- }
-
- /** Possible user actions. */
- public static enum Action {
- NONE(-1),
- CALL_VOICEMAIL(R.string.voicemail_status_action_call_server),
- CONFIGURE_VOICEMAIL(R.string.voicemail_status_action_configure);
-
- private final int mMessageId;
- private Action(int messageId) {
- mMessageId = messageId;
- }
-
- public int getMessageId() {
- return mMessageId;
- }
- }
-
- /**
- * Overall state of the source status. Each state is associated with the corresponding display
- * string and the corrective action. The states are also assigned a relative priority which is
- * used to order the messages from different sources.
- */
- private static enum OverallState {
- // TODO: Add separate string for call details and call log pages for the states that needs
- // to be shown in both.
- /** Both notification and data channel are not working. */
- NO_CONNECTION(0, Action.CALL_VOICEMAIL, R.string.voicemail_status_voicemail_not_available,
- R.string.voicemail_status_audio_not_available),
- /** Notifications working, but data channel is not working. Audio cannot be downloaded. */
- NO_DATA(1, Action.CALL_VOICEMAIL, R.string.voicemail_status_voicemail_not_available,
- R.string.voicemail_status_audio_not_available),
- /** Messages are known to be waiting but data channel is not working. */
- MESSAGE_WAITING(2, Action.CALL_VOICEMAIL, R.string.voicemail_status_messages_waiting,
- R.string.voicemail_status_audio_not_available),
- /** Notification channel not working, but data channel is. */
- NO_NOTIFICATIONS(3, Action.CALL_VOICEMAIL,
- R.string.voicemail_status_voicemail_not_available),
- /** Invite user to set up voicemail. */
- INVITE_FOR_CONFIGURATION(4, Action.CONFIGURE_VOICEMAIL,
- R.string.voicemail_status_configure_voicemail),
- /**
- * No detailed notifications, but data channel is working.
- * This is normal mode of operation for certain sources. No action needed.
- */
- NO_DETAILED_NOTIFICATION(5, Action.NONE, -1),
- /** Visual voicemail not yet set up. No local action needed. */
- NOT_CONFIGURED(6, Action.NONE, -1),
- /** Everything is OK. */
- OK(7, Action.NONE, -1),
- /** If one or more state value set by the source is not valid. */
- INVALID(8, Action.NONE, -1);
-
- private final int mPriority;
- private final Action mAction;
- private final int mCallLogMessageId;
- private final int mCallDetailsMessageId;
-
- private OverallState(int priority, Action action, int callLogMessageId) {
- this(priority, action, callLogMessageId, -1);
- }
-
- private OverallState(int priority, Action action, int callLogMessageId,
- int callDetailsMessageId) {
- mPriority = priority;
- mAction = action;
- mCallLogMessageId = callLogMessageId;
- mCallDetailsMessageId = callDetailsMessageId;
- }
-
- public Action getAction() {
- return mAction;
- }
-
- public int getPriority() {
- return mPriority;
- }
-
- public int getCallLogMessageId() {
- return mCallLogMessageId;
- }
-
- public int getCallDetailsMessageId() {
- return mCallDetailsMessageId;
- }
- }
-
- /** A wrapper on {@link StatusMessage} which additionally stores the priority of the message. */
- private static class MessageStatusWithPriority {
- private final StatusMessage mMessage;
- private final int mPriority;
-
- public MessageStatusWithPriority(StatusMessage message, int priority) {
- mMessage = message;
- mPriority = priority;
- }
- }
-
- @Override
- public List<StatusMessage> getStatusMessages(Cursor cursor) {
- List<MessageStatusWithPriority> messages =
- new ArrayList<VoicemailStatusHelperImpl.MessageStatusWithPriority>();
- cursor.moveToPosition(-1);
- while(cursor.moveToNext()) {
- MessageStatusWithPriority message = getMessageForStatusEntry(cursor);
- if (message != null) {
- messages.add(message);
- }
- }
- // Finally reorder the messages by their priority.
- return reorderMessages(messages);
- }
-
- @Override
- public int getNumberActivityVoicemailSources(Cursor cursor) {
- int count = 0;
- cursor.moveToPosition(-1);
- while(cursor.moveToNext()) {
- if (isVoicemailSourceActive(cursor)) {
- ++count;
- }
- }
- return count;
- }
-
- /** Returns whether the source status in the cursor corresponds to an active source. */
- private boolean isVoicemailSourceActive(Cursor cursor) {
- return cursor.getString(SOURCE_PACKAGE_INDEX) != null
- && cursor.getInt(CONFIGURATION_STATE_INDEX) == Status.CONFIGURATION_STATE_OK;
- }
-
- private List<StatusMessage> reorderMessages(List<MessageStatusWithPriority> messageWrappers) {
- Collections.sort(messageWrappers, new Comparator<MessageStatusWithPriority>() {
- @Override
- public int compare(MessageStatusWithPriority msg1, MessageStatusWithPriority msg2) {
- return msg1.mPriority - msg2.mPriority;
- }
- });
- List<StatusMessage> reorderMessages = new ArrayList<VoicemailStatusHelper.StatusMessage>();
- // Copy the ordered message objects into the final list.
- for (MessageStatusWithPriority messageWrapper : messageWrappers) {
- reorderMessages.add(messageWrapper.mMessage);
- }
- return reorderMessages;
- }
-
- /**
- * Returns the message for the status entry pointed to by the cursor.
- */
- private MessageStatusWithPriority getMessageForStatusEntry(Cursor cursor) {
- final String sourcePackage = cursor.getString(SOURCE_PACKAGE_INDEX);
- if (sourcePackage == null) {
- return null;
- }
- final OverallState overallState = getOverallState(cursor.getInt(CONFIGURATION_STATE_INDEX),
- cursor.getInt(DATA_CHANNEL_STATE_INDEX),
- cursor.getInt(NOTIFICATION_CHANNEL_STATE_INDEX));
- final Action action = overallState.getAction();
-
- // No source package or no action, means no message shown.
- if (action == Action.NONE) {
- return null;
- }
-
- Uri actionUri = null;
- if (action == Action.CALL_VOICEMAIL) {
- actionUri = UriUtils.parseUriOrNull(cursor.getString(VOICEMAIL_ACCESS_URI_INDEX));
- // Even if actionUri is null, it is still be useful to show the notification.
- } else if (action == Action.CONFIGURE_VOICEMAIL) {
- actionUri = UriUtils.parseUriOrNull(cursor.getString(SETTINGS_URI_INDEX));
- // If there is no settings URI, there is no point in showing the notification.
- if (actionUri == null) {
- return null;
- }
- }
- return new MessageStatusWithPriority(
- new StatusMessage(sourcePackage, overallState.getCallLogMessageId(),
- overallState.getCallDetailsMessageId(), action.getMessageId(),
- actionUri),
- overallState.getPriority());
- }
-
- private OverallState getOverallState(int configurationState, int dataChannelState,
- int notificationChannelState) {
- if (configurationState == CONFIGURATION_STATE_OK) {
- // Voicemail is configured. Let's see how is the data channel.
- if (dataChannelState == DATA_CHANNEL_STATE_OK) {
- // Data channel is fine. What about notification channel?
- if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_OK) {
- return OverallState.OK;
- } else if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING) {
- return OverallState.NO_DETAILED_NOTIFICATION;
- } else if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_NO_CONNECTION) {
- return OverallState.NO_NOTIFICATIONS;
- }
- } else if (dataChannelState == DATA_CHANNEL_STATE_NO_CONNECTION) {
- // Data channel is not working. What about notification channel?
- if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_OK) {
- return OverallState.NO_DATA;
- } else if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING) {
- return OverallState.MESSAGE_WAITING;
- } else if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_NO_CONNECTION) {
- return OverallState.NO_CONNECTION;
- }
- }
- } else if (configurationState == CONFIGURATION_STATE_CAN_BE_CONFIGURED) {
- // Voicemail not configured. data/notification channel states are irrelevant.
- return OverallState.INVITE_FOR_CONFIGURATION;
- } else if (configurationState == Status.CONFIGURATION_STATE_NOT_CONFIGURED) {
- // Voicemail not configured. data/notification channel states are irrelevant.
- return OverallState.NOT_CONFIGURED;
- }
- // Will reach here only if the source has set an invalid value.
- return OverallState.INVALID;
- }
-}
diff --git a/src/com/android/dialer/voicemail/WiredHeadsetManager.java b/src/com/android/dialer/voicemail/WiredHeadsetManager.java
deleted file mode 100644
index 7351f4f01..000000000
--- a/src/com/android/dialer/voicemail/WiredHeadsetManager.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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.voicemail;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.media.AudioManager;
-import android.util.Log;
-
-/** Listens for and caches headset state. */
-class WiredHeadsetManager {
- private static final String TAG = WiredHeadsetManager.class.getSimpleName();
-
- interface Listener {
- void onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn);
- }
-
- /** Receiver for wired headset plugged and unplugged events. */
- private class WiredHeadsetBroadcastReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (AudioManager.ACTION_HEADSET_PLUG.equals(intent.getAction())) {
- boolean isPluggedIn = intent.getIntExtra("state", 0) == 1;
- Log.v(TAG, "ACTION_HEADSET_PLUG event, plugged in: " + isPluggedIn);
- onHeadsetPluggedInChanged(isPluggedIn);
- }
- }
- }
-
- private final WiredHeadsetBroadcastReceiver mReceiver;
- private boolean mIsPluggedIn;
- private Listener mListener;
- private Context mContext;
-
- WiredHeadsetManager(Context context) {
- mContext = context;
- mReceiver = new WiredHeadsetBroadcastReceiver();
-
- AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- mIsPluggedIn = audioManager.isWiredHeadsetOn();
-
- }
-
- void setListener(Listener listener) {
- mListener = listener;
- }
-
- boolean isPluggedIn() {
- return mIsPluggedIn;
- }
-
- void registerReceiver() {
- // Register for misc other intent broadcasts.
- IntentFilter intentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
- mContext.registerReceiver(mReceiver, intentFilter);
- }
-
- void unregisterReceiver() {
- mContext.unregisterReceiver(mReceiver);
- }
-
- private void onHeadsetPluggedInChanged(boolean isPluggedIn) {
- if (mIsPluggedIn != isPluggedIn) {
- Log.v(TAG, "onHeadsetPluggedInChanged, mIsPluggedIn: " + mIsPluggedIn + " -> "
- + isPluggedIn);
- boolean oldIsPluggedIn = mIsPluggedIn;
- mIsPluggedIn = isPluggedIn;
- if (mListener != null) {
- mListener.onWiredHeadsetPluggedInChanged(oldIsPluggedIn, mIsPluggedIn);
- }
- }
- }
-} \ No newline at end of file
diff --git a/src/com/android/dialer/widget/ActionBarController.java b/src/com/android/dialer/widget/ActionBarController.java
deleted file mode 100644
index edf57b163..000000000
--- a/src/com/android/dialer/widget/ActionBarController.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Copyright (C) 2013 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.widget;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.os.Bundle;
-import android.util.Log;
-
-import com.android.dialer.DialtactsActivity;
-import com.android.phone.common.animation.AnimUtils.AnimationCallback;
-
-/**
- * Controls the various animated properties of the actionBar: showing/hiding, fading/revealing,
- * and collapsing/expanding, and assigns suitable properties to the actionBar based on the
- * current state of the UI.
- */
-public class ActionBarController {
- public static final boolean DEBUG = DialtactsActivity.DEBUG;
- public static final String TAG = "ActionBarController";
- private static final String KEY_IS_SLID_UP = "key_actionbar_is_slid_up";
- private static final String KEY_IS_FADED_OUT = "key_actionbar_is_faded_out";
- private static final String KEY_IS_EXPANDED = "key_actionbar_is_expanded";
-
- private ActivityUi mActivityUi;
- private SearchEditTextLayout mSearchBox;
-
- private boolean mIsActionBarSlidUp;
-
- private final AnimationCallback mFadeOutCallback = new AnimationCallback() {
- @Override
- public void onAnimationEnd() {
- slideActionBar(true /* slideUp */, false /* animate */);
- }
-
- @Override
- public void onAnimationCancel() {
- slideActionBar(true /* slideUp */, false /* animate */);
- }
- };
-
- public interface ActivityUi {
- public boolean isInSearchUi();
- public boolean hasSearchQuery();
- public boolean shouldShowActionBar();
- public int getActionBarHeight();
- public int getActionBarHideOffset();
- public void setActionBarHideOffset(int offset);
- }
-
- public ActionBarController(ActivityUi activityUi, SearchEditTextLayout searchBox) {
- mActivityUi = activityUi;
- mSearchBox = searchBox;
- }
-
- /**
- * @return Whether or not the action bar is currently showing (both slid down and visible)
- */
- public boolean isActionBarShowing() {
- return !mIsActionBarSlidUp && !mSearchBox.isFadedOut();
- }
-
- /**
- * Called when the user has tapped on the collapsed search box, to start a new search query.
- */
- public void onSearchBoxTapped() {
- if (DEBUG) {
- Log.d(TAG, "OnSearchBoxTapped: isInSearchUi " + mActivityUi.isInSearchUi());
- }
- if (!mActivityUi.isInSearchUi()) {
- mSearchBox.expand(true /* animate */, true /* requestFocus */);
- }
- }
-
- /**
- * Called when search UI has been exited for some reason.
- */
- public void onSearchUiExited() {
- if (DEBUG) {
- Log.d(TAG, "OnSearchUIExited: isExpanded " + mSearchBox.isExpanded()
- + " isFadedOut: " + mSearchBox.isFadedOut()
- + " shouldShowActionBar: " + mActivityUi.shouldShowActionBar());
- }
- if (mSearchBox.isExpanded()) {
- mSearchBox.collapse(true /* animate */);
- }
- if (mSearchBox.isFadedOut()) {
- mSearchBox.fadeIn();
- }
-
- if (mActivityUi.shouldShowActionBar()) {
- slideActionBar(false /* slideUp */, false /* animate */);
- } else {
- slideActionBar(true /* slideUp */, false /* animate */);
- }
- }
-
- /**
- * Called to indicate that the user is trying to hide the dialpad. Should be called before
- * any state changes have actually occurred.
- */
- public void onDialpadDown() {
- if (DEBUG) {
- Log.d(TAG, "OnDialpadDown: isInSearchUi " + mActivityUi.isInSearchUi()
- + " hasSearchQuery: " + mActivityUi.hasSearchQuery()
- + " isFadedOut: " + mSearchBox.isFadedOut()
- + " isExpanded: " + mSearchBox.isExpanded());
- }
- if (mActivityUi.isInSearchUi()) {
- 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();
- }
- }
- }
-
- /**
- * Called to indicate that the user is trying to show the dialpad. Should be called before
- * any state changes have actually occurred.
- */
- public void onDialpadUp() {
- if (DEBUG) {
- Log.d(TAG, "OnDialpadUp: isInSearchUi " + mActivityUi.isInSearchUi());
- }
- if (mActivityUi.isInSearchUi()) {
- slideActionBar(true /* slideUp */, true /* animate */);
- } else {
- // From the lists fragment
- mSearchBox.fadeOut(mFadeOutCallback);
- }
- }
-
- public void slideActionBar(boolean slideUp, boolean animate) {
- if (DEBUG) {
- Log.d(TAG, "Sliding actionBar - up: " + slideUp + " animate: " + animate);
- }
- if (animate) {
- ValueAnimator animator =
- slideUp ? ValueAnimator.ofFloat(0, 1) : ValueAnimator.ofFloat(1, 0);
- animator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- final float value = (float) animation.getAnimatedValue();
- setHideOffset(
- (int) (mActivityUi.getActionBarHeight() * value));
- }
- });
- animator.start();
- } else {
- setHideOffset(slideUp ? mActivityUi.getActionBarHeight() : 0);
- }
- mIsActionBarSlidUp = slideUp;
- }
-
- public void setAlpha(float alphaValue) {
- mSearchBox.animate().alpha(alphaValue).start();
- }
-
- public void setHideOffset(int offset) {
- mIsActionBarSlidUp = offset >= mActivityUi.getActionBarHeight();
- mActivityUi.setActionBarHideOffset(offset);
- }
-
- /**
- * @return The offset the action bar is being translated upwards by
- */
- public int getHideOffset() {
- return mActivityUi.getActionBarHideOffset();
- }
-
- public int getActionBarHeight() {
- return mActivityUi.getActionBarHeight();
- }
-
- /**
- * Saves the current state of the action bar into a provided {@link Bundle}
- */
- public void saveInstanceState(Bundle outState) {
- outState.putBoolean(KEY_IS_SLID_UP, mIsActionBarSlidUp);
- outState.putBoolean(KEY_IS_FADED_OUT, mSearchBox.isFadedOut());
- outState.putBoolean(KEY_IS_EXPANDED, mSearchBox.isExpanded());
- }
-
- /**
- * Restores the action bar state from a provided {@link Bundle}.
- */
- public void restoreInstanceState(Bundle inState) {
- mIsActionBarSlidUp = inState.getBoolean(KEY_IS_SLID_UP);
-
- final boolean isSearchBoxFadedOut = inState.getBoolean(KEY_IS_FADED_OUT);
- if (isSearchBoxFadedOut) {
- if (!mSearchBox.isFadedOut()) {
- mSearchBox.setVisible(false);
- }
- } else if (mSearchBox.isFadedOut()) {
- mSearchBox.setVisible(true);
- }
-
- final boolean isSearchBoxExpanded = inState.getBoolean(KEY_IS_EXPANDED);
- if (isSearchBoxExpanded) {
- if (!mSearchBox.isExpanded()) {
- mSearchBox.expand(false, false);
- }
- } else if (mSearchBox.isExpanded()) {
- mSearchBox.collapse(false);
- }
- }
-
- /**
- * This should be called after onCreateOptionsMenu has been called, when the actionbar has
- * been laid out and actually has a height.
- */
- public void restoreActionBarOffset() {
- slideActionBar(mIsActionBarSlidUp /* slideUp */, false /* animate */);
- }
-
- @VisibleForTesting
- public boolean getIsActionBarSlidUp() {
- return mIsActionBarSlidUp;
- }
-}
diff --git a/src/com/android/dialer/widget/EmptyContentView.java b/src/com/android/dialer/widget/EmptyContentView.java
deleted file mode 100644
index 719fd3ff8..000000000
--- a/src/com/android/dialer/widget/EmptyContentView.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.dialer.R;
-
-public class EmptyContentView extends LinearLayout implements View.OnClickListener {
-
- public static final int NO_LABEL = 0;
- public static final int NO_IMAGE = 0;
-
- private ImageView mImageView;
- private TextView mDescriptionView;
- private TextView mActionView;
- private OnEmptyViewActionButtonClickedListener mOnActionButtonClickedListener;
-
- public interface OnEmptyViewActionButtonClickedListener {
- public void onEmptyViewActionButtonClicked();
- }
-
- public EmptyContentView(Context context) {
- this(context, null);
- }
-
- public EmptyContentView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public EmptyContentView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public EmptyContentView(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- setOrientation(LinearLayout.VERTICAL);
-
- final LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- inflater.inflate(R.layout.empty_content_view, this);
- // Don't let touches fall through the empty view.
- setClickable(true);
- mImageView = (ImageView) findViewById(R.id.emptyListViewImage);
- mDescriptionView = (TextView) findViewById(R.id.emptyListViewMessage);
- mActionView = (TextView) findViewById(R.id.emptyListViewAction);
- mActionView.setOnClickListener(this);
- }
-
- public void setDescription(int resourceId) {
- if (resourceId == NO_LABEL) {
- mDescriptionView.setText(null);
- mDescriptionView.setVisibility(View.GONE);
- } else {
- mDescriptionView.setText(resourceId);
- mDescriptionView.setVisibility(View.VISIBLE);
- }
- }
-
- public void setImage(int resourceId) {
- if (resourceId == NO_LABEL) {
- mImageView.setImageDrawable(null);
- mImageView.setVisibility(View.GONE);
- } else {
- mImageView.setImageResource(resourceId);
- mImageView.setVisibility(View.VISIBLE);
- }
- }
-
- public void setActionLabel(int resourceId) {
- if (resourceId == NO_LABEL) {
- mActionView.setText(null);
- mActionView.setVisibility(View.GONE);
- } else {
- mActionView.setText(resourceId);
- mActionView.setVisibility(View.VISIBLE);
- }
- }
-
- public boolean isShowingContent() {
- return mImageView.getVisibility() == View.VISIBLE
- || mDescriptionView.getVisibility() == View.VISIBLE
- || mActionView.getVisibility() == View.VISIBLE;
- }
-
- public void setActionClickedListener(OnEmptyViewActionButtonClickedListener listener) {
- mOnActionButtonClickedListener = listener;
- }
-
- @Override
- public void onClick(View v) {
- if (mOnActionButtonClickedListener != null) {
- mOnActionButtonClickedListener.onEmptyViewActionButtonClicked();
- }
- }
-}
diff --git a/src/com/android/dialer/widget/SearchEditTextLayout.java b/src/com/android/dialer/widget/SearchEditTextLayout.java
deleted file mode 100644
index 4f100dc44..000000000
--- a/src/com/android/dialer/widget/SearchEditTextLayout.java
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * Copyright (C) 2014 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.widget;
-
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.view.KeyEvent;
-import android.view.View;
-import android.widget.EditText;
-import android.widget.FrameLayout;
-
-import com.android.dialer.R;
-import com.android.dialer.util.DialerUtils;
-import com.android.phone.common.animation.AnimUtils;
-
-public class SearchEditTextLayout extends FrameLayout {
- private static final float EXPAND_MARGIN_FRACTION_START = 0.8f;
- private static final int ANIMATION_DURATION = 200;
-
- private OnKeyListener mPreImeKeyListener;
- private int mTopMargin;
- private int mBottomMargin;
- private int mLeftMargin;
- private int mRightMargin;
-
- private float mCollapsedElevation;
-
- /* Subclass-visible for testing */
- protected boolean mIsExpanded = false;
- protected boolean mIsFadedOut = false;
-
- private View mCollapsed;
- private View mExpanded;
- private EditText mSearchView;
- private View mSearchIcon;
- private View mCollapsedSearchBox;
- private View mVoiceSearchButtonView;
- private View mOverflowButtonView;
- private View mBackButtonView;
- private View mExpandedSearchBox;
- private View mClearButtonView;
-
- private ValueAnimator mAnimator;
-
- private Callback mCallback;
-
- /**
- * Listener for the back button next to the search view being pressed
- */
- public interface Callback {
- public void onBackButtonClicked();
- public void onSearchViewClicked();
- }
-
- 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();
- mTopMargin = params.topMargin;
- mBottomMargin = params.bottomMargin;
- mLeftMargin = params.leftMargin;
- mRightMargin = params.rightMargin;
-
- mCollapsedElevation = getElevation();
-
- mCollapsed = findViewById(R.id.search_box_collapsed);
- mExpanded = findViewById(R.id.search_box_expanded);
- mSearchView = (EditText) mExpanded.findViewById(R.id.search_view);
-
- mSearchIcon = findViewById(R.id.search_magnifying_glass);
- mCollapsedSearchBox = findViewById(R.id.search_box_start_search);
- mVoiceSearchButtonView = findViewById(R.id.voice_search_button);
- mOverflowButtonView = findViewById(R.id.dialtacts_options_menu_button);
- mBackButtonView = findViewById(R.id.search_back_button);
- mExpandedSearchBox = findViewById(R.id.search_box_expanded);
- mClearButtonView = findViewById(R.id.search_close_button);
-
- // Convert a long click into a click to expand the search box, and then long click on the
- // search view. This accelerates the long-press scenario for copy/paste.
- mCollapsedSearchBox.setOnLongClickListener(new OnLongClickListener() {
- @Override
- public boolean onLongClick(View view) {
- mCollapsedSearchBox.performClick();
- mSearchView.performLongClick();
- return false;
- }
- });
-
- mSearchView.setOnFocusChangeListener(new OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- if (hasFocus) {
- DialerUtils.showInputMethod(v);
- } else {
- DialerUtils.hideInputMethod(v);
- }
- }
- });
-
- mSearchView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mCallback != null) {
- mCallback.onSearchViewClicked();
- }
- }
- });
-
- mSearchView.addTextChangedListener(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) {
- mClearButtonView.setVisibility(TextUtils.isEmpty(s) ? View.GONE : View.VISIBLE);
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- }
- });
-
- 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;
- }
-
- public void fadeIn() {
- AnimUtils.fadeIn(this, ANIMATION_DURATION);
- mIsFadedOut = false;
- }
-
- public void setVisible(boolean visible) {
- if (visible) {
- setAlpha(1);
- setVisibility(View.VISIBLE);
- mIsFadedOut = false;
- } else {
- setAlpha(0);
- setVisibility(View.GONE);
- mIsFadedOut = true;
- }
- }
-
- public void expand(boolean animate, boolean requestFocus) {
- updateVisibility(true /* isExpand */);
-
- if (animate) {
- AnimUtils.crossFadeViews(mExpanded, mCollapsed, ANIMATION_DURATION);
- mAnimator = ValueAnimator.ofFloat(EXPAND_MARGIN_FRACTION_START, 0f);
- setMargins(EXPAND_MARGIN_FRACTION_START);
- prepareAnimator(true);
- } else {
- mExpanded.setVisibility(View.VISIBLE);
- mExpanded.setAlpha(1);
- setMargins(0f);
- mCollapsed.setVisibility(View.GONE);
- }
-
- // Set 9-patch background. This owns the padding, so we need to restore the original values.
- int paddingTop = this.getPaddingTop();
- int paddingStart = this.getPaddingStart();
- int paddingBottom = this.getPaddingBottom();
- int paddingEnd = this.getPaddingEnd();
- setBackgroundResource(R.drawable.search_shadow);
- setElevation(0);
- setPaddingRelative(paddingStart, paddingTop, paddingEnd, paddingBottom);
-
- if (requestFocus) {
- mSearchView.requestFocus();
- }
- mIsExpanded = true;
- }
-
- public void collapse(boolean animate) {
- updateVisibility(false /* isExpand */);
-
- if (animate) {
- AnimUtils.crossFadeViews(mCollapsed, mExpanded, ANIMATION_DURATION);
- mAnimator = ValueAnimator.ofFloat(0f, 1f);
- prepareAnimator(false);
- } else {
- mCollapsed.setVisibility(View.VISIBLE);
- mCollapsed.setAlpha(1);
- setMargins(1f);
- mExpanded.setVisibility(View.GONE);
- }
-
- mIsExpanded = false;
- setElevation(mCollapsedElevation);
- setBackgroundResource(R.drawable.rounded_corner);
- }
-
- /**
- * Updates the visibility of views depending on whether we will show the expanded or collapsed
- * search view. This helps prevent some jank with the crossfading if we are animating.
- *
- * @param isExpand Whether we are about to show the expanded search box.
- */
- private void updateVisibility(boolean isExpand) {
- int collapsedViewVisibility = isExpand ? View.GONE : View.VISIBLE;
- int expandedViewVisibility = isExpand ? View.VISIBLE : View.GONE;
-
- mSearchIcon.setVisibility(collapsedViewVisibility);
- mCollapsedSearchBox.setVisibility(collapsedViewVisibility);
- mVoiceSearchButtonView.setVisibility(collapsedViewVisibility);
- mOverflowButtonView.setVisibility(collapsedViewVisibility);
- mBackButtonView.setVisibility(expandedViewVisibility);
- // TODO: Prevents keyboard from jumping up in landscape mode after exiting the
- // SearchFragment when the query string is empty. More elegant fix?
- //mExpandedSearchBox.setVisibility(expandedViewVisibility);
- if (TextUtils.isEmpty(mSearchView.getText())) {
- mClearButtonView.setVisibility(View.GONE);
- } else {
- mClearButtonView.setVisibility(expandedViewVisibility);
- }
- }
-
- private void prepareAnimator(final boolean expand) {
- if (mAnimator != null) {
- mAnimator.cancel();
- }
-
- mAnimator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- final Float fraction = (Float) animation.getAnimatedValue();
- setMargins(fraction);
- }
- });
-
- mAnimator.setDuration(ANIMATION_DURATION);
- mAnimator.start();
- }
-
- public boolean isExpanded() {
- return mIsExpanded;
- }
-
- public boolean isFadedOut() {
- return mIsFadedOut;
- }
-
- /**
- * Assigns margins to the search box as a fraction of its maximum margin size
- *
- * @param fraction How large the margins should be as a fraction of their full size
- */
- private void setMargins(float fraction) {
- MarginLayoutParams params = (MarginLayoutParams) getLayoutParams();
- params.topMargin = (int) (mTopMargin * fraction);
- params.bottomMargin = (int) (mBottomMargin * fraction);
- params.leftMargin = (int) (mLeftMargin * fraction);
- params.rightMargin = (int) (mRightMargin * fraction);
- requestLayout();
- }
-}
diff --git a/src/com/android/dialerbind/DatabaseHelperManager.java b/src/com/android/dialerbind/DatabaseHelperManager.java
deleted file mode 100644
index c92993242..000000000
--- a/src/com/android/dialerbind/DatabaseHelperManager.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialerbind;
-
-import android.content.Context;
-
-import com.android.dialer.database.DialerDatabaseHelper;
-
-
-public class DatabaseHelperManager {
- public static DialerDatabaseHelper getDatabaseHelper(Context context) {
- return DialerDatabaseHelper.getInstance(context);
- }
-}
diff --git a/src/com/android/dialerbind/ObjectFactory.java b/src/com/android/dialerbind/ObjectFactory.java
deleted file mode 100644
index 342f39cb9..000000000
--- a/src/com/android/dialerbind/ObjectFactory.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialerbind;
-
-import static com.android.dialer.calllog.CallLogAdapter.CallFetcher;
-
-import android.content.Context;
-import android.support.annotation.Nullable;
-
-import com.android.dialer.calllog.CallLogAdapter;
-import com.android.dialer.calllog.ContactInfoHelper;
-import com.android.dialer.list.RegularSearchFragment;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.service.CachedNumberLookupService;
-import com.android.dialer.service.ExtendedCallInfoService;
-import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
-
-/**
- * Default static binding for various objects.
- */
-public class ObjectFactory {
-
- public static CachedNumberLookupService newCachedNumberLookupService() {
- // no-op
- return null;
- }
-
- public static String getFilteredNumberProviderAuthority() {
- return "com.android.dialer.database.filterednumberprovider";
- }
-
- public static String getVoicemailArchiveProviderAuthority() {
- return "com.android.dialer.database.voicemailarchiveprovider";
- }
-
- public static boolean isVoicemailArchiveEnabled(Context context) {
- return false;
- }
-
- public static boolean isVoicemailShareEnabled(Context context) {
- return false;
- }
-
- public static boolean isNewBlockingEnabled(Context context) {
- return true;
- }
-
- @Nullable
- public static ExtendedCallInfoService newExtendedCallInfoService(Context context) {
- return null;
- }
-
- /**
- * Create a new instance of the call log adapter.
- * @param context The context to use.
- * @param callFetcher Instance of call fetcher to use.
- * @param contactInfoHelper Instance of contact info helper class to use.
- * @return Instance of CallLogAdapter.
- */
- public static CallLogAdapter newCallLogAdapter(
- Context context,
- CallFetcher callFetcher,
- ContactInfoHelper contactInfoHelper,
- VoicemailPlaybackPresenter voicemailPlaybackPresenter,
- int activityType) {
- return new CallLogAdapter(
- context,
- callFetcher,
- contactInfoHelper,
- voicemailPlaybackPresenter,
- activityType);
- }
-
- public static Logger getLoggerInstance() {
- // no-op
- return null;
- }
-
- public static RegularSearchFragment newRegularSearchFragment() {
- return new RegularSearchFragment();
- }
-}