From 2cc8b342ff7ebefb2f97fde8b877452528f03d4e Mon Sep 17 00:00:00 2001 From: calderwoodra Date: Mon, 29 Jan 2018 20:48:58 -0800 Subject: Update FragUtils to encourage better readability in Activities. Bug: 72525324 Test: existing PiperOrigin-RevId: 183776841 Change-Id: Ia78002d3da823a228cf5a29f93cd53ad21105f94 --- java/com/android/dialer/common/FragmentUtils.java | 49 +++--- .../dialer/dialpadview/DialpadFragment.java | 3 +- .../com/android/dialer/main/impl/MainActivity.java | 176 +++++++++++++++------ 3 files changed, 163 insertions(+), 65 deletions(-) (limited to 'java/com/android') diff --git a/java/com/android/dialer/common/FragmentUtils.java b/java/com/android/dialer/common/FragmentUtils.java index ad7ec7390..947a9b20a 100644 --- a/java/com/android/dialer/common/FragmentUtils.java +++ b/java/com/android/dialer/common/FragmentUtils.java @@ -16,13 +16,11 @@ package com.android.dialer.common; -import android.app.Activity; import android.support.annotation.CheckResult; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentActivity; /** Utility methods for working with Fragments */ public class FragmentUtils { @@ -35,8 +33,8 @@ public class FragmentUtils { } /** - * @return The parent of frag that implements the callbackInterface or null if no such parent can - * be found + * Returns an instance of the {@code callbackInterface} that is defined in the parent of the + * {@code fragment}, or null if no such call back can be found. */ @CheckResult(suggest = "#checkParent(Fragment, Class)}") @Nullable @@ -52,18 +50,22 @@ public class FragmentUtils { @SuppressWarnings("unchecked") // Casts are checked using runtime methods T parent = (T) parentFragment; return parent; - } else { - FragmentActivity activity = fragment.getActivity(); - if (callbackInterface.isInstance(activity)) { - @SuppressWarnings("unchecked") // Casts are checked using runtime methods - T parent = (T) activity; - return parent; - } + } else if (callbackInterface.isInstance(fragment.getActivity())) { + @SuppressWarnings("unchecked") // Casts are checked using runtime methods + T parent = (T) fragment.getActivity(); + return parent; + } else if (fragment.getActivity() instanceof FragmentUtilListener) { + @SuppressWarnings("unchecked") // Casts are checked using runtime methods + T parent = ((FragmentUtilListener) fragment.getActivity()).getImpl(callbackInterface); + return parent; } return null; } - /** Version of {@link #getParent(Fragment, Class)} which supports {@link android.app.Fragment}. */ + /** + * Returns an instance of the {@code callbackInterface} that is defined in the parent of the + * {@code fragment}, or null if no such call back can be found. + */ @CheckResult(suggest = "#checkParent(Fragment, Class)}") @Nullable public static T getParent( @@ -79,13 +81,14 @@ public class FragmentUtils { @SuppressWarnings("unchecked") // Casts are checked using runtime methods T parent = (T) parentFragment; return parent; - } else { - Activity activity = fragment.getActivity(); - if (callbackInterface.isInstance(activity)) { - @SuppressWarnings("unchecked") // Casts are checked using runtime methods - T parent = (T) activity; - return parent; - } + } else if (callbackInterface.isInstance(fragment.getActivity())) { + @SuppressWarnings("unchecked") // Casts are checked using runtime methods + T parent = (T) fragment.getActivity(); + return parent; + } else if (fragment.getActivity() instanceof FragmentUtilListener) { + @SuppressWarnings("unchecked") // Casts are checked using runtime methods + T parent = ((FragmentUtilListener) fragment.getActivity()).getImpl(callbackInterface); + return parent; } return null; } @@ -133,4 +136,12 @@ public class FragmentUtils { + parent); } } + + /** Useful interface for activities that don't want to implement arbitrary listeners. */ + public interface FragmentUtilListener { + + /** Returns an implementation of T if parent has one, otherwise null. */ + @Nullable + T getImpl(Class callbackInterface); + } } diff --git a/java/com/android/dialer/dialpadview/DialpadFragment.java b/java/com/android/dialer/dialpadview/DialpadFragment.java index 6b8401e6b..680159057 100644 --- a/java/com/android/dialer/dialpadview/DialpadFragment.java +++ b/java/com/android/dialer/dialpadview/DialpadFragment.java @@ -415,7 +415,8 @@ public class DialpadFragment extends Fragment if (isDigitsEmpty()) { if (getActivity() != null) { LogUtil.i("DialpadFragment.onCreateView", "dialpad spacer touched"); - return ((HostInterface) getActivity()).onDialpadSpacerTouchWithEmptyQuery(); + return FragmentUtils.getParentUnsafe(this, HostInterface.class) + .onDialpadSpacerTouchWithEmptyQuery(); } return true; } diff --git a/java/com/android/dialer/main/impl/MainActivity.java b/java/com/android/dialer/main/impl/MainActivity.java index a7a9e6c5a..0308b891b 100644 --- a/java/com/android/dialer/main/impl/MainActivity.java +++ b/java/com/android/dialer/main/impl/MainActivity.java @@ -22,14 +22,17 @@ import android.net.Uri; import android.os.Bundle; import android.provider.CallLog.Calls; import android.provider.ContactsContract.QuickContact; +import android.support.annotation.Nullable; import android.support.design.widget.FloatingActionButton; import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.ImageView; 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.DialerExecutorComponent; +import com.android.dialer.common.concurrent.UiListener; import com.android.dialer.compat.CompatUtils; import com.android.dialer.constants.ActivityRequestCodes; import com.android.dialer.contactsfragment.ContactsFragment; @@ -48,23 +51,28 @@ import com.android.dialer.smartdial.util.SmartDialPrefix; import com.android.dialer.speeddial.SpeedDialFragment; import com.android.dialer.telecom.TelecomUtil; import com.android.dialer.voicemail.listui.NewVoicemailFragment; +import com.google.common.util.concurrent.ListenableFuture; /** This is the main activity for dialer. It hosts favorites, call log, search, dialpad, etc... */ -public final class MainActivity extends AppCompatActivity - implements OnContactSelectedListener, - OnDialpadQueryChangedListener, - DialpadListener, - DialpadFragment.HostInterface, - SearchFragmentListener { +public final class MainActivity extends AppCompatActivity implements FragmentUtilListener { private static final String KEY_SAVED_LANGUAGE_CODE = "saved_language_code"; + private final MainOnContactSelectedListener onContactSelectedListener = + new MainOnContactSelectedListener(this); + private final MainDialpadFragmentHost dialpadFragmentHostInterface = + new MainDialpadFragmentHost(); + private MainSearchController searchController; + private MainOnDialpadQueryChangedListener onDialpadQueryChangedListener; + private MainDialpadListener dialpadListener; + private MainSearchFragmentListener searchFragmentListener; /** Language the device was in last time {@link #onSaveInstanceState(Bundle)} was called. */ private String savedLanguageCode; private View snackbarContainer; + private UiListener getLastOutgoingCallListener; /** * @param context Context of the application package implementing MainActivity class. @@ -81,10 +89,17 @@ public final class MainActivity extends AppCompatActivity super.onCreate(savedInstanceState); LogUtil.enterBlock("MainActivity.onCreate"); setContentView(R.layout.main_activity); + initUiListeners(); initLayout(savedInstanceState); SmartDialPrefix.initializeNanpSettings(this); } + private void initUiListeners() { + getLastOutgoingCallListener = + DialerExecutorComponent.get(this) + .createUiListener(getFragmentManager(), "Query last phone number"); + } + private void initLayout(Bundle savedInstanceState) { snackbarContainer = findViewById(R.id.coordinator_layout); @@ -100,6 +115,10 @@ public final class MainActivity extends AppCompatActivity searchController = new MainSearchController(this, bottomNav, fab, toolbar); toolbar.setSearchBarListener(searchController); + onDialpadQueryChangedListener = new MainOnDialpadQueryChangedListener(searchController); + dialpadListener = new MainDialpadListener(this, searchController, getLastOutgoingCallListener); + searchFragmentListener = new MainSearchFragmentListener(searchController); + // Restore our view state if needed, else initialize as if the app opened for the first time if (savedInstanceState != null) { savedLanguageCode = savedInstanceState.getString(KEY_SAVED_LANGUAGE_CODE); @@ -152,60 +171,127 @@ public final class MainActivity extends AppCompatActivity } @Override - public void onContactSelected(ImageView photo, Uri contactUri, long contactId) { - // TODO(calderwoodra): Add impression logging - QuickContact.showQuickContact( - this, photo, contactUri, QuickContact.MODE_LARGE, null /* excludeMimes */); + public void onBackPressed() { + if (searchController.onBackPressed()) { + return; + } + super.onBackPressed(); } - @Override // OnDialpadQueryChangedListener - public void onDialpadQueryChanged(String query) { - searchController.onDialpadQueryChanged(query); + @Nullable + @Override + @SuppressWarnings("unchecked") // Casts are checked using runtime methods + public T getImpl(Class callbackInterface) { + if (callbackInterface.isInstance(onContactSelectedListener)) { + return (T) onContactSelectedListener; + } else if (callbackInterface.isInstance(onDialpadQueryChangedListener)) { + return (T) onDialpadQueryChangedListener; + } else if (callbackInterface.isInstance(dialpadListener)) { + return (T) dialpadListener; + } else if (callbackInterface.isInstance(dialpadFragmentHostInterface)) { + return (T) dialpadFragmentHostInterface; + } else if (callbackInterface.isInstance(searchFragmentListener)) { + return (T) searchFragmentListener; + } else { + return null; + } } - @Override // DialpadListener - public void getLastOutgoingCall(LastOutgoingCallCallback callback) { - DialerExecutorComponent.get(this) - .dialerExecutorFactory() - .createUiTaskBuilder( - getFragmentManager(), "Query last phone number", Calls::getLastOutgoingCall) - .onSuccess(output -> callback.lastOutgoingCall(output)) - .build() - .executeParallel(this); - } + /** @see OnContactSelectedListener */ + private static final class MainOnContactSelectedListener implements OnContactSelectedListener { - @Override // DialpadListener - public void onDialpadShown() { - searchController.onDialpadShown(); - } + private final Context context; + + MainOnContactSelectedListener(Context context) { + this.context = context; + } - @Override // DialpadListener - public void onCallPlacedFromDialpad() { - // TODO(calderwoodra): logging + @Override + public void onContactSelected(ImageView photo, Uri contactUri, long contactId) { + // TODO(calderwoodra): Add impression logging + QuickContact.showQuickContact( + context, photo, contactUri, QuickContact.MODE_LARGE, null /* excludeMimes */); + } } - @Override - public void onBackPressed() { - if (searchController.onBackPressed()) { - return; + /** @see OnDialpadQueryChangedListener */ + private static final class MainOnDialpadQueryChangedListener + implements OnDialpadQueryChangedListener { + + private final MainSearchController searchController; + + MainOnDialpadQueryChangedListener(MainSearchController searchController) { + this.searchController = searchController; + } + + @Override + public void onDialpadQueryChanged(String query) { + searchController.onDialpadQueryChanged(query); } - super.onBackPressed(); } - @Override // DialpadFragment.HostInterface - public boolean onDialpadSpacerTouchWithEmptyQuery() { - // No-op, just let the clicks fall through to the search list - return false; + /** @see DialpadListener */ + private static final class MainDialpadListener implements DialpadListener { + + private final MainSearchController searchController; + private final Context context; + private final UiListener listener; + + MainDialpadListener( + Context context, MainSearchController searchController, UiListener uiListener) { + this.context = context; + this.searchController = searchController; + this.listener = uiListener; + } + + @Override + public void getLastOutgoingCall(LastOutgoingCallCallback callback) { + ListenableFuture listenableFuture = + DialerExecutorComponent.get(context) + .backgroundExecutor() + .submit(() -> Calls.getLastOutgoingCall(context)); + listener.listen(context, listenableFuture, callback::lastOutgoingCall, throwable -> {}); + } + + @Override + public void onDialpadShown() { + searchController.onDialpadShown(); + } + + @Override + public void onCallPlacedFromDialpad() { + // TODO(calderwoodra): logging + } } - @Override // SearchFragmentListener - public void onSearchListTouch() { - searchController.onSearchListTouch(); + /** @see SearchFragmentListener */ + private static final class MainSearchFragmentListener implements SearchFragmentListener { + + private final MainSearchController searchController; + + MainSearchFragmentListener(MainSearchController searchController) { + this.searchController = searchController; + } + + @Override + public void onSearchListTouch() { + searchController.onSearchListTouch(); + } + + @Override + public void onCallPlacedFromSearch() { + // TODO(calderwoodra): logging + } } - @Override // SearchFragmentListener - public void onCallPlacedFromSearch() { - // TODO(calderwoodra): logging + /** @see DialpadFragment.HostInterface */ + private static final class MainDialpadFragmentHost implements DialpadFragment.HostInterface { + + @Override + public boolean onDialpadSpacerTouchWithEmptyQuery() { + // No-op, just let the clicks fall through to the search list + return false; + } } /** -- cgit v1.2.3 From 54e90e5c61b563a432ad70066505f08b352837c3 Mon Sep 17 00:00:00 2001 From: roldenburg Date: Tue, 30 Jan 2018 12:24:16 -0800 Subject: Allow delete of Restricted number entries Bug: 72639087 Test: CallDetailsActivityTest PiperOrigin-RevId: 183872706 Change-Id: I2c3241b3b9059fe4ed82d79902a474df3d5975a4 --- java/com/android/dialer/calldetails/CallDetailsFooterViewHolder.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'java/com/android') diff --git a/java/com/android/dialer/calldetails/CallDetailsFooterViewHolder.java b/java/com/android/dialer/calldetails/CallDetailsFooterViewHolder.java index eeb19a862..30b28d83a 100644 --- a/java/com/android/dialer/calldetails/CallDetailsFooterViewHolder.java +++ b/java/com/android/dialer/calldetails/CallDetailsFooterViewHolder.java @@ -36,7 +36,6 @@ final class CallDetailsFooterViewHolder extends RecyclerView.ViewHolder implemen private final ReportCallIdListener reportCallIdListener; private final DeleteCallDetailsListener deleteCallDetailsListener; - private final View container; private final View copy; private final View edit; private final View reportCallerId; @@ -51,7 +50,6 @@ final class CallDetailsFooterViewHolder extends RecyclerView.ViewHolder implemen super(view); this.reportCallIdListener = reportCallIdListener; this.deleteCallDetailsListener = deleteCallDetailsListener; - container = view.findViewById(R.id.footer_container); copy = view.findViewById(R.id.call_detail_action_copy); edit = view.findViewById(R.id.call_detail_action_edit_before_call); reportCallerId = view.findViewById(R.id.call_detail_action_report_caller_id); @@ -65,7 +63,8 @@ final class CallDetailsFooterViewHolder extends RecyclerView.ViewHolder implemen public void setPhoneNumber(String number) { this.number = number; if (TextUtils.isEmpty(number)) { - container.setVisibility(View.GONE); + copy.setVisibility(View.GONE); + edit.setVisibility(View.GONE); } else if (reportCallIdListener.canReportCallerId(number)) { reportCallerId.setVisibility(View.VISIBLE); } -- cgit v1.2.3 From 5e550aadbb9a99885b2c719501d00a491d2ba9db Mon Sep 17 00:00:00 2001 From: calderwoodra Date: Tue, 30 Jan 2018 14:00:24 -0800 Subject: Added badge count feature to bottom nav. This Change doesn't actually fetch the badge counts, but it's now possible to set badge counts. Bug: 72525595 Test: manual PiperOrigin-RevId: 183887322 Change-Id: I452ca6352133befc8cc2a39c44cd84a33fd66d42 --- .../com/android/dialer/main/impl/BottomNavBar.java | 14 +++++++++++ .../android/dialer/main/impl/BottomNavItem.java | 14 +++++++++++ .../main/impl/res/drawable/notification_badge.xml | 22 +++++++++++++++++ .../main/impl/res/layout/bottom_nav_item.xml | 28 ++++++++++++++++++---- 4 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 java/com/android/dialer/main/impl/res/drawable/notification_badge.xml (limited to 'java/com/android') diff --git a/java/com/android/dialer/main/impl/BottomNavBar.java b/java/com/android/dialer/main/impl/BottomNavBar.java index 66a57becd..a4ddc0652 100644 --- a/java/com/android/dialer/main/impl/BottomNavBar.java +++ b/java/com/android/dialer/main/impl/BottomNavBar.java @@ -119,6 +119,20 @@ final class BottomNavBar extends LinearLayout { } } + void setNotificationCount(@TabIndex int tab, int count) { + if (tab == TabIndex.SPEED_DIAL) { + speedDial.setNotificationCount(count); + } else if (tab == TabIndex.HISTORY) { + callLog.setNotificationCount(count); + } else if (tab == TabIndex.CONTACTS) { + contacts.setNotificationCount(count); + } else if (tab == TabIndex.VOICEMAIL) { + voicemail.setNotificationCount(count); + } else { + throw new IllegalStateException("Invalid tab: " + tab); + } + } + void setOnTabSelectedListener(OnBottomNavTabSelectedListener listener) { this.listener = listener; } diff --git a/java/com/android/dialer/main/impl/BottomNavItem.java b/java/com/android/dialer/main/impl/BottomNavItem.java index 14706ab34..af7399b6c 100644 --- a/java/com/android/dialer/main/impl/BottomNavItem.java +++ b/java/com/android/dialer/main/impl/BottomNavItem.java @@ -22,15 +22,18 @@ import android.support.annotation.DrawableRes; import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.util.AttributeSet; +import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import com.android.dialer.common.Assert; /** Navigation item in a bottom nav. */ final class BottomNavItem extends LinearLayout { private ImageView image; private TextView text; + private TextView notificationBadge; public BottomNavItem(Context context, @Nullable AttributeSet attrs) { super(context, attrs); @@ -41,6 +44,7 @@ final class BottomNavItem extends LinearLayout { super.onFinishInflate(); image = findViewById(R.id.bottom_nav_item_image); text = findViewById(R.id.bottom_nav_item_text); + notificationBadge = findViewById(R.id.notification_badge); } @Override @@ -56,4 +60,14 @@ final class BottomNavItem extends LinearLayout { text.setText(stringRes); image.setImageResource(drawableRes); } + + void setNotificationCount(int count) { + Assert.checkArgument(count >= 0, "Invalid count: " + count); + if (count == 0) { + notificationBadge.setVisibility(View.GONE); + } else { + notificationBadge.setVisibility(View.VISIBLE); + notificationBadge.setText(String.format(Integer.toString(count))); + } + } } diff --git a/java/com/android/dialer/main/impl/res/drawable/notification_badge.xml b/java/com/android/dialer/main/impl/res/drawable/notification_badge.xml new file mode 100644 index 000000000..2d0dafe93 --- /dev/null +++ b/java/com/android/dialer/main/impl/res/drawable/notification_badge.xml @@ -0,0 +1,22 @@ + + + + + + \ No newline at end of file diff --git a/java/com/android/dialer/main/impl/res/layout/bottom_nav_item.xml b/java/com/android/dialer/main/impl/res/layout/bottom_nav_item.xml index f9f2b6102..2d9998af2 100644 --- a/java/com/android/dialer/main/impl/res/layout/bottom_nav_item.xml +++ b/java/com/android/dialer/main/impl/res/layout/bottom_nav_item.xml @@ -25,13 +25,31 @@ android:paddingStart="12dp" android:paddingEnd="12dp" android:gravity="center" + android:theme="@style/Theme.AppCompat" android:background="?android:selectableItemBackgroundBorderless"> - + + + + + + Date: Tue, 30 Jan 2018 14:50:44 -0800 Subject: Add old speed dial fragment to NUI. Bug: 72618868 Test: existing PiperOrigin-RevId: 183896896 Change-Id: I9b90aaa02e1c40f38fbbe636f4e51cfd01c570f6 --- java/com/android/dialer/app/DialtactsActivity.java | 5 + .../dialer/app/list/OldSpeedDialFragment.java | 174 ++++++-------- .../com/android/dialer/main/impl/MainActivity.java | 253 ++++++++++++++++++--- .../dialer/main/impl/res/layout/main_activity.xml | 14 ++ 4 files changed, 308 insertions(+), 138 deletions(-) (limited to 'java/com/android') diff --git a/java/com/android/dialer/app/DialtactsActivity.java b/java/com/android/dialer/app/DialtactsActivity.java index 23f4d403c..293ebed87 100644 --- a/java/com/android/dialer/app/DialtactsActivity.java +++ b/java/com/android/dialer/app/DialtactsActivity.java @@ -1508,6 +1508,11 @@ public class DialtactsActivity extends TransactionSafeActivity @Override public void onDroppedOnRemove() {} + @Override + public ImageView getDragShadowOverlay() { + return findViewById(R.id.contact_tile_drag_shadow_overlay); + } + /** * Allows the SpeedDialFragment to attach the drag controller to mRemoveViewContainer once it has * been attached to the activity. diff --git a/java/com/android/dialer/app/list/OldSpeedDialFragment.java b/java/com/android/dialer/app/list/OldSpeedDialFragment.java index 1b366c1ee..caa5e91f0 100644 --- a/java/com/android/dialer/app/list/OldSpeedDialFragment.java +++ b/java/com/android/dialer/app/list/OldSpeedDialFragment.java @@ -20,7 +20,6 @@ import static android.Manifest.permission.READ_CONTACTS; import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; -import android.app.Activity; import android.app.Fragment; import android.app.LoaderManager; import android.content.CursorLoader; @@ -50,6 +49,7 @@ import com.android.contacts.common.list.ContactTileView; import com.android.contacts.common.list.OnPhoneNumberPickerActionListener; import com.android.dialer.app.R; import com.android.dialer.callintent.CallSpecificAppData; +import com.android.dialer.common.FragmentUtils; import com.android.dialer.common.LogUtil; import com.android.dialer.contactphoto.ContactPhotoManager; import com.android.dialer.util.PermissionsUtil; @@ -77,22 +77,17 @@ public class OldSpeedDialFragment extends Fragment private static final long KEY_REMOVED_ITEM_HEIGHT = Long.MAX_VALUE; private static final String TAG = "OldSpeedDialFragment"; - private static final boolean DEBUG = false; /** Used with LoaderManager. */ private static final int LOADER_ID_CONTACT_TILE = 1; private final LongSparseArray itemIdTopMap = new LongSparseArray<>(); private final LongSparseArray itemIdLeftMap = new LongSparseArray<>(); private final ContactTileView.Listener contactTileAdapterListener = - new ContactTileAdapterListener(); - private final LoaderManager.LoaderCallbacks contactTileLoaderListener = - new ContactTileLoaderListener(); - private final ScrollListener scrollListener = new ScrollListener(); + new ContactTileAdapterListener(this); + private final ScrollListener scrollListener = new ScrollListener(this); + private LoaderManager.LoaderCallbacks contactTileLoaderListener; private int animationDuration; - private OnPhoneNumberPickerActionListener phoneNumberPickerActionListener; - private OnListFragmentScrolledListener activityScrollListener; private PhoneFavoritesTileAdapter contactTileAdapter; - private View parentView; private PhoneFavoriteListView listView; private View contactTileFrame; /** Layout used when there are no favorites. */ @@ -100,9 +95,6 @@ public class OldSpeedDialFragment extends Fragment @Override public void onCreate(Bundle savedState) { - if (DEBUG) { - LogUtil.d("OldSpeedDialFragment.onCreate", null); - } Trace.beginSection(TAG + " onCreate"); super.onCreate(savedState); @@ -110,8 +102,9 @@ public class OldSpeedDialFragment extends Fragment // We don't construct the resultant adapter at this moment since it requires LayoutInflater // that will be available on onCreateView(). contactTileAdapter = - new PhoneFavoritesTileAdapter(getActivity(), contactTileAdapterListener, this); - contactTileAdapter.setPhotoLoader(ContactPhotoManager.getInstance(getActivity())); + new PhoneFavoritesTileAdapter(getContext(), contactTileAdapterListener, this); + contactTileAdapter.setPhotoLoader(ContactPhotoManager.getInstance(getContext())); + contactTileLoaderListener = new ContactTileLoaderListener(this, contactTileAdapter); animationDuration = getResources().getInteger(R.integer.fade_duration); Trace.endSection(); } @@ -123,7 +116,7 @@ public class OldSpeedDialFragment extends Fragment if (contactTileAdapter != null) { contactTileAdapter.refreshContactsPreferences(); } - if (PermissionsUtil.hasContactsReadPermissions(getActivity())) { + if (PermissionsUtil.hasContactsReadPermissions(getContext())) { if (getLoaderManager().getLoader(LOADER_ID_CONTACT_TILE) == null) { getLoaderManager().initLoader(LOADER_ID_CONTACT_TILE, null, contactTileLoaderListener); @@ -144,7 +137,7 @@ public class OldSpeedDialFragment extends Fragment public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Trace.beginSection(TAG + " onCreateView"); - parentView = inflater.inflate(R.layout.speed_dial_fragment, container, false); + View parentView = inflater.inflate(R.layout.speed_dial_fragment, container, false); listView = (PhoneFavoriteListView) parentView.findViewById(R.id.contact_tile_list); listView.setOnItemClickListener(this); @@ -152,10 +145,8 @@ public class OldSpeedDialFragment extends Fragment listView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT); listView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY); listView.getDragDropController().addOnDragDropListener(contactTileAdapter); - - final ImageView dragShadowOverlay = - (ImageView) getActivity().findViewById(R.id.contact_tile_drag_shadow_overlay); - listView.setDragShadowOverlay(dragShadowOverlay); + listView.setDragShadowOverlay( + FragmentUtils.getParentUnsafe(this, HostInterface.class).getDragShadowOverlay()); emptyView = (EmptyContentView) parentView.findViewById(R.id.empty_list_view); emptyView.setImage(R.drawable.empty_speed_dial); @@ -165,7 +156,7 @@ public class OldSpeedDialFragment extends Fragment final LayoutAnimationController controller = new LayoutAnimationController( - AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_in)); + AnimationUtils.loadAnimation(getContext(), android.R.anim.fade_in)); controller.setDelay(0); listView.setLayoutAnimation(controller); listView.setAdapter(contactTileAdapter); @@ -206,36 +197,16 @@ public class OldSpeedDialFragment extends Fragment @Override public void onStart() { super.onStart(); - - final Activity activity = getActivity(); - - try { - activityScrollListener = (OnListFragmentScrolledListener) activity; - } catch (ClassCastException e) { - throw new ClassCastException( - activity.toString() + " must implement OnListFragmentScrolledListener"); - } - - try { - OnDragDropListener listener = (OnDragDropListener) activity; - listView.getDragDropController().addOnDragDropListener(listener); - ((HostInterface) activity).setDragDropController(listView.getDragDropController()); - } catch (ClassCastException e) { - throw new ClassCastException( - activity.toString() + " must implement OnDragDropListener and HostInterface"); - } - - try { - phoneNumberPickerActionListener = (OnPhoneNumberPickerActionListener) activity; - } catch (ClassCastException e) { - throw new ClassCastException( - activity.toString() + " must implement PhoneFavoritesFragment.listener"); - } + listView + .getDragDropController() + .addOnDragDropListener(FragmentUtils.getParentUnsafe(this, OnDragDropListener.class)); + FragmentUtils.getParentUnsafe(this, HostInterface.class) + .setDragDropController(listView.getDragDropController()); // Use initLoader() instead of restartLoader() to refraining unnecessary reload. // This method call implicitly assures ContactTileLoaderListener's onLoadFinished() will // be called, on which we'll check if "all" contacts should be reloaded again or not. - if (PermissionsUtil.hasContactsReadPermissions(activity)) { + if (PermissionsUtil.hasContactsReadPermissions(getContext())) { getLoaderManager().initLoader(LOADER_ID_CONTACT_TILE, null, contactTileLoaderListener); } else { setEmptyViewVisibility(true); @@ -268,9 +239,6 @@ public class OldSpeedDialFragment extends Fragment */ private void saveOffsets(int removedItemHeight) { final int firstVisiblePosition = listView.getFirstVisiblePosition(); - if (DEBUG) { - LogUtil.d("OldSpeedDialFragment.saveOffsets", "Child count : " + listView.getChildCount()); - } for (int i = 0; i < listView.getChildCount(); i++) { final View child = listView.getChildAt(i); final int position = firstVisiblePosition + i; @@ -281,11 +249,6 @@ public class OldSpeedDialFragment extends Fragment continue; } final long itemId = contactTileAdapter.getItemId(position); - if (DEBUG) { - LogUtil.d( - "OldSpeedDialFragment.saveOffsets", - "Saving itemId: " + itemId + " for listview child " + i + " Top: " + child.getTop()); - } itemIdTopMap.put(itemId, child.getTop()); itemIdLeftMap.put(itemId, child.getLeft()); } @@ -350,19 +313,6 @@ public class OldSpeedDialFragment extends Fragment animators.add(ObjectAnimator.ofFloat(child, "translationY", deltaY, 0.0f)); } } - - if (DEBUG) { - LogUtil.d( - "OldSpeedDialFragment.onPreDraw", - "Found itemId: " - + itemId - + " for listview child " - + i - + " Top: " - + top - + " Delta: " - + deltaY); - } } } @@ -399,11 +349,6 @@ public class OldSpeedDialFragment extends Fragment @Override public void onEmptyViewActionButtonClicked() { - final Activity activity = getActivity(); - if (activity == null) { - return; - } - String[] deniedPermissions = PermissionsUtil.getPermissionsCurrentlyDenied( getContext(), PermissionsUtil.allContactsGroupPermissionsUsedInDialer); @@ -415,7 +360,7 @@ public class OldSpeedDialFragment extends Fragment this, deniedPermissions, READ_CONTACTS_PERMISSION_REQUEST_CODE); } else { // Switch tabs - ((HostInterface) activity).showAllContactsTab(); + FragmentUtils.getParentUnsafe(this, HostInterface.class).showAllContactsTab(); } } @@ -424,79 +369,88 @@ public class OldSpeedDialFragment extends Fragment int requestCode, String[] permissions, int[] grantResults) { if (requestCode == READ_CONTACTS_PERMISSION_REQUEST_CODE) { if (grantResults.length == 1 && PackageManager.PERMISSION_GRANTED == grantResults[0]) { - PermissionsUtil.notifyPermissionGranted(getActivity(), READ_CONTACTS); + PermissionsUtil.notifyPermissionGranted(getContext(), READ_CONTACTS); } } } - public interface HostInterface { + private static final class ContactTileLoaderListener + implements LoaderManager.LoaderCallbacks { - void setDragDropController(DragDropController controller); + private final OldSpeedDialFragment fragment; + private final PhoneFavoritesTileAdapter adapter; - void showAllContactsTab(); - } - - class ContactTileLoaderListener implements LoaderManager.LoaderCallbacks { + ContactTileLoaderListener(OldSpeedDialFragment fragment, PhoneFavoritesTileAdapter adapter) { + this.fragment = fragment; + this.adapter = adapter; + } @Override public CursorLoader onCreateLoader(int id, Bundle args) { - if (DEBUG) { - LogUtil.d("ContactTileLoaderListener.onCreateLoader", null); - } - return ContactTileLoaderFactory.createStrequentPhoneOnlyLoader(getActivity()); + return ContactTileLoaderFactory.createStrequentPhoneOnlyLoader(fragment.getContext()); } @Override public void onLoadFinished(Loader loader, Cursor data) { - if (DEBUG) { - LogUtil.d("ContactTileLoaderListener.onLoadFinished", null); - } - contactTileAdapter.setContactCursor(data); - setEmptyViewVisibility(contactTileAdapter.getCount() == 0); + adapter.setContactCursor(data); + fragment.setEmptyViewVisibility(adapter.getCount() == 0); } @Override - public void onLoaderReset(Loader loader) { - if (DEBUG) { - LogUtil.d("ContactTileLoaderListener.onLoaderReset", null); - } - } + public void onLoaderReset(Loader loader) {} } - private class ContactTileAdapterListener implements ContactTileView.Listener { + private static final class ContactTileAdapterListener implements ContactTileView.Listener { + + private final OldSpeedDialFragment fragment; + + ContactTileAdapterListener(OldSpeedDialFragment fragment) { + this.fragment = fragment; + } @Override public void onContactSelected( Uri contactUri, Rect targetRect, CallSpecificAppData callSpecificAppData) { - if (phoneNumberPickerActionListener != null) { - phoneNumberPickerActionListener.onPickDataUri( - contactUri, false /* isVideoCall */, callSpecificAppData); - } + FragmentUtils.getParentUnsafe(fragment, OnPhoneNumberPickerActionListener.class) + .onPickDataUri(contactUri, false /* isVideoCall */, callSpecificAppData); } @Override public void onCallNumberDirectly(String phoneNumber, CallSpecificAppData callSpecificAppData) { - if (phoneNumberPickerActionListener != null) { - phoneNumberPickerActionListener.onPickPhoneNumber( - phoneNumber, false /* isVideoCall */, callSpecificAppData); - } + FragmentUtils.getParentUnsafe(fragment, OnPhoneNumberPickerActionListener.class) + .onPickPhoneNumber(phoneNumber, false /* isVideoCall */, callSpecificAppData); } } - private class ScrollListener implements ListView.OnScrollListener { + private static class ScrollListener implements ListView.OnScrollListener { + + private final OldSpeedDialFragment fragment; + + ScrollListener(OldSpeedDialFragment fragment) { + this.fragment = fragment; + } @Override public void onScroll( AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { - if (activityScrollListener != null) { - activityScrollListener.onListFragmentScroll( - firstVisibleItem, visibleItemCount, totalItemCount); - } + FragmentUtils.getParentUnsafe(fragment, OnListFragmentScrolledListener.class) + .onListFragmentScroll(firstVisibleItem, visibleItemCount, totalItemCount); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { - activityScrollListener.onListFragmentScrollStateChange(scrollState); + FragmentUtils.getParentUnsafe(fragment, OnListFragmentScrolledListener.class) + .onListFragmentScrollStateChange(scrollState); } } + + /** Interface for parents of OldSpeedDialFragment to implement. */ + public interface HostInterface { + + void setDragDropController(DragDropController controller); + + void showAllContactsTab(); + + ImageView getDragShadowOverlay(); + } } diff --git a/java/com/android/dialer/main/impl/MainActivity.java b/java/com/android/dialer/main/impl/MainActivity.java index 0308b891b..4bc15da98 100644 --- a/java/com/android/dialer/main/impl/MainActivity.java +++ b/java/com/android/dialer/main/impl/MainActivity.java @@ -16,6 +16,8 @@ package com.android.dialer.main.impl; +import android.app.Fragment; +import android.app.FragmentManager; import android.content.Context; import android.content.Intent; import android.net.Uri; @@ -25,15 +27,24 @@ import android.provider.ContactsContract.QuickContact; import android.support.annotation.Nullable; import android.support.design.widget.FloatingActionButton; import android.support.v4.app.FragmentTransaction; -import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.ImageView; +import com.android.contacts.common.list.OnPhoneNumberPickerActionListener; +import com.android.dialer.app.list.DragDropController; +import com.android.dialer.app.list.OldSpeedDialFragment; +import com.android.dialer.app.list.OnDragDropListener; +import com.android.dialer.app.list.OnListFragmentScrolledListener; +import com.android.dialer.app.list.PhoneFavoriteSquareTileView; +import com.android.dialer.callintent.CallIntentBuilder; +import com.android.dialer.callintent.CallSpecificAppData; import com.android.dialer.calllog.ui.NewCallLogFragment; +import com.android.dialer.common.Assert; import com.android.dialer.common.FragmentUtils.FragmentUtilListener; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.DialerExecutorComponent; import com.android.dialer.common.concurrent.UiListener; import com.android.dialer.compat.CompatUtils; +import com.android.dialer.configprovider.ConfigProviderComponent; import com.android.dialer.constants.ActivityRequestCodes; import com.android.dialer.contactsfragment.ContactsFragment; import com.android.dialer.contactsfragment.ContactsFragment.Header; @@ -43,18 +54,30 @@ import com.android.dialer.dialpadview.DialpadFragment; import com.android.dialer.dialpadview.DialpadFragment.DialpadListener; import com.android.dialer.dialpadview.DialpadFragment.LastOutgoingCallCallback; import com.android.dialer.dialpadview.DialpadFragment.OnDialpadQueryChangedListener; +import com.android.dialer.interactions.PhoneNumberInteraction; +import com.android.dialer.interactions.PhoneNumberInteraction.DisambigDialogDismissedListener; +import com.android.dialer.interactions.PhoneNumberInteraction.InteractionErrorCode; +import com.android.dialer.interactions.PhoneNumberInteraction.InteractionErrorListener; import com.android.dialer.main.impl.BottomNavBar.OnBottomNavTabSelectedListener; import com.android.dialer.main.impl.toolbar.MainToolbar; import com.android.dialer.postcall.PostCall; +import com.android.dialer.precall.PreCall; import com.android.dialer.searchfragment.list.NewSearchFragment.SearchFragmentListener; import com.android.dialer.smartdial.util.SmartDialPrefix; import com.android.dialer.speeddial.SpeedDialFragment; import com.android.dialer.telecom.TelecomUtil; +import com.android.dialer.util.DialerUtils; +import com.android.dialer.util.TransactionSafeActivity; import com.android.dialer.voicemail.listui.NewVoicemailFragment; import com.google.common.util.concurrent.ListenableFuture; /** This is the main activity for dialer. It hosts favorites, call log, search, dialpad, etc... */ -public final class MainActivity extends AppCompatActivity implements FragmentUtilListener { +// TODO(calderwoodra): Do not extend TransactionSafeActivity after new SpeedDial is launched +public final class MainActivity extends TransactionSafeActivity + implements FragmentUtilListener, + // TODO(calderwoodra): remove these 2 interfaces when we migrate to new speed dial fragment + InteractionErrorListener, + DisambigDialogDismissedListener { private static final String KEY_SAVED_LANGUAGE_CODE = "saved_language_code"; @@ -67,6 +90,10 @@ public final class MainActivity extends AppCompatActivity implements FragmentUti private MainOnDialpadQueryChangedListener onDialpadQueryChangedListener; private MainDialpadListener dialpadListener; private MainSearchFragmentListener searchFragmentListener; + private MainOnListFragmentScrolledListener onListFragmentScrolledListener; + private MainOnPhoneNumberPickerActionListener onPhoneNumberPickerActionListener; + private MainOldSpeedDialFragmentHostInterface oldSpeedDialFragmentHostInterface; + private MainOnDragDropListener onDragDropListener; /** Language the device was in last time {@link #onSaveInstanceState(Bundle)} was called. */ private String savedLanguageCode; @@ -110,7 +137,10 @@ public final class MainActivity extends AppCompatActivity implements FragmentUti setSupportActionBar(findViewById(R.id.toolbar)); BottomNavBar bottomNav = findViewById(R.id.bottom_nav_bar); - bottomNav.setOnTabSelectedListener(new MainBottomNavBarBottomNavTabListener()); + MainBottomNavBarBottomNavTabListener bottomNavTabListener = + new MainBottomNavBarBottomNavTabListener( + this, getFragmentManager(), getSupportFragmentManager()); + bottomNav.setOnTabSelectedListener(bottomNavTabListener); searchController = new MainSearchController(this, bottomNav, fab, toolbar); toolbar.setSearchBarListener(searchController); @@ -118,6 +148,12 @@ public final class MainActivity extends AppCompatActivity implements FragmentUti onDialpadQueryChangedListener = new MainOnDialpadQueryChangedListener(searchController); dialpadListener = new MainDialpadListener(this, searchController, getLastOutgoingCallListener); searchFragmentListener = new MainSearchFragmentListener(searchController); + onListFragmentScrolledListener = new MainOnListFragmentScrolledListener(snackbarContainer); + onPhoneNumberPickerActionListener = new MainOnPhoneNumberPickerActionListener(this); + oldSpeedDialFragmentHostInterface = + new MainOldSpeedDialFragmentHostInterface( + bottomNavTabListener, findViewById(R.id.contact_tile_drag_shadow_overlay)); + onDragDropListener = new MainOnDragDropListener(); // Restore our view state if needed, else initialize as if the app opened for the first time if (savedInstanceState != null) { @@ -192,11 +228,41 @@ public final class MainActivity extends AppCompatActivity implements FragmentUti return (T) dialpadFragmentHostInterface; } else if (callbackInterface.isInstance(searchFragmentListener)) { return (T) searchFragmentListener; + } else if (callbackInterface.isInstance(onListFragmentScrolledListener)) { + return (T) onListFragmentScrolledListener; + } else if (callbackInterface.isInstance(onPhoneNumberPickerActionListener)) { + return (T) onPhoneNumberPickerActionListener; + } else if (callbackInterface.isInstance(oldSpeedDialFragmentHostInterface)) { + return (T) oldSpeedDialFragmentHostInterface; + } else if (callbackInterface.isInstance(onDragDropListener)) { + return (T) onDragDropListener; } else { return null; } } + @Override + public void interactionError(@InteractionErrorCode int interactionErrorCode) { + switch (interactionErrorCode) { + case InteractionErrorCode.USER_LEAVING_ACTIVITY: + // This is expected to happen if the user exits the activity before the interaction occurs. + return; + case InteractionErrorCode.CONTACT_NOT_FOUND: + case InteractionErrorCode.CONTACT_HAS_NO_NUMBER: + case InteractionErrorCode.OTHER_ERROR: + default: + // All other error codes are unexpected. For example, it should be impossible to start an + // interaction with an invalid contact from this activity. + throw Assert.createIllegalStateFailException( + "PhoneNumberInteraction error: " + interactionErrorCode); + } + } + + @Override + public void onDisambigDialogDismissed() { + // Don't do anything; the app will remain open with favorites tiles displayed. + } + /** @see OnContactSelectedListener */ private static final class MainOnContactSelectedListener implements OnContactSelectedListener { @@ -294,11 +360,123 @@ public final class MainActivity extends AppCompatActivity implements FragmentUti } } + /** @see OnListFragmentScrolledListener */ + private static final class MainOnListFragmentScrolledListener + implements OnListFragmentScrolledListener { + + private final View parentLayout; + + MainOnListFragmentScrolledListener(View parentLayout) { + this.parentLayout = parentLayout; + } + + @Override + public void onListFragmentScrollStateChange(int scrollState) { + DialerUtils.hideInputMethod(parentLayout); + } + + @Override + public void onListFragmentScroll( + int firstVisibleItem, int visibleItemCount, int totalItemCount) { + // TODO: No-op for now. This should eventually show/hide the actionBar based on + // interactions with the ListsFragments. + } + } + + /** @see OnPhoneNumberPickerActionListener */ + private static final class MainOnPhoneNumberPickerActionListener + implements OnPhoneNumberPickerActionListener { + + private final TransactionSafeActivity activity; + + MainOnPhoneNumberPickerActionListener(TransactionSafeActivity activity) { + this.activity = activity; + } + + @Override + public void onPickDataUri( + Uri dataUri, boolean isVideoCall, CallSpecificAppData callSpecificAppData) { + PhoneNumberInteraction.startInteractionForPhoneCall( + activity, dataUri, isVideoCall, callSpecificAppData); + } + + @Override + public void onPickPhoneNumber( + String phoneNumber, boolean isVideoCall, CallSpecificAppData callSpecificAppData) { + if (phoneNumber == null) { + // Invalid phone number, but let the call go through so that InCallUI can show + // an error message. + phoneNumber = ""; + } + PreCall.start( + activity, + new CallIntentBuilder(phoneNumber, callSpecificAppData) + .setIsVideoCall(isVideoCall) + .setAllowAssistedDial(callSpecificAppData.getAllowAssistedDialing())); + } + + @Override + public void onHomeInActionBarSelected() { + // TODO(calderwoodra): investigate if we need to exit search here + // PhoneNumberPickerFragment#onOptionsItemSelected + } + } + + /** @see OldSpeedDialFragment.HostInterface */ + private static final class MainOldSpeedDialFragmentHostInterface + implements OldSpeedDialFragment.HostInterface { + + private final MainBottomNavBarBottomNavTabListener listener; + private final ImageView dragShadowOverlay; + + // TODO(calderwoodra): Use this for drag and drop + @SuppressWarnings("unused") + private DragDropController dragDropController; + + MainOldSpeedDialFragmentHostInterface( + MainBottomNavBarBottomNavTabListener listener, ImageView dragShadowOverlay) { + this.listener = listener; + this.dragShadowOverlay = dragShadowOverlay; + } + + @Override + public void setDragDropController(DragDropController dragDropController) { + this.dragDropController = dragDropController; + } + + @Override + public void showAllContactsTab() { + listener.onContactsSelected(); + } + + @Override + public ImageView getDragShadowOverlay() { + return dragShadowOverlay; + } + } + + /** @see com.android.dialer.app.list.OnDragDropListener */ + // TODO(calderwoodra): implement drag and drop + private static final class MainOnDragDropListener implements OnDragDropListener { + + @Override + public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView view) {} + + @Override + public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView view) {} + + @Override + public void onDragFinished(int x, int y) {} + + @Override + public void onDroppedOnRemove() {} + } + /** * Implementation of {@link OnBottomNavTabSelectedListener} that handles logic for showing each of * the main tabs. */ - private final class MainBottomNavBarBottomNavTabListener + private static final class MainBottomNavBarBottomNavTabListener implements OnBottomNavTabSelectedListener { private static final String SPEED_DIAL_TAG = "speed_dial"; @@ -306,18 +484,37 @@ public final class MainActivity extends AppCompatActivity implements FragmentUti private static final String CONTACTS_TAG = "contacts"; private static final String VOICEMAIL_TAG = "voicemail"; + private final Context context; + private final FragmentManager fragmentManager; + private final android.support.v4.app.FragmentManager supportFragmentManager; + + private MainBottomNavBarBottomNavTabListener( + Context context, + FragmentManager fragmentManager, + android.support.v4.app.FragmentManager supportFragmentManager) { + this.context = context; + this.fragmentManager = fragmentManager; + this.supportFragmentManager = supportFragmentManager; + } + @Override public void onSpeedDialSelected() { hideAllFragments(); - SpeedDialFragment fragment = - (SpeedDialFragment) getFragmentManager().findFragmentByTag(SPEED_DIAL_TAG); + Fragment fragment = fragmentManager.findFragmentByTag(SPEED_DIAL_TAG); if (fragment == null) { - getFragmentManager() + if (ConfigProviderComponent.get(context) + .getConfigProvider() + .getBoolean("enable_new_favorites_tab", false)) { + fragment = SpeedDialFragment.newInstance(); + } else { + fragment = new OldSpeedDialFragment(); + } + fragmentManager .beginTransaction() - .add(R.id.fragment_container, SpeedDialFragment.newInstance(), SPEED_DIAL_TAG) + .add(R.id.fragment_container, fragment, SPEED_DIAL_TAG) .commit(); } else { - getFragmentManager().beginTransaction().show(fragment).commit(); + fragmentManager.beginTransaction().show(fragment).commit(); } } @@ -325,14 +522,14 @@ public final class MainActivity extends AppCompatActivity implements FragmentUti public void onCallLogSelected() { hideAllFragments(); NewCallLogFragment fragment = - (NewCallLogFragment) getSupportFragmentManager().findFragmentByTag(CALL_LOG_TAG); + (NewCallLogFragment) supportFragmentManager.findFragmentByTag(CALL_LOG_TAG); if (fragment == null) { - getSupportFragmentManager() + supportFragmentManager .beginTransaction() .add(R.id.fragment_container, new NewCallLogFragment(), CALL_LOG_TAG) .commit(); } else { - getSupportFragmentManager().beginTransaction().show(fragment).commit(); + supportFragmentManager.beginTransaction().show(fragment).commit(); } } @@ -340,9 +537,9 @@ public final class MainActivity extends AppCompatActivity implements FragmentUti public void onContactsSelected() { hideAllFragments(); ContactsFragment fragment = - (ContactsFragment) getFragmentManager().findFragmentByTag(CONTACTS_TAG); + (ContactsFragment) fragmentManager.findFragmentByTag(CONTACTS_TAG); if (fragment == null) { - getFragmentManager() + fragmentManager .beginTransaction() .add( R.id.fragment_container, @@ -350,7 +547,7 @@ public final class MainActivity extends AppCompatActivity implements FragmentUti CONTACTS_TAG) .commit(); } else { - getFragmentManager().beginTransaction().show(fragment).commit(); + fragmentManager.beginTransaction().show(fragment).commit(); } } @@ -358,33 +555,33 @@ public final class MainActivity extends AppCompatActivity implements FragmentUti public void onVoicemailSelected() { hideAllFragments(); NewVoicemailFragment fragment = - (NewVoicemailFragment) getSupportFragmentManager().findFragmentByTag(VOICEMAIL_TAG); + (NewVoicemailFragment) supportFragmentManager.findFragmentByTag(VOICEMAIL_TAG); if (fragment == null) { - getSupportFragmentManager() + supportFragmentManager .beginTransaction() .add(R.id.fragment_container, new NewVoicemailFragment(), VOICEMAIL_TAG) .commit(); } else { - getSupportFragmentManager().beginTransaction().show(fragment).commit(); + supportFragmentManager.beginTransaction().show(fragment).commit(); } } private void hideAllFragments() { - FragmentTransaction supportTransaction = getSupportFragmentManager().beginTransaction(); - if (getSupportFragmentManager().findFragmentByTag(CALL_LOG_TAG) != null) { - supportTransaction.hide(getSupportFragmentManager().findFragmentByTag(CALL_LOG_TAG)); + FragmentTransaction supportTransaction = supportFragmentManager.beginTransaction(); + if (supportFragmentManager.findFragmentByTag(CALL_LOG_TAG) != null) { + supportTransaction.hide(supportFragmentManager.findFragmentByTag(CALL_LOG_TAG)); } - if (getSupportFragmentManager().findFragmentByTag(VOICEMAIL_TAG) != null) { - supportTransaction.hide(getSupportFragmentManager().findFragmentByTag(VOICEMAIL_TAG)); + if (supportFragmentManager.findFragmentByTag(VOICEMAIL_TAG) != null) { + supportTransaction.hide(supportFragmentManager.findFragmentByTag(VOICEMAIL_TAG)); } supportTransaction.commit(); - android.app.FragmentTransaction transaction = getFragmentManager().beginTransaction(); - if (getFragmentManager().findFragmentByTag(SPEED_DIAL_TAG) != null) { - transaction.hide(getFragmentManager().findFragmentByTag(SPEED_DIAL_TAG)); + android.app.FragmentTransaction transaction = fragmentManager.beginTransaction(); + if (fragmentManager.findFragmentByTag(SPEED_DIAL_TAG) != null) { + transaction.hide(fragmentManager.findFragmentByTag(SPEED_DIAL_TAG)); } - if (getFragmentManager().findFragmentByTag(CONTACTS_TAG) != null) { - transaction.hide(getFragmentManager().findFragmentByTag(CONTACTS_TAG)); + if (fragmentManager.findFragmentByTag(CONTACTS_TAG) != null) { + transaction.hide(fragmentManager.findFragmentByTag(CONTACTS_TAG)); } transaction.commit(); } diff --git a/java/com/android/dialer/main/impl/res/layout/main_activity.xml b/java/com/android/dialer/main/impl/res/layout/main_activity.xml index aaba8da77..2094a7329 100644 --- a/java/com/android/dialer/main/impl/res/layout/main_activity.xml +++ b/java/com/android/dialer/main/impl/res/layout/main_activity.xml @@ -66,4 +66,18 @@ + + + + + + \ No newline at end of file -- cgit v1.2.3 From beb5e596ac4629e661488b3ec8741b9ab1500dd6 Mon Sep 17 00:00:00 2001 From: calderwoodra Date: Tue, 30 Jan 2018 15:26:01 -0800 Subject: Added old call log to NUI. Bug: 72618828 Test: existing PiperOrigin-RevId: 183902491 Change-Id: I079aca188b52c29c12b3cae7e57bf71a4e883898 --- .../dialer/app/calllog/CallLogFragment.java | 31 ++++-- .../com/android/dialer/app/list/ListsFragment.java | 10 +- .../com/android/dialer/main/impl/MainActivity.java | 107 +++++++++++++++++++-- 3 files changed, 127 insertions(+), 21 deletions(-) (limited to 'java/com/android') diff --git a/java/com/android/dialer/app/calllog/CallLogFragment.java b/java/com/android/dialer/app/calllog/CallLogFragment.java index 4f5035fc6..7f635dbca 100644 --- a/java/com/android/dialer/app/calllog/CallLogFragment.java +++ b/java/com/android/dialer/app/calllog/CallLogFragment.java @@ -53,10 +53,10 @@ import com.android.dialer.app.calllog.calllogcache.CallLogCache; import com.android.dialer.app.contactinfo.ContactInfoCache; import com.android.dialer.app.contactinfo.ContactInfoCache.OnContactInfoChangedListener; import com.android.dialer.app.contactinfo.ExpirableCacheHeadlessFragment; -import com.android.dialer.app.list.ListsFragment; import com.android.dialer.app.voicemail.VoicemailPlaybackPresenter; import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler; import com.android.dialer.common.Assert; +import com.android.dialer.common.FragmentUtils; import com.android.dialer.common.LogUtil; import com.android.dialer.database.CallLogQueryHandler; import com.android.dialer.database.CallLogQueryHandler.Listener; @@ -344,9 +344,10 @@ public class CallLogFragment extends Fragment recyclerView, this, this, - activityType == CallLogAdapter.ACTIVITY_TYPE_DIALTACTS - ? (CallLogAdapter.OnActionModeStateChangedListener) getActivity() - : null, + // We aren't calling getParentUnsafe because CallLogActivity doesn't need to + // implement this listener + FragmentUtils.getParent( + this, CallLogAdapter.OnActionModeStateChangedListener.class), new CallLogCache(getActivity()), contactInfoCache, getVoicemailPlaybackPresenter(), @@ -479,7 +480,7 @@ public class CallLogFragment extends Fragment public void fetchCalls() { callLogQueryHandler.fetchCalls(callTypeFilter, dateLimit); if (!isCallLogActivity) { - ((ListsFragment) getParentFragment()).updateTabUnreadCounts(); + FragmentUtils.getParentUnsafe(this, CallLogFragmentListener.class).updateTabUnreadCounts(); } } @@ -616,7 +617,8 @@ public class CallLogFragment extends Fragment public void onVisible() { LogUtil.enterBlock("CallLogFragment.onPageSelected"); if (getActivity() != null && getActivity() instanceof HostInterface) { - ((HostInterface) getActivity()).enableFloatingButton(!isModalAlertVisible()); + FragmentUtils.getParentUnsafe(this, HostInterface.class) + .enableFloatingButton(!isModalAlertVisible()); } } @@ -638,7 +640,7 @@ public class CallLogFragment extends Fragment this, getUserVisibleHint()); getAdapter().notifyDataSetChanged(); - HostInterface hostInterface = (HostInterface) getActivity(); + HostInterface hostInterface = FragmentUtils.getParent(this, HostInterface.class); if (show) { recyclerView.setVisibility(View.GONE); modalAlertView.setVisibility(View.VISIBLE); @@ -659,7 +661,8 @@ public class CallLogFragment extends Fragment multiSelectUnSelectAllViewContent.setVisibility(show ? View.VISIBLE : View.GONE); multiSelectUnSelectAllViewContent.setAlpha(show ? 0 : 1); multiSelectUnSelectAllViewContent.animate().alpha(show ? 1 : 0).start(); - ((ListsFragment) getParentFragment()).showMultiSelectRemoveView(show); + FragmentUtils.getParentUnsafe(this, CallLogFragmentListener.class) + .showMultiSelectRemoveView(show); } @Override @@ -717,4 +720,16 @@ public class CallLogFragment extends Fragment refreshDataRequired = true; } } + + /** Useful callback for ListsFragment children to use to call into ListsFragment. */ + public interface CallLogFragmentListener { + + /** + * External method to update unread count because the unread count changes when the user expands + * a voicemail in the call log or when the user expands an unread call in the call history tab. + */ + void updateTabUnreadCounts(); + + void showMultiSelectRemoveView(boolean show); + } } diff --git a/java/com/android/dialer/app/list/ListsFragment.java b/java/com/android/dialer/app/list/ListsFragment.java index bbbf056bf..d314917ef 100644 --- a/java/com/android/dialer/app/list/ListsFragment.java +++ b/java/com/android/dialer/app/list/ListsFragment.java @@ -34,6 +34,7 @@ import android.view.ViewGroup; import com.android.contacts.common.list.ViewPagerTabs; import com.android.dialer.app.R; import com.android.dialer.app.calllog.CallLogFragment; +import com.android.dialer.app.calllog.CallLogFragment.CallLogFragmentListener; import com.android.dialer.app.calllog.CallLogNotificationsService; import com.android.dialer.app.calllog.VisualVoicemailCallLogFragment; import com.android.dialer.common.LogUtil; @@ -59,7 +60,8 @@ import java.util.ArrayList; * Contacts list. This will also eventually contain the logic that allows sliding the ViewPager * containing the lists up above the search bar and pin it against the top of the screen. */ -public class ListsFragment extends Fragment implements OnPageChangeListener, Listener { +public class ListsFragment extends Fragment + implements OnPageChangeListener, Listener, CallLogFragmentListener { private static final String TAG = "ListsFragment"; @@ -423,10 +425,7 @@ public class ListsFragment extends Fragment implements OnPageChangeListener, Lis return true; } - /** - * External method to update unread count because the unread count changes when the user expands a - * voicemail in the call log or when the user expands an unread call in the call history tab. - */ + @Override public void updateTabUnreadCounts() { if (callLogQueryHandler != null) { callLogQueryHandler.fetchMissedCallsUnreadCount(); @@ -450,6 +449,7 @@ public class ListsFragment extends Fragment implements OnPageChangeListener, Lis removeView.animate().alpha(show ? 1 : 0).start(); } + @Override public void showMultiSelectRemoveView(boolean show) { viewPagerTabs.setVisibility(show ? View.GONE : View.VISIBLE); viewPager.setEnableSwipingPages(!show); diff --git a/java/com/android/dialer/main/impl/MainActivity.java b/java/com/android/dialer/main/impl/MainActivity.java index 4bc15da98..57cc684e3 100644 --- a/java/com/android/dialer/main/impl/MainActivity.java +++ b/java/com/android/dialer/main/impl/MainActivity.java @@ -30,6 +30,9 @@ import android.support.v4.app.FragmentTransaction; import android.view.View; import android.widget.ImageView; import com.android.contacts.common.list.OnPhoneNumberPickerActionListener; +import com.android.dialer.app.calllog.CallLogAdapter; +import com.android.dialer.app.calllog.CallLogFragment; +import com.android.dialer.app.calllog.CallLogFragment.CallLogFragmentListener; import com.android.dialer.app.list.DragDropController; import com.android.dialer.app.list.OldSpeedDialFragment; import com.android.dialer.app.list.OnDragDropListener; @@ -90,6 +93,10 @@ public final class MainActivity extends TransactionSafeActivity private MainOnDialpadQueryChangedListener onDialpadQueryChangedListener; private MainDialpadListener dialpadListener; private MainSearchFragmentListener searchFragmentListener; + private MainCallLogAdapterOnActionModeStateChangedListener + callLogAdapterOnActionModeStateChangedListener; + private MainCallLogHost callLogHostInterface; + private MainCallLogFragmentListener callLogFragmentListener; private MainOnListFragmentScrolledListener onListFragmentScrolledListener; private MainOnPhoneNumberPickerActionListener onPhoneNumberPickerActionListener; private MainOldSpeedDialFragmentHostInterface oldSpeedDialFragmentHostInterface; @@ -148,6 +155,10 @@ public final class MainActivity extends TransactionSafeActivity onDialpadQueryChangedListener = new MainOnDialpadQueryChangedListener(searchController); dialpadListener = new MainDialpadListener(this, searchController, getLastOutgoingCallListener); searchFragmentListener = new MainSearchFragmentListener(searchController); + callLogAdapterOnActionModeStateChangedListener = + new MainCallLogAdapterOnActionModeStateChangedListener(); + callLogHostInterface = new MainCallLogHost(searchController, fab); + callLogFragmentListener = new MainCallLogFragmentListener(); onListFragmentScrolledListener = new MainOnListFragmentScrolledListener(snackbarContainer); onPhoneNumberPickerActionListener = new MainOnPhoneNumberPickerActionListener(this); oldSpeedDialFragmentHostInterface = @@ -228,6 +239,12 @@ public final class MainActivity extends TransactionSafeActivity return (T) dialpadFragmentHostInterface; } else if (callbackInterface.isInstance(searchFragmentListener)) { return (T) searchFragmentListener; + } else if (callbackInterface.isInstance(callLogAdapterOnActionModeStateChangedListener)) { + return (T) callLogAdapterOnActionModeStateChangedListener; + } else if (callbackInterface.isInstance(callLogHostInterface)) { + return (T) callLogHostInterface; + } else if (callbackInterface.isInstance(callLogFragmentListener)) { + return (T) callLogFragmentListener; } else if (callbackInterface.isInstance(onListFragmentScrolledListener)) { return (T) onListFragmentScrolledListener; } else if (callbackInterface.isInstance(onPhoneNumberPickerActionListener)) { @@ -360,6 +377,60 @@ public final class MainActivity extends TransactionSafeActivity } } + /** @see CallLogAdapter.OnActionModeStateChangedListener */ + // TODO(a bug): handle multiselect mode + private static final class MainCallLogAdapterOnActionModeStateChangedListener + implements CallLogAdapter.OnActionModeStateChangedListener { + + @Override + public void onActionModeStateChanged(boolean isEnabled) {} + + @Override + public boolean isActionModeStateEnabled() { + return false; + } + } + + /** @see CallLogFragment.HostInterface */ + private static final class MainCallLogHost implements CallLogFragment.HostInterface { + + private final MainSearchController searchController; + private final FloatingActionButton fab; + + MainCallLogHost(MainSearchController searchController, FloatingActionButton fab) { + this.searchController = searchController; + this.fab = fab; + } + + @Override + public void showDialpad() { + searchController.showDialpad(true); + } + + @Override + public void enableFloatingButton(boolean enabled) { + if (enabled) { + fab.show(); + } else { + fab.hide(); + } + } + } + + /** @see CallLogFragmentListener */ + private static final class MainCallLogFragmentListener implements CallLogFragmentListener { + + @Override + public void updateTabUnreadCounts() { + // TODO(a bug): implement unread counts + } + + @Override + public void showMultiSelectRemoveView(boolean show) { + // TODO(a bug): handle multiselect mode + } + } + /** @see OnListFragmentScrolledListener */ private static final class MainOnListFragmentScrolledListener implements OnListFragmentScrolledListener { @@ -521,15 +592,30 @@ public final class MainActivity extends TransactionSafeActivity @Override public void onCallLogSelected() { hideAllFragments(); - NewCallLogFragment fragment = - (NewCallLogFragment) supportFragmentManager.findFragmentByTag(CALL_LOG_TAG); - if (fragment == null) { - supportFragmentManager - .beginTransaction() - .add(R.id.fragment_container, new NewCallLogFragment(), CALL_LOG_TAG) - .commit(); + if (ConfigProviderComponent.get(context) + .getConfigProvider() + .getBoolean("enable_new_call_log", false)) { + NewCallLogFragment fragment = + (NewCallLogFragment) supportFragmentManager.findFragmentByTag(CALL_LOG_TAG); + if (fragment == null) { + supportFragmentManager + .beginTransaction() + .add(R.id.fragment_container, new NewCallLogFragment(), CALL_LOG_TAG) + .commit(); + } else { + supportFragmentManager.beginTransaction().show(fragment).commit(); + } } else { - supportFragmentManager.beginTransaction().show(fragment).commit(); + CallLogFragment fragment = + (CallLogFragment) fragmentManager.findFragmentByTag(CALL_LOG_TAG); + if (fragment == null) { + fragmentManager + .beginTransaction() + .add(R.id.fragment_container, new CallLogFragment(), CALL_LOG_TAG) + .commit(); + } else { + fragmentManager.beginTransaction().show(fragment).commit(); + } } } @@ -569,6 +655,7 @@ public final class MainActivity extends TransactionSafeActivity private void hideAllFragments() { FragmentTransaction supportTransaction = supportFragmentManager.beginTransaction(); if (supportFragmentManager.findFragmentByTag(CALL_LOG_TAG) != null) { + // NewCallLogFragment supportTransaction.hide(supportFragmentManager.findFragmentByTag(CALL_LOG_TAG)); } if (supportFragmentManager.findFragmentByTag(VOICEMAIL_TAG) != null) { @@ -580,6 +667,10 @@ public final class MainActivity extends TransactionSafeActivity if (fragmentManager.findFragmentByTag(SPEED_DIAL_TAG) != null) { transaction.hide(fragmentManager.findFragmentByTag(SPEED_DIAL_TAG)); } + if (fragmentManager.findFragmentByTag(CALL_LOG_TAG) != null) { + // Old CallLogFragment + transaction.hide(fragmentManager.findFragmentByTag(CALL_LOG_TAG)); + } if (fragmentManager.findFragmentByTag(CONTACTS_TAG) != null) { transaction.hide(fragmentManager.findFragmentByTag(CONTACTS_TAG)); } -- cgit v1.2.3 From 73b231dfc38d895eb6f05f86c61a49a728f9d8b9 Mon Sep 17 00:00:00 2001 From: maxwelb Date: Tue, 30 Jan 2018 16:28:22 -0800 Subject: Make default lastModified timestamp configurable Bug: 72320869 Test: manually verified that the config value is used PiperOrigin-RevId: 183911716 Change-Id: I15bc2c4505306eb65446a792b305226255ae4315 --- .../dialer/database/DialerDatabaseHelper.java | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'java/com/android') diff --git a/java/com/android/dialer/database/DialerDatabaseHelper.java b/java/com/android/dialer/database/DialerDatabaseHelper.java index 18c61342d..cb07615a5 100644 --- a/java/com/android/dialer/database/DialerDatabaseHelper.java +++ b/java/com/android/dialer/database/DialerDatabaseHelper.java @@ -42,6 +42,7 @@ 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.configprovider.ConfigProviderBindings; import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns; import com.android.dialer.smartdial.util.SmartDialNameMatcher; import com.android.dialer.smartdial.util.SmartDialPrefix; @@ -76,6 +77,10 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper { private static final String DATABASE_LAST_CREATED_SHARED_PREF = "com.android.dialer"; private static final String LAST_UPDATED_MILLIS = "last_updated_millis"; + + @VisibleForTesting + static final String DEFAULT_LAST_UPDATED_CONFIG_KEY = "smart_dial_default_last_update_millis"; + private static final String DATABASE_VERSION_PROPERTY = "database_version"; private static final int MAX_ENTRIES = 20; @@ -635,12 +640,17 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper { /** Gets the last update time on the database. */ final SharedPreferences databaseLastUpdateSharedPref = context.getSharedPreferences(DATABASE_LAST_CREATED_SHARED_PREF, Context.MODE_PRIVATE); - final String lastUpdateMillis = - String.valueOf( - forceUpdate ? 0 : databaseLastUpdateSharedPref.getLong(LAST_UPDATED_MILLIS, 0)); - LogUtil.v( - "DialerDatabaseHelper.updateSmartDialDatabase", "last updated at " + lastUpdateMillis); + long defaultLastUpdateMillis = + ConfigProviderBindings.get(context).getLong(DEFAULT_LAST_UPDATED_CONFIG_KEY, 0); + + long sharedPrefLastUpdateMillis = + databaseLastUpdateSharedPref.getLong(LAST_UPDATED_MILLIS, defaultLastUpdateMillis); + + final String lastUpdateMillis = String.valueOf(forceUpdate ? 0 : sharedPrefLastUpdateMillis); + + LogUtil.i( + "DialerDatabaseHelper.updateSmartDialDatabase", "last updated at %s", lastUpdateMillis); /** Sets the time after querying the database as the current update time. */ final Long currentMillis = System.currentTimeMillis(); -- cgit v1.2.3 From d6b91d1f57b6da8e09a459609498b49abafe5445 Mon Sep 17 00:00:00 2001 From: wangqi Date: Tue, 30 Jan 2018 16:59:03 -0800 Subject: Don't override contactExists if there is no update from Cequint Caller ID. Bug: 72485878 Test: none PiperOrigin-RevId: 183915863 Change-Id: Ib035855d944c91145418222cc390be066744b4fb --- java/com/android/incallui/ContactInfoCache.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'java/com/android') diff --git a/java/com/android/incallui/ContactInfoCache.java b/java/com/android/incallui/ContactInfoCache.java index fc41df469..d2ae70939 100644 --- a/java/com/android/incallui/ContactInfoCache.java +++ b/java/com/android/incallui/ContactInfoCache.java @@ -541,7 +541,9 @@ public class ContactInfoCache implements OnImageLoadCompleteListener { hasUpdate = true; } // Set contact to exist to avoid phone number service lookup. - callerInfo.contactExists = hasUpdate; + if (hasUpdate) { + callerInfo.contactExists = true; + } } /** -- cgit v1.2.3