MediaRouteControllerDialog.java revision e49860b0f76d8336c1d41831ed370b0ff94278ef
1cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn/*
2cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn * Copyright (C) 2013 The Android Open Source Project
3cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn *
4cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn * Licensed under the Apache License, Version 2.0 (the "License");
5cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn * you may not use this file except in compliance with the License.
6cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn * You may obtain a copy of the License at
7cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn *
8cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn *      http://www.apache.org/licenses/LICENSE-2.0
9cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn *
10cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn * Unless required by applicable law or agreed to in writing, software
11cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn * distributed under the License is distributed on an "AS IS" BASIS,
12cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn * See the License for the specific language governing permissions and
14cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn * limitations under the License.
15cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn */
16cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
17cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackbornpackage android.support.v7.app;
18cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
19cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackbornimport android.app.Dialog;
20cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackbornimport android.content.Context;
21cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackbornimport android.content.IntentSender;
22cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackbornimport android.content.IntentSender.SendIntentException;
23fed04169c31e7e3d8c7a328876358dd3564062bbClara Bayarriimport android.graphics.drawable.Drawable;
24cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackbornimport android.os.Bundle;
25cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackbornimport android.os.RemoteException;
26cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackbornimport android.support.v4.media.MediaDescriptionCompat;
27cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackbornimport android.support.v4.media.MediaMetadataCompat;
28cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackbornimport android.support.v4.media.session.MediaControllerCompat;
29e1e957f536c20ebe2a7e6c417ccb2fd8f7fa845bSvetoslavimport android.support.v4.media.session.MediaSessionCompat;
30a3ff3273e976adf19770651dcf473fa67b38eb22Tor Norbyeimport android.support.v4.media.session.PlaybackStateCompat;
31a3ff3273e976adf19770651dcf473fa67b38eb22Tor Norbyeimport android.support.v7.media.MediaRouteSelector;
322290993eddf5262a8df7fc9478daed52401e325aDianne Hackbornimport android.support.v7.media.MediaRouter;
33cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackbornimport android.support.v7.mediarouter.R;
34bf0947be2ead9b3d8e5865bcd3d3652d02a2aa5aChris Banesimport android.text.TextUtils;
35cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackbornimport android.util.Log;
36be2c79d9a5439922030d2a3846c81c61f0e16912Dianne Hackbornimport android.view.KeyEvent;
37cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackbornimport android.view.View;
38cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackbornimport android.view.Window;
39cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackbornimport android.widget.Button;
40cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackbornimport android.widget.FrameLayout;
41cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackbornimport android.widget.ImageButton;
42cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackbornimport android.widget.ImageView;
43cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackbornimport android.widget.TextView;
44cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
45cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn/**
46cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn * This class implements the route controller dialog for {@link MediaRouter}.
47cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn * <p>
48cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn * This dialog allows the user to control or disconnect from the currently selected route.
49cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn * </p>
50cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn *
51cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn * @see MediaRouteButton
52cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn * @see MediaRouteActionProvider
53cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn */
54cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackbornpublic class MediaRouteControllerDialog extends Dialog {
55cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private static final String TAG = "MediaRouteControllerDialog";
56cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
57cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private final MediaRouter mRouter;
58cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private final MediaRouterCallback mCallback;
59cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private final MediaRouter.RouteInfo mRoute;
60cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
61e4417c91a0bb2fba42a0aaa99edcca1b238af21aDianne Hackborn    private boolean mCreated;
62cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private boolean mAttachedToWindow;
63340a183c3293d98842e4a2671b500e8e80258818Todd Kennedy    private Drawable mMediaRouteConnectingDrawable;
64cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private Drawable mMediaRouteOnDrawable;
65cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private Drawable mCurrentIconDrawable;
66cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private Drawable mSettingsDrawable;
67cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
68c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell    private View mControlView;
69cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
70cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private Button mDisconnectButton;
71cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private Button mStopCastingButton;
72cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private ImageButton mPlayPauseButton;
73cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private ImageButton mSettingsButton;
74cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
75cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private ImageView mArtView;
76cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private TextView mTitleView;
77e4417c91a0bb2fba42a0aaa99edcca1b238af21aDianne Hackborn    private TextView mSubtitleView;
78cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private TextView mRouteNameView;
79340a183c3293d98842e4a2671b500e8e80258818Todd Kennedy    private View mTitlesWrapper;
80cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
81cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private MediaControllerCompat mMediaController;
82cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private MediaControllerCallback mControllerCallback;
83cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private PlaybackStateCompat mState;
84cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private MediaDescriptionCompat mDescription;
85cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
86cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
87cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    public MediaRouteControllerDialog(Context context) {
88cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        this(context, 0);
89cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    }
90e4417c91a0bb2fba42a0aaa99edcca1b238af21aDianne Hackborn
91cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    public MediaRouteControllerDialog(Context context, int theme) {
92340a183c3293d98842e4a2671b500e8e80258818Todd Kennedy        super(MediaRouterThemeHelper.createThemedContext(context), theme);
93cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        context = getContext();
94cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
95d608cf6e08769bf320c1b595cbbd9a7664160449Todd Kennedy        mControllerCallback = new MediaControllerCallback();
96c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell        mRouter = MediaRouter.getInstance(context);
97c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell        mCallback = new MediaRouterCallback();
98c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell        mRoute = mRouter.getSelectedRoute();
99c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell        setMediaSession(mRouter.getMediaSessionToken());
100c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell    }
101c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell
102c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell    /**
103d608cf6e08769bf320c1b595cbbd9a7664160449Todd Kennedy     * Gets the route that this dialog is controlling.
104c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell     */
105d608cf6e08769bf320c1b595cbbd9a7664160449Todd Kennedy    public MediaRouter.RouteInfo getRoute() {
106c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell        return mRoute;
107c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell    }
108c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell
109c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell    /**
110c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell     * Provides the subclass an opportunity to create a view that will
111c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell     * be included within the body of the dialog to offer additional media controls
112c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell     * for the currently playing content.
113c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell     *
114c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell     * @param savedInstanceState The dialog's saved instance state.
115c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell     * @return The media control view, or null if none.
116c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell     */
117c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell    public View onCreateMediaControlView(Bundle savedInstanceState) {
118c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell        return null;
119c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell    }
120c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell
121c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell    /**
122c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell     * Gets the media control view that was created by {@link #onCreateMediaControlView(Bundle)}.
123cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn     *
124c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell     * @return The media control view, or null if none.
125cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn     */
126cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    public View getMediaControlView() {
127d608cf6e08769bf320c1b595cbbd9a7664160449Todd Kennedy        return mControlView;
128cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    }
129cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
130cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    /**
131cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn     * Set the session to use for metadata and transport controls. The dialog
132cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn     * will listen to changes on this session and update the UI automatically in
133cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn     * response to changes.
134cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn     *
135cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn     * @param sessionToken The token for the session to use.
136cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn     */
137cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private void setMediaSession(MediaSessionCompat.Token sessionToken) {
138cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        if (mMediaController != null) {
139cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            mMediaController.unregisterCallback(mControllerCallback);
140e4417c91a0bb2fba42a0aaa99edcca1b238af21aDianne Hackborn            mMediaController = null;
141cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        }
142340a183c3293d98842e4a2671b500e8e80258818Todd Kennedy        if (sessionToken == null) {
143cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            return;
144cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        }
145cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        if (!mAttachedToWindow) {
146cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            return;
147cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        }
148cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        try {
149cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            mMediaController = new MediaControllerCompat(getContext(), sessionToken);
150cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        } catch (RemoteException e) {
151cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            Log.e(TAG, "Error creating media controller in setMediaSession.", e);
152cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        }
153cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        if (mMediaController != null) {
154cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            mMediaController.registerCallback(mControllerCallback);
155cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        }
156cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        MediaMetadataCompat metadata = mMediaController == null ? null
157cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                : mMediaController.getMetadata();
158cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        mDescription = metadata == null ? null : metadata.getDescription();
159cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        mState = mMediaController == null ? null : mMediaController.getPlaybackState();
160cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        update();
161cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    }
1629dcd2e58138ca4eb4b18f80b50e8979329e859d6Scott Main
163cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    /**
1649dcd2e58138ca4eb4b18f80b50e8979329e859d6Scott Main     * Gets the description being used by the default UI.
1659dcd2e58138ca4eb4b18f80b50e8979329e859d6Scott Main     *
1669dcd2e58138ca4eb4b18f80b50e8979329e859d6Scott Main     * @return The current description.
1679dcd2e58138ca4eb4b18f80b50e8979329e859d6Scott Main     */
1689dcd2e58138ca4eb4b18f80b50e8979329e859d6Scott Main    public MediaSessionCompat.Token getMediaSession() {
1699dcd2e58138ca4eb4b18f80b50e8979329e859d6Scott Main        return mMediaController == null ? null : mMediaController.getSessionToken();
1709dcd2e58138ca4eb4b18f80b50e8979329e859d6Scott Main    }
1719dcd2e58138ca4eb4b18f80b50e8979329e859d6Scott Main
172cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    @Override
173cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    protected void onCreate(Bundle savedInstanceState) {
1742290993eddf5262a8df7fc9478daed52401e325aDianne Hackborn        super.onCreate(savedInstanceState);
1752290993eddf5262a8df7fc9478daed52401e325aDianne Hackborn
1769f4a046cbeb22a24d322a644323b41022ad9d15aGeorge Mount        getWindow().requestFeature(Window.FEATURE_NO_TITLE);
1779f4a046cbeb22a24d322a644323b41022ad9d15aGeorge Mount
1789f4a046cbeb22a24d322a644323b41022ad9d15aGeorge Mount        setContentView(R.layout.mr_media_route_controller_material_dialog_b);
179cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
180cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        ClickListener listener = new ClickListener();
181cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
182e4417c91a0bb2fba42a0aaa99edcca1b238af21aDianne Hackborn        mDisconnectButton = (Button) findViewById(R.id.disconnect);
183e4417c91a0bb2fba42a0aaa99edcca1b238af21aDianne Hackborn        mDisconnectButton.setOnClickListener(listener);
184e4417c91a0bb2fba42a0aaa99edcca1b238af21aDianne Hackborn
185cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        mStopCastingButton = (Button) findViewById(R.id.stop);
186cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        mStopCastingButton.setOnClickListener(listener);
187cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
188cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        mSettingsButton = (ImageButton) findViewById(R.id.settings);
189cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        mSettingsButton.setOnClickListener(listener);
190cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
191cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        mArtView = (ImageView) findViewById(R.id.art);
192cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        mTitleView = (TextView) findViewById(R.id.title);
193cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        mSubtitleView = (TextView) findViewById(R.id.subtitle);
194cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        mTitlesWrapper = findViewById(R.id.text_wrapper);
195cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        mPlayPauseButton = (ImageButton) findViewById(R.id.play_pause);
196cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        mPlayPauseButton.setOnClickListener(listener);
197cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        mRouteNameView = (TextView) findViewById(R.id.route_name);
198cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
199cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        mCreated = true;
200cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        if (update()) {
201cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            mControlView = onCreateMediaControlView(savedInstanceState);
202cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            FrameLayout controlFrame =
203cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                    (FrameLayout)findViewById(R.id.media_route_control_frame);
204cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            if (mControlView != null) {
205cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                controlFrame.findViewById(R.id.default_control_frame).setVisibility(View.GONE);
206cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                controlFrame.addView(mControlView);
207cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            }
208cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        }
209cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    }
210cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
211cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    @Override
212cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    public void onAttachedToWindow() {
2132c4b5dbfee5232bcbbcb74b84ce9147b62a9d789Dianne Hackborn        super.onAttachedToWindow();
2142c4b5dbfee5232bcbbcb74b84ce9147b62a9d789Dianne Hackborn        mAttachedToWindow = true;
2152c4b5dbfee5232bcbbcb74b84ce9147b62a9d789Dianne Hackborn
216cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        mRouter.addCallback(MediaRouteSelector.EMPTY, mCallback,
217cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS);
218cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        setMediaSession(mRouter.getMediaSessionToken());
219cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    }
220cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
221cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    @Override
222cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    public void onDetachedFromWindow() {
223cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        mRouter.removeCallback(mCallback);
224cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        setMediaSession(null);
225cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        mAttachedToWindow = false;
226cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        super.onDetachedFromWindow();
227cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    }
228cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
229cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    @Override
230cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    public boolean onKeyDown(int keyCode, KeyEvent event) {
231cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
232cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
233d608cf6e08769bf320c1b595cbbd9a7664160449Todd Kennedy            mRoute.requestUpdateVolume(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ? -1 : 1);
234cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            return true;
235cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        }
236d608cf6e08769bf320c1b595cbbd9a7664160449Todd Kennedy        return super.onKeyDown(keyCode, event);
237cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    }
238cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
239cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    @Override
240abc968f1eba800c34a4008deb43b015da5d23a5fAdam Powell    public boolean onKeyUp(int keyCode, KeyEvent event) {
241cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
242d608cf6e08769bf320c1b595cbbd9a7664160449Todd Kennedy                || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
2438491eb62f621cd5de4b4caed839be09c77011f53Todd Kennedy            return true;
2440adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        }
2450adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        return super.onKeyUp(keyCode, event);
2460adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    }
2470adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
248c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell    private boolean update() {
249c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell        if (!mRoute.isSelected() || mRoute.isDefault()) {
250c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell            dismiss();
251c077b4cd990a9f220a27ad5eca04828e17136064Adam Powell            return false;
2520adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        }
2530adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        if (!mCreated) {
2540adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            return false;
255cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        }
256cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
257cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        mRouteNameView.setText(mRoute.getName());
258cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
259cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        if (mRoute.canDisconnect()) {
260cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            mDisconnectButton.setVisibility(View.VISIBLE);
261cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        } else {
262cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            mDisconnectButton.setVisibility(View.GONE);
263cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        }
264cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
265cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        if (mRoute.getSettingsIntent() != null) {
266cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            mSettingsButton.setVisibility(View.VISIBLE);
267cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        } else {
268cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            mSettingsButton.setVisibility(View.GONE);
269cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        }
270cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
271cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        if (mControlView == null) {
272e4417c91a0bb2fba42a0aaa99edcca1b238af21aDianne Hackborn            if (mDescription != null) {
273e4417c91a0bb2fba42a0aaa99edcca1b238af21aDianne Hackborn                if (mDescription.getIconBitmap() != null) {
274e4417c91a0bb2fba42a0aaa99edcca1b238af21aDianne Hackborn                    mArtView.setImageBitmap(mDescription.getIconBitmap());
275cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                    mArtView.setVisibility(View.VISIBLE);
276cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                } else if (mDescription.getIconUri() != null) {
277cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                    // TODO replace with background load of icon
2782bdd09a243b422960168170e2344274cc9cfd788Todd Kennedy                    mArtView.setImageURI(mDescription.getIconUri());
279cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                    mArtView.setVisibility(View.VISIBLE);
280cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                } else {
281cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                    mArtView.setImageDrawable(null);
282cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                    mArtView.setVisibility(View.GONE);
283cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                }
2842a4d8518f36346ea25a22a736453ff28f2954165Dianne Hackborn
2852a4d8518f36346ea25a22a736453ff28f2954165Dianne Hackborn                boolean haveText = false;
2862a4d8518f36346ea25a22a736453ff28f2954165Dianne Hackborn                CharSequence text = mDescription.getTitle();
2872a4d8518f36346ea25a22a736453ff28f2954165Dianne Hackborn                if (!TextUtils.isEmpty(text)) {
288cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                    mTitleView.setText(text);
289cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                    haveText = true;
290cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                } else {
291cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                    mTitleView.setText(null);
292cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                    mTitleView.setVisibility(View.GONE);
293cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                }
294cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                text = mDescription.getSubtitle();
295cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                if (!TextUtils.isEmpty(text)) {
296cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                    mSubtitleView.setText(mDescription.getSubtitle());
297cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                    haveText = true;
298cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                } else {
299cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                    mSubtitleView.setText(null);
300cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                    mSubtitleView.setVisibility(View.GONE);
301cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                }
302abc968f1eba800c34a4008deb43b015da5d23a5fAdam Powell                if (!haveText) {
303abc968f1eba800c34a4008deb43b015da5d23a5fAdam Powell                    mTitlesWrapper.setVisibility(View.GONE);
304abc968f1eba800c34a4008deb43b015da5d23a5fAdam Powell                } else {
305abc968f1eba800c34a4008deb43b015da5d23a5fAdam Powell                    mTitlesWrapper.setVisibility(View.VISIBLE);
30679398eaefea45e61d839cf4e0534f0eafee70a09Adam Powell                }
30779398eaefea45e61d839cf4e0534f0eafee70a09Adam Powell            } else {
30879398eaefea45e61d839cf4e0534f0eafee70a09Adam Powell                mArtView.setVisibility(View.GONE);
309abc968f1eba800c34a4008deb43b015da5d23a5fAdam Powell                mTitlesWrapper.setVisibility(View.GONE);
310cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            }
311cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            if (mState != null) {
312cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                boolean isPlaying = mState.getState() == PlaybackStateCompat.STATE_BUFFERING
3139f4a046cbeb22a24d322a644323b41022ad9d15aGeorge Mount                        || mState.getState() == PlaybackStateCompat.STATE_PLAYING;
3149f4a046cbeb22a24d322a644323b41022ad9d15aGeorge Mount                boolean supportsPlay = (mState.getActions() & (PlaybackStateCompat.ACTION_PLAY
3159f4a046cbeb22a24d322a644323b41022ad9d15aGeorge Mount                        | PlaybackStateCompat.ACTION_PLAY_PAUSE)) != 0;
3169f4a046cbeb22a24d322a644323b41022ad9d15aGeorge Mount                boolean supportsPause = (mState.getActions() & (PlaybackStateCompat.ACTION_PAUSE
3179f4a046cbeb22a24d322a644323b41022ad9d15aGeorge Mount                                | PlaybackStateCompat.ACTION_PLAY_PAUSE)) != 0;
3189f4a046cbeb22a24d322a644323b41022ad9d15aGeorge Mount                if (isPlaying && supportsPause) {
3199f4a046cbeb22a24d322a644323b41022ad9d15aGeorge Mount                    mPlayPauseButton.setVisibility(View.VISIBLE);
3209f4a046cbeb22a24d322a644323b41022ad9d15aGeorge Mount                    mPlayPauseButton.setImageResource(MediaRouterThemeHelper.getThemeResource(
3219f4a046cbeb22a24d322a644323b41022ad9d15aGeorge Mount                            getContext(), R.attr.mediaRoutePauseDrawable));
3229f4a046cbeb22a24d322a644323b41022ad9d15aGeorge Mount                    mPlayPauseButton.setContentDescription(getContext().getResources()
3239306f07b1d66f4974a81f781d29739b30124fff0George Mount                            .getText(R.string.mr_media_route_controller_pause));
3249306f07b1d66f4974a81f781d29739b30124fff0George Mount                } else if (!isPlaying && supportsPlay) {
3259f4a046cbeb22a24d322a644323b41022ad9d15aGeorge Mount                    mPlayPauseButton.setVisibility(View.VISIBLE);
326cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                    mPlayPauseButton.setImageResource(MediaRouterThemeHelper.getThemeResource(
3275c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn                            getContext(), R.attr.mediaRoutePlayDrawable));
3285c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn                    mPlayPauseButton.setContentDescription(getContext().getResources()
3295c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn                            .getText(R.string.mr_media_route_controller_play));
3305c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn                } else {
3315c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn                    mPlayPauseButton.setVisibility(View.GONE);
3325c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn                }
3335c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn            } else {
3345c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn                mPlayPauseButton.setVisibility(View.GONE);
3355c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn            }
3365c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn        }
3375c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn        return true;
3385c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn    }
3395c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn
3405c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn    private Drawable getIconDrawable() {
3415c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn        if (mRoute.isConnecting()) {
3425c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn            if (mMediaRouteConnectingDrawable == null) {
3435c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn                mMediaRouteConnectingDrawable = MediaRouterThemeHelper.getThemeDrawable(
3445c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn                        getContext(), R.attr.mediaRouteConnectingDrawable);
3455c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn            }
3465c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn            return mMediaRouteConnectingDrawable;
3475c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn        } else {
3485c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn            if (mMediaRouteOnDrawable == null) {
3495c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn                mMediaRouteOnDrawable = MediaRouterThemeHelper.getThemeDrawable(
3505c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn                        getContext(), R.attr.mediaRouteOnDrawable);
3515c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn            }
3525c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn            return mMediaRouteOnDrawable;
3535c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn        }
3545c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn    }
3555c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn
3565c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn    private final class MediaRouterCallback extends MediaRouter.Callback {
357d608cf6e08769bf320c1b595cbbd9a7664160449Todd Kennedy        @Override
3585c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn        public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo route) {
3595c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn            update();
3605c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn        }
3615c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn
362d608cf6e08769bf320c1b595cbbd9a7664160449Todd Kennedy        @Override
3635c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn        public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) {
3645c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn            update();
3655c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn        }
3665c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn
3675c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn        @Override
3685c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn        public void onRouteVolumeChanged(MediaRouter router, MediaRouter.RouteInfo route) {
3695c1637087453de15e31861f073eae5133c4e9f7bDianne Hackborn            if (route == mRoute) {
370cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            }
371cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        }
372cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    }
373cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
374cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private final class MediaControllerCallback extends MediaControllerCompat.Callback {
375cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        @Override
376cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        public void onSessionDestroyed() {
377cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            if (mMediaController != null) {
378cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                mMediaController.unregisterCallback(mControllerCallback);
379cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                mMediaController = null;
380cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            }
381cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        }
382cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
383cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        @Override
384cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        public void onPlaybackStateChanged(PlaybackStateCompat state) {
385cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            mState = state;
386cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            update();
387cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        }
388cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
389cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        @Override
390cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        public void onMetadataChanged(MediaMetadataCompat metadata) {
391cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            mDescription = metadata == null ? null : metadata.getDescription();
392cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            update();
393cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        }
394cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    }
395cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn
396cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    private final class ClickListener implements View.OnClickListener {
397cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        @Override
398cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        public void onClick(View v) {
399cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            int id = v.getId();
400cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            if (id == R.id.stop || id == R.id.disconnect) {
401cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                if (mRoute.isSelected()) {
402cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                    mRouter.unselect(id == R.id.stop ?
403cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                            MediaRouter.UNSELECT_REASON_STOPPED :
404cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                            MediaRouter.UNSELECT_REASON_DISCONNECTED);
405cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                }
406cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                dismiss();
407cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            } else if (id == R.id.play_pause) {
408cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                if (mMediaController != null && mState != null) {
409cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                    if (mState.getState() == PlaybackStateCompat.STATE_PLAYING) {
410cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                        mMediaController.getTransportControls().pause();
411cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                    } else {
412cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                        mMediaController.getTransportControls().play();
413cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                    }
414cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                }
415cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            } else if (id == R.id.settings) {
416cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                IntentSender is = mRoute.getSettingsIntent();
417cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                if (is != null) {
418cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                    try {
419cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                        is.sendIntent(null, 0, null, null, null);
420cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                        dismiss();
421c861fd8b28b0b878ccc194ee6099b3b1bc4c3ae8Scott Kennedy                    } catch (Exception e) {
422cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                        Log.e(TAG, "Error opening route settings.", e);
423cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                    }
424cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn                }
425cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn            }
426cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn        }
427cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn    }
428cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn}
429cba2e2c881e8e16ea5025b564c94320174d65f01Dianne Hackborn