MediaRouter: Apply route list changes at a time

If the provider updates too frequently, the route list will be updated
awkwardly. So this change will gather route updates for 300ms, and apply
those changes at a time. Moreover, the initial route list will be shown
just after showing dialog.

Bug: 29061029
Change-Id: Id4070a37d74140ec41b1dc4f5b2c092e182d787e
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
index 08338b6..c2a5f3a 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
@@ -27,6 +27,9 @@
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
 import android.preference.PreferenceManager;
 import android.support.annotation.NonNull;
 import android.support.v7.media.MediaRouteSelector;
@@ -65,6 +68,10 @@
 public class MediaRouteChooserDialog extends Dialog {
     private static final String TAG = "MediaRouteChooserDialog";
 
+    // Do not update the route list immediately to avoid unnatural dialog change.
+    private static final long UPDATE_ROUTES_DELAY_MS = 300L;
+    private static final int MSG_UPDATE_ROUTES = 1;
+
     private final MediaRouter mRouter;
     private final MediaRouterCallback mCallback;
 
@@ -75,6 +82,17 @@
     private boolean mAttachedToWindow;
     private AsyncTask<Void, Void, Void> mRefreshRoutesTask;
     private AsyncTask<Void, Void, Void> mOnItemClickTask;
+    private long mLastUpdateTime;
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message message) {
+            switch (message.what) {
+                case MSG_UPDATE_ROUTES:
+                    updateRoutes();
+                    break;
+            }
+        }
+    };
 
     public MediaRouteChooserDialog(Context context) {
         this(context, 0);
@@ -185,6 +203,13 @@
 
         mAttachedToWindow = true;
         mRouter.addCallback(mSelector, mCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
+        // refreshRoutes() could take some time, so initial list should be shown from the beginning.
+        List<MediaRouter.RouteInfo> routes = new ArrayList<>(mRouter.getRoutes());
+        onFilterRoutes(routes);
+        mRoutes.clear();
+        mRoutes.addAll(routes);
+        mAdapter.notifyDataSetChanged();
+        mLastUpdateTime = 0;
         refreshRoutes();
     }
 
@@ -192,6 +217,7 @@
     public void onDetachedFromWindow() {
         mAttachedToWindow = false;
         mRouter.removeCallback(mCallback);
+        mHandler.removeMessages(MSG_UPDATE_ROUTES);
 
         super.onDetachedFromWindow();
     }
@@ -231,13 +257,23 @@
                     mRoutes.clear();
                     mRoutes.addAll(mNewRoutes);
                     Collections.sort(mRoutes, RouteComparator.sInstance);
-                    mAdapter.notifyDataSetChanged();
                     mRefreshRoutesTask = null;
+                    if (SystemClock.uptimeMillis() - mLastUpdateTime >= UPDATE_ROUTES_DELAY_MS) {
+                        updateRoutes();
+                    } else if (!mHandler.hasMessages(MSG_UPDATE_ROUTES)) {
+                        mHandler.sendEmptyMessageAtTime(MSG_UPDATE_ROUTES,
+                                mLastUpdateTime + UPDATE_ROUTES_DELAY_MS);
+                    }
                 }
             }.execute();
         }
     }
 
+    private void updateRoutes() {
+        mLastUpdateTime = SystemClock.uptimeMillis();
+        mAdapter.notifyDataSetChanged();
+    }
+
     private final class RouteAdapter extends ArrayAdapter<MediaRouter.RouteInfo>
             implements ListView.OnItemClickListener {
         private final LayoutInflater mInflater;