diff options
Diffstat (limited to 'java/com/android/dialer')
18 files changed, 408 insertions, 69 deletions
diff --git a/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java b/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java index 680424a78..bca4265b6 100644 --- a/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java +++ b/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java @@ -279,7 +279,7 @@ public class PhoneCallDetailsHelper return; } - if (PhoneNumberUtils.isEmergencyNumber(details.number.toString())) { + if (PhoneNumberUtils.isEmergencyNumber(details.displayNumber)) { views.nameView.setText(R.string.emergency_number); views.nameView.setTextDirection(View.TEXT_DIRECTION_INHERIT); return; diff --git a/java/com/android/dialer/calllog/database/Coalescer.java b/java/com/android/dialer/calllog/database/Coalescer.java index ed09eea68..6b1a9e1f5 100644 --- a/java/com/android/dialer/calllog/database/Coalescer.java +++ b/java/com/android/dialer/calllog/database/Coalescer.java @@ -18,6 +18,7 @@ package com.android.dialer.calllog.database; import android.content.ContentValues; import android.database.Cursor; import android.database.MatrixCursor; +import android.provider.CallLog.Calls; import android.support.annotation.NonNull; import android.support.annotation.WorkerThread; import android.telecom.PhoneAccountHandle; @@ -151,7 +152,6 @@ public class Coalescer { TelecomUtil.composePhoneAccountHandle( row2.getAsString(AnnotatedCallLog.PHONE_ACCOUNT_COMPONENT_NAME), row2.getAsString(AnnotatedCallLog.PHONE_ACCOUNT_ID)); - if (!Objects.equals(phoneAccount1, phoneAccount2)) { return false; } @@ -161,7 +161,7 @@ public class Coalescer { return false; } - if (!meetsAssistedDialingCriteria(row1, row2)) { + if (!meetsCallFeatureCriteria(row1, row2)) { return false; } @@ -185,20 +185,25 @@ public class Coalescer { } /** - * Returns a boolean indicating whether or not FEATURES_ASSISTED_DIALING is mutually exclusive - * between two rows. + * Returns true if column {@link AnnotatedCallLog#FEATURES} of the two given rows indicate that + * they can be coalesced. */ - private static boolean meetsAssistedDialingCriteria(ContentValues row1, ContentValues row2) { - int row1Assisted = - row1.getAsInteger(AnnotatedCallLog.FEATURES) - & TelephonyManagerCompat.FEATURES_ASSISTED_DIALING; - int row2Assisted = - row2.getAsInteger(AnnotatedCallLog.FEATURES) - & TelephonyManagerCompat.FEATURES_ASSISTED_DIALING; - - // FEATURES_ASSISTED_DIALING should not be combined with calls that are - // !FEATURES_ASSISTED_DIALING - return row1Assisted == row2Assisted; + private static boolean meetsCallFeatureCriteria(ContentValues row1, ContentValues row2) { + int row1Features = row1.getAsInteger(AnnotatedCallLog.FEATURES); + int row2Features = row2.getAsInteger(AnnotatedCallLog.FEATURES); + + // A row with FEATURES_ASSISTED_DIALING should not be combined with one without it. + if ((row1Features & TelephonyManagerCompat.FEATURES_ASSISTED_DIALING) + != (row2Features & TelephonyManagerCompat.FEATURES_ASSISTED_DIALING)) { + return false; + } + + // A video call should not be combined with one that is not a video call. + if ((row1Features & Calls.FEATURES_VIDEO) != (row2Features & Calls.FEATURES_VIDEO)) { + return false; + } + + return true; } /** diff --git a/java/com/android/dialer/calllog/ui/menu/Modules.java b/java/com/android/dialer/calllog/ui/menu/Modules.java index aeb69a7a8..e316f66b5 100644 --- a/java/com/android/dialer/calllog/ui/menu/Modules.java +++ b/java/com/android/dialer/calllog/ui/menu/Modules.java @@ -27,8 +27,10 @@ import com.android.dialer.callintent.CallInitiationType; import com.android.dialer.calllog.model.CoalescedRow; import com.android.dialer.calllogutils.CallLogEntryText; import com.android.dialer.calllogutils.NumberAttributesConverter; +import com.android.dialer.duo.DuoConstants; import com.android.dialer.glidephotomanager.PhotoInfo; import com.android.dialer.historyitemactions.DividerModule; +import com.android.dialer.historyitemactions.DuoCallModule; import com.android.dialer.historyitemactions.HistoryItemActionModule; import com.android.dialer.historyitemactions.IntentModule; import com.android.dialer.historyitemactions.SharedModules; @@ -134,9 +136,15 @@ final class Modules { // Add a video item if (1) the call log entry is for a video call, and (2) the call is not spam. if ((row.getFeatures() & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO && !row.getNumberAttributes().getIsSpam()) { + boolean isDuoCall = + DuoConstants.PHONE_ACCOUNT_COMPONENT_NAME + .flattenToString() + .equals(row.getPhoneAccountComponentName()); modules.add( - IntentModule.newVideoCallModule( - context, normalizedNumber, phoneAccountHandle, CallInitiationType.Type.CALL_LOG)); + isDuoCall + ? new DuoCallModule(context, normalizedNumber, CallInitiationType.Type.CALL_LOG) + : IntentModule.newCarrierVideoCallModule( + context, normalizedNumber, phoneAccountHandle, CallInitiationType.Type.CALL_LOG)); } // TODO(zachh): Also show video option if the call log entry is for an audio call but video diff --git a/java/com/android/dialer/calllogutils/res/values/strings.xml b/java/com/android/dialer/calllogutils/res/values/strings.xml index bc19ce22a..1c1f31446 100644 --- a/java/com/android/dialer/calllogutils/res/values/strings.xml +++ b/java/com/android/dialer/calllogutils/res/values/strings.xml @@ -131,7 +131,7 @@ <!-- String to be displayed to indicate in the call log that a call just now occurred. --> <string name="just_now">Just now</string> - <!-- Text to show in call log for a carrier video call. [CHAR LIMIT=30] --> + <!-- Text to show in call log for a carrier video call. [CHAR LIMIT=31] --> <string name="new_call_log_carrier_video">Carrier video</string> <!-- Text to show in call log for a duo video call. [CHAR LIMIT=30] --> @@ -145,4 +145,4 @@ <!-- String used to display calls from spam numbers in the call log. [CHAR LIMIT=30] --> <string name="new_call_log_secondary_spam">Spam</string> -</resources>
\ No newline at end of file +</resources> diff --git a/java/com/android/dialer/constants/ActivityRequestCodes.java b/java/com/android/dialer/constants/ActivityRequestCodes.java index 66c38fa64..7fd619ba9 100644 --- a/java/com/android/dialer/constants/ActivityRequestCodes.java +++ b/java/com/android/dialer/constants/ActivityRequestCodes.java @@ -16,8 +16,6 @@ package com.android.dialer.constants; -import com.android.dialer.duo.Duo; - /** * Class containing {@link android.app.Activity#onActivityResult(int, int, android.content.Intent)} * request codes. @@ -32,7 +30,9 @@ public final class ActivityRequestCodes { /** Request code for {@link com.android.dialer.callcomposer.CallComposerActivity} intent. */ public static final int DIALTACTS_CALL_COMPOSER = 2; - /** Request code for {@link Duo#getIntent(android.content.Context, String)}. */ + /** + * Request code for {@link com.android.dialer.duo.Duo#getIntent(android.content.Context, String)}. + */ public static final int DIALTACTS_DUO = 3; /** Request code for {@link com.android.dialer.calldetails.OldCallDetailsActivity} intent. */ diff --git a/java/com/android/dialer/database/DialerDatabaseHelper.java b/java/com/android/dialer/database/DialerDatabaseHelper.java index efff11ecc..b172d7039 100644 --- a/java/com/android/dialer/database/DialerDatabaseHelper.java +++ b/java/com/android/dialer/database/DialerDatabaseHelper.java @@ -32,21 +32,23 @@ import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.Directory; -import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import android.support.annotation.WorkerThread; import android.text.TextUtils; import com.android.contacts.common.R; import com.android.contacts.common.util.StopWatch; import com.android.dialer.common.LogUtil; -import com.android.dialer.common.concurrent.DialerExecutor.Worker; +import com.android.dialer.common.concurrent.DefaultFutureCallback; import com.android.dialer.common.concurrent.DialerExecutorComponent; +import com.android.dialer.common.concurrent.DialerFutureSerializer; import com.android.dialer.common.database.Selection; import com.android.dialer.configprovider.ConfigProviderBindings; import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns; import com.android.dialer.smartdial.util.SmartDialNameMatcher; import com.android.dialer.smartdial.util.SmartDialPrefix; import com.android.dialer.util.PermissionsUtil; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.MoreExecutors; import java.util.ArrayList; import java.util.HashSet; import java.util.Objects; @@ -85,6 +87,8 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper { private static final int MAX_ENTRIES = 20; private final Context context; + private final DialerFutureSerializer dialerFutureSerializer = new DialerFutureSerializer(); + private boolean isTestInstance = false; protected DialerDatabaseHelper(Context context, String databaseName, int dbVersion) { @@ -344,11 +348,19 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper { */ public void startSmartDialUpdateThread(boolean forceUpdate) { if (PermissionsUtil.hasContactsReadPermissions(context)) { - DialerExecutorComponent.get(context) - .dialerExecutorFactory() - .createNonUiTaskBuilder(new UpdateSmartDialWorker()) - .build() - .executeParallel(forceUpdate); + Futures.addCallback( + // Serialize calls to updateSmartDialDatabase. Use FutureSerializer instead of + // synchronizing on the method to prevent deadlocking thread pool. FutureSerializer + // provides the guarantee that the next AsyncCallable won't even be submitted until the + // ListenableFuture returned by the previous one completes. See a bug. + dialerFutureSerializer.submit( + () -> { + updateSmartDialDatabase(forceUpdate); + return null; + }, + DialerExecutorComponent.get(context).backgroundExecutor()), + new DefaultFutureCallback<>(), + MoreExecutors.directExecutor()); } } @@ -657,7 +669,7 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper { * @param forceUpdate If set to true, update the database by reloading all contacts. */ @WorkerThread - public synchronized void updateSmartDialDatabase(boolean forceUpdate) { + public void updateSmartDialDatabase(boolean forceUpdate) { LogUtil.enterBlock("DialerDatabaseHelper.updateSmartDialDatabase"); final SQLiteDatabase db = getWritableDatabase(); @@ -1296,14 +1308,4 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper { return false; } } - - private class UpdateSmartDialWorker implements Worker<Boolean, Void> { - - @Nullable - @Override - public Void doInBackground(Boolean forceUpdate) throws Throwable { - updateSmartDialDatabase(forceUpdate); - return null; - } - } } diff --git a/java/com/android/dialer/duo/PlaceDuoCallNotifier.java b/java/com/android/dialer/duo/PlaceDuoCallNotifier.java new file mode 100644 index 000000000..8fde981a0 --- /dev/null +++ b/java/com/android/dialer/duo/PlaceDuoCallNotifier.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.dialer.duo; + +import android.content.Context; +import android.content.Intent; +import android.support.v4.content.LocalBroadcastManager; +import com.android.dialer.common.LogUtil; + +/** Notifies that a Duo video call should be started. */ +public final class PlaceDuoCallNotifier { + + private PlaceDuoCallNotifier() {} + + /** + * Broadcasts an intent notifying that a Duo call should be started. + * + * <p>See {@link PlaceDuoCallReceiver} for how the intent is handled. + * + * @param phoneNumber The number to start a Duo call. It can be of any format. + */ + public static void notify(Context context, String phoneNumber) { + LogUtil.enterBlock("PlaceDuoCallNotifier.notify"); + + Intent intent = new Intent(); + intent.setAction(PlaceDuoCallReceiver.ACTION_START_DUO_CALL); + intent.putExtra(PlaceDuoCallReceiver.EXTRA_PHONE_NUMBER, phoneNumber); + + LocalBroadcastManager.getInstance(context).sendBroadcast(intent); + } +} diff --git a/java/com/android/dialer/duo/PlaceDuoCallReceiver.java b/java/com/android/dialer/duo/PlaceDuoCallReceiver.java new file mode 100644 index 000000000..913132c88 --- /dev/null +++ b/java/com/android/dialer/duo/PlaceDuoCallReceiver.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.dialer.duo; + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import com.android.dialer.common.Assert; +import com.android.dialer.common.LogUtil; +import com.android.dialer.constants.ActivityRequestCodes; + +/** A {@link BroadcastReceiver} that starts a Duo video call. */ +public final class PlaceDuoCallReceiver extends BroadcastReceiver { + + static final String ACTION_START_DUO_CALL = "start_duo_call"; + static final String EXTRA_PHONE_NUMBER = "phone_number"; + + /** + * {@link Activity} needed to launch Duo. + * + * <p>A Duo call can only be placed via {@link Activity#startActivityForResult(Intent, int)}. + */ + private final Activity activity; + + /** Returns an {@link IntentFilter} containing all actions accepted by this broadcast receiver. */ + public static IntentFilter getIntentFilter() { + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(ACTION_START_DUO_CALL); + return intentFilter; + } + + public PlaceDuoCallReceiver(Activity activity) { + this.activity = activity; + } + + @Override + public void onReceive(Context context, Intent intent) { + LogUtil.enterBlock("PlaceDuoCallReceiver.onReceive"); + + String action = intent.getAction(); + + switch (Assert.isNotNull(action)) { + case ACTION_START_DUO_CALL: + startDuoCall(context, intent); + break; + default: + throw new IllegalStateException("Unsupported action: " + action); + } + } + + private void startDuoCall(Context context, Intent intent) { + LogUtil.enterBlock("PlaceDuoCallReceiver.startDuoCall"); + + Assert.checkArgument(intent.hasExtra(EXTRA_PHONE_NUMBER)); + String phoneNumber = intent.getStringExtra(EXTRA_PHONE_NUMBER); + + Duo duo = DuoComponent.get(context).getDuo(); + activity.startActivityForResult( + duo.getIntent(context, phoneNumber), ActivityRequestCodes.DIALTACTS_DUO); + } +} diff --git a/java/com/android/dialer/historyitemactions/DuoCallModule.java b/java/com/android/dialer/historyitemactions/DuoCallModule.java new file mode 100644 index 000000000..b0d6a11fc --- /dev/null +++ b/java/com/android/dialer/historyitemactions/DuoCallModule.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dialer.historyitemactions; + +import android.Manifest.permission; +import android.content.Context; +import android.support.annotation.RequiresPermission; +import com.android.dialer.callintent.CallInitiationType; +import com.android.dialer.callintent.CallIntentBuilder; +import com.android.dialer.duo.Duo; +import com.android.dialer.duo.DuoComponent; +import com.android.dialer.duo.PlaceDuoCallNotifier; +import com.android.dialer.precall.PreCall; + +/** {@link HistoryItemActionModule} for making a Duo call. */ +public class DuoCallModule implements HistoryItemActionModule { + + private final Context context; + private final String phoneNumber; + private final CallInitiationType.Type callInitiationType; + + /** + * Creates a module for making a Duo call. + * + * @param phoneNumber The number to start a Duo call. It can be of any format. + */ + public DuoCallModule( + Context context, String phoneNumber, CallInitiationType.Type callInitiationType) { + this.context = context; + this.phoneNumber = phoneNumber; + this.callInitiationType = callInitiationType; + } + + @Override + public int getStringId() { + return R.string.video_call; + } + + @Override + public int getDrawableId() { + return R.drawable.quantum_ic_videocam_vd_white_24; + } + + @Override + @RequiresPermission(permission.READ_PHONE_STATE) + public boolean onClick() { + if (canPlaceDuoCall(context, phoneNumber)) { + PlaceDuoCallNotifier.notify(context, phoneNumber); + } else { + // If a Duo call can't be placed, fall back to an IMS video call. + PreCall.start( + context, new CallIntentBuilder(phoneNumber, callInitiationType).setIsVideoCall(true)); + } + + return true; // Close the bottom sheet. + } + + private boolean canPlaceDuoCall(Context context, String phoneNumber) { + Duo duo = DuoComponent.get(context).getDuo(); + + return duo.isInstalled(context) + && duo.isEnabled(context) + && duo.isActivated(context) + && duo.isReachable(context, phoneNumber); + } +} diff --git a/java/com/android/dialer/historyitemactions/IntentModule.java b/java/com/android/dialer/historyitemactions/IntentModule.java index efb10e8bb..a5236c57a 100644 --- a/java/com/android/dialer/historyitemactions/IntentModule.java +++ b/java/com/android/dialer/historyitemactions/IntentModule.java @@ -77,7 +77,7 @@ public class IntentModule implements HistoryItemActionModule { R.drawable.quantum_ic_call_white_24); } - public static IntentModule newVideoCallModule( + public static IntentModule newCarrierVideoCallModule( Context context, String number, @Nullable PhoneAccountHandle phoneAccountHandle, diff --git a/java/com/android/dialer/main/impl/MainActivity.java b/java/com/android/dialer/main/impl/MainActivity.java index 2046b048f..3f660f56c 100644 --- a/java/com/android/dialer/main/impl/MainActivity.java +++ b/java/com/android/dialer/main/impl/MainActivity.java @@ -24,6 +24,7 @@ import com.android.dialer.blockreportspam.ShowBlockReportSpamDialogReceiver; import com.android.dialer.calllog.config.CallLogConfigComponent; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; +import com.android.dialer.duo.PlaceDuoCallReceiver; import com.android.dialer.interactions.PhoneNumberInteraction.DisambigDialogDismissedListener; import com.android.dialer.interactions.PhoneNumberInteraction.InteractionErrorCode; import com.android.dialer.interactions.PhoneNumberInteraction.InteractionErrorListener; @@ -47,6 +48,9 @@ public class MainActivity extends TransactionSafeActivity */ private ShowBlockReportSpamDialogReceiver showBlockReportSpamDialogReceiver; + /** {@link android.content.BroadcastReceiver} that starts a Duo call. */ + private PlaceDuoCallReceiver placeDuoCallReceiver; + public static Intent getShowCallLogIntent(Context context) { return getShowTabIntent(context, TabIndex.CALL_LOG); } @@ -79,6 +83,7 @@ public class MainActivity extends TransactionSafeActivity activePeer.onActivityCreate(savedInstanceState); showBlockReportSpamDialogReceiver = new ShowBlockReportSpamDialogReceiver(getFragmentManager()); + placeDuoCallReceiver = new PlaceDuoCallReceiver(/* activity = */ this); } protected MainActivityPeer getNewPeer() { @@ -104,6 +109,8 @@ public class MainActivity extends TransactionSafeActivity LocalBroadcastManager.getInstance(this) .registerReceiver( showBlockReportSpamDialogReceiver, ShowBlockReportSpamDialogReceiver.getIntentFilter()); + LocalBroadcastManager.getInstance(this) + .registerReceiver(placeDuoCallReceiver, PlaceDuoCallReceiver.getIntentFilter()); } @Override @@ -118,6 +125,7 @@ public class MainActivity extends TransactionSafeActivity activePeer.onActivityPause(); LocalBroadcastManager.getInstance(this).unregisterReceiver(showBlockReportSpamDialogReceiver); + LocalBroadcastManager.getInstance(this).unregisterReceiver(placeDuoCallReceiver); } @Override diff --git a/java/com/android/dialer/main/impl/OldMainActivityPeer.java b/java/com/android/dialer/main/impl/OldMainActivityPeer.java index 6d78a5171..c15d7c1a8 100644 --- a/java/com/android/dialer/main/impl/OldMainActivityPeer.java +++ b/java/com/android/dialer/main/impl/OldMainActivityPeer.java @@ -68,10 +68,12 @@ import com.android.dialer.callcomposer.CallComposerActivity; import com.android.dialer.calldetails.OldCallDetailsActivity; import com.android.dialer.callintent.CallIntentBuilder; import com.android.dialer.callintent.CallSpecificAppData; +import com.android.dialer.calllog.CallLogComponent; import com.android.dialer.calllog.config.CallLogConfigComponent; import com.android.dialer.calllog.ui.NewCallLogFragment; import com.android.dialer.common.FragmentUtils.FragmentUtilListener; import com.android.dialer.common.LogUtil; +import com.android.dialer.common.concurrent.DefaultFutureCallback; import com.android.dialer.common.concurrent.DialerExecutorComponent; import com.android.dialer.common.concurrent.ThreadUtil; import com.android.dialer.common.concurrent.UiListener; @@ -96,6 +98,7 @@ import com.android.dialer.main.MainActivityPeer; import com.android.dialer.main.impl.bottomnav.BottomNavBar; import com.android.dialer.main.impl.bottomnav.BottomNavBar.OnBottomNavTabSelectedListener; import com.android.dialer.main.impl.bottomnav.BottomNavBar.TabIndex; +import com.android.dialer.main.impl.bottomnav.MissedCallCountObserver; import com.android.dialer.main.impl.toolbar.MainToolbar; import com.android.dialer.metrics.Metrics; import com.android.dialer.metrics.MetricsComponent; @@ -113,7 +116,9 @@ import com.android.dialer.voicemail.listui.error.VoicemailStatusCorruptionHandle import com.android.dialer.voicemailstatus.VisualVoicemailEnabledChecker; import com.android.dialer.voicemailstatus.VoicemailStatusHelper; import com.android.voicemail.VoicemailComponent; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; import java.util.Locale; import java.util.concurrent.TimeUnit; @@ -184,7 +189,9 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen private BottomNavBar bottomNav; private MainBottomNavBarBottomNavTabListener bottomNavTabListener; private View snackbarContainer; + private MissedCallCountObserver missedCallCountObserver; private UiListener<String> getLastOutgoingCallListener; + private UiListener<Integer> missedCallObserverUiListener; public static Intent getShowTabIntent(Context context, @TabIndex int tabIndex) { Intent intent = new Intent(context, MainActivity.class); @@ -219,6 +226,9 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen getLastOutgoingCallListener = DialerExecutorComponent.get(activity) .createUiListener(activity.getFragmentManager(), "Query last phone number"); + missedCallObserverUiListener = + DialerExecutorComponent.get(activity) + .createUiListener(activity.getFragmentManager(), "Missed call observer"); } private void initLayout(Bundle savedInstanceState) { @@ -247,9 +257,13 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen boolean showVoicemailTab = canVoicemailTabBeShown(activity); bottomNav.showVoicemail(showVoicemailTab); + missedCallCountObserver = + new MissedCallCountObserver( + activity.getApplicationContext(), bottomNav, missedCallObserverUiListener); + callLogFragmentListener = new MainCallLogFragmentListener( - activity, activity.getContentResolver(), bottomNav, toolbar); + activity, activity.getContentResolver(), bottomNav, toolbar, bottomNavTabListener); bottomNav.addOnTabSelectedListener(callLogFragmentListener); searchController = @@ -471,6 +485,13 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen */ bottomNavTabListener.ensureCorrectCallLogShown(); + if (bottomNavTabListener.newCallLogFragmentActive()) { + missedCallCountObserver.onChange(false); // Set the initial value for the badge + activity + .getContentResolver() + .registerContentObserver(Calls.CONTENT_URI, true, missedCallCountObserver); + } + // add 1 sec delay to get memory snapshot so that dialer wont react slowly on resume. ThreadUtil.postDelayedOnUiThread( () -> @@ -489,6 +510,7 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen public void onActivityPause() { searchController.onActivityPause(); LocalBroadcastManager.getInstance(activity).unregisterReceiver(disableNewCallLogReceiver); + activity.getContentResolver().unregisterContentObserver(missedCallCountObserver); } @Override @@ -819,6 +841,7 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen private final Context context; private final BottomNavBar bottomNavBar; private final Toolbar toolbar; + private final MainBottomNavBarBottomNavTabListener bottomNavTabListener; private @TabIndex int currentTab = TabIndex.SPEED_DIAL; private long timeSelected = -1; @@ -837,11 +860,13 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen Context context, ContentResolver contentResolver, BottomNavBar bottomNavBar, - Toolbar toolbar) { + Toolbar toolbar, + MainBottomNavBarBottomNavTabListener bottomNavTabListener) { callLogQueryHandler = new CallLogQueryHandler(context, contentResolver, this); this.context = context; this.bottomNavBar = bottomNavBar; this.toolbar = toolbar; + this.bottomNavTabListener = bottomNavTabListener; } private void registerVoicemailStatusContentObserver(Context context) { @@ -953,8 +978,15 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen } private void markMissedCallsAsReadAndRemoveNotification() { - callLogQueryHandler.markMissedCallsAsRead(); - CallLogNotificationsService.cancelAllMissedCalls(context); + if (bottomNavTabListener.newCallLogFragmentActive()) { + Futures.addCallback( + CallLogComponent.get(context).getClearMissedCalls().clearAll(), + new DefaultFutureCallback<>(), + MoreExecutors.directExecutor()); + } else { + callLogQueryHandler.markMissedCallsAsRead(); + CallLogNotificationsService.cancelAllMissedCalls(context); + } } private void setCurrentTab(@TabIndex int tabIndex) { @@ -969,7 +1001,9 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen activityIsAlive = true; registerVoicemailStatusContentObserver(context); callLogQueryHandler.fetchVoicemailStatus(); - callLogQueryHandler.fetchMissedCallsUnreadCount(); + if (!bottomNavTabListener.newCallLogFragmentActive()) { + callLogQueryHandler.fetchMissedCallsUnreadCount(); + } // Reset the tab on resume to restart the timer setCurrentTab(bottomNavBar.getSelectedTab()); } @@ -978,7 +1012,11 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen public void onActivityStop(boolean changingConfigurations, boolean keyguardLocked) { context.getContentResolver().unregisterContentObserver(voicemailStatusObserver); activityIsAlive = false; - if (viewedCallLogTabPastTimeThreshold() && !changingConfigurations && !keyguardLocked) { + // The new call log fragment handles this on its own. + if (!bottomNavTabListener.newCallLogFragmentActive() + && viewedCallLogTabPastTimeThreshold() + && !changingConfigurations + && !keyguardLocked) { markMissedCallsAsReadAndRemoveNotification(); } } @@ -1239,6 +1277,14 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen } } + boolean newCallLogFragmentActive() { + return supportFragmentManager.findFragmentByTag(CALL_LOG_TAG) != null + || (fragmentManager.findFragmentByTag(CALL_LOG_TAG) == null + && CallLogConfigComponent.get(activity) + .callLogConfig() + .isNewCallLogFragmentEnabled()); + } + @Override public void onContactsSelected() { LogUtil.enterBlock("MainBottomNavBarBottomNavTabListener.onContactsSelected"); diff --git a/java/com/android/dialer/main/impl/bottomnav/MissedCallCountObserver.java b/java/com/android/dialer/main/impl/bottomnav/MissedCallCountObserver.java new file mode 100644 index 000000000..276063474 --- /dev/null +++ b/java/com/android/dialer/main/impl/bottomnav/MissedCallCountObserver.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.dialer.main.impl.bottomnav; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.database.ContentObserver; +import android.database.Cursor; +import android.provider.CallLog.Calls; +import com.android.dialer.common.concurrent.DialerExecutorComponent; +import com.android.dialer.common.concurrent.UiListener; +import com.android.dialer.main.impl.bottomnav.BottomNavBar.TabIndex; +import com.android.dialer.util.PermissionsUtil; +import com.google.common.util.concurrent.ListenableFuture; + +/** + * Observes the call log and updates the badge count to show the number of unread missed calls. + * + * <p>Used only when the new call log fragment is enabled. + */ +public final class MissedCallCountObserver extends ContentObserver { + private final Context appContext; + private final BottomNavBar bottomNavBar; + private final UiListener<Integer> uiListener; + + public MissedCallCountObserver( + Context appContext, BottomNavBar bottomNavBar, UiListener<Integer> uiListener) { + super(null); + this.appContext = appContext; + this.bottomNavBar = bottomNavBar; + this.uiListener = uiListener; + } + + @SuppressLint("MissingPermission") + @Override + public void onChange(boolean selfChange) { + ListenableFuture<Integer> countFuture = + DialerExecutorComponent.get(appContext) + .backgroundExecutor() + .submit( + () -> { + if (!PermissionsUtil.hasCallLogReadPermissions(appContext)) { + return 0; + } + try (Cursor cursor = + appContext + .getContentResolver() + .query( + Calls.CONTENT_URI, + new String[] {Calls._ID}, + Calls.IS_READ + " = ? AND " + Calls.TYPE + " = ?", + new String[] {"0", Integer.toString(Calls.MISSED_TYPE)}, + /* sortOrder= */ null)) { + return cursor == null ? 0 : cursor.getCount(); + } + }); + uiListener.listen( + appContext, + countFuture, + count -> bottomNavBar.setNotificationCount(TabIndex.CALL_LOG, count == null ? 0 : count), + throwable -> { + throw new RuntimeException(throwable); + }); + } +} diff --git a/java/com/android/dialer/oem/AndroidManifest.xml b/java/com/android/dialer/oem/AndroidManifest.xml index a781521c6..94cd4fcba 100644 --- a/java/com/android/dialer/oem/AndroidManifest.xml +++ b/java/com/android/dialer/oem/AndroidManifest.xml @@ -19,5 +19,5 @@ <uses-permission android:name="com.cequint.ecid.CALLER_ID_LOOKUP"/> <!-- This is used by MotorolaInCallUiNotifier to send broadcasts. --> - <uses-permission android:name="com.motorola.incallui.action.INCOMING_CALL_VISIBILITY_CHANGED"/> -</manifest>
\ No newline at end of file + <uses-permission android:name="com.motorola.incallui.permission.INCOMING_CALL_VISIBILITY_CHANGED"/> +</manifest> diff --git a/java/com/android/dialer/rootcomponentgenerator/processor/MetadataGeneratingStep.java b/java/com/android/dialer/rootcomponentgenerator/processor/MetadataGeneratingStep.java index 70ad1b2e7..b7d31c0e9 100644 --- a/java/com/android/dialer/rootcomponentgenerator/processor/MetadataGeneratingStep.java +++ b/java/com/android/dialer/rootcomponentgenerator/processor/MetadataGeneratingStep.java @@ -18,7 +18,6 @@ package com.android.dialer.rootcomponentgenerator.processor; import static javax.tools.Diagnostic.Kind.ERROR; -import com.android.dialer.rootcomponentgenerator.annotation.DialerComponent; import com.android.dialer.rootcomponentgenerator.annotation.InstallIn; import com.android.dialer.rootcomponentgenerator.annotation.RootComponentGeneratorMetadata; import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep; @@ -26,6 +25,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.SetMultimap; import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.TypeSpec; +import dagger.Subcomponent; import java.lang.annotation.Annotation; import java.util.Collections; import java.util.Set; @@ -33,7 +33,7 @@ import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; /** - * Genereates metadata for every type annotated by {@link InstallIn} and {@link DialerComponent}. + * Genereates metadata for every type annotated by {@link InstallIn} and {@link Subcomponent}. * * <p>The metadata has the information where the annotated types are and it is used by annotation * processor when the processor tries to generate root component. @@ -48,15 +48,15 @@ final class MetadataGeneratingStep implements ProcessingStep { @Override public Set<? extends Class<? extends Annotation>> annotations() { - return ImmutableSet.of(DialerComponent.class, InstallIn.class); + return ImmutableSet.of(Subcomponent.class, InstallIn.class); } @Override public Set<? extends Element> process( SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) { - for (Element element : elementsByAnnotation.get(DialerComponent.class)) { - generateMetadataFor(DialerComponent.class, element); + for (Element element : elementsByAnnotation.get(Subcomponent.class)) { + generateMetadataFor(Subcomponent.class, element); } for (Element element : elementsByAnnotation.get(InstallIn.class)) { if (element.getAnnotation(InstallIn.class).variants().length == 0) { diff --git a/java/com/android/dialer/rootcomponentgenerator/processor/RootComponentGeneratingStep.java b/java/com/android/dialer/rootcomponentgenerator/processor/RootComponentGeneratingStep.java index 86a030856..9b97adafd 100644 --- a/java/com/android/dialer/rootcomponentgenerator/processor/RootComponentGeneratingStep.java +++ b/java/com/android/dialer/rootcomponentgenerator/processor/RootComponentGeneratingStep.java @@ -20,7 +20,6 @@ import static com.google.auto.common.AnnotationMirrors.getAnnotationValue; import static com.google.auto.common.MoreElements.getAnnotationMirror; import static com.google.auto.common.MoreElements.isAnnotationPresent; -import com.android.dialer.rootcomponentgenerator.annotation.DialerComponent; import com.android.dialer.rootcomponentgenerator.annotation.DialerRootComponent; import com.android.dialer.rootcomponentgenerator.annotation.DialerVariant; import com.android.dialer.rootcomponentgenerator.annotation.InstallIn; @@ -36,6 +35,7 @@ import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.TypeSpec; import dagger.Component; +import dagger.Subcomponent; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Collections; @@ -90,11 +90,7 @@ final class RootComponentGeneratingStep implements ProcessingStep { .addAnnotation(Singleton.class); for (TypeElement componentWithSuperInterface : componentList) { rootComponentClassBuilder.addSuperinterface( - ClassName.get(componentWithSuperInterface) - .peerClass( - RootComponentUtils.GENERATED_COMPONENT_PREFIX - + componentWithSuperInterface.getSimpleName()) - .nestedClass("HasComponent")); + ClassName.get(componentWithSuperInterface).nestedClass("HasComponent")); } AnnotationSpec.Builder componentAnnotation = AnnotationSpec.builder(Component.class); for (TypeElement annotatedElement : componentModuleMap.get(dialerVariant)) { @@ -108,7 +104,7 @@ final class RootComponentGeneratingStep implements ProcessingStep { private List<TypeElement> generateComponentList() { List<TypeElement> list = new ArrayList<>(); - extractInfoFromMetadata(DialerComponent.class, list::add); + extractInfoFromMetadata(Subcomponent.class, list::add); return list; } diff --git a/java/com/android/dialer/rootcomponentgenerator/processor/RootComponentProcessor.java b/java/com/android/dialer/rootcomponentgenerator/processor/RootComponentProcessor.java index 5e083d29d..56caa9ea4 100644 --- a/java/com/android/dialer/rootcomponentgenerator/processor/RootComponentProcessor.java +++ b/java/com/android/dialer/rootcomponentgenerator/processor/RootComponentProcessor.java @@ -29,7 +29,6 @@ public class RootComponentProcessor extends BasicAnnotationProcessor { @Override protected Iterable<? extends ProcessingStep> initSteps() { return ImmutableList.of( - new ComponentGeneratingStep(processingEnv), new MetadataGeneratingStep(processingEnv), new RootComponentGeneratingStep(processingEnv)); } diff --git a/java/com/android/dialer/telecom/TelecomUtil.java b/java/com/android/dialer/telecom/TelecomUtil.java index f05ec202d..2608cb2aa 100644 --- a/java/com/android/dialer/telecom/TelecomUtil.java +++ b/java/com/android/dialer/telecom/TelecomUtil.java @@ -26,7 +26,6 @@ import android.net.Uri; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.provider.CallLog.Calls; -import android.provider.Settings; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.RequiresPermission; @@ -300,11 +299,6 @@ public abstract class TelecomUtil { return instance.isDefaultDialer(context); } - public static boolean isRttEnabled(Context context) { - return Settings.System.getInt(context.getContentResolver(), Settings.System.RTT_CALLING_MODE, 0) - != 0; - } - /** @return the other SIM based PhoneAccountHandle that is not {@code currentAccount} */ @Nullable @RequiresPermission(permission.READ_PHONE_STATE) |