From 4ef51165ffd5e2b9fc804b18d5bb8a49937cd44d Mon Sep 17 00:00:00 2001 From: linyuh Date: Tue, 27 Feb 2018 16:49:39 -0800 Subject: Internal cleanup for the new UI's bottom sheet. Test: Existing tests PiperOrigin-RevId: 187254014 Change-Id: I8a57b632d45e87ad075eb8bbb25180858e890f08 --- .../calllog/ui/menu/DeleteCallLogItemModule.java | 6 +- .../android/dialer/calllog/ui/menu/Modules.java | 108 ++++---- .../dialer/calllog/ui/menu/NewCallLogMenu.java | 4 +- .../dialer/calllog/ui/menu/PrimaryAction.java | 6 +- .../dialer/contactactions/AndroidManifest.xml | 16 -- .../contactactions/ContactActionBottomSheet.java | 135 ---------- .../dialer/contactactions/ContactActionModule.java | 38 --- .../contactactions/ContactPrimaryActionInfo.java | 74 ------ .../dialer/contactactions/DividerModule.java | 41 --- .../dialer/contactactions/IntentModule.java | 95 ------- .../dialer/contactactions/SharedModules.java | 288 --------------------- .../res/drawable-xxxhdpi/ic_unblock.png | Bin 1034 -> 0 bytes .../contactactions/res/layout/contact_layout.xml | 54 ---- .../contactactions/res/layout/divider_layout.xml | 23 -- .../contactactions/res/layout/module_layout.xml | 40 --- .../contactactions/res/layout/sheet_layout.xml | 24 -- .../dialer/contactactions/res/values/dimens.xml | 20 -- .../dialer/contactactions/res/values/strings.xml | 47 ---- .../dialer/historyitemactions/AndroidManifest.xml | 16 ++ .../dialer/historyitemactions/DividerModule.java | 41 +++ .../HistoryItemActionBottomSheet.java | 137 ++++++++++ .../HistoryItemActionModule.java | 37 +++ .../HistoryItemPrimaryActionInfo.java | 73 ++++++ .../dialer/historyitemactions/IntentModule.java | 96 +++++++ .../dialer/historyitemactions/SharedModules.java | 284 ++++++++++++++++++++ .../res/drawable-xxxhdpi/ic_unblock.png | Bin 0 -> 1034 bytes .../res/layout/contact_layout.xml | 54 ++++ .../res/layout/divider_layout.xml | 23 ++ .../res/layout/module_layout.xml | 40 +++ .../historyitemactions/res/layout/sheet_layout.xml | 24 ++ .../historyitemactions/res/values/dimens.xml | 20 ++ .../historyitemactions/res/values/strings.xml | 47 ++++ .../dialer/voicemail/listui/menu/Modules.java | 67 +++-- .../voicemail/listui/menu/NewVoicemailMenu.java | 4 +- .../voicemail/listui/menu/PrimaryAction.java | 6 +- 35 files changed, 1007 insertions(+), 981 deletions(-) delete mode 100644 java/com/android/dialer/contactactions/AndroidManifest.xml delete mode 100644 java/com/android/dialer/contactactions/ContactActionBottomSheet.java delete mode 100644 java/com/android/dialer/contactactions/ContactActionModule.java delete mode 100644 java/com/android/dialer/contactactions/ContactPrimaryActionInfo.java delete mode 100644 java/com/android/dialer/contactactions/DividerModule.java delete mode 100644 java/com/android/dialer/contactactions/IntentModule.java delete mode 100644 java/com/android/dialer/contactactions/SharedModules.java delete mode 100644 java/com/android/dialer/contactactions/res/drawable-xxxhdpi/ic_unblock.png delete mode 100644 java/com/android/dialer/contactactions/res/layout/contact_layout.xml delete mode 100644 java/com/android/dialer/contactactions/res/layout/divider_layout.xml delete mode 100644 java/com/android/dialer/contactactions/res/layout/module_layout.xml delete mode 100644 java/com/android/dialer/contactactions/res/layout/sheet_layout.xml delete mode 100644 java/com/android/dialer/contactactions/res/values/dimens.xml delete mode 100644 java/com/android/dialer/contactactions/res/values/strings.xml create mode 100644 java/com/android/dialer/historyitemactions/AndroidManifest.xml create mode 100644 java/com/android/dialer/historyitemactions/DividerModule.java create mode 100644 java/com/android/dialer/historyitemactions/HistoryItemActionBottomSheet.java create mode 100644 java/com/android/dialer/historyitemactions/HistoryItemActionModule.java create mode 100644 java/com/android/dialer/historyitemactions/HistoryItemPrimaryActionInfo.java create mode 100644 java/com/android/dialer/historyitemactions/IntentModule.java create mode 100644 java/com/android/dialer/historyitemactions/SharedModules.java create mode 100644 java/com/android/dialer/historyitemactions/res/drawable-xxxhdpi/ic_unblock.png create mode 100644 java/com/android/dialer/historyitemactions/res/layout/contact_layout.xml create mode 100644 java/com/android/dialer/historyitemactions/res/layout/divider_layout.xml create mode 100644 java/com/android/dialer/historyitemactions/res/layout/module_layout.xml create mode 100644 java/com/android/dialer/historyitemactions/res/layout/sheet_layout.xml create mode 100644 java/com/android/dialer/historyitemactions/res/values/dimens.xml create mode 100644 java/com/android/dialer/historyitemactions/res/values/strings.xml (limited to 'java') diff --git a/java/com/android/dialer/calllog/ui/menu/DeleteCallLogItemModule.java b/java/com/android/dialer/calllog/ui/menu/DeleteCallLogItemModule.java index ac2e3b3da..ad5671e45 100644 --- a/java/com/android/dialer/calllog/ui/menu/DeleteCallLogItemModule.java +++ b/java/com/android/dialer/calllog/ui/menu/DeleteCallLogItemModule.java @@ -28,13 +28,13 @@ import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.DialerExecutor.Worker; import com.android.dialer.common.concurrent.DialerExecutorComponent; import com.android.dialer.common.database.Selection; -import com.android.dialer.contactactions.ContactActionModule; +import com.android.dialer.historyitemactions.HistoryItemActionModule; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; -/** {@link ContactActionModule} for deleting a call log item in the new call log. */ -public final class DeleteCallLogItemModule implements ContactActionModule { +/** {@link HistoryItemActionModule} for deleting a call log item in the new call log. */ +public final class DeleteCallLogItemModule implements HistoryItemActionModule { private static final String TAG = DeleteCallLogItemModule.class.getName(); private final Context context; diff --git a/java/com/android/dialer/calllog/ui/menu/Modules.java b/java/com/android/dialer/calllog/ui/menu/Modules.java index d86c04dd5..147972710 100644 --- a/java/com/android/dialer/calllog/ui/menu/Modules.java +++ b/java/com/android/dialer/calllog/ui/menu/Modules.java @@ -25,36 +25,42 @@ import com.android.dialer.callintent.CallInitiationType; import com.android.dialer.calllog.model.CoalescedRow; import com.android.dialer.calllogutils.CallLogContactTypes; import com.android.dialer.calllogutils.PhoneNumberDisplayUtil; -import com.android.dialer.contactactions.ContactActionModule; -import com.android.dialer.contactactions.DividerModule; -import com.android.dialer.contactactions.IntentModule; -import com.android.dialer.contactactions.SharedModules; import com.android.dialer.dialercontact.DialerContact; +import com.android.dialer.historyitemactions.DividerModule; +import com.android.dialer.historyitemactions.HistoryItemActionModule; +import com.android.dialer.historyitemactions.IntentModule; +import com.android.dialer.historyitemactions.SharedModules; import com.android.dialer.logging.ReportingLocation; import com.android.dialer.phonenumberutil.PhoneNumberHelper; import com.android.dialer.telecom.TelecomUtil; import com.google.common.base.Optional; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** * Configures the modules for the bottom sheet; these are the rows below the top row (primary * action) in the bottom sheet. */ +@SuppressWarnings("Guava") final class Modules { - static List fromRow(Context context, CoalescedRow row) { + static List fromRow(Context context, CoalescedRow row) { // Conditionally add each module, which are items in the bottom sheet's menu. - List modules = new ArrayList<>(); + List modules = new ArrayList<>(); String normalizedNumber = row.number().getNormalizedNumber(); boolean canPlaceCalls = PhoneNumberHelper.canPlaceCallsTo(normalizedNumber, row.numberPresentation()); if (canPlaceCalls) { - addModuleForCalls(context, modules, row, normalizedNumber); - SharedModules.maybeAddModuleForSendingTextMessage( - context, modules, normalizedNumber, row.numberAttributes().getIsBlocked()); + modules.addAll(createModulesForCalls(context, row, normalizedNumber)); + Optional moduleForSendingTextMessage = + SharedModules.createModuleForSendingTextMessage( + context, normalizedNumber, row.numberAttributes().getIsBlocked()); + if (moduleForSendingTextMessage.isPresent()) { + modules.add(moduleForSendingTextMessage.get()); + } } if (!modules.isEmpty()) { @@ -65,45 +71,52 @@ final class Modules { // TODO(zachh): Module for CallComposer. if (canPlaceCalls) { - SharedModules.maybeAddModuleForAddingToContacts( - context, - modules, - row.number(), - row.numberAttributes().getName(), - row.numberAttributes().getLookupUri(), - row.numberAttributes().getIsBlocked(), - row.numberAttributes().getIsSpam()); - SharedModules.addModulesHandlingBlockedOrSpamNumber( - context, - modules, - row.number().getNormalizedNumber(), - row.number().getCountryIso(), - row.callType(), - row.numberAttributes().getIsBlocked(), - row.numberAttributes().getIsSpam(), - ReportingLocation.Type.CALL_LOG_HISTORY); - SharedModules.maybeAddModuleForCopyingNumber(context, modules, normalizedNumber); + Optional moduleForAddingToContacts = + SharedModules.createModuleForAddingToContacts( + context, + row.number(), + row.numberAttributes().getName(), + row.numberAttributes().getLookupUri(), + row.numberAttributes().getIsBlocked(), + row.numberAttributes().getIsSpam()); + if (moduleForAddingToContacts.isPresent()) { + modules.add(moduleForAddingToContacts.get()); + } + + modules.addAll( + SharedModules.createModulesHandlingBlockedOrSpamNumber( + context, + row.number().getNormalizedNumber(), + row.number().getCountryIso(), + row.callType(), + row.numberAttributes().getIsBlocked(), + row.numberAttributes().getIsSpam(), + ReportingLocation.Type.CALL_LOG_HISTORY)); + + Optional moduleForCopyingNumber = + SharedModules.createModuleForCopyingNumber(context, normalizedNumber); + if (moduleForCopyingNumber.isPresent()) { + modules.add(moduleForCopyingNumber.get()); + } } // TODO(zachh): Revisit if DialerContact is the best thing to pass to CallDetails; could - // it use a ContactPrimaryActionInfo instead? - addModuleForAccessingCallDetails(context, modules, row); + // it use a HistoryItemPrimaryActionInfo instead? + modules.add(createModuleForAccessingCallDetails(context, row)); modules.add(new DeleteCallLogItemModule(context, row.coalescedIds())); return modules; } - private static void addModuleForCalls( - Context context, - List modules, - CoalescedRow row, - String normalizedNumber) { + private static List createModulesForCalls( + Context context, CoalescedRow row, String normalizedNumber) { // Don't add call options if a number is blocked. if (row.numberAttributes().getIsBlocked()) { - return; + return Collections.emptyList(); } + List modules = new ArrayList<>(); PhoneAccountHandle phoneAccountHandle = TelecomUtil.composePhoneAccountHandle( row.phoneAccountComponentName(), row.phoneAccountId()); @@ -123,24 +136,25 @@ final class Modules { // TODO(zachh): Also show video option if the call log entry is for an audio call but video // capabilities are present? + + return modules; } - private static void addModuleForAccessingCallDetails( - Context context, List modules, CoalescedRow row) { + private static HistoryItemActionModule createModuleForAccessingCallDetails( + Context context, CoalescedRow row) { boolean canReportAsInvalidNumber = row.numberAttributes().getCanReportAsInvalidNumber(); boolean canSupportAssistedDialing = !TextUtils.isEmpty(row.numberAttributes().getLookupUri()); - modules.add( - new IntentModule( + return new IntentModule( + context, + CallDetailsActivity.newInstance( context, - CallDetailsActivity.newInstance( - context, - row.coalescedIds(), - createDialerContactFromRow(context, row), - canReportAsInvalidNumber, - canSupportAssistedDialing), - R.string.call_details_menu_label, - R.drawable.quantum_ic_info_outline_vd_theme_24)); + row.coalescedIds(), + createDialerContactFromRow(context, row), + canReportAsInvalidNumber, + canSupportAssistedDialing), + R.string.call_details_menu_label, + R.drawable.quantum_ic_info_outline_vd_theme_24); } private static DialerContact createDialerContactFromRow(Context context, CoalescedRow row) { diff --git a/java/com/android/dialer/calllog/ui/menu/NewCallLogMenu.java b/java/com/android/dialer/calllog/ui/menu/NewCallLogMenu.java index 02724e628..9efe907e3 100644 --- a/java/com/android/dialer/calllog/ui/menu/NewCallLogMenu.java +++ b/java/com/android/dialer/calllog/ui/menu/NewCallLogMenu.java @@ -22,8 +22,8 @@ import android.view.View; import com.android.dialer.calllog.CallLogComponent; import com.android.dialer.calllog.model.CoalescedRow; import com.android.dialer.common.concurrent.DefaultFutureCallback; -import com.android.dialer.contactactions.ContactActionBottomSheet; import com.android.dialer.glidephotomanager.GlidePhotoManager; +import com.android.dialer.historyitemactions.HistoryItemActionBottomSheet; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.MoreExecutors; @@ -34,7 +34,7 @@ public final class NewCallLogMenu { public static View.OnClickListener createOnClickListener( Context context, CoalescedRow row, GlidePhotoManager glidePhotoManager) { return view -> { - ContactActionBottomSheet.show( + HistoryItemActionBottomSheet.show( context, PrimaryAction.fromRow(context, row), Modules.fromRow(context, row), diff --git a/java/com/android/dialer/calllog/ui/menu/PrimaryAction.java b/java/com/android/dialer/calllog/ui/menu/PrimaryAction.java index 92a8453f4..de4a70c30 100644 --- a/java/com/android/dialer/calllog/ui/menu/PrimaryAction.java +++ b/java/com/android/dialer/calllog/ui/menu/PrimaryAction.java @@ -22,14 +22,14 @@ import com.android.dialer.calllog.model.CoalescedRow; import com.android.dialer.calllogutils.CallLogEntryText; import com.android.dialer.calllogutils.CallLogIntents; import com.android.dialer.calllogutils.NumberAttributesConverter; -import com.android.dialer.contactactions.ContactPrimaryActionInfo; +import com.android.dialer.historyitemactions.HistoryItemPrimaryActionInfo; /** Configures the primary action row (top row) for the bottom sheet. */ final class PrimaryAction { - static ContactPrimaryActionInfo fromRow(Context context, CoalescedRow row) { + static HistoryItemPrimaryActionInfo fromRow(Context context, CoalescedRow row) { CharSequence primaryText = CallLogEntryText.buildPrimaryText(context, row); - return ContactPrimaryActionInfo.builder() + return HistoryItemPrimaryActionInfo.builder() .setNumber(row.number()) .setPhotoInfo( NumberAttributesConverter.toPhotoInfoBuilder(row.numberAttributes()) diff --git a/java/com/android/dialer/contactactions/AndroidManifest.xml b/java/com/android/dialer/contactactions/AndroidManifest.xml deleted file mode 100644 index a64066e0b..000000000 --- a/java/com/android/dialer/contactactions/AndroidManifest.xml +++ /dev/null @@ -1,16 +0,0 @@ - - diff --git a/java/com/android/dialer/contactactions/ContactActionBottomSheet.java b/java/com/android/dialer/contactactions/ContactActionBottomSheet.java deleted file mode 100644 index 98a5dd115..000000000 --- a/java/com/android/dialer/contactactions/ContactActionBottomSheet.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.dialer.contactactions; - -import android.content.Context; -import android.os.Bundle; -import android.support.design.widget.BottomSheetDialog; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; -import com.android.dialer.common.Assert; -import com.android.dialer.glidephotomanager.GlidePhotoManager; -import java.util.List; - -/** - * {@link BottomSheetDialog} used for building a list of contact actions in a bottom sheet menu. - * - *

{@link #show(Context, ContactPrimaryActionInfo, List)} should be used to create and display - * the menu. Modules are built using {@link ContactActionModule} and some defaults are provided by - * {@link IntentModule} and {@link DividerModule}. - */ -public class ContactActionBottomSheet extends BottomSheetDialog implements OnClickListener { - - private final List modules; - private final ContactPrimaryActionInfo contactPrimaryActionInfo; - private final GlidePhotoManager glidePhotoManager; - - private ContactActionBottomSheet( - Context context, - ContactPrimaryActionInfo contactPrimaryActionInfo, - List modules, - GlidePhotoManager glidePhotoManager) { - super(context); - this.modules = modules; - this.contactPrimaryActionInfo = contactPrimaryActionInfo; - this.glidePhotoManager = glidePhotoManager; - setContentView(LayoutInflater.from(context).inflate(R.layout.sheet_layout, null)); - } - - public static ContactActionBottomSheet show( - Context context, - ContactPrimaryActionInfo contactPrimaryActionInfo, - List modules, - GlidePhotoManager glidePhotoManager) { - ContactActionBottomSheet sheet = - new ContactActionBottomSheet(context, contactPrimaryActionInfo, modules, glidePhotoManager); - sheet.show(); - return sheet; - } - - @Override - protected void onCreate(Bundle bundle) { - super.onCreate(bundle); - LinearLayout container = Assert.isNotNull(findViewById(R.id.action_container)); - container.addView(getContactView(container)); - - for (ContactActionModule module : modules) { - if (module instanceof DividerModule) { - container.addView(getDividerView(container)); - } else { - container.addView(getModuleView(container, module)); - } - } - } - - private View getContactView(ViewGroup container) { - LayoutInflater inflater = LayoutInflater.from(getContext()); - View contactView = inflater.inflate(R.layout.contact_layout, container, false); - - // TODO(zachh): The contact image should be badged with a video icon if it is for a video call. - glidePhotoManager.loadQuickContactBadge( - contactView.findViewById(R.id.quick_contact_photo), contactPrimaryActionInfo.photoInfo()); - - TextView primaryTextView = contactView.findViewById(R.id.primary_text); - TextView secondaryTextView = contactView.findViewById(R.id.secondary_text); - - primaryTextView.setText(contactPrimaryActionInfo.primaryText()); - if (!TextUtils.isEmpty(contactPrimaryActionInfo.secondaryText())) { - secondaryTextView.setText(contactPrimaryActionInfo.secondaryText()); - } else { - secondaryTextView.setVisibility(View.GONE); - secondaryTextView.setText(null); - } - if (contactPrimaryActionInfo.intent() != null) { - contactView.setOnClickListener( - (view) -> { - getContext().startActivity(contactPrimaryActionInfo.intent()); - dismiss(); - }); - } - return contactView; - } - - private View getDividerView(ViewGroup container) { - LayoutInflater inflater = LayoutInflater.from(getContext()); - return inflater.inflate(R.layout.divider_layout, container, false); - } - - private View getModuleView(ViewGroup container, ContactActionModule module) { - LayoutInflater inflater = LayoutInflater.from(getContext()); - View moduleView = inflater.inflate(R.layout.module_layout, container, false); - ((TextView) moduleView.findViewById(R.id.module_text)).setText(module.getStringId()); - ((ImageView) moduleView.findViewById(R.id.module_image)) - .setImageResource(module.getDrawableId()); - moduleView.setOnClickListener(this); - moduleView.setTag(module); - return moduleView; - } - - @Override - public void onClick(View view) { - if (((ContactActionModule) view.getTag()).onClick()) { - dismiss(); - } - } -} diff --git a/java/com/android/dialer/contactactions/ContactActionModule.java b/java/com/android/dialer/contactactions/ContactActionModule.java deleted file mode 100644 index 8089edc7b..000000000 --- a/java/com/android/dialer/contactactions/ContactActionModule.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.dialer.contactactions; - -import android.support.annotation.DrawableRes; -import android.support.annotation.StringRes; - -/** - * Modules used to build {@link ContactActionBottomSheet}. - * - *

Contacts as they relate to this class should be thought of as any entity that an action can be - * performed on like unknown/restricted contacts, along with saved and non-saved contacts. - */ -public interface ContactActionModule { - - @StringRes - int getStringId(); - - @DrawableRes - int getDrawableId(); - - /** @return true if the bottom sheet should close, false otherwise */ - boolean onClick(); -} diff --git a/java/com/android/dialer/contactactions/ContactPrimaryActionInfo.java b/java/com/android/dialer/contactactions/ContactPrimaryActionInfo.java deleted file mode 100644 index 5017d83b9..000000000 --- a/java/com/android/dialer/contactactions/ContactPrimaryActionInfo.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.dialer.contactactions; - -import android.content.Intent; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import com.android.dialer.DialerPhoneNumber; -import com.android.dialer.glidephotomanager.PhotoInfo; -import com.google.auto.value.AutoValue; - -/** - * Contains information necessary to construct the primary action for a contact bottom sheet. - * - *

This may include information about the call, for instance when the bottom sheet is shown from - * the call log. - */ -@AutoValue -public abstract class ContactPrimaryActionInfo { - - @Nullable - public abstract DialerPhoneNumber number(); - - @NonNull - public abstract PhotoInfo photoInfo(); - - @Nullable - public abstract CharSequence primaryText(); - - @Nullable - public abstract CharSequence secondaryText(); - - /** - * The intent to fire when the user clicks the top row of the bottom sheet. Null if no action - * should occur (e.g. if the number is unknown). - */ - @Nullable - public abstract Intent intent(); - - // TODO(zachh): Add SIM info here if should be shown in bottom sheet. - - /** Builder for {@link ContactPrimaryActionInfo}. */ - @AutoValue.Builder - public abstract static class Builder { - public abstract Builder setNumber(@Nullable DialerPhoneNumber dialerPhoneNumber); - - public abstract Builder setPhotoInfo(@NonNull PhotoInfo photoInfo); - - public abstract Builder setPrimaryText(@Nullable CharSequence primaryText); - - public abstract Builder setSecondaryText(@Nullable CharSequence secondaryText); - - public abstract Builder setIntent(@Nullable Intent intent); - - public abstract ContactPrimaryActionInfo build(); - } - - public static Builder builder() { - return new AutoValue_ContactPrimaryActionInfo.Builder(); - } -} diff --git a/java/com/android/dialer/contactactions/DividerModule.java b/java/com/android/dialer/contactactions/DividerModule.java deleted file mode 100644 index aabd160db..000000000 --- a/java/com/android/dialer/contactactions/DividerModule.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.dialer.contactactions; - -import com.android.dialer.common.Assert; - -/** - * A module that inserts a grey line divider into {@link ContactActionModule}. Layout it provided in - * R.layout.divider_layout.xml - */ -public final class DividerModule implements ContactActionModule { - - @Override - public int getStringId() { - throw Assert.createUnsupportedOperationFailException(); - } - - @Override - public int getDrawableId() { - throw Assert.createUnsupportedOperationFailException(); - } - - @Override - public boolean onClick() { - throw Assert.createUnsupportedOperationFailException(); - } -} diff --git a/java/com/android/dialer/contactactions/IntentModule.java b/java/com/android/dialer/contactactions/IntentModule.java deleted file mode 100644 index 9a345c669..000000000 --- a/java/com/android/dialer/contactactions/IntentModule.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.dialer.contactactions; - -import android.content.Context; -import android.content.Intent; -import android.support.annotation.DrawableRes; -import android.support.annotation.Nullable; -import android.support.annotation.StringRes; -import android.telecom.PhoneAccountHandle; -import com.android.dialer.callintent.CallInitiationType.Type; -import com.android.dialer.callintent.CallIntentBuilder; -import com.android.dialer.precall.PreCall; -import com.android.dialer.util.DialerUtils; - -/** - * {@link ContactActionModule} useful for making easy to build modules based on starting an intent. - */ -public class IntentModule implements ContactActionModule { - - private final Context context; - private final Intent intent; - private final @StringRes int text; - private final @DrawableRes int image; - - public IntentModule(Context context, Intent intent, @StringRes int text, @DrawableRes int image) { - this.context = context; - this.intent = intent; - this.text = text; - this.image = image; - } - - @Override - public int getStringId() { - return text; - } - - @Override - public int getDrawableId() { - return image; - } - - @Override - public boolean onClick() { - DialerUtils.startActivityWithErrorToast(context, intent); - return true; - } - - public static IntentModule newCallModule( - Context context, - String number, - @Nullable PhoneAccountHandle phoneAccountHandle, - Type initiationType) { - // TODO(zachh): Support post-dial digits; consider using DialerPhoneNumber. - return new IntentModule( - context, - PreCall.getIntent( - context, - new CallIntentBuilder(number, initiationType) - .setPhoneAccountHandle(phoneAccountHandle)), - R.string.voice_call, - R.drawable.quantum_ic_call_white_24); - } - - public static IntentModule newVideoCallModule( - Context context, - String number, - @Nullable PhoneAccountHandle phoneAccountHandle, - Type initiationType) { - // TODO(zachh): Support post-dial digits; consider using DialerPhoneNumber. - return new IntentModule( - context, - PreCall.getIntent( - context, - new CallIntentBuilder(number, initiationType) - .setPhoneAccountHandle(phoneAccountHandle) - .setIsVideoCall(true)), - R.string.video_call, - R.drawable.quantum_ic_videocam_white_24); - } -} diff --git a/java/com/android/dialer/contactactions/SharedModules.java b/java/com/android/dialer/contactactions/SharedModules.java deleted file mode 100644 index 3007a5990..000000000 --- a/java/com/android/dialer/contactactions/SharedModules.java +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.dialer.contactactions; - -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.provider.ContactsContract; -import android.support.annotation.Nullable; -import android.text.TextUtils; -import android.widget.Toast; -import com.android.dialer.DialerPhoneNumber; -import com.android.dialer.blockreportspam.ShowBlockReportSpamDialogNotifier; -import com.android.dialer.clipboard.ClipboardUtils; -import com.android.dialer.logging.ReportingLocation; -import com.android.dialer.util.IntentUtil; -import com.android.dialer.util.UriUtils; -import java.util.List; -import java.util.Locale; - -/** - * Modules for the bottom sheet that are shared between NewVoicemailFragment and NewCallLogFragment - */ -public class SharedModules { - - public static void maybeAddModuleForAddingToContacts( - Context context, - List modules, - DialerPhoneNumber dialerPhoneNumber, - String name, - String lookupUri, - boolean isBlocked, - boolean isSpam) { - // Skip showing the menu item for a spam/blocked number. - if (isBlocked || isSpam) { - return; - } - - // Skip showing the menu item for existing contacts. - if (isExistingContact(lookupUri)) { - return; - } - - // Skip showing the menu item if there is no number. - String normalizedNumber = dialerPhoneNumber.getNormalizedNumber(); - if (TextUtils.isEmpty(normalizedNumber)) { - return; - } - - Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT); - intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE); - intent.putExtra(ContactsContract.Intents.Insert.PHONE, normalizedNumber); - - if (!TextUtils.isEmpty(name)) { - intent.putExtra(ContactsContract.Intents.Insert.NAME, name); - } - modules.add( - new IntentModule( - context, - intent, - R.string.add_to_contacts, - R.drawable.quantum_ic_person_add_vd_theme_24)); - } - - /** - * Lookup URIs are currently fetched from the cached column of the system call log. This URI - * contains encoded information for non-contacts for the purposes of populating contact cards. - * - *

We infer whether a contact is existing or not by checking if the lookup URI is "encoded" or - * not. - * - *

TODO(zachh): We should revisit this once the contact URI is no longer being read from the - * cached column in the system database, in case we decide not to overload the column. - */ - public static boolean isExistingContact(@Nullable String lookupUri) { - return !TextUtils.isEmpty(lookupUri) && !UriUtils.isEncodedContactUri(Uri.parse(lookupUri)); - } - - public static void maybeAddModuleForSendingTextMessage( - Context context, - List modules, - String normalizedNumber, - boolean isBlocked) { - // Don't show the option to send a text message if the number is blocked. - if (isBlocked) { - return; - } - - // TODO(zachh): There are some conditions where this module should not be shown; consider - // voicemail, business numbers, etc. - - if (!TextUtils.isEmpty(normalizedNumber)) { - modules.add( - new IntentModule( - context, - IntentUtil.getSendSmsIntent(normalizedNumber), - R.string.send_a_message, - R.drawable.quantum_ic_message_vd_theme_24)); - } - } - - /** - * Add modules related to blocking/unblocking a number and/or reporting it as spam/not spam. - * - * @param normalizedNumber The number to be blocked / unblocked / marked as spam/not spam - * @param countryIso The ISO 3166-1 two letters country code for the number - * @param callType Call type defined in {@link android.provider.CallLog.Calls} - * @param reportingLocation The location where the number is reported. See {@link - * ReportingLocation.Type}. - */ - public static void addModulesHandlingBlockedOrSpamNumber( - Context context, - List modules, - String normalizedNumber, - String countryIso, - int callType, - boolean isBlocked, - boolean isSpam, - ReportingLocation.Type reportingLocation) { - // For a spam number, add two options: - // (1) "Not spam" and "Block", or - // (2) "Not spam" and "Unblock". - if (isSpam) { - addModuleForMarkingNumberAsNonSpam( - context, modules, normalizedNumber, countryIso, callType, reportingLocation); - addModuleForBlockingOrUnblockingNumber(context, modules, normalizedNumber, isBlocked); - return; - } - - // For a blocked non-spam number, add "Unblock" option. - if (isBlocked) { - addModuleForBlockingOrUnblockingNumber(context, modules, normalizedNumber, isBlocked); - return; - } - - // For a number that is neither a spam number nor blocked, add "Block/Report spam" option. - addModuleForBlockingNumberAndOptionallyReportingSpam( - context, modules, normalizedNumber, countryIso, callType, reportingLocation); - } - - /** - * Add "Not spam" module. - * - * @param normalizedNumber The number to be marked as not spam - * @param countryIso The ISO 3166-1 two letters country code for the number - * @param callType Call type defined in {@link android.provider.CallLog.Calls} - * @param reportingLocation The location where the number is reported. See {@link - * ReportingLocation.Type}. - */ - private static void addModuleForMarkingNumberAsNonSpam( - Context context, - List modules, - String normalizedNumber, - String countryIso, - int callType, - ReportingLocation.Type reportingLocation) { - modules.add( - new ContactActionModule() { - @Override - public int getStringId() { - return R.string.not_spam; - } - - @Override - public int getDrawableId() { - return R.drawable.quantum_ic_report_off_vd_theme_24; - } - - @Override - public boolean onClick() { - ShowBlockReportSpamDialogNotifier.notifyShowDialogToReportNotSpam( - context, normalizedNumber, countryIso, callType, reportingLocation); - return true; // Close the bottom sheet. - } - }); - } - - private static void addModuleForBlockingOrUnblockingNumber( - Context context, - List modules, - String normalizedNumber, - boolean isBlocked) { - modules.add( - new ContactActionModule() { - @Override - public int getStringId() { - return isBlocked ? R.string.unblock_number : R.string.block_number; - } - - @Override - public int getDrawableId() { - return isBlocked - ? R.drawable.ic_unblock // TODO(a bug): use a vector icon - : R.drawable.quantum_ic_block_vd_theme_24; - } - - @Override - public boolean onClick() { - // TODO(a bug): implement this method. - Toast.makeText( - context, - String.format( - Locale.ENGLISH, - "TODO: " + (isBlocked ? "Unblock " : "Block ") + " number %s.", - normalizedNumber), - Toast.LENGTH_SHORT) - .show(); - return true; // Close the bottom sheet. - } - }); - } - - /** - * Add "Block/Report spam" module - * - * @param normalizedNumber The number to be blocked / unblocked / marked as spam/not spam - * @param countryIso The ISO 3166-1 two letters country code for the number - * @param callType Call type defined in {@link android.provider.CallLog.Calls} - * @param reportingLocation The location where the number is reported. See {@link - * ReportingLocation.Type}. - */ - private static void addModuleForBlockingNumberAndOptionallyReportingSpam( - Context context, - List modules, - String normalizedNumber, - String countryIso, - int callType, - ReportingLocation.Type reportingLocation) { - modules.add( - new ContactActionModule() { - @Override - public int getStringId() { - return R.string.block_and_optionally_report_spam; - } - - @Override - public int getDrawableId() { - return R.drawable.quantum_ic_block_vd_theme_24; - } - - @Override - public boolean onClick() { - ShowBlockReportSpamDialogNotifier.notifyShowDialogToBlockNumberAndOptionallyReportSpam( - context, normalizedNumber, countryIso, callType, reportingLocation); - return true; // Close the bottom sheet. - } - }); - } - - public static void maybeAddModuleForCopyingNumber( - Context context, List modules, String normalizedNumber) { - if (TextUtils.isEmpty(normalizedNumber)) { - return; - } - modules.add( - new ContactActionModule() { - @Override - public int getStringId() { - return R.string.copy_number; - } - - @Override - public int getDrawableId() { - return R.drawable.quantum_ic_content_copy_vd_theme_24; - } - - @Override - public boolean onClick() { - ClipboardUtils.copyText(context, null, normalizedNumber, true); - return false; - } - }); - } -} diff --git a/java/com/android/dialer/contactactions/res/drawable-xxxhdpi/ic_unblock.png b/java/com/android/dialer/contactactions/res/drawable-xxxhdpi/ic_unblock.png deleted file mode 100644 index 01551e2fc..000000000 Binary files a/java/com/android/dialer/contactactions/res/drawable-xxxhdpi/ic_unblock.png and /dev/null differ diff --git a/java/com/android/dialer/contactactions/res/layout/contact_layout.xml b/java/com/android/dialer/contactactions/res/layout/contact_layout.xml deleted file mode 100644 index 4deef3e07..000000000 --- a/java/com/android/dialer/contactactions/res/layout/contact_layout.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/java/com/android/dialer/contactactions/res/layout/divider_layout.xml b/java/com/android/dialer/contactactions/res/layout/divider_layout.xml deleted file mode 100644 index fdee50e48..000000000 --- a/java/com/android/dialer/contactactions/res/layout/divider_layout.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - diff --git a/java/com/android/dialer/contactactions/res/layout/module_layout.xml b/java/com/android/dialer/contactactions/res/layout/module_layout.xml deleted file mode 100644 index 9aee67937..000000000 --- a/java/com/android/dialer/contactactions/res/layout/module_layout.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/java/com/android/dialer/contactactions/res/layout/sheet_layout.xml b/java/com/android/dialer/contactactions/res/layout/sheet_layout.xml deleted file mode 100644 index 6984367bf..000000000 --- a/java/com/android/dialer/contactactions/res/layout/sheet_layout.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - diff --git a/java/com/android/dialer/contactactions/res/values/dimens.xml b/java/com/android/dialer/contactactions/res/values/dimens.xml deleted file mode 100644 index 47bf804ac..000000000 --- a/java/com/android/dialer/contactactions/res/values/dimens.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - 48dp - 12dp - \ No newline at end of file diff --git a/java/com/android/dialer/contactactions/res/values/strings.xml b/java/com/android/dialer/contactactions/res/values/strings.xml deleted file mode 100644 index 4d598a930..000000000 --- a/java/com/android/dialer/contactactions/res/values/strings.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - Voice call - - - Video call - - - Add contact - - - Message - - - Not spam - - - Block - - - Unblock - - - Block/Report spam - - - Copy number - \ No newline at end of file diff --git a/java/com/android/dialer/historyitemactions/AndroidManifest.xml b/java/com/android/dialer/historyitemactions/AndroidManifest.xml new file mode 100644 index 000000000..4d302685f --- /dev/null +++ b/java/com/android/dialer/historyitemactions/AndroidManifest.xml @@ -0,0 +1,16 @@ + + diff --git a/java/com/android/dialer/historyitemactions/DividerModule.java b/java/com/android/dialer/historyitemactions/DividerModule.java new file mode 100644 index 000000000..a5ca62abe --- /dev/null +++ b/java/com/android/dialer/historyitemactions/DividerModule.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 com.android.dialer.common.Assert; + +/** + * A module that inserts a grey line divider into {@link HistoryItemActionModule}. Layout it + * provided in R.layout.divider_layout.xml + */ +public final class DividerModule implements HistoryItemActionModule { + + @Override + public int getStringId() { + throw Assert.createUnsupportedOperationFailException(); + } + + @Override + public int getDrawableId() { + throw Assert.createUnsupportedOperationFailException(); + } + + @Override + public boolean onClick() { + throw Assert.createUnsupportedOperationFailException(); + } +} diff --git a/java/com/android/dialer/historyitemactions/HistoryItemActionBottomSheet.java b/java/com/android/dialer/historyitemactions/HistoryItemActionBottomSheet.java new file mode 100644 index 000000000..47aa1003f --- /dev/null +++ b/java/com/android/dialer/historyitemactions/HistoryItemActionBottomSheet.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.content.Context; +import android.os.Bundle; +import android.support.design.widget.BottomSheetDialog; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; +import com.android.dialer.common.Assert; +import com.android.dialer.glidephotomanager.GlidePhotoManager; +import java.util.List; + +/** + * {@link BottomSheetDialog} used to show a list of actions in a bottom sheet menu. + * + *

{@link #show(Context, HistoryItemPrimaryActionInfo, List, GlidePhotoManager)} should be used + * to create and display the menu. Modules are built using {@link HistoryItemActionModule} and some + * defaults are provided by {@link IntentModule} and {@link DividerModule}. + */ +public class HistoryItemActionBottomSheet extends BottomSheetDialog implements OnClickListener { + + private final List modules; + private final HistoryItemPrimaryActionInfo historyItemPrimaryActionInfo; + private final GlidePhotoManager glidePhotoManager; + + private HistoryItemActionBottomSheet( + Context context, + HistoryItemPrimaryActionInfo historyItemPrimaryActionInfo, + List modules, + GlidePhotoManager glidePhotoManager) { + super(context); + this.modules = modules; + this.historyItemPrimaryActionInfo = historyItemPrimaryActionInfo; + this.glidePhotoManager = glidePhotoManager; + setContentView(LayoutInflater.from(context).inflate(R.layout.sheet_layout, null)); + } + + public static HistoryItemActionBottomSheet show( + Context context, + HistoryItemPrimaryActionInfo historyItemPrimaryActionInfo, + List modules, + GlidePhotoManager glidePhotoManager) { + HistoryItemActionBottomSheet sheet = + new HistoryItemActionBottomSheet( + context, historyItemPrimaryActionInfo, modules, glidePhotoManager); + sheet.show(); + return sheet; + } + + @Override + protected void onCreate(Bundle bundle) { + super.onCreate(bundle); + LinearLayout container = Assert.isNotNull(findViewById(R.id.action_container)); + container.addView(getContactView(container)); + + for (HistoryItemActionModule module : modules) { + if (module instanceof DividerModule) { + container.addView(getDividerView(container)); + } else { + container.addView(getModuleView(container, module)); + } + } + } + + private View getContactView(ViewGroup container) { + LayoutInflater inflater = LayoutInflater.from(getContext()); + View contactView = inflater.inflate(R.layout.contact_layout, container, false); + + // TODO(zachh): The contact image should be badged with a video icon if it is for a video call. + glidePhotoManager.loadQuickContactBadge( + contactView.findViewById(R.id.quick_contact_photo), + historyItemPrimaryActionInfo.photoInfo()); + + TextView primaryTextView = contactView.findViewById(R.id.primary_text); + TextView secondaryTextView = contactView.findViewById(R.id.secondary_text); + + primaryTextView.setText(historyItemPrimaryActionInfo.primaryText()); + if (!TextUtils.isEmpty(historyItemPrimaryActionInfo.secondaryText())) { + secondaryTextView.setText(historyItemPrimaryActionInfo.secondaryText()); + } else { + secondaryTextView.setVisibility(View.GONE); + secondaryTextView.setText(null); + } + if (historyItemPrimaryActionInfo.intent() != null) { + contactView.setOnClickListener( + (view) -> { + getContext().startActivity(historyItemPrimaryActionInfo.intent()); + dismiss(); + }); + } + return contactView; + } + + private View getDividerView(ViewGroup container) { + LayoutInflater inflater = LayoutInflater.from(getContext()); + return inflater.inflate(R.layout.divider_layout, container, false); + } + + private View getModuleView(ViewGroup container, HistoryItemActionModule module) { + LayoutInflater inflater = LayoutInflater.from(getContext()); + View moduleView = inflater.inflate(R.layout.module_layout, container, false); + ((TextView) moduleView.findViewById(R.id.module_text)).setText(module.getStringId()); + ((ImageView) moduleView.findViewById(R.id.module_image)) + .setImageResource(module.getDrawableId()); + moduleView.setOnClickListener(this); + moduleView.setTag(module); + return moduleView; + } + + @Override + public void onClick(View view) { + if (((HistoryItemActionModule) view.getTag()).onClick()) { + dismiss(); + } + } +} diff --git a/java/com/android/dialer/historyitemactions/HistoryItemActionModule.java b/java/com/android/dialer/historyitemactions/HistoryItemActionModule.java new file mode 100644 index 000000000..d64cbca53 --- /dev/null +++ b/java/com/android/dialer/historyitemactions/HistoryItemActionModule.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.support.annotation.DrawableRes; +import android.support.annotation.StringRes; + +/** + * Modules used to build {@link HistoryItemActionBottomSheet}. + * + *

A history item is one that is displayed in the call log or the voicemail fragment. + */ +public interface HistoryItemActionModule { + + @StringRes + int getStringId(); + + @DrawableRes + int getDrawableId(); + + /** @return true if the bottom sheet should close, false otherwise */ + boolean onClick(); +} diff --git a/java/com/android/dialer/historyitemactions/HistoryItemPrimaryActionInfo.java b/java/com/android/dialer/historyitemactions/HistoryItemPrimaryActionInfo.java new file mode 100644 index 000000000..f4338ce7a --- /dev/null +++ b/java/com/android/dialer/historyitemactions/HistoryItemPrimaryActionInfo.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.content.Intent; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import com.android.dialer.DialerPhoneNumber; +import com.android.dialer.glidephotomanager.PhotoInfo; +import com.google.auto.value.AutoValue; + +/** + * Contains information necessary to construct the primary action for a history item's bottom sheet. + * + *

A history item is one that is displayed in the call log or the voicemail fragment. + */ +@AutoValue +public abstract class HistoryItemPrimaryActionInfo { + + @Nullable + public abstract DialerPhoneNumber number(); + + @NonNull + public abstract PhotoInfo photoInfo(); + + @Nullable + public abstract CharSequence primaryText(); + + @Nullable + public abstract CharSequence secondaryText(); + + /** + * The intent to fire when the user clicks the top row of the bottom sheet. Null if no action + * should occur (e.g. if the number is unknown). + */ + @Nullable + public abstract Intent intent(); + + // TODO(zachh): Add SIM info here if should be shown in bottom sheet. + + /** Builder for {@link HistoryItemPrimaryActionInfo}. */ + @AutoValue.Builder + public abstract static class Builder { + public abstract Builder setNumber(@Nullable DialerPhoneNumber dialerPhoneNumber); + + public abstract Builder setPhotoInfo(@NonNull PhotoInfo photoInfo); + + public abstract Builder setPrimaryText(@Nullable CharSequence primaryText); + + public abstract Builder setSecondaryText(@Nullable CharSequence secondaryText); + + public abstract Builder setIntent(@Nullable Intent intent); + + public abstract HistoryItemPrimaryActionInfo build(); + } + + public static Builder builder() { + return new AutoValue_HistoryItemPrimaryActionInfo.Builder(); + } +} diff --git a/java/com/android/dialer/historyitemactions/IntentModule.java b/java/com/android/dialer/historyitemactions/IntentModule.java new file mode 100644 index 000000000..dfe9bb064 --- /dev/null +++ b/java/com/android/dialer/historyitemactions/IntentModule.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.content.Context; +import android.content.Intent; +import android.support.annotation.DrawableRes; +import android.support.annotation.Nullable; +import android.support.annotation.StringRes; +import android.telecom.PhoneAccountHandle; +import com.android.dialer.callintent.CallInitiationType.Type; +import com.android.dialer.callintent.CallIntentBuilder; +import com.android.dialer.precall.PreCall; +import com.android.dialer.util.DialerUtils; + +/** + * {@link HistoryItemActionModule} useful for making easy to build modules based on starting an + * intent. + */ +public class IntentModule implements HistoryItemActionModule { + + private final Context context; + private final Intent intent; + private final @StringRes int text; + private final @DrawableRes int image; + + public IntentModule(Context context, Intent intent, @StringRes int text, @DrawableRes int image) { + this.context = context; + this.intent = intent; + this.text = text; + this.image = image; + } + + @Override + public int getStringId() { + return text; + } + + @Override + public int getDrawableId() { + return image; + } + + @Override + public boolean onClick() { + DialerUtils.startActivityWithErrorToast(context, intent); + return true; + } + + public static IntentModule newCallModule( + Context context, + String number, + @Nullable PhoneAccountHandle phoneAccountHandle, + Type initiationType) { + // TODO(zachh): Support post-dial digits; consider using DialerPhoneNumber. + return new IntentModule( + context, + PreCall.getIntent( + context, + new CallIntentBuilder(number, initiationType) + .setPhoneAccountHandle(phoneAccountHandle)), + R.string.voice_call, + R.drawable.quantum_ic_call_white_24); + } + + public static IntentModule newVideoCallModule( + Context context, + String number, + @Nullable PhoneAccountHandle phoneAccountHandle, + Type initiationType) { + // TODO(zachh): Support post-dial digits; consider using DialerPhoneNumber. + return new IntentModule( + context, + PreCall.getIntent( + context, + new CallIntentBuilder(number, initiationType) + .setPhoneAccountHandle(phoneAccountHandle) + .setIsVideoCall(true)), + R.string.video_call, + R.drawable.quantum_ic_videocam_white_24); + } +} diff --git a/java/com/android/dialer/historyitemactions/SharedModules.java b/java/com/android/dialer/historyitemactions/SharedModules.java new file mode 100644 index 000000000..7cfee37bb --- /dev/null +++ b/java/com/android/dialer/historyitemactions/SharedModules.java @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.provider.ContactsContract; +import android.support.annotation.Nullable; +import android.text.TextUtils; +import android.widget.Toast; +import com.android.dialer.DialerPhoneNumber; +import com.android.dialer.blockreportspam.ShowBlockReportSpamDialogNotifier; +import com.android.dialer.clipboard.ClipboardUtils; +import com.android.dialer.logging.ReportingLocation; +import com.android.dialer.util.IntentUtil; +import com.android.dialer.util.UriUtils; +import com.google.common.base.Optional; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +/** + * Modules for the bottom sheet that are shared between NewVoicemailFragment and NewCallLogFragment + */ +@SuppressWarnings("Guava") +public class SharedModules { + + public static Optional createModuleForAddingToContacts( + Context context, + DialerPhoneNumber dialerPhoneNumber, + String name, + String lookupUri, + boolean isBlocked, + boolean isSpam) { + // Skip showing the menu item for a spam/blocked number. + if (isBlocked || isSpam) { + return Optional.absent(); + } + + // Skip showing the menu item for existing contacts. + if (isExistingContact(lookupUri)) { + return Optional.absent(); + } + + // Skip showing the menu item if there is no number. + String normalizedNumber = dialerPhoneNumber.getNormalizedNumber(); + if (TextUtils.isEmpty(normalizedNumber)) { + return Optional.absent(); + } + + Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT); + intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE); + intent.putExtra(ContactsContract.Intents.Insert.PHONE, normalizedNumber); + + if (!TextUtils.isEmpty(name)) { + intent.putExtra(ContactsContract.Intents.Insert.NAME, name); + } + + return Optional.of( + new IntentModule( + context, + intent, + R.string.add_to_contacts, + R.drawable.quantum_ic_person_add_vd_theme_24)); + } + + /** + * Lookup URIs are currently fetched from the cached column of the system call log. This URI + * contains encoded information for non-contacts for the purposes of populating contact cards. + * + *

We infer whether a contact is existing or not by checking if the lookup URI is "encoded" or + * not. + * + *

TODO(zachh): We should revisit this once the contact URI is no longer being read from the + * cached column in the system database, in case we decide not to overload the column. + */ + private static boolean isExistingContact(@Nullable String lookupUri) { + return !TextUtils.isEmpty(lookupUri) && !UriUtils.isEncodedContactUri(Uri.parse(lookupUri)); + } + + public static Optional createModuleForSendingTextMessage( + Context context, String normalizedNumber, boolean isBlocked) { + // Don't show the option to send a text message if the number is blocked. + if (isBlocked) { + return Optional.absent(); + } + + // TODO(zachh): There are some conditions where this module should not be shown; consider + // voicemail, business numbers, etc. + + return !TextUtils.isEmpty(normalizedNumber) + ? Optional.of( + new IntentModule( + context, + IntentUtil.getSendSmsIntent(normalizedNumber), + R.string.send_a_message, + R.drawable.quantum_ic_message_vd_theme_24)) + : Optional.absent(); + } + + /** + * Add modules related to blocking/unblocking a number and/or reporting it as spam/not spam. + * + * @param normalizedNumber The number to be blocked / unblocked / marked as spam/not spam + * @param countryIso The ISO 3166-1 two letters country code for the number + * @param callType Call type defined in {@link android.provider.CallLog.Calls} + * @param reportingLocation The location where the number is reported. See {@link + * ReportingLocation.Type}. + */ + public static List createModulesHandlingBlockedOrSpamNumber( + Context context, + String normalizedNumber, + String countryIso, + int callType, + boolean isBlocked, + boolean isSpam, + ReportingLocation.Type reportingLocation) { + List modules = new ArrayList<>(); + + // For a spam number, add two options: + // (1) "Not spam" and "Block", or + // (2) "Not spam" and "Unblock". + if (isSpam) { + modules.add( + createModuleForMarkingNumberAsNonSpam( + context, normalizedNumber, countryIso, callType, reportingLocation)); + modules.add(createModuleForBlockingOrUnblockingNumber(context, normalizedNumber, isBlocked)); + return modules; + } + + // For a blocked non-spam number, add "Unblock" option. + if (isBlocked) { + modules.add(createModuleForBlockingOrUnblockingNumber(context, normalizedNumber, isBlocked)); + return modules; + } + + // For a number that is neither a spam number nor blocked, add "Block/Report spam" option. + modules.add( + createModuleForBlockingNumberAndOptionallyReportingSpam( + context, normalizedNumber, countryIso, callType, reportingLocation)); + return modules; + } + + /** + * Add "Not spam" module. + * + * @param normalizedNumber The number to be marked as not spam + * @param countryIso The ISO 3166-1 two letters country code for the number + * @param callType Call type defined in {@link android.provider.CallLog.Calls} + * @param reportingLocation The location where the number is reported. See {@link + * ReportingLocation.Type}. + */ + private static HistoryItemActionModule createModuleForMarkingNumberAsNonSpam( + Context context, + String normalizedNumber, + String countryIso, + int callType, + ReportingLocation.Type reportingLocation) { + return new HistoryItemActionModule() { + @Override + public int getStringId() { + return R.string.not_spam; + } + + @Override + public int getDrawableId() { + return R.drawable.quantum_ic_report_off_vd_theme_24; + } + + @Override + public boolean onClick() { + ShowBlockReportSpamDialogNotifier.notifyShowDialogToReportNotSpam( + context, normalizedNumber, countryIso, callType, reportingLocation); + return true; // Close the bottom sheet. + } + }; + } + + private static HistoryItemActionModule createModuleForBlockingOrUnblockingNumber( + Context context, String normalizedNumber, boolean isBlocked) { + return new HistoryItemActionModule() { + @Override + public int getStringId() { + return isBlocked ? R.string.unblock_number : R.string.block_number; + } + + @Override + public int getDrawableId() { + return isBlocked + ? R.drawable.ic_unblock // TODO(a bug): use a vector icon + : R.drawable.quantum_ic_block_vd_theme_24; + } + + @Override + public boolean onClick() { + // TODO(a bug): implement this method. + Toast.makeText( + context, + String.format( + Locale.ENGLISH, + "TODO: " + (isBlocked ? "Unblock " : "Block ") + " number %s.", + normalizedNumber), + Toast.LENGTH_SHORT) + .show(); + return true; // Close the bottom sheet. + } + }; + } + + /** + * Add "Block/Report spam" module + * + * @param normalizedNumber The number to be blocked / unblocked / marked as spam/not spam + * @param countryIso The ISO 3166-1 two letters country code for the number + * @param callType Call type defined in {@link android.provider.CallLog.Calls} + * @param reportingLocation The location where the number is reported. See {@link + * ReportingLocation.Type}. + */ + private static HistoryItemActionModule createModuleForBlockingNumberAndOptionallyReportingSpam( + Context context, + String normalizedNumber, + String countryIso, + int callType, + ReportingLocation.Type reportingLocation) { + return new HistoryItemActionModule() { + @Override + public int getStringId() { + return R.string.block_and_optionally_report_spam; + } + + @Override + public int getDrawableId() { + return R.drawable.quantum_ic_block_vd_theme_24; + } + + @Override + public boolean onClick() { + ShowBlockReportSpamDialogNotifier.notifyShowDialogToBlockNumberAndOptionallyReportSpam( + context, normalizedNumber, countryIso, callType, reportingLocation); + return true; // Close the bottom sheet. + } + }; + } + + public static Optional createModuleForCopyingNumber( + Context context, String normalizedNumber) { + if (TextUtils.isEmpty(normalizedNumber)) { + return Optional.absent(); + } + return Optional.of( + new HistoryItemActionModule() { + @Override + public int getStringId() { + return R.string.copy_number; + } + + @Override + public int getDrawableId() { + return R.drawable.quantum_ic_content_copy_vd_theme_24; + } + + @Override + public boolean onClick() { + ClipboardUtils.copyText(context, null, normalizedNumber, true); + return false; + } + }); + } +} diff --git a/java/com/android/dialer/historyitemactions/res/drawable-xxxhdpi/ic_unblock.png b/java/com/android/dialer/historyitemactions/res/drawable-xxxhdpi/ic_unblock.png new file mode 100644 index 000000000..01551e2fc Binary files /dev/null and b/java/com/android/dialer/historyitemactions/res/drawable-xxxhdpi/ic_unblock.png differ diff --git a/java/com/android/dialer/historyitemactions/res/layout/contact_layout.xml b/java/com/android/dialer/historyitemactions/res/layout/contact_layout.xml new file mode 100644 index 000000000..4deef3e07 --- /dev/null +++ b/java/com/android/dialer/historyitemactions/res/layout/contact_layout.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/java/com/android/dialer/historyitemactions/res/layout/divider_layout.xml b/java/com/android/dialer/historyitemactions/res/layout/divider_layout.xml new file mode 100644 index 000000000..fdee50e48 --- /dev/null +++ b/java/com/android/dialer/historyitemactions/res/layout/divider_layout.xml @@ -0,0 +1,23 @@ + + + diff --git a/java/com/android/dialer/historyitemactions/res/layout/module_layout.xml b/java/com/android/dialer/historyitemactions/res/layout/module_layout.xml new file mode 100644 index 000000000..9aee67937 --- /dev/null +++ b/java/com/android/dialer/historyitemactions/res/layout/module_layout.xml @@ -0,0 +1,40 @@ + + + + + + + + \ No newline at end of file diff --git a/java/com/android/dialer/historyitemactions/res/layout/sheet_layout.xml b/java/com/android/dialer/historyitemactions/res/layout/sheet_layout.xml new file mode 100644 index 000000000..6984367bf --- /dev/null +++ b/java/com/android/dialer/historyitemactions/res/layout/sheet_layout.xml @@ -0,0 +1,24 @@ + + + diff --git a/java/com/android/dialer/historyitemactions/res/values/dimens.xml b/java/com/android/dialer/historyitemactions/res/values/dimens.xml new file mode 100644 index 000000000..47bf804ac --- /dev/null +++ b/java/com/android/dialer/historyitemactions/res/values/dimens.xml @@ -0,0 +1,20 @@ + + + + 48dp + 12dp + \ No newline at end of file diff --git a/java/com/android/dialer/historyitemactions/res/values/strings.xml b/java/com/android/dialer/historyitemactions/res/values/strings.xml new file mode 100644 index 000000000..4d598a930 --- /dev/null +++ b/java/com/android/dialer/historyitemactions/res/values/strings.xml @@ -0,0 +1,47 @@ + + + + + + Voice call + + + Video call + + + Add contact + + + Message + + + Not spam + + + Block + + + Unblock + + + Block/Report spam + + + Copy number + \ No newline at end of file diff --git a/java/com/android/dialer/voicemail/listui/menu/Modules.java b/java/com/android/dialer/voicemail/listui/menu/Modules.java index 76ce172b8..9473b80e1 100644 --- a/java/com/android/dialer/voicemail/listui/menu/Modules.java +++ b/java/com/android/dialer/voicemail/listui/menu/Modules.java @@ -17,11 +17,12 @@ package com.android.dialer.voicemail.listui.menu; import android.content.Context; -import com.android.dialer.contactactions.ContactActionModule; -import com.android.dialer.contactactions.DividerModule; -import com.android.dialer.contactactions.SharedModules; +import com.android.dialer.historyitemactions.DividerModule; +import com.android.dialer.historyitemactions.HistoryItemActionModule; +import com.android.dialer.historyitemactions.SharedModules; import com.android.dialer.logging.ReportingLocation; import com.android.dialer.voicemail.model.VoicemailEntry; +import com.google.common.base.Optional; import java.util.ArrayList; import java.util.List; @@ -29,43 +30,57 @@ import java.util.List; * Configures the modules for the voicemail bottom sheet; these are the rows below the top row * (primary action) in the bottom sheet. */ +@SuppressWarnings("Guava") final class Modules { - static List fromVoicemailEntry( + static List fromVoicemailEntry( Context context, VoicemailEntry voicemailEntry) { // Conditionally add each module, which are items in the bottom sheet's menu. - List modules = new ArrayList<>(); + List modules = new ArrayList<>(); // TODO(uabdullah): Handle maybeAddModuleForVideoOrAudioCall(context, modules, row); - SharedModules.maybeAddModuleForAddingToContacts( - context, - modules, - voicemailEntry.number(), - voicemailEntry.numberAttributes().getName(), - voicemailEntry.numberAttributes().getLookupUri(), - voicemailEntry.numberAttributes().getIsBlocked(), - voicemailEntry.numberAttributes().getIsSpam()); + Optional moduleForAddingContacts = + SharedModules.createModuleForAddingToContacts( + context, + voicemailEntry.number(), + voicemailEntry.numberAttributes().getName(), + voicemailEntry.numberAttributes().getLookupUri(), + voicemailEntry.numberAttributes().getIsBlocked(), + voicemailEntry.numberAttributes().getIsSpam()); + if (moduleForAddingContacts.isPresent()) { + modules.add(moduleForAddingContacts.get()); + } - String normalizedNumber = voicemailEntry.number().getNormalizedNumber(); - SharedModules.maybeAddModuleForSendingTextMessage( - context, modules, normalizedNumber, voicemailEntry.numberAttributes().getIsBlocked()); + Optional moduleForSendingTextMessage = + SharedModules.createModuleForSendingTextMessage( + context, + voicemailEntry.number().getNormalizedNumber(), + voicemailEntry.numberAttributes().getIsBlocked()); + if (moduleForSendingTextMessage.isPresent()) { + modules.add(moduleForSendingTextMessage.get()); + } if (!modules.isEmpty()) { modules.add(new DividerModule()); } - SharedModules.addModulesHandlingBlockedOrSpamNumber( - context, - modules, - voicemailEntry.number().getNormalizedNumber(), - voicemailEntry.number().getCountryIso(), - voicemailEntry.callType(), - voicemailEntry.numberAttributes().getIsBlocked(), - voicemailEntry.numberAttributes().getIsSpam(), - ReportingLocation.Type.VOICEMAIL_HISTORY); + modules.addAll( + SharedModules.createModulesHandlingBlockedOrSpamNumber( + context, + voicemailEntry.number().getNormalizedNumber(), + voicemailEntry.number().getCountryIso(), + voicemailEntry.callType(), + voicemailEntry.numberAttributes().getIsBlocked(), + voicemailEntry.numberAttributes().getIsSpam(), + ReportingLocation.Type.VOICEMAIL_HISTORY)); // TODO(zachh): Module for CallComposer. - SharedModules.maybeAddModuleForCopyingNumber(context, modules, normalizedNumber); + Optional moduleForCopyingNumber = + SharedModules.createModuleForCopyingNumber( + context, voicemailEntry.number().getNormalizedNumber()); + if (moduleForCopyingNumber.isPresent()) { + modules.add(moduleForCopyingNumber.get()); + } return modules; } diff --git a/java/com/android/dialer/voicemail/listui/menu/NewVoicemailMenu.java b/java/com/android/dialer/voicemail/listui/menu/NewVoicemailMenu.java index fbd7fe854..7c669e194 100644 --- a/java/com/android/dialer/voicemail/listui/menu/NewVoicemailMenu.java +++ b/java/com/android/dialer/voicemail/listui/menu/NewVoicemailMenu.java @@ -18,8 +18,8 @@ package com.android.dialer.voicemail.listui.menu; import android.content.Context; import android.view.View; -import com.android.dialer.contactactions.ContactActionBottomSheet; import com.android.dialer.glidephotomanager.GlidePhotoManager; +import com.android.dialer.historyitemactions.HistoryItemActionBottomSheet; import com.android.dialer.voicemail.model.VoicemailEntry; /** Handles configuration of the bottom sheet menus for voicemail entries. */ @@ -29,7 +29,7 @@ public final class NewVoicemailMenu { public static View.OnClickListener createOnClickListener( Context context, VoicemailEntry voicemailEntry, GlidePhotoManager glidePhotoManager) { return (view) -> - ContactActionBottomSheet.show( + HistoryItemActionBottomSheet.show( context, PrimaryAction.fromVoicemailEntry(context, voicemailEntry), Modules.fromVoicemailEntry(context, voicemailEntry), diff --git a/java/com/android/dialer/voicemail/listui/menu/PrimaryAction.java b/java/com/android/dialer/voicemail/listui/menu/PrimaryAction.java index 91f505cb0..9a5aa1886 100644 --- a/java/com/android/dialer/voicemail/listui/menu/PrimaryAction.java +++ b/java/com/android/dialer/voicemail/listui/menu/PrimaryAction.java @@ -19,7 +19,7 @@ package com.android.dialer.voicemail.listui.menu; import android.content.Context; import android.text.TextUtils; import com.android.dialer.calllogutils.NumberAttributesConverter; -import com.android.dialer.contactactions.ContactPrimaryActionInfo; +import com.android.dialer.historyitemactions.HistoryItemPrimaryActionInfo; import com.android.dialer.voicemail.model.VoicemailEntry; /** Configures the primary action row (top row) for theottom sheet for the Voicemail Tab */ @@ -31,9 +31,9 @@ final class PrimaryAction { // setIntent - allow video calling // setPrimaryText - check in with UX // setSecondaryText - check in with UX - static ContactPrimaryActionInfo fromVoicemailEntry( + static HistoryItemPrimaryActionInfo fromVoicemailEntry( Context context, VoicemailEntry voicemailEntry) { - return ContactPrimaryActionInfo.builder() + return HistoryItemPrimaryActionInfo.builder() .setNumber(voicemailEntry.number()) .setPhotoInfo( NumberAttributesConverter.toPhotoInfoBuilder(voicemailEntry.numberAttributes()) -- cgit v1.2.3