1692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim/*
2692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * Copyright 2018 The Android Open Source Project
3692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim *
4692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * Licensed under the Apache License, Version 2.0 (the "License");
5692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * you may not use this file except in compliance with the License.
6692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * You may obtain a copy of the License at
7692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim *
8692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim *      http://www.apache.org/licenses/LICENSE-2.0
9692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim *
10692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * Unless required by applicable law or agreed to in writing, software
11692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * distributed under the License is distributed on an "AS IS" BASIS,
12692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * See the License for the specific language governing permissions and
14692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * limitations under the License.
15692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim */
16692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
17692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limpackage com.android.support.mediarouter.app;
18692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
19692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport static com.android.support.mediarouter.media.MediaRouter.RouteInfo.CONNECTION_STATE_CONNECTED;
20692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport static com.android.support.mediarouter.media.MediaRouter.RouteInfo.CONNECTION_STATE_CONNECTING;
21692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
22692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.annotation.NonNull;
230bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Limimport android.app.Dialog;
24692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.content.Context;
250bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Limimport android.content.res.Resources;
26692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.content.res.TypedArray;
27692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.graphics.drawable.Drawable;
28692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.net.Uri;
29692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.os.Bundle;
30692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.os.Handler;
31692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.os.Message;
32692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.os.SystemClock;
33692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.support.v7.app.AppCompatDialog;
34692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.text.TextUtils;
35692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.util.Log;
360bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Limimport android.view.ContextThemeWrapper;
37692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.view.Gravity;
38692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.view.LayoutInflater;
39692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.view.View;
40692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.view.ViewGroup;
41692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.widget.AdapterView;
42692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.widget.ArrayAdapter;
43692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.widget.ImageView;
44692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.widget.ListView;
45692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.widget.TextView;
46692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
470bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Limimport com.android.media.update.ApiHelper;
48692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport com.android.media.update.R;
49692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport com.android.support.mediarouter.media.MediaRouteSelector;
50692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport com.android.support.mediarouter.media.MediaRouter;
51692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
52692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport java.io.IOException;
53692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport java.io.InputStream;
54692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport java.util.ArrayList;
55692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport java.util.Collections;
56692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport java.util.Comparator;
57692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport java.util.List;
58692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
59692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim/**
60692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * This class implements the route chooser dialog for {@link MediaRouter}.
61692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * <p>
62692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * This dialog allows the user to choose a route that matches a given selector.
63692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * </p>
64692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim *
65692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * @see MediaRouteButton
66692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * @see MediaRouteActionProvider
67692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim */
680bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Limpublic class MediaRouteChooserDialog extends Dialog {
69692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    static final String TAG = "MediaRouteChooserDialog";
70692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
71692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    // Do not update the route list immediately to avoid unnatural dialog change.
72692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    private static final long UPDATE_ROUTES_DELAY_MS = 300L;
73692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    static final int MSG_UPDATE_ROUTES = 1;
74692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
75692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    private final MediaRouter mRouter;
76692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    private final MediaRouterCallback mCallback;
77692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
78692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    private TextView mTitleView;
79692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    private MediaRouteSelector mSelector = MediaRouteSelector.EMPTY;
80692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    private ArrayList<MediaRouter.RouteInfo> mRoutes;
81692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    private RouteAdapter mAdapter;
82692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    private ListView mListView;
83692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    private boolean mAttachedToWindow;
84692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    private long mLastUpdateTime;
85692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    private final Handler mHandler = new Handler() {
86692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        @Override
87692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        public void handleMessage(Message message) {
88692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            switch (message.what) {
89692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                case MSG_UPDATE_ROUTES:
90692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                    updateRoutes((List<MediaRouter.RouteInfo>) message.obj);
91692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                    break;
92692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            }
93692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        }
94692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    };
95692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
96692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    public MediaRouteChooserDialog(Context context) {
97692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        this(context, 0);
98692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    }
99692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
100692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    public MediaRouteChooserDialog(Context context, int theme) {
101d5b1f00dbd91f5bc64f30891ec7a35f28274dbb0Sungsoo Lim        // TODO (b/72975976): Avoid to use ContextThemeWrapper with app context and lib theme.
10292fd9a9e07a33a01d12ef9c47f73b2c7193a5fd9Christofer Ã…kersten        super(new ContextThemeWrapper(context, ApiHelper.getLibTheme(context,
10392fd9a9e07a33a01d12ef9c47f73b2c7193a5fd9Christofer Ã…kersten                MediaRouterThemeHelper.getRouterThemeId(context))), theme);
104692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        context = getContext();
105692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
106692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        mRouter = MediaRouter.getInstance(context);
107692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        mCallback = new MediaRouterCallback();
108692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    }
109692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
110692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    /**
111692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     * Gets the media route selector for filtering the routes that the user can select.
112692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     *
113692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     * @return The selector, never null.
114692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     */
115692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    @NonNull
116692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    public MediaRouteSelector getRouteSelector() {
117692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        return mSelector;
118692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    }
119692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
120692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    /**
121692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     * Sets the media route selector for filtering the routes that the user can select.
122692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     *
123692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     * @param selector The selector, must not be null.
124692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     */
125692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    public void setRouteSelector(@NonNull MediaRouteSelector selector) {
126692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        if (selector == null) {
127692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            throw new IllegalArgumentException("selector must not be null");
128692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        }
129692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
130692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        if (!mSelector.equals(selector)) {
131692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            mSelector = selector;
132692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
133692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            if (mAttachedToWindow) {
134692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                mRouter.removeCallback(mCallback);
135692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                mRouter.addCallback(selector, mCallback,
136692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                        MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
137692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            }
138692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
139692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            refreshRoutes();
140692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        }
141692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    }
142692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
143692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    /**
144692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     * Called to filter the set of routes that should be included in the list.
145692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     * <p>
146692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     * The default implementation iterates over all routes in the provided list and
147692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     * removes those for which {@link #onFilterRoute} returns false.
148692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     * </p>
149692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     *
150692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     * @param routes The list of routes to filter in-place, never null.
151692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     */
152692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    public void onFilterRoutes(@NonNull List<MediaRouter.RouteInfo> routes) {
153692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        for (int i = routes.size(); i-- > 0; ) {
154692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            if (!onFilterRoute(routes.get(i))) {
155692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                routes.remove(i);
156692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            }
157692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        }
158692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    }
159692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
160692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    /**
161692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     * Returns true if the route should be included in the list.
162692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     * <p>
163692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     * The default implementation returns true for enabled non-default routes that
164692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     * match the selector.  Subclasses can override this method to filter routes
165692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     * differently.
166692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     * </p>
167692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     *
168692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     * @param route The route to consider, never null.
169692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     * @return True if the route should be included in the chooser dialog.
170692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     */
171692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    public boolean onFilterRoute(@NonNull MediaRouter.RouteInfo route) {
172692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        return !route.isDefaultOrBluetooth() && route.isEnabled()
173692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                && route.matchesSelector(mSelector);
174692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    }
175692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
176692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    @Override
177692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    public void setTitle(CharSequence title) {
178692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        mTitleView.setText(title);
179692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    }
180692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
181692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    @Override
182692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    public void setTitle(int titleId) {
183692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        mTitleView.setText(titleId);
184692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    }
185692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
186692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    @Override
187692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    protected void onCreate(Bundle savedInstanceState) {
188692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        super.onCreate(savedInstanceState);
189692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
19092fd9a9e07a33a01d12ef9c47f73b2c7193a5fd9Christofer Ã…kersten        setContentView(ApiHelper.inflateLibLayout(getContext(), ApiHelper.getLibTheme(getContext(),
19192fd9a9e07a33a01d12ef9c47f73b2c7193a5fd9Christofer Ã…kersten                MediaRouterThemeHelper.getRouterThemeId(getContext())),
1920bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim                R.layout.mr_chooser_dialog));
193692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
194692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        mRoutes = new ArrayList<>();
195692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        mAdapter = new RouteAdapter(getContext(), mRoutes);
196692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        mListView = (ListView)findViewById(R.id.mr_chooser_list);
197692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        mListView.setAdapter(mAdapter);
198692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        mListView.setOnItemClickListener(mAdapter);
199692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        mListView.setEmptyView(findViewById(android.R.id.empty));
200692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        mTitleView = findViewById(R.id.mr_chooser_title);
201692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
202692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        updateLayout();
203692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    }
204692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
205692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    /**
206692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     * Sets the width of the dialog. Also called when configuration changes.
207692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     */
208692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    void updateLayout() {
20992fd9a9e07a33a01d12ef9c47f73b2c7193a5fd9Christofer Ã…kersten        getWindow().setLayout(MediaRouteDialogHelper.getDialogWidth(getContext()),
210692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                ViewGroup.LayoutParams.WRAP_CONTENT);
211692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    }
212692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
213692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    @Override
214692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    public void onAttachedToWindow() {
215692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        super.onAttachedToWindow();
216692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
217692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        mAttachedToWindow = true;
218692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        mRouter.addCallback(mSelector, mCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
219692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        refreshRoutes();
220692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    }
221692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
222692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    @Override
223692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    public void onDetachedFromWindow() {
224692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        mAttachedToWindow = false;
225692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        mRouter.removeCallback(mCallback);
226692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        mHandler.removeMessages(MSG_UPDATE_ROUTES);
227692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
228692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        super.onDetachedFromWindow();
229692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    }
230692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
231692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    /**
232692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     * Refreshes the list of routes that are shown in the chooser dialog.
233692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim     */
234692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    public void refreshRoutes() {
235692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        if (mAttachedToWindow) {
236692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            ArrayList<MediaRouter.RouteInfo> routes = new ArrayList<>(mRouter.getRoutes());
237692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            onFilterRoutes(routes);
238692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            Collections.sort(routes, RouteComparator.sInstance);
239692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            if (SystemClock.uptimeMillis() - mLastUpdateTime >= UPDATE_ROUTES_DELAY_MS) {
240692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                updateRoutes(routes);
241692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            } else {
242692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                mHandler.removeMessages(MSG_UPDATE_ROUTES);
243692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_UPDATE_ROUTES, routes),
244692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                        mLastUpdateTime + UPDATE_ROUTES_DELAY_MS);
245692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            }
246692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        }
247692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    }
248692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
249692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    void updateRoutes(List<MediaRouter.RouteInfo> routes) {
250692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        mLastUpdateTime = SystemClock.uptimeMillis();
251692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        mRoutes.clear();
252692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        mRoutes.addAll(routes);
253692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        mAdapter.notifyDataSetChanged();
254692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    }
255692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
256692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    private final class RouteAdapter extends ArrayAdapter<MediaRouter.RouteInfo>
257692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            implements ListView.OnItemClickListener {
258692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        private final Drawable mDefaultIcon;
259692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        private final Drawable mTvIcon;
260692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        private final Drawable mSpeakerIcon;
261692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        private final Drawable mSpeakerGroupIcon;
262692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
263692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        public RouteAdapter(Context context, List<MediaRouter.RouteInfo> routes) {
264692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            super(context, 0, routes);
2650bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim
26692fd9a9e07a33a01d12ef9c47f73b2c7193a5fd9Christofer Ã…kersten            TypedArray styledAttributes = ApiHelper.getLibTheme(context,
2670bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim                    MediaRouterThemeHelper.getRouterThemeId(context)).obtainStyledAttributes(
2680bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim                            new int[] {
2690bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim                                R.attr.mediaRouteDefaultIconDrawable,
2700bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim                                R.attr.mediaRouteTvIconDrawable,
2710bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim                                R.attr.mediaRouteSpeakerIconDrawable,
2720bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim                                R.attr.mediaRouteSpeakerGroupIconDrawable
2730bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim                            });
2740bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim
275692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            mDefaultIcon = styledAttributes.getDrawable(0);
276692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            mTvIcon = styledAttributes.getDrawable(1);
277692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            mSpeakerIcon = styledAttributes.getDrawable(2);
278692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            mSpeakerGroupIcon = styledAttributes.getDrawable(3);
279692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            styledAttributes.recycle();
280692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        }
281692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
282692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        @Override
283692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        public boolean areAllItemsEnabled() {
284692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            return false;
285692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        }
286692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
287692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        @Override
288692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        public boolean isEnabled(int position) {
289692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            return getItem(position).isEnabled();
290692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        }
291692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
292692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        @Override
293692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        public View getView(int position, View convertView, ViewGroup parent) {
294692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            View view = convertView;
295692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            if (view == null) {
2960bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim                view = ApiHelper.inflateLibLayout(getContext(),
29792fd9a9e07a33a01d12ef9c47f73b2c7193a5fd9Christofer Ã…kersten                        ApiHelper.getLibTheme(getContext(),
2980bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim                                MediaRouterThemeHelper.getRouterThemeId(getContext())),
2990bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim                        R.layout.mr_chooser_list_item, parent, false);
300692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            }
301692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
302692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            MediaRouter.RouteInfo route = getItem(position);
303692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            TextView text1 = (TextView) view.findViewById(R.id.mr_chooser_route_name);
304692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            TextView text2 = (TextView) view.findViewById(R.id.mr_chooser_route_desc);
305692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            text1.setText(route.getName());
306692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            String description = route.getDescription();
307692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            boolean isConnectedOrConnecting =
308692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                    route.getConnectionState() == CONNECTION_STATE_CONNECTED
309692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                            || route.getConnectionState() == CONNECTION_STATE_CONNECTING;
310692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            if (isConnectedOrConnecting && !TextUtils.isEmpty(description)) {
311692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                text1.setGravity(Gravity.BOTTOM);
312692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                text2.setVisibility(View.VISIBLE);
313692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                text2.setText(description);
314692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            } else {
315692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                text1.setGravity(Gravity.CENTER_VERTICAL);
316692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                text2.setVisibility(View.GONE);
317692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                text2.setText("");
318692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            }
319692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            view.setEnabled(route.isEnabled());
320692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
321692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            ImageView iconView = (ImageView) view.findViewById(R.id.mr_chooser_route_icon);
322692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            if (iconView != null) {
323692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                iconView.setImageDrawable(getIconDrawable(route));
324692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            }
325692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            return view;
326692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        }
327692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
328692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        @Override
329692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
330692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            MediaRouter.RouteInfo route = getItem(position);
331692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            if (route.isEnabled()) {
332692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                route.select();
333692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                dismiss();
334692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            }
335692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        }
336692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
337692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        private Drawable getIconDrawable(MediaRouter.RouteInfo route) {
338692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            Uri iconUri = route.getIconUri();
339692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            if (iconUri != null) {
340692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                try {
341692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                    InputStream is = getContext().getContentResolver().openInputStream(iconUri);
342692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                    Drawable drawable = Drawable.createFromStream(is, null);
343692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                    if (drawable != null) {
344692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                        return drawable;
345692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                    }
346692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                } catch (IOException e) {
347692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                    Log.w(TAG, "Failed to load " + iconUri, e);
348692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                    // Falls back.
349692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                }
350692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            }
351692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            return getDefaultIconDrawable(route);
352692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        }
353692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
354692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        private Drawable getDefaultIconDrawable(MediaRouter.RouteInfo route) {
355692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            // If the type of the receiver device is specified, use it.
356692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            switch (route.getDeviceType()) {
357692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                case  MediaRouter.RouteInfo.DEVICE_TYPE_TV:
358692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                    return mTvIcon;
359692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                case MediaRouter.RouteInfo.DEVICE_TYPE_SPEAKER:
360692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                    return mSpeakerIcon;
361692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            }
362692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
363692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            // Otherwise, make the best guess based on other route information.
364692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            if (route instanceof MediaRouter.RouteGroup) {
365692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                // Only speakers can be grouped for now.
366692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim                return mSpeakerGroupIcon;
367692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            }
368692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            return mDefaultIcon;
369692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        }
370692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    }
371692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
372692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    private final class MediaRouterCallback extends MediaRouter.Callback {
373692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        MediaRouterCallback() {
374692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        }
375692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
376692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        @Override
377692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) {
378692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            refreshRoutes();
379692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        }
380692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
381692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        @Override
382692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) {
383692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            refreshRoutes();
384692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        }
385692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
386692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        @Override
387692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) {
388692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            refreshRoutes();
389692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        }
390692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
391692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        @Override
392692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) {
393692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            dismiss();
394692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        }
395692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    }
396692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
397692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    static final class RouteComparator implements Comparator<MediaRouter.RouteInfo> {
398692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        public static final RouteComparator sInstance = new RouteComparator();
399692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim
400692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        @Override
401692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        public int compare(MediaRouter.RouteInfo lhs, MediaRouter.RouteInfo rhs) {
402692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim            return lhs.getName().compareToIgnoreCase(rhs.getName());
403692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim        }
404692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim    }
405692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim}
406