summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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();
- }
-}