diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2018-01-31 03:18:45 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2018-01-31 03:18:45 +0000 |
commit | 4cd323f2273dd967448ed756c873d0a4865345ee (patch) | |
tree | b788d5c1b5b752e88e8676806895b8ef638f310f /java/com/android | |
parent | a6b96247b1348dd6316fb0b5355c980883765530 (diff) | |
parent | d6b91d1f57b6da8e09a459609498b49abafe5445 (diff) |
Merge changes Ib035855d,I15bc2c45,I079aca18,I9b90aaa0,I452ca635, ...
* changes:
Don't override contactExists if there is no update from Cequint Caller ID.
Make default lastModified timestamp configurable
Added old call log to NUI.
Add old speed dial fragment to NUI.
Added badge count feature to bottom nav.
Allow delete of Restricted number entries
Update FragUtils to encourage better readability in Activities.
Diffstat (limited to 'java/com/android')
15 files changed, 685 insertions, 232 deletions
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/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/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<Integer> itemIdTopMap = new LongSparseArray<>(); private final LongSparseArray<Integer> itemIdLeftMap = new LongSparseArray<>(); private final ContactTileView.Listener contactTileAdapterListener = - new ContactTileAdapterListener(); - private final LoaderManager.LoaderCallbacks<Cursor> contactTileLoaderListener = - new ContactTileLoaderListener(); - private final ScrollListener scrollListener = new ScrollListener(); + new ContactTileAdapterListener(this); + private final ScrollListener scrollListener = new ScrollListener(this); + private LoaderManager.LoaderCallbacks<Cursor> 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<Cursor> { - void setDragDropController(DragDropController controller); + private final OldSpeedDialFragment fragment; + private final PhoneFavoritesTileAdapter adapter; - void showAllContactsTab(); - } - - class ContactTileLoaderListener implements LoaderManager.LoaderCallbacks<Cursor> { + 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<Cursor> 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<Cursor> loader) { - if (DEBUG) { - LogUtil.d("ContactTileLoaderListener.onLoaderReset", null); - } - } + public void onLoaderReset(Loader<Cursor> 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/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); } 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> 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> T getImpl(Class<T> callbackInterface); + } } 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(); 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/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/MainActivity.java b/java/com/android/dialer/main/impl/MainActivity.java index a7a9e6c5a..57cc684e3 100644 --- a/java/com/android/dialer/main/impl/MainActivity.java +++ b/java/com/android/dialer/main/impl/MainActivity.java @@ -16,21 +16,38 @@ 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; 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.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; +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; @@ -40,31 +57,56 @@ 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 OnContactSelectedListener, - OnDialpadQueryChangedListener, - DialpadListener, - DialpadFragment.HostInterface, - SearchFragmentListener { +// 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"; + 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; + private MainCallLogAdapterOnActionModeStateChangedListener + callLogAdapterOnActionModeStateChangedListener; + private MainCallLogHost callLogHostInterface; + private MainCallLogFragmentListener callLogFragmentListener; + 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; private View snackbarContainer; + private UiListener<String> getLastOutgoingCallListener; /** * @param context Context of the application package implementing MainActivity class. @@ -81,10 +123,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); @@ -95,11 +144,28 @@ public final class MainActivity extends AppCompatActivity 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); + 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 = + 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) { savedLanguageCode = savedInstanceState.getString(KEY_SAVED_LANGUAGE_CODE); @@ -152,67 +218,336 @@ 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> T getImpl(Class<T> 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 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)) { + return (T) onPhoneNumberPickerActionListener; + } else if (callbackInterface.isInstance(oldSpeedDialFragmentHostInterface)) { + return (T) oldSpeedDialFragmentHostInterface; + } else if (callbackInterface.isInstance(onDragDropListener)) { + return (T) onDragDropListener; + } 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); + @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 // DialpadListener - public void onDialpadShown() { - searchController.onDialpadShown(); + @Override + public void onDisambigDialogDismissed() { + // Don't do anything; the app will remain open with favorites tiles displayed. } - @Override // DialpadListener - public void onCallPlacedFromDialpad() { - // TODO(calderwoodra): logging + /** @see OnContactSelectedListener */ + private static final class MainOnContactSelectedListener implements OnContactSelectedListener { + + private final Context context; + + MainOnContactSelectedListener(Context context) { + this.context = context; + } + + @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); + } + } + + /** @see DialpadListener */ + private static final class MainDialpadListener implements DialpadListener { + + private final MainSearchController searchController; + private final Context context; + private final UiListener<String> listener; + + MainDialpadListener( + Context context, MainSearchController searchController, UiListener<String> uiListener) { + this.context = context; + this.searchController = searchController; + this.listener = uiListener; + } + + @Override + public void getLastOutgoingCall(LastOutgoingCallCallback callback) { + ListenableFuture<String> 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 + } + } + + /** @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 + } + } + + /** @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; + } + } + + /** @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(); + } } - super.onBackPressed(); } - @Override // DialpadFragment.HostInterface - public boolean onDialpadSpacerTouchWithEmptyQuery() { - // No-op, just let the clicks fall through to the search list - return false; + /** @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 { + + 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 + } } - @Override // SearchFragmentListener - public void onSearchListTouch() { - searchController.onSearchListTouch(); + /** @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; + } } - @Override // SearchFragmentListener - public void onCallPlacedFromSearch() { - // TODO(calderwoodra): logging + /** @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"; @@ -220,33 +555,67 @@ public final class MainActivity extends AppCompatActivity 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(); } } @Override public void onCallLogSelected() { hideAllFragments(); - NewCallLogFragment fragment = - (NewCallLogFragment) getSupportFragmentManager().findFragmentByTag(CALL_LOG_TAG); - if (fragment == null) { - getSupportFragmentManager() - .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 { - getSupportFragmentManager().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(); + } } } @@ -254,9 +623,9 @@ public final class MainActivity extends AppCompatActivity 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, @@ -264,7 +633,7 @@ public final class MainActivity extends AppCompatActivity CONTACTS_TAG) .commit(); } else { - getFragmentManager().beginTransaction().show(fragment).commit(); + fragmentManager.beginTransaction().show(fragment).commit(); } } @@ -272,33 +641,38 @@ public final class MainActivity extends AppCompatActivity 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) { + // NewCallLogFragment + 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 (fragmentManager.findFragmentByTag(CALL_LOG_TAG) != null) { + // Old CallLogFragment + transaction.hide(fragmentManager.findFragmentByTag(CALL_LOG_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/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 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2018 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<shape + xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="oval"> + <solid android:color="@color/dialer_secondary_color"/> + <size android:height="14dp" android:width="14dp"/> +</shape>
\ 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"> - <ImageView - android:id="@+id/bottom_nav_item_image" - android:layout_width="24dp" - android:layout_height="24dp" - android:layout_marginBottom="6dp"/> + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="2dp"> + + <ImageView + android:id="@+id/bottom_nav_item_image" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_margin="4dp"/> + + <TextView + android:id="@+id/notification_badge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="top|end" + android:gravity="center" + android:textSize="12sp" + android:textColor="@color/dialer_primary_text_color_white" + android:background="@drawable/notification_badge" + android:visibility="gone"/> + </FrameLayout> <TextView android:id="@+id/bottom_nav_item_text" 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 @@ <include android:id="@+id/toolbar" layout="@layout/toolbar_layout"/> + + <!-- TODO(calderwoodra): investigate what this is for and why we want it. --> + <!-- Host container for the contact tile drag shadow --> + <FrameLayout + android:id="@+id/activity_overlay" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <ImageView + android:id="@+id/contact_tile_drag_shadow_overlay" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:importantForAccessibility="no" + android:visibility="gone"/> + </FrameLayout> </RelativeLayout>
\ No newline at end of file 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; + } } /** |