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/dialer') 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