diff options
Diffstat (limited to 'java/com/android/dialer/calllog/ui/NewCallLogFragment.java')
-rw-r--r-- | java/com/android/dialer/calllog/ui/NewCallLogFragment.java | 159 |
1 files changed, 82 insertions, 77 deletions
diff --git a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java index 5e676f072..bb1a7303e 100644 --- a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java +++ b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java @@ -22,44 +22,32 @@ import android.support.annotation.VisibleForTesting; import android.support.v4.app.Fragment; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.Loader; +import android.support.v4.content.LocalBroadcastManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.android.dialer.calllog.CallLogComponent; -import com.android.dialer.calllog.CallLogFramework; -import com.android.dialer.calllog.CallLogFramework.CallLogUi; -import com.android.dialer.calllog.RefreshAnnotatedCallLogWorker; +import com.android.dialer.calllog.RefreshAnnotatedCallLogReceiver; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.DefaultFutureCallback; -import com.android.dialer.common.concurrent.DialerExecutorComponent; import com.android.dialer.common.concurrent.ThreadUtil; -import com.android.dialer.common.concurrent.UiListener; +import com.android.dialer.metrics.Metrics; +import com.android.dialer.metrics.MetricsComponent; +import com.android.dialer.metrics.jank.RecyclerViewJankLogger; import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import java.util.concurrent.TimeUnit; /** The "new" call log fragment implementation, which is built on top of the annotated call log. */ -public final class NewCallLogFragment extends Fragment - implements CallLogUi, LoaderCallbacks<Cursor> { - - /* - * This is a reasonable time that it might take between related call log writes, that also - * shouldn't slow down single-writes too much. For example, when populating the database using - * the simulator, using this value results in ~6 refresh cycles (on a release build) to write 120 - * call log entries. - */ - private static final long REFRESH_ANNOTATED_CALL_LOG_WAIT_MILLIS = 100L; +public final class NewCallLogFragment extends Fragment implements LoaderCallbacks<Cursor> { @VisibleForTesting static final long MARK_ALL_CALLS_READ_WAIT_MILLIS = TimeUnit.SECONDS.toMillis(3); - private RefreshAnnotatedCallLogWorker refreshAnnotatedCallLogWorker; - private UiListener<Void> refreshAnnotatedCallLogListener; + private RefreshAnnotatedCallLogReceiver refreshAnnotatedCallLogReceiver; private RecyclerView recyclerView; - @Nullable private Runnable refreshAnnotatedCallLogRunnable; private boolean shouldMarkCallsRead = false; private final Runnable setShouldMarkCallsReadTrue = () -> shouldMarkCallsRead = true; @@ -74,16 +62,7 @@ public final class NewCallLogFragment extends Fragment LogUtil.enterBlock("NewCallLogFragment.onActivityCreated"); - CallLogComponent component = CallLogComponent.get(getContext()); - CallLogFramework callLogFramework = component.callLogFramework(); - callLogFramework.attachUi(this); - - // TODO(zachh): Use support fragment manager and add support for them in executors library. - refreshAnnotatedCallLogListener = - DialerExecutorComponent.get(getContext()) - .createUiListener( - getActivity().getFragmentManager(), "NewCallLogFragment.refreshAnnotatedCallLog"); - refreshAnnotatedCallLogWorker = component.getRefreshAnnotatedCallLogWorker(); + refreshAnnotatedCallLogReceiver = new RefreshAnnotatedCallLogReceiver(getContext()); } @Override @@ -97,13 +76,52 @@ public final class NewCallLogFragment extends Fragment public void onResume() { super.onResume(); - LogUtil.enterBlock("NewCallLogFragment.onResume"); + boolean isHidden = isHidden(); + LogUtil.i("NewCallLogFragment.onResume", "isHidden = %s", isHidden); + + // As a fragment's onResume() is tied to the containing Activity's onResume(), being resumed is + // not equivalent to becoming visible. + // For example, when an activity with a hidden fragment is resumed, the fragment's onResume() + // will be called but it is not visible. + if (!isHidden) { + onFragmentShown(); + } + } + + @Override + public void onPause() { + super.onPause(); + LogUtil.enterBlock("NewCallLogFragment.onPause"); + + onFragmentHidden(); + } - CallLogFramework callLogFramework = CallLogComponent.get(getContext()).callLogFramework(); - callLogFramework.attachUi(this); + @Override + public void onHiddenChanged(boolean hidden) { + super.onHiddenChanged(hidden); + LogUtil.i("NewCallLogFragment.onHiddenChanged", "hidden = %s", hidden); - // TODO(zachh): Consider doing this when fragment becomes visible. - refreshAnnotatedCallLog(true /* checkDirty */); + if (hidden) { + onFragmentHidden(); + } else { + onFragmentShown(); + } + } + + /** + * To be called when the fragment becomes visible. + * + * <p>Note that for a fragment, being resumed is not equivalent to becoming visible. + * + * <p>For example, when an activity with a hidden fragment is resumed, the fragment's onResume() + * will be called but it is not visible. + */ + private void onFragmentShown() { + registerRefreshAnnotatedCallLogReceiver(); + + CallLogComponent.get(getContext()) + .getRefreshAnnotatedCallLogNotifier() + .notify(/* checkDirty = */ true); // There are some types of data that we show in the call log that are not represented in the // AnnotatedCallLog. For example, CP2 information for invalid numbers can sometimes only be @@ -123,18 +141,22 @@ public final class NewCallLogFragment extends Fragment .postDelayed(setShouldMarkCallsReadTrue, MARK_ALL_CALLS_READ_WAIT_MILLIS); } - @Override - public void onPause() { - super.onPause(); - - LogUtil.enterBlock("NewCallLogFragment.onPause"); - + /** + * To be called when the fragment becomes hidden. + * + * <p>This can happen in the following two cases: + * + * <ul> + * <li>hide the fragment but keep the parent activity visible (e.g., calling {@link + * android.support.v4.app.FragmentTransaction#hide(Fragment)} in an activity, or + * <li>the parent activity is paused. + * </ul> + */ + private void onFragmentHidden() { // This is pending work that we don't actually need to follow through with. - ThreadUtil.getUiThreadHandler().removeCallbacks(refreshAnnotatedCallLogRunnable); ThreadUtil.getUiThreadHandler().removeCallbacks(setShouldMarkCallsReadTrue); - CallLogFramework callLogFramework = CallLogComponent.get(getContext()).callLogFramework(); - callLogFramework.detachUi(); + unregisterRefreshAnnotatedCallLogReceiver(); if (shouldMarkCallsRead) { Futures.addCallback( @@ -151,48 +173,31 @@ public final class NewCallLogFragment extends Fragment View view = inflater.inflate(R.layout.new_call_log_fragment, container, false); recyclerView = view.findViewById(R.id.new_call_log_recycler_view); + recyclerView.addOnScrollListener( + new RecyclerViewJankLogger( + MetricsComponent.get(getContext()).metrics(), Metrics.NEW_CALL_LOG_JANK_EVENT_NAME)); getLoaderManager().restartLoader(0, null, this); return view; } - private void refreshAnnotatedCallLog(boolean checkDirty) { - LogUtil.enterBlock("NewCallLogFragment.refreshAnnotatedCallLog"); - - // If we already scheduled a refresh, cancel it and schedule a new one so that repeated requests - // in quick succession don't result in too much work. For example, if we get 10 requests in - // 10ms, and a complete refresh takes a constant 200ms, the refresh will take 300ms (100ms wait - // and 1 iteration @200ms) instead of 2 seconds (10 iterations @ 200ms) since the work requests - // are serialized in RefreshAnnotatedCallLogWorker. - // - // We might get many requests in quick succession, for example, when the simulator inserts - // hundreds of rows into the system call log, or when the data for a new call is incrementally - // written to different columns as it becomes available. - ThreadUtil.getUiThreadHandler().removeCallbacks(refreshAnnotatedCallLogRunnable); - - refreshAnnotatedCallLogRunnable = - () -> { - ListenableFuture<Void> future = - checkDirty - ? refreshAnnotatedCallLogWorker.refreshWithDirtyCheck() - : refreshAnnotatedCallLogWorker.refreshWithoutDirtyCheck(); - refreshAnnotatedCallLogListener.listen( - getContext(), - future, - unused -> {}, - throwable -> { - throw new RuntimeException(throwable); - }); - }; - ThreadUtil.getUiThreadHandler() - .postDelayed(refreshAnnotatedCallLogRunnable, REFRESH_ANNOTATED_CALL_LOG_WAIT_MILLIS); + private void registerRefreshAnnotatedCallLogReceiver() { + LogUtil.enterBlock("NewCallLogFragment.registerRefreshAnnotatedCallLogReceiver"); + + LocalBroadcastManager.getInstance(getContext()) + .registerReceiver( + refreshAnnotatedCallLogReceiver, RefreshAnnotatedCallLogReceiver.getIntentFilter()); } - @Override - public void invalidateUi() { - LogUtil.enterBlock("NewCallLogFragment.invalidateUi"); - refreshAnnotatedCallLog(false /* checkDirty */); + private void unregisterRefreshAnnotatedCallLogReceiver() { + LogUtil.enterBlock("NewCallLogFragment.unregisterRefreshAnnotatedCallLogReceiver"); + + // Cancel pending work as we don't need it any more. + CallLogComponent.get(getContext()).getRefreshAnnotatedCallLogNotifier().cancel(); + + LocalBroadcastManager.getInstance(getContext()) + .unregisterReceiver(refreshAnnotatedCallLogReceiver); } @Override |