Merge "Update the media router dialogs and integrate into system UI." into klp-dev
This commit is contained in:
@ -16,10 +16,7 @@
|
|||||||
|
|
||||||
package android.app;
|
package android.app;
|
||||||
|
|
||||||
import com.android.internal.app.MediaRouteChooserDialogFragment;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.ContextWrapper;
|
|
||||||
import android.media.MediaRouter;
|
import android.media.MediaRouter;
|
||||||
import android.media.MediaRouter.RouteInfo;
|
import android.media.MediaRouter.RouteInfo;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@ -30,22 +27,38 @@ import android.view.ViewGroup;
|
|||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The media route action provider displays a {@link MediaRouteButton media route button}
|
||||||
|
* in the application's {@link ActionBar} to allow the user to select routes and
|
||||||
|
* to control the currently selected route.
|
||||||
|
* <p>
|
||||||
|
* The application must specify the kinds of routes that the user should be allowed
|
||||||
|
* to select by specifying the route types with the {@link #setRouteTypes} method.
|
||||||
|
* </p><p>
|
||||||
|
* Refer to {@link MediaRouteButton} for a description of the button that will
|
||||||
|
* appear in the action bar menu. Note that instead of disabling the button
|
||||||
|
* when no routes are available, the action provider will instead make the
|
||||||
|
* menu item invisible. In this way, the button will only be visible when it
|
||||||
|
* is possible for the user to discover and select a matching route.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
public class MediaRouteActionProvider extends ActionProvider {
|
public class MediaRouteActionProvider extends ActionProvider {
|
||||||
private static final String TAG = "MediaRouteActionProvider";
|
private static final String TAG = "MediaRouteActionProvider";
|
||||||
|
|
||||||
private Context mContext;
|
private final Context mContext;
|
||||||
private MediaRouter mRouter;
|
private final MediaRouter mRouter;
|
||||||
private MenuItem mMenuItem;
|
private final MediaRouterCallback mCallback;
|
||||||
private MediaRouteButton mView;
|
|
||||||
private int mRouteTypes;
|
private int mRouteTypes;
|
||||||
|
private MediaRouteButton mButton;
|
||||||
private View.OnClickListener mExtendedSettingsListener;
|
private View.OnClickListener mExtendedSettingsListener;
|
||||||
private RouterCallback mCallback;
|
|
||||||
|
|
||||||
public MediaRouteActionProvider(Context context) {
|
public MediaRouteActionProvider(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
|
mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
|
||||||
mCallback = new RouterCallback(this);
|
mCallback = new MediaRouterCallback(this);
|
||||||
|
|
||||||
// Start with live audio by default.
|
// Start with live audio by default.
|
||||||
// TODO Update this when new route types are added; segment by API level
|
// TODO Update this when new route types are added; segment by API level
|
||||||
@ -53,82 +66,76 @@ public class MediaRouteActionProvider extends ActionProvider {
|
|||||||
setRouteTypes(MediaRouter.ROUTE_TYPE_LIVE_AUDIO);
|
setRouteTypes(MediaRouter.ROUTE_TYPE_LIVE_AUDIO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the types of routes that will be shown in the media route chooser dialog
|
||||||
|
* launched by this button.
|
||||||
|
*
|
||||||
|
* @param types The route types to match.
|
||||||
|
*/
|
||||||
public void setRouteTypes(int types) {
|
public void setRouteTypes(int types) {
|
||||||
if (mRouteTypes == types) return;
|
if (mRouteTypes != types) {
|
||||||
|
// FIXME: We currently have no way of knowing whether the action provider
|
||||||
|
// is still needed by the UI. Unfortunately this means the action provider
|
||||||
|
// may leak callbacks until garbage collection occurs. This may result in
|
||||||
|
// media route providers doing more work than necessary in the short term
|
||||||
|
// while trying to discover routes that are no longer of interest to the
|
||||||
|
// application. To solve this problem, the action provider will need some
|
||||||
|
// indication from the framework that it is being destroyed.
|
||||||
if (mRouteTypes != 0) {
|
if (mRouteTypes != 0) {
|
||||||
mRouter.removeCallback(mCallback);
|
mRouter.removeCallback(mCallback);
|
||||||
}
|
}
|
||||||
mRouteTypes = types;
|
mRouteTypes = types;
|
||||||
if (types != 0) {
|
if (types != 0) {
|
||||||
mRouter.addCallback(types, mCallback, MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
|
mRouter.addCallback(types, mCallback,
|
||||||
|
MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
|
||||||
}
|
}
|
||||||
if (mView != null) {
|
refreshRoute();
|
||||||
mView.setRouteTypes(mRouteTypes);
|
|
||||||
|
if (mButton != null) {
|
||||||
|
mButton.setRouteTypes(mRouteTypes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExtendedSettingsClickListener(View.OnClickListener listener) {
|
||||||
|
mExtendedSettingsListener = listener;
|
||||||
|
if (mButton != null) {
|
||||||
|
mButton.setExtendedSettingsClickListener(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public View onCreateActionView() {
|
public View onCreateActionView() {
|
||||||
throw new UnsupportedOperationException("Use onCreateActionView(MenuItem) instead.");
|
throw new UnsupportedOperationException("Use onCreateActionView(MenuItem) instead.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateActionView(MenuItem item) {
|
public View onCreateActionView(MenuItem item) {
|
||||||
if (mMenuItem != null || mView != null) {
|
if (mButton != null) {
|
||||||
Log.e(TAG, "onCreateActionView: this ActionProvider is already associated " +
|
Log.e(TAG, "onCreateActionView: this ActionProvider is already associated " +
|
||||||
"with a menu item. Don't reuse MediaRouteActionProvider instances! " +
|
"with a menu item. Don't reuse MediaRouteActionProvider instances! " +
|
||||||
"Abandoning the old one...");
|
"Abandoning the old one...");
|
||||||
}
|
}
|
||||||
mMenuItem = item;
|
|
||||||
mView = new MediaRouteButton(mContext);
|
mButton = new MediaRouteButton(mContext);
|
||||||
mView.setCheatSheetEnabled(true);
|
mButton.setCheatSheetEnabled(true);
|
||||||
mView.setRouteTypes(mRouteTypes);
|
mButton.setRouteTypes(mRouteTypes);
|
||||||
mView.setExtendedSettingsClickListener(mExtendedSettingsListener);
|
mButton.setExtendedSettingsClickListener(mExtendedSettingsListener);
|
||||||
mView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
mButton.setLayoutParams(new ViewGroup.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT));
|
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
return mView;
|
return mButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onPerformDefaultAction() {
|
public boolean onPerformDefaultAction() {
|
||||||
final FragmentManager fm = getActivity().getFragmentManager();
|
if (mButton != null) {
|
||||||
// See if one is already attached to this activity.
|
return mButton.showDialogInternal();
|
||||||
MediaRouteChooserDialogFragment dialogFragment =
|
}
|
||||||
(MediaRouteChooserDialogFragment) fm.findFragmentByTag(
|
|
||||||
MediaRouteChooserDialogFragment.FRAGMENT_TAG);
|
|
||||||
if (dialogFragment != null) {
|
|
||||||
Log.w(TAG, "onPerformDefaultAction(): Chooser dialog already showing!");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
dialogFragment = new MediaRouteChooserDialogFragment();
|
|
||||||
dialogFragment.setExtendedSettingsClickListener(mExtendedSettingsListener);
|
|
||||||
dialogFragment.setRouteTypes(mRouteTypes);
|
|
||||||
dialogFragment.show(fm, MediaRouteChooserDialogFragment.FRAGMENT_TAG);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Activity getActivity() {
|
|
||||||
// Gross way of unwrapping the Activity so we can get the FragmentManager
|
|
||||||
Context context = mContext;
|
|
||||||
while (context instanceof ContextWrapper && !(context instanceof Activity)) {
|
|
||||||
context = ((ContextWrapper) context).getBaseContext();
|
|
||||||
}
|
|
||||||
if (!(context instanceof Activity)) {
|
|
||||||
throw new IllegalStateException("The MediaRouteActionProvider's Context " +
|
|
||||||
"is not an Activity.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return (Activity) context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExtendedSettingsClickListener(View.OnClickListener listener) {
|
|
||||||
mExtendedSettingsListener = listener;
|
|
||||||
if (mView != null) {
|
|
||||||
mView.setExtendedSettingsClickListener(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean overridesItemVisibility() {
|
public boolean overridesItemVisibility() {
|
||||||
return true;
|
return true;
|
||||||
@ -136,36 +143,43 @@ public class MediaRouteActionProvider extends ActionProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isVisible() {
|
public boolean isVisible() {
|
||||||
return mRouter.getRouteCount() > 1;
|
return mRouter.isRouteAvailable(mRouteTypes,
|
||||||
|
MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class RouterCallback extends MediaRouter.SimpleCallback {
|
private void refreshRoute() {
|
||||||
private WeakReference<MediaRouteActionProvider> mAp;
|
refreshVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
RouterCallback(MediaRouteActionProvider ap) {
|
private static class MediaRouterCallback extends MediaRouter.SimpleCallback {
|
||||||
mAp = new WeakReference<MediaRouteActionProvider>(ap);
|
private final WeakReference<MediaRouteActionProvider> mProviderWeak;
|
||||||
|
|
||||||
|
public MediaRouterCallback(MediaRouteActionProvider provider) {
|
||||||
|
mProviderWeak = new WeakReference<MediaRouteActionProvider>(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRouteAdded(MediaRouter router, RouteInfo info) {
|
public void onRouteAdded(MediaRouter router, RouteInfo info) {
|
||||||
final MediaRouteActionProvider ap = mAp.get();
|
refreshRoute(router);
|
||||||
if (ap == null) {
|
|
||||||
router.removeCallback(this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ap.refreshVisibility();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRouteRemoved(MediaRouter router, RouteInfo info) {
|
public void onRouteRemoved(MediaRouter router, RouteInfo info) {
|
||||||
final MediaRouteActionProvider ap = mAp.get();
|
refreshRoute(router);
|
||||||
if (ap == null) {
|
|
||||||
router.removeCallback(this);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ap.refreshVisibility();
|
@Override
|
||||||
|
public void onRouteChanged(MediaRouter router, RouteInfo info) {
|
||||||
|
refreshRoute(router);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshRoute(MediaRouter router) {
|
||||||
|
MediaRouteActionProvider provider = mProviderWeak.get();
|
||||||
|
if (provider != null) {
|
||||||
|
provider.refreshRoute();
|
||||||
|
} else {
|
||||||
|
router.removeCallback(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
package android.app;
|
package android.app;
|
||||||
|
|
||||||
import com.android.internal.R;
|
import com.android.internal.R;
|
||||||
import com.android.internal.app.MediaRouteChooserDialogFragment;
|
import com.android.internal.app.MediaRouteDialogPresenter;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.ContextWrapper;
|
import android.content.ContextWrapper;
|
||||||
@ -30,7 +30,6 @@ import android.media.MediaRouter.RouteGroup;
|
|||||||
import android.media.MediaRouter.RouteInfo;
|
import android.media.MediaRouter.RouteInfo;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.HapticFeedbackConstants;
|
import android.view.HapticFeedbackConstants;
|
||||||
import android.view.SoundEffectConstants;
|
import android.view.SoundEffectConstants;
|
||||||
@ -38,17 +37,15 @@ import android.view.View;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
public class MediaRouteButton extends View {
|
public class MediaRouteButton extends View {
|
||||||
private static final String TAG = "MediaRouteButton";
|
private final MediaRouter mRouter;
|
||||||
|
private final MediaRouterCallback mCallback;
|
||||||
|
|
||||||
private MediaRouter mRouter;
|
|
||||||
private final MediaRouteCallback mRouterCallback = new MediaRouteCallback();
|
|
||||||
private int mRouteTypes;
|
private int mRouteTypes;
|
||||||
|
|
||||||
private boolean mAttachedToWindow;
|
private boolean mAttachedToWindow;
|
||||||
|
|
||||||
private Drawable mRemoteIndicator;
|
private Drawable mRemoteIndicator;
|
||||||
private boolean mRemoteActive;
|
private boolean mRemoteActive;
|
||||||
private boolean mToggleMode;
|
|
||||||
private boolean mCheatSheetEnabled;
|
private boolean mCheatSheetEnabled;
|
||||||
private boolean mIsConnecting;
|
private boolean mIsConnecting;
|
||||||
|
|
||||||
@ -56,12 +53,13 @@ public class MediaRouteButton extends View {
|
|||||||
private int mMinHeight;
|
private int mMinHeight;
|
||||||
|
|
||||||
private OnClickListener mExtendedSettingsClickListener;
|
private OnClickListener mExtendedSettingsClickListener;
|
||||||
private MediaRouteChooserDialogFragment mDialogFragment;
|
|
||||||
|
|
||||||
|
// The checked state is used when connected to a remote route.
|
||||||
private static final int[] CHECKED_STATE_SET = {
|
private static final int[] CHECKED_STATE_SET = {
|
||||||
R.attr.state_checked
|
R.attr.state_checked
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The activated state is used while connecting to a remote route.
|
||||||
private static final int[] ACTIVATED_STATE_SET = {
|
private static final int[] ACTIVATED_STATE_SET = {
|
||||||
R.attr.state_activated
|
R.attr.state_activated
|
||||||
};
|
};
|
||||||
@ -78,6 +76,7 @@ public class MediaRouteButton extends View {
|
|||||||
super(context, attrs, defStyleAttr);
|
super(context, attrs, defStyleAttr);
|
||||||
|
|
||||||
mRouter = (MediaRouter)context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
|
mRouter = (MediaRouter)context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
|
||||||
|
mCallback = new MediaRouterCallback();
|
||||||
|
|
||||||
TypedArray a = context.obtainStyledAttributes(attrs,
|
TypedArray a = context.obtainStyledAttributes(attrs,
|
||||||
com.android.internal.R.styleable.MediaRouteButton, defStyleAttr, 0);
|
com.android.internal.R.styleable.MediaRouteButton, defStyleAttr, 0);
|
||||||
@ -98,19 +97,87 @@ public class MediaRouteButton extends View {
|
|||||||
setRouteTypes(routeTypes);
|
setRouteTypes(routeTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setRemoteIndicatorDrawable(Drawable d) {
|
/**
|
||||||
if (mRemoteIndicator != null) {
|
* Gets the media route types for filtering the routes that the user can
|
||||||
mRemoteIndicator.setCallback(null);
|
* select using the media route chooser dialog.
|
||||||
unscheduleDrawable(mRemoteIndicator);
|
*
|
||||||
}
|
* @return The route types.
|
||||||
mRemoteIndicator = d;
|
*/
|
||||||
if (d != null) {
|
public int getRouteTypes() {
|
||||||
d.setCallback(this);
|
return mRouteTypes;
|
||||||
d.setState(getDrawableState());
|
|
||||||
d.setVisible(getVisibility() == VISIBLE, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshDrawableState();
|
/**
|
||||||
|
* Sets the types of routes that will be shown in the media route chooser dialog
|
||||||
|
* launched by this button.
|
||||||
|
*
|
||||||
|
* @param types The route types to match.
|
||||||
|
*/
|
||||||
|
public void setRouteTypes(int types) {
|
||||||
|
if (mRouteTypes != types) {
|
||||||
|
if (mAttachedToWindow && mRouteTypes != 0) {
|
||||||
|
mRouter.removeCallback(mCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
mRouteTypes = types;
|
||||||
|
|
||||||
|
if (mAttachedToWindow && types != 0) {
|
||||||
|
mRouter.addCallback(types, mCallback,
|
||||||
|
MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshRoute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExtendedSettingsClickListener(OnClickListener listener) {
|
||||||
|
mExtendedSettingsClickListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the route chooser or controller dialog.
|
||||||
|
* <p>
|
||||||
|
* If the default route is selected or if the currently selected route does
|
||||||
|
* not match the {@link #getRouteTypes route types}, then shows the route chooser dialog.
|
||||||
|
* Otherwise, shows the route controller dialog to offer the user
|
||||||
|
* a choice to disconnect from the route or perform other control actions
|
||||||
|
* such as setting the route's volume.
|
||||||
|
* </p><p>
|
||||||
|
* This will attach a {@link DialogFragment} to the containing Activity.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public void showDialog() {
|
||||||
|
showDialogInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean showDialogInternal() {
|
||||||
|
if (!mAttachedToWindow) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogFragment f = MediaRouteDialogPresenter.showDialogFragment(getActivity(),
|
||||||
|
mRouteTypes, mExtendedSettingsClickListener);
|
||||||
|
return f != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Activity getActivity() {
|
||||||
|
// Gross way of unwrapping the Activity so we can get the FragmentManager
|
||||||
|
Context context = getContext();
|
||||||
|
while (context instanceof ContextWrapper) {
|
||||||
|
if (context instanceof Activity) {
|
||||||
|
return (Activity)context;
|
||||||
|
}
|
||||||
|
context = ((ContextWrapper)context).getBaseContext();
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("The MediaRouteButton's Context is not an Activity.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether to enable showing a toast with the content descriptor of the
|
||||||
|
* button when the button is long pressed.
|
||||||
|
*/
|
||||||
|
void setCheatSheetEnabled(boolean enable) {
|
||||||
|
mCheatSheetEnabled = enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -120,29 +187,7 @@ public class MediaRouteButton extends View {
|
|||||||
if (!handled) {
|
if (!handled) {
|
||||||
playSoundEffect(SoundEffectConstants.CLICK);
|
playSoundEffect(SoundEffectConstants.CLICK);
|
||||||
}
|
}
|
||||||
|
return showDialogInternal() || handled;
|
||||||
if (mToggleMode) {
|
|
||||||
if (mRemoteActive) {
|
|
||||||
mRouter.selectRouteInt(mRouteTypes, mRouter.getDefaultRoute(), true);
|
|
||||||
} else {
|
|
||||||
final int N = mRouter.getRouteCount();
|
|
||||||
for (int i = 0; i < N; i++) {
|
|
||||||
final RouteInfo route = mRouter.getRouteAt(i);
|
|
||||||
if ((route.getSupportedTypes() & mRouteTypes) != 0 &&
|
|
||||||
route != mRouter.getDefaultRoute()) {
|
|
||||||
mRouter.selectRouteInt(mRouteTypes, route, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showDialog();
|
|
||||||
}
|
|
||||||
|
|
||||||
return handled;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setCheatSheetEnabled(boolean enable) {
|
|
||||||
mCheatSheetEnabled = enable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -183,87 +228,9 @@ public class MediaRouteButton extends View {
|
|||||||
}
|
}
|
||||||
cheatSheet.show();
|
cheatSheet.show();
|
||||||
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRouteTypes(int types) {
|
|
||||||
if (types == mRouteTypes) {
|
|
||||||
// Already registered; nothing to do.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mAttachedToWindow && mRouteTypes != 0) {
|
|
||||||
mRouter.removeCallback(mRouterCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
mRouteTypes = types;
|
|
||||||
|
|
||||||
if (mAttachedToWindow) {
|
|
||||||
updateRouteInfo();
|
|
||||||
mRouter.addCallback(types, mRouterCallback,
|
|
||||||
MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateRouteInfo() {
|
|
||||||
updateRemoteIndicator();
|
|
||||||
updateRouteCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRouteTypes() {
|
|
||||||
return mRouteTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateRemoteIndicator() {
|
|
||||||
final RouteInfo selected = mRouter.getSelectedRoute(mRouteTypes);
|
|
||||||
final boolean isRemote = selected != mRouter.getDefaultRoute();
|
|
||||||
final boolean isConnecting = selected != null && selected.isConnecting();
|
|
||||||
|
|
||||||
boolean needsRefresh = false;
|
|
||||||
if (mRemoteActive != isRemote) {
|
|
||||||
mRemoteActive = isRemote;
|
|
||||||
needsRefresh = true;
|
|
||||||
}
|
|
||||||
if (mIsConnecting != isConnecting) {
|
|
||||||
mIsConnecting = isConnecting;
|
|
||||||
needsRefresh = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needsRefresh) {
|
|
||||||
refreshDrawableState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateRouteCount() {
|
|
||||||
final int N = mRouter.getRouteCount();
|
|
||||||
int count = 0;
|
|
||||||
boolean scanRequired = false;
|
|
||||||
for (int i = 0; i < N; i++) {
|
|
||||||
final RouteInfo route = mRouter.getRouteAt(i);
|
|
||||||
final int routeTypes = route.getSupportedTypes();
|
|
||||||
if ((routeTypes & mRouteTypes) != 0) {
|
|
||||||
if (route instanceof RouteGroup) {
|
|
||||||
count += ((RouteGroup) route).getRouteCount();
|
|
||||||
} else {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
if (((routeTypes & MediaRouter.ROUTE_TYPE_LIVE_VIDEO
|
|
||||||
| MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)) != 0) {
|
|
||||||
scanRequired = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setEnabled(count != 0);
|
|
||||||
|
|
||||||
// Only allow toggling if we have more than just user routes.
|
|
||||||
// Don't toggle if we support video or remote display routes, we may have to
|
|
||||||
// let the dialog scan.
|
|
||||||
mToggleMode = count == 2 && (mRouteTypes & MediaRouter.ROUTE_TYPE_LIVE_AUDIO) != 0
|
|
||||||
&& !scanRequired;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int[] onCreateDrawableState(int extraSpace) {
|
protected int[] onCreateDrawableState(int extraSpace) {
|
||||||
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
|
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
|
||||||
@ -291,6 +258,21 @@ public class MediaRouteButton extends View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setRemoteIndicatorDrawable(Drawable d) {
|
||||||
|
if (mRemoteIndicator != null) {
|
||||||
|
mRemoteIndicator.setCallback(null);
|
||||||
|
unscheduleDrawable(mRemoteIndicator);
|
||||||
|
}
|
||||||
|
mRemoteIndicator = d;
|
||||||
|
if (d != null) {
|
||||||
|
d.setCallback(this);
|
||||||
|
d.setState(getDrawableState());
|
||||||
|
d.setVisible(getVisibility() == VISIBLE, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshDrawableState();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean verifyDrawable(Drawable who) {
|
protected boolean verifyDrawable(Drawable who) {
|
||||||
return super.verifyDrawable(who) || who == mRemoteIndicator;
|
return super.verifyDrawable(who) || who == mRemoteIndicator;
|
||||||
@ -299,12 +281,16 @@ public class MediaRouteButton extends View {
|
|||||||
@Override
|
@Override
|
||||||
public void jumpDrawablesToCurrentState() {
|
public void jumpDrawablesToCurrentState() {
|
||||||
super.jumpDrawablesToCurrentState();
|
super.jumpDrawablesToCurrentState();
|
||||||
if (mRemoteIndicator != null) mRemoteIndicator.jumpToCurrentState();
|
|
||||||
|
if (mRemoteIndicator != null) {
|
||||||
|
mRemoteIndicator.jumpToCurrentState();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setVisibility(int visibility) {
|
public void setVisibility(int visibility) {
|
||||||
super.setVisibility(visibility);
|
super.setVisibility(visibility);
|
||||||
|
|
||||||
if (mRemoteIndicator != null) {
|
if (mRemoteIndicator != null) {
|
||||||
mRemoteIndicator.setVisible(getVisibility() == VISIBLE, false);
|
mRemoteIndicator.setVisible(getVisibility() == VISIBLE, false);
|
||||||
}
|
}
|
||||||
@ -313,20 +299,22 @@ public class MediaRouteButton extends View {
|
|||||||
@Override
|
@Override
|
||||||
public void onAttachedToWindow() {
|
public void onAttachedToWindow() {
|
||||||
super.onAttachedToWindow();
|
super.onAttachedToWindow();
|
||||||
|
|
||||||
mAttachedToWindow = true;
|
mAttachedToWindow = true;
|
||||||
if (mRouteTypes != 0) {
|
if (mRouteTypes != 0) {
|
||||||
mRouter.addCallback(mRouteTypes, mRouterCallback,
|
mRouter.addCallback(mRouteTypes, mCallback,
|
||||||
MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
|
MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
|
||||||
updateRouteInfo();
|
|
||||||
}
|
}
|
||||||
|
refreshRoute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDetachedFromWindow() {
|
public void onDetachedFromWindow() {
|
||||||
if (mRouteTypes != 0) {
|
|
||||||
mRouter.removeCallback(mRouterCallback);
|
|
||||||
}
|
|
||||||
mAttachedToWindow = false;
|
mAttachedToWindow = false;
|
||||||
|
if (mRouteTypes != 0) {
|
||||||
|
mRouter.removeCallback(mCallback);
|
||||||
|
}
|
||||||
|
|
||||||
super.onDetachedFromWindow();
|
super.onDetachedFromWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,93 +377,71 @@ public class MediaRouteButton extends View {
|
|||||||
final int drawLeft = left + (right - left - drawWidth) / 2;
|
final int drawLeft = left + (right - left - drawWidth) / 2;
|
||||||
final int drawTop = top + (bottom - top - drawHeight) / 2;
|
final int drawTop = top + (bottom - top - drawHeight) / 2;
|
||||||
|
|
||||||
mRemoteIndicator.setBounds(drawLeft, drawTop, drawLeft + drawWidth, drawTop + drawHeight);
|
mRemoteIndicator.setBounds(drawLeft, drawTop,
|
||||||
|
drawLeft + drawWidth, drawTop + drawHeight);
|
||||||
mRemoteIndicator.draw(canvas);
|
mRemoteIndicator.draw(canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setExtendedSettingsClickListener(OnClickListener listener) {
|
private void refreshRoute() {
|
||||||
mExtendedSettingsClickListener = listener;
|
if (mAttachedToWindow) {
|
||||||
if (mDialogFragment != null) {
|
final MediaRouter.RouteInfo route = mRouter.getSelectedRoute();
|
||||||
mDialogFragment.setExtendedSettingsClickListener(listener);
|
final boolean isRemote = !route.isDefault() && route.matchesTypes(mRouteTypes);
|
||||||
|
final boolean isConnecting = isRemote && route.isConnecting();
|
||||||
|
|
||||||
|
boolean needsRefresh = false;
|
||||||
|
if (mRemoteActive != isRemote) {
|
||||||
|
mRemoteActive = isRemote;
|
||||||
|
needsRefresh = true;
|
||||||
|
}
|
||||||
|
if (mIsConnecting != isConnecting) {
|
||||||
|
mIsConnecting = isConnecting;
|
||||||
|
needsRefresh = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsRefresh) {
|
||||||
|
refreshDrawableState();
|
||||||
|
}
|
||||||
|
|
||||||
|
setEnabled(mRouter.isRouteAvailable(mRouteTypes,
|
||||||
|
MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private final class MediaRouterCallback extends MediaRouter.SimpleCallback {
|
||||||
* Asynchronously show the route chooser dialog.
|
|
||||||
* This will attach a {@link DialogFragment} to the containing Activity.
|
|
||||||
*/
|
|
||||||
public void showDialog() {
|
|
||||||
final FragmentManager fm = getActivity().getFragmentManager();
|
|
||||||
if (mDialogFragment == null) {
|
|
||||||
// See if one is already attached to this activity.
|
|
||||||
mDialogFragment = (MediaRouteChooserDialogFragment) fm.findFragmentByTag(
|
|
||||||
MediaRouteChooserDialogFragment.FRAGMENT_TAG);
|
|
||||||
}
|
|
||||||
if (mDialogFragment != null) {
|
|
||||||
Log.w(TAG, "showDialog(): Already showing!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mDialogFragment = new MediaRouteChooserDialogFragment();
|
|
||||||
mDialogFragment.setExtendedSettingsClickListener(mExtendedSettingsClickListener);
|
|
||||||
mDialogFragment.setLauncherListener(new MediaRouteChooserDialogFragment.LauncherListener() {
|
|
||||||
@Override
|
|
||||||
public void onDetached(MediaRouteChooserDialogFragment detachedFragment) {
|
|
||||||
mDialogFragment = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mDialogFragment.setRouteTypes(mRouteTypes);
|
|
||||||
mDialogFragment.show(fm, MediaRouteChooserDialogFragment.FRAGMENT_TAG);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Activity getActivity() {
|
|
||||||
// Gross way of unwrapping the Activity so we can get the FragmentManager
|
|
||||||
Context context = getContext();
|
|
||||||
while (context instanceof ContextWrapper && !(context instanceof Activity)) {
|
|
||||||
context = ((ContextWrapper) context).getBaseContext();
|
|
||||||
}
|
|
||||||
if (!(context instanceof Activity)) {
|
|
||||||
throw new IllegalStateException("The MediaRouteButton's Context is not an Activity.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return (Activity) context;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MediaRouteCallback extends MediaRouter.SimpleCallback {
|
|
||||||
@Override
|
|
||||||
public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
|
|
||||||
updateRemoteIndicator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
|
|
||||||
updateRemoteIndicator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRouteChanged(MediaRouter router, RouteInfo info) {
|
|
||||||
updateRemoteIndicator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRouteAdded(MediaRouter router, RouteInfo info) {
|
public void onRouteAdded(MediaRouter router, RouteInfo info) {
|
||||||
updateRouteCount();
|
refreshRoute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRouteRemoved(MediaRouter router, RouteInfo info) {
|
public void onRouteRemoved(MediaRouter router, RouteInfo info) {
|
||||||
updateRouteCount();
|
refreshRoute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRouteChanged(MediaRouter router, RouteInfo info) {
|
||||||
|
refreshRoute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
|
||||||
|
refreshRoute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
|
||||||
|
refreshRoute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
|
public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
|
||||||
int index) {
|
int index) {
|
||||||
updateRouteCount();
|
refreshRoute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
|
public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
|
||||||
updateRouteCount();
|
refreshRoute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
272
core/java/com/android/internal/app/MediaRouteChooserDialog.java
Normal file
272
core/java/com/android/internal/app/MediaRouteChooserDialog.java
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.internal.app;
|
||||||
|
|
||||||
|
import com.android.internal.R;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.media.MediaRouter;
|
||||||
|
import android.media.MediaRouter.RouteInfo;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class implements the route chooser dialog for {@link MediaRouter}.
|
||||||
|
* <p>
|
||||||
|
* This dialog allows the user to choose a route that matches a given selector.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @see MediaRouteButton
|
||||||
|
* @see MediaRouteActionProvider
|
||||||
|
*
|
||||||
|
* TODO: Move this back into the API, as in the support library media router.
|
||||||
|
*/
|
||||||
|
public class MediaRouteChooserDialog extends Dialog {
|
||||||
|
private final MediaRouter mRouter;
|
||||||
|
private final MediaRouterCallback mCallback;
|
||||||
|
|
||||||
|
private int mRouteTypes;
|
||||||
|
private View.OnClickListener mExtendedSettingsClickListener;
|
||||||
|
private RouteAdapter mAdapter;
|
||||||
|
private ListView mListView;
|
||||||
|
private Button mExtendedSettingsButton;
|
||||||
|
private boolean mAttachedToWindow;
|
||||||
|
|
||||||
|
public MediaRouteChooserDialog(Context context, int theme) {
|
||||||
|
super(context, theme);
|
||||||
|
|
||||||
|
mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
|
||||||
|
mCallback = new MediaRouterCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the media route types for filtering the routes that the user can
|
||||||
|
* select using the media route chooser dialog.
|
||||||
|
*
|
||||||
|
* @return The route types.
|
||||||
|
*/
|
||||||
|
public int getRouteTypes() {
|
||||||
|
return mRouteTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the types of routes that will be shown in the media route chooser dialog
|
||||||
|
* launched by this button.
|
||||||
|
*
|
||||||
|
* @param types The route types to match.
|
||||||
|
*/
|
||||||
|
public void setRouteTypes(int types) {
|
||||||
|
if (mRouteTypes != types) {
|
||||||
|
mRouteTypes = types;
|
||||||
|
|
||||||
|
if (mAttachedToWindow) {
|
||||||
|
mRouter.removeCallback(mCallback);
|
||||||
|
mRouter.addCallback(types, mCallback,
|
||||||
|
MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshRoutes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExtendedSettingsClickListener(View.OnClickListener listener) {
|
||||||
|
if (listener != mExtendedSettingsClickListener) {
|
||||||
|
mExtendedSettingsClickListener = listener;
|
||||||
|
updateExtendedSettingsButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the route should be included in the list.
|
||||||
|
* <p>
|
||||||
|
* The default implementation returns true for non-default routes that
|
||||||
|
* match the selector. Subclasses can override this method to filter routes
|
||||||
|
* differently.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param route The route to consider, never null.
|
||||||
|
* @return True if the route should be included in the chooser dialog.
|
||||||
|
*/
|
||||||
|
public boolean onFilterRoute(MediaRouter.RouteInfo route) {
|
||||||
|
return !route.isDefault() && route.matchesTypes(mRouteTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
getWindow().requestFeature(Window.FEATURE_LEFT_ICON);
|
||||||
|
|
||||||
|
setContentView(R.layout.media_route_chooser_dialog);
|
||||||
|
setTitle(R.string.media_route_chooser_title);
|
||||||
|
|
||||||
|
// Must be called after setContentView.
|
||||||
|
getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
|
||||||
|
R.drawable.ic_media_route_off_holo_dark);
|
||||||
|
|
||||||
|
mAdapter = new RouteAdapter(getContext());
|
||||||
|
mListView = (ListView)findViewById(R.id.media_route_list);
|
||||||
|
mListView.setAdapter(mAdapter);
|
||||||
|
mListView.setOnItemClickListener(mAdapter);
|
||||||
|
mListView.setEmptyView(findViewById(android.R.id.empty));
|
||||||
|
|
||||||
|
mExtendedSettingsButton = (Button)findViewById(R.id.media_route_extended_settings_button);
|
||||||
|
updateExtendedSettingsButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateExtendedSettingsButton() {
|
||||||
|
if (mExtendedSettingsButton != null) {
|
||||||
|
mExtendedSettingsButton.setOnClickListener(mExtendedSettingsClickListener);
|
||||||
|
mExtendedSettingsButton.setVisibility(
|
||||||
|
mExtendedSettingsClickListener != null ? View.VISIBLE : View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttachedToWindow() {
|
||||||
|
super.onAttachedToWindow();
|
||||||
|
|
||||||
|
mAttachedToWindow = true;
|
||||||
|
mRouter.addCallback(mRouteTypes, mCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
|
||||||
|
refreshRoutes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetachedFromWindow() {
|
||||||
|
mAttachedToWindow = false;
|
||||||
|
mRouter.removeCallback(mCallback);
|
||||||
|
|
||||||
|
super.onDetachedFromWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the list of routes that are shown in the chooser dialog.
|
||||||
|
*/
|
||||||
|
public void refreshRoutes() {
|
||||||
|
if (mAttachedToWindow) {
|
||||||
|
mAdapter.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class RouteAdapter extends ArrayAdapter<MediaRouter.RouteInfo>
|
||||||
|
implements ListView.OnItemClickListener {
|
||||||
|
private final LayoutInflater mInflater;
|
||||||
|
|
||||||
|
public RouteAdapter(Context context) {
|
||||||
|
super(context, 0);
|
||||||
|
mInflater = LayoutInflater.from(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
clear();
|
||||||
|
final int count = mRouter.getRouteCount();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
MediaRouter.RouteInfo route = mRouter.getRouteAt(i);
|
||||||
|
if (onFilterRoute(route)) {
|
||||||
|
add(route);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort(RouteComparator.sInstance);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areAllItemsEnabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled(int position) {
|
||||||
|
return getItem(position).isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
|
View view = convertView;
|
||||||
|
if (view == null) {
|
||||||
|
view = mInflater.inflate(R.layout.media_route_list_item, parent, false);
|
||||||
|
}
|
||||||
|
MediaRouter.RouteInfo route = getItem(position);
|
||||||
|
TextView text1 = (TextView)view.findViewById(android.R.id.text1);
|
||||||
|
TextView text2 = (TextView)view.findViewById(android.R.id.text2);
|
||||||
|
text1.setText(route.getName());
|
||||||
|
CharSequence description = route.getDescription();
|
||||||
|
if (TextUtils.isEmpty(description)) {
|
||||||
|
text2.setVisibility(View.GONE);
|
||||||
|
text2.setText("");
|
||||||
|
} else {
|
||||||
|
text2.setVisibility(View.VISIBLE);
|
||||||
|
text2.setText(description);
|
||||||
|
}
|
||||||
|
view.setEnabled(route.isEnabled());
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
MediaRouter.RouteInfo route = getItem(position);
|
||||||
|
if (route.isEnabled()) {
|
||||||
|
route.select();
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class MediaRouterCallback extends MediaRouter.SimpleCallback {
|
||||||
|
@Override
|
||||||
|
public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) {
|
||||||
|
refreshRoutes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) {
|
||||||
|
refreshRoutes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) {
|
||||||
|
refreshRoutes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class RouteComparator implements Comparator<MediaRouter.RouteInfo> {
|
||||||
|
public static final RouteComparator sInstance = new RouteComparator();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(MediaRouter.RouteInfo lhs, MediaRouter.RouteInfo rhs) {
|
||||||
|
return lhs.getName().toString().compareTo(rhs.getName().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012 The Android Open Source Project
|
* Copyright (C) 2013 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -16,675 +16,86 @@
|
|||||||
|
|
||||||
package com.android.internal.app;
|
package com.android.internal.app;
|
||||||
|
|
||||||
import com.android.internal.R;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.app.DialogFragment;
|
import android.app.DialogFragment;
|
||||||
import android.app.MediaRouteActionProvider;
|
|
||||||
import android.app.MediaRouteButton;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.hardware.display.DisplayManager;
|
|
||||||
import android.media.MediaRouter;
|
|
||||||
import android.media.MediaRouter.RouteCategory;
|
|
||||||
import android.media.MediaRouter.RouteGroup;
|
|
||||||
import android.media.MediaRouter.RouteInfo;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.view.KeyEvent;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.View.OnClickListener;
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.BaseAdapter;
|
|
||||||
import android.widget.CheckBox;
|
|
||||||
import android.widget.Checkable;
|
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.ListView;
|
|
||||||
import android.widget.SeekBar;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class implements the route chooser dialog for {@link MediaRouter}.
|
* Media route chooser dialog fragment.
|
||||||
|
* <p>
|
||||||
|
* Creates a {@link MediaRouteChooserDialog}. The application may subclass
|
||||||
|
* this dialog fragment to customize the media route chooser dialog.
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* @see MediaRouteButton
|
* TODO: Move this back into the API, as in the support library media router.
|
||||||
* @see MediaRouteActionProvider
|
|
||||||
*/
|
*/
|
||||||
public class MediaRouteChooserDialogFragment extends DialogFragment {
|
public class MediaRouteChooserDialogFragment extends DialogFragment {
|
||||||
private static final String TAG = "MediaRouteChooserDialogFragment";
|
private final String ARGUMENT_ROUTE_TYPES = "routeTypes";
|
||||||
public static final String FRAGMENT_TAG = "android:MediaRouteChooserDialogFragment";
|
|
||||||
|
|
||||||
private static final int[] ITEM_LAYOUTS = new int[] {
|
private View.OnClickListener mExtendedSettingsClickListener;
|
||||||
R.layout.media_route_list_item_top_header,
|
|
||||||
R.layout.media_route_list_item_section_header,
|
|
||||||
R.layout.media_route_list_item,
|
|
||||||
R.layout.media_route_list_item_checkable,
|
|
||||||
R.layout.media_route_list_item_collapse_group
|
|
||||||
};
|
|
||||||
|
|
||||||
MediaRouter mRouter;
|
|
||||||
private int mRouteTypes;
|
|
||||||
|
|
||||||
private LayoutInflater mInflater;
|
|
||||||
private LauncherListener mLauncherListener;
|
|
||||||
private View.OnClickListener mExtendedSettingsListener;
|
|
||||||
private RouteAdapter mAdapter;
|
|
||||||
private ListView mListView;
|
|
||||||
private SeekBar mVolumeSlider;
|
|
||||||
private ImageView mVolumeIcon;
|
|
||||||
|
|
||||||
final RouteComparator mComparator = new RouteComparator();
|
|
||||||
final MediaRouterCallback mCallback = new MediaRouterCallback();
|
|
||||||
private boolean mIgnoreSliderVolumeChanges;
|
|
||||||
private boolean mIgnoreCallbackVolumeChanges;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a media route chooser dialog fragment.
|
||||||
|
* <p>
|
||||||
|
* All subclasses of this class must also possess a default constructor.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
public MediaRouteChooserDialogFragment() {
|
public MediaRouteChooserDialogFragment() {
|
||||||
setStyle(STYLE_NO_TITLE, R.style.Theme_DeviceDefault_Dialog);
|
setCancelable(true);
|
||||||
|
setStyle(STYLE_NORMAL, android.R.style.Theme_DeviceDefault_Dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLauncherListener(LauncherListener listener) {
|
public int getRouteTypes() {
|
||||||
mLauncherListener = listener;
|
Bundle args = getArguments();
|
||||||
}
|
return args != null ? args.getInt(ARGUMENT_ROUTE_TYPES) : 0;
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAttach(Activity activity) {
|
|
||||||
super.onAttach(activity);
|
|
||||||
mRouter = (MediaRouter) activity.getSystemService(Context.MEDIA_ROUTER_SERVICE);
|
|
||||||
mRouter.addCallback(mRouteTypes, mCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDetach() {
|
|
||||||
super.onDetach();
|
|
||||||
if (mLauncherListener != null) {
|
|
||||||
mLauncherListener.onDetached(this);
|
|
||||||
}
|
|
||||||
if (mAdapter != null) {
|
|
||||||
mAdapter = null;
|
|
||||||
}
|
|
||||||
mInflater = null;
|
|
||||||
mRouter.removeCallback(mCallback);
|
|
||||||
mRouter = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExtendedSettingsClickListener(View.OnClickListener listener) {
|
|
||||||
mExtendedSettingsListener = listener;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRouteTypes(int types) {
|
public void setRouteTypes(int types) {
|
||||||
mRouteTypes = types;
|
if (types != getRouteTypes()) {
|
||||||
|
Bundle args = getArguments();
|
||||||
|
if (args == null) {
|
||||||
|
args = new Bundle();
|
||||||
}
|
}
|
||||||
|
args.putInt(ARGUMENT_ROUTE_TYPES, types);
|
||||||
|
setArguments(args);
|
||||||
|
|
||||||
void updateVolume() {
|
MediaRouteChooserDialog dialog = (MediaRouteChooserDialog)getDialog();
|
||||||
if (mRouter == null) return;
|
if (dialog != null) {
|
||||||
|
dialog.setRouteTypes(types);
|
||||||
final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
|
|
||||||
mVolumeIcon.setImageResource(selectedRoute == null ||
|
|
||||||
selectedRoute.getPlaybackType() == RouteInfo.PLAYBACK_TYPE_LOCAL ?
|
|
||||||
R.drawable.ic_audio_vol : R.drawable.ic_media_route_on_holo_dark);
|
|
||||||
|
|
||||||
mIgnoreSliderVolumeChanges = true;
|
|
||||||
|
|
||||||
if (selectedRoute == null ||
|
|
||||||
selectedRoute.getVolumeHandling() == RouteInfo.PLAYBACK_VOLUME_FIXED) {
|
|
||||||
// Disable the slider and show it at max volume.
|
|
||||||
mVolumeSlider.setMax(1);
|
|
||||||
mVolumeSlider.setProgress(1);
|
|
||||||
mVolumeSlider.setEnabled(false);
|
|
||||||
} else {
|
|
||||||
mVolumeSlider.setEnabled(true);
|
|
||||||
mVolumeSlider.setMax(selectedRoute.getVolumeMax());
|
|
||||||
mVolumeSlider.setProgress(selectedRoute.getVolume());
|
|
||||||
}
|
|
||||||
|
|
||||||
mIgnoreSliderVolumeChanges = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void changeVolume(int newValue) {
|
|
||||||
if (mIgnoreSliderVolumeChanges) return;
|
|
||||||
|
|
||||||
final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
|
|
||||||
if (selectedRoute != null &&
|
|
||||||
selectedRoute.getVolumeHandling() == RouteInfo.PLAYBACK_VOLUME_VARIABLE) {
|
|
||||||
final int maxVolume = selectedRoute.getVolumeMax();
|
|
||||||
newValue = Math.max(0, Math.min(newValue, maxVolume));
|
|
||||||
selectedRoute.requestSetVolume(newValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
|
||||||
Bundle savedInstanceState) {
|
|
||||||
mInflater = inflater;
|
|
||||||
final View layout = inflater.inflate(R.layout.media_route_chooser_layout, container, false);
|
|
||||||
|
|
||||||
mVolumeIcon = (ImageView) layout.findViewById(R.id.volume_icon);
|
|
||||||
mVolumeSlider = (SeekBar) layout.findViewById(R.id.volume_slider);
|
|
||||||
updateVolume();
|
|
||||||
mVolumeSlider.setOnSeekBarChangeListener(new VolumeSliderChangeListener());
|
|
||||||
|
|
||||||
if (mExtendedSettingsListener != null) {
|
|
||||||
final View extendedSettingsButton = layout.findViewById(R.id.extended_settings);
|
|
||||||
extendedSettingsButton.setVisibility(View.VISIBLE);
|
|
||||||
extendedSettingsButton.setOnClickListener(mExtendedSettingsListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
final ListView list = (ListView) layout.findViewById(R.id.list);
|
|
||||||
list.setItemsCanFocus(true);
|
|
||||||
list.setAdapter(mAdapter = new RouteAdapter());
|
|
||||||
list.setOnItemClickListener(mAdapter);
|
|
||||||
|
|
||||||
mListView = list;
|
|
||||||
|
|
||||||
mAdapter.scrollToSelectedItem();
|
|
||||||
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
||||||
return new RouteChooserDialog(getActivity(), getTheme());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ViewHolder {
|
|
||||||
public TextView text1;
|
|
||||||
public TextView text2;
|
|
||||||
public ImageView icon;
|
|
||||||
public ImageButton expandGroupButton;
|
|
||||||
public RouteAdapter.ExpandGroupListener expandGroupListener;
|
|
||||||
public int position;
|
|
||||||
public CheckBox check;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class RouteAdapter extends BaseAdapter implements ListView.OnItemClickListener {
|
|
||||||
private static final int VIEW_TOP_HEADER = 0;
|
|
||||||
private static final int VIEW_SECTION_HEADER = 1;
|
|
||||||
private static final int VIEW_ROUTE = 2;
|
|
||||||
private static final int VIEW_GROUPING_ROUTE = 3;
|
|
||||||
private static final int VIEW_GROUPING_DONE = 4;
|
|
||||||
|
|
||||||
private int mSelectedItemPosition = -1;
|
|
||||||
private final ArrayList<Object> mItems = new ArrayList<Object>();
|
|
||||||
|
|
||||||
private RouteCategory mCategoryEditingGroups;
|
|
||||||
private RouteGroup mEditingGroup;
|
|
||||||
|
|
||||||
// Temporary lists for manipulation
|
|
||||||
private final ArrayList<RouteInfo> mCatRouteList = new ArrayList<RouteInfo>();
|
|
||||||
private final ArrayList<RouteInfo> mSortRouteList = new ArrayList<RouteInfo>();
|
|
||||||
|
|
||||||
private boolean mIgnoreUpdates;
|
|
||||||
|
|
||||||
RouteAdapter() {
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void update() {
|
|
||||||
/*
|
|
||||||
* This is kind of wacky, but our data sets are going to be
|
|
||||||
* fairly small on average. Ideally we should be able to do some of this stuff
|
|
||||||
* in-place instead.
|
|
||||||
*
|
|
||||||
* Basic idea: each entry in mItems represents an item in the list for quick access.
|
|
||||||
* Entries can be a RouteCategory (section header), a RouteInfo with a category of
|
|
||||||
* mCategoryEditingGroups (a flattened RouteInfo pulled out of its group, allowing
|
|
||||||
* the user to change the group),
|
|
||||||
*/
|
|
||||||
if (mIgnoreUpdates) return;
|
|
||||||
|
|
||||||
mItems.clear();
|
|
||||||
|
|
||||||
final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
|
|
||||||
mSelectedItemPosition = -1;
|
|
||||||
|
|
||||||
List<RouteInfo> routes;
|
|
||||||
final int catCount = mRouter.getCategoryCount();
|
|
||||||
for (int i = 0; i < catCount; i++) {
|
|
||||||
final RouteCategory cat = mRouter.getCategoryAt(i);
|
|
||||||
routes = cat.getRoutes(mCatRouteList);
|
|
||||||
|
|
||||||
if (!cat.isSystem()) {
|
|
||||||
mItems.add(cat);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cat == mCategoryEditingGroups) {
|
|
||||||
addGroupEditingCategoryRoutes(routes);
|
|
||||||
} else {
|
|
||||||
addSelectableRoutes(selectedRoute, routes);
|
|
||||||
}
|
|
||||||
|
|
||||||
routes.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyDataSetChanged();
|
|
||||||
if (mListView != null && mSelectedItemPosition >= 0) {
|
|
||||||
mListView.setItemChecked(mSelectedItemPosition, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void scrollToEditingGroup() {
|
|
||||||
if (mCategoryEditingGroups == null || mListView == null) return;
|
|
||||||
|
|
||||||
int pos = 0;
|
|
||||||
int bound = 0;
|
|
||||||
final int itemCount = mItems.size();
|
|
||||||
for (int i = 0; i < itemCount; i++) {
|
|
||||||
final Object item = mItems.get(i);
|
|
||||||
if (item != null && item == mCategoryEditingGroups) {
|
|
||||||
bound = i;
|
|
||||||
}
|
|
||||||
if (item == null) {
|
|
||||||
pos = i;
|
|
||||||
break; // this is always below the category header; we can stop here.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mListView.smoothScrollToPosition(pos, bound);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scrollToSelectedItem() {
|
|
||||||
if (mListView == null || mSelectedItemPosition < 0) return;
|
|
||||||
|
|
||||||
mListView.smoothScrollToPosition(mSelectedItemPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addSelectableRoutes(RouteInfo selectedRoute, List<RouteInfo> from) {
|
|
||||||
final int routeCount = from.size();
|
|
||||||
for (int j = 0; j < routeCount; j++) {
|
|
||||||
final RouteInfo info = from.get(j);
|
|
||||||
if (info == selectedRoute) {
|
|
||||||
mSelectedItemPosition = mItems.size();
|
|
||||||
}
|
|
||||||
mItems.add(info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void addGroupEditingCategoryRoutes(List<RouteInfo> from) {
|
|
||||||
// Unpack groups and flatten for presentation
|
|
||||||
// mSortRouteList will always be empty here.
|
|
||||||
final int topCount = from.size();
|
|
||||||
for (int i = 0; i < topCount; i++) {
|
|
||||||
final RouteInfo route = from.get(i);
|
|
||||||
final RouteGroup group = route.getGroup();
|
|
||||||
if (group == route) {
|
|
||||||
// This is a group, unpack it.
|
|
||||||
final int groupCount = group.getRouteCount();
|
|
||||||
for (int j = 0; j < groupCount; j++) {
|
|
||||||
final RouteInfo innerRoute = group.getRouteAt(j);
|
|
||||||
mSortRouteList.add(innerRoute);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mSortRouteList.add(route);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Sort by name. This will keep the route positions relatively stable even though they
|
|
||||||
// will be repeatedly added and removed.
|
|
||||||
Collections.sort(mSortRouteList, mComparator);
|
|
||||||
|
|
||||||
mItems.addAll(mSortRouteList);
|
|
||||||
mSortRouteList.clear();
|
|
||||||
|
|
||||||
mItems.add(null); // Sentinel reserving space for the "done" button.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCount() {
|
|
||||||
return mItems.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getViewTypeCount() {
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemViewType(int position) {
|
|
||||||
final Object item = getItem(position);
|
|
||||||
if (item instanceof RouteCategory) {
|
|
||||||
return position == 0 ? VIEW_TOP_HEADER : VIEW_SECTION_HEADER;
|
|
||||||
} else if (item == null) {
|
|
||||||
return VIEW_GROUPING_DONE;
|
|
||||||
} else {
|
|
||||||
final RouteInfo info = (RouteInfo) item;
|
|
||||||
if (info.getCategory() == mCategoryEditingGroups) {
|
|
||||||
return VIEW_GROUPING_ROUTE;
|
|
||||||
}
|
|
||||||
return VIEW_ROUTE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean areAllItemsEnabled() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled(int position) {
|
|
||||||
switch (getItemViewType(position)) {
|
|
||||||
case VIEW_ROUTE:
|
|
||||||
return ((RouteInfo) mItems.get(position)).isEnabled();
|
|
||||||
case VIEW_GROUPING_ROUTE:
|
|
||||||
case VIEW_GROUPING_DONE:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getItem(int position) {
|
|
||||||
return mItems.get(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getItemId(int position) {
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
|
||||||
final int viewType = getItemViewType(position);
|
|
||||||
|
|
||||||
ViewHolder holder;
|
|
||||||
if (convertView == null) {
|
|
||||||
convertView = mInflater.inflate(ITEM_LAYOUTS[viewType], parent, false);
|
|
||||||
holder = new ViewHolder();
|
|
||||||
holder.position = position;
|
|
||||||
holder.text1 = (TextView) convertView.findViewById(R.id.text1);
|
|
||||||
holder.text2 = (TextView) convertView.findViewById(R.id.text2);
|
|
||||||
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
|
|
||||||
holder.check = (CheckBox) convertView.findViewById(R.id.check);
|
|
||||||
holder.expandGroupButton = (ImageButton) convertView.findViewById(
|
|
||||||
R.id.expand_button);
|
|
||||||
if (holder.expandGroupButton != null) {
|
|
||||||
holder.expandGroupListener = new ExpandGroupListener();
|
|
||||||
holder.expandGroupButton.setOnClickListener(holder.expandGroupListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
final View fview = convertView;
|
|
||||||
final ListView list = (ListView) parent;
|
|
||||||
final ViewHolder fholder = holder;
|
|
||||||
convertView.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override public void onClick(View v) {
|
|
||||||
list.performItemClick(fview, fholder.position, 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
convertView.setTag(holder);
|
|
||||||
} else {
|
|
||||||
holder = (ViewHolder) convertView.getTag();
|
|
||||||
holder.position = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (viewType) {
|
|
||||||
case VIEW_ROUTE:
|
|
||||||
case VIEW_GROUPING_ROUTE:
|
|
||||||
bindItemView(position, holder);
|
|
||||||
break;
|
|
||||||
case VIEW_SECTION_HEADER:
|
|
||||||
case VIEW_TOP_HEADER:
|
|
||||||
bindHeaderView(position, holder);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
convertView.setActivated(position == mSelectedItemPosition);
|
|
||||||
convertView.setEnabled(isEnabled(position));
|
|
||||||
|
|
||||||
return convertView;
|
|
||||||
}
|
|
||||||
|
|
||||||
void bindItemView(int position, ViewHolder holder) {
|
|
||||||
RouteInfo info = (RouteInfo) mItems.get(position);
|
|
||||||
holder.text1.setText(info.getName(getActivity()));
|
|
||||||
final CharSequence status = info.getStatus();
|
|
||||||
if (TextUtils.isEmpty(status)) {
|
|
||||||
holder.text2.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
holder.text2.setVisibility(View.VISIBLE);
|
|
||||||
holder.text2.setText(status);
|
|
||||||
}
|
|
||||||
Drawable icon = info.getIconDrawable();
|
|
||||||
if (icon != null) {
|
|
||||||
// Make sure we have a fresh drawable where it doesn't matter if we mutate it
|
|
||||||
icon = icon.getConstantState().newDrawable(getResources());
|
|
||||||
}
|
|
||||||
holder.icon.setImageDrawable(icon);
|
|
||||||
holder.icon.setVisibility(icon != null ? View.VISIBLE : View.GONE);
|
|
||||||
|
|
||||||
RouteCategory cat = info.getCategory();
|
|
||||||
boolean canGroup = false;
|
|
||||||
if (cat == mCategoryEditingGroups) {
|
|
||||||
RouteGroup group = info.getGroup();
|
|
||||||
holder.check.setEnabled(group.getRouteCount() > 1);
|
|
||||||
holder.check.setChecked(group == mEditingGroup);
|
|
||||||
} else {
|
|
||||||
if (cat.isGroupable()) {
|
|
||||||
final RouteGroup group = (RouteGroup) info;
|
|
||||||
canGroup = group.getRouteCount() > 1 ||
|
|
||||||
getItemViewType(position - 1) == VIEW_ROUTE ||
|
|
||||||
(position < getCount() - 1 &&
|
|
||||||
getItemViewType(position + 1) == VIEW_ROUTE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (holder.expandGroupButton != null) {
|
|
||||||
holder.expandGroupButton.setVisibility(canGroup ? View.VISIBLE : View.GONE);
|
|
||||||
holder.expandGroupListener.position = position;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void bindHeaderView(int position, ViewHolder holder) {
|
|
||||||
RouteCategory cat = (RouteCategory) mItems.get(position);
|
|
||||||
holder.text1.setText(cat.getName(getActivity()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
final int type = getItemViewType(position);
|
|
||||||
if (type == VIEW_SECTION_HEADER || type == VIEW_TOP_HEADER) {
|
|
||||||
return;
|
|
||||||
} else if (type == VIEW_GROUPING_DONE) {
|
|
||||||
finishGrouping();
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
final Object item = getItem(position);
|
|
||||||
if (!(item instanceof RouteInfo)) {
|
|
||||||
// Oops. Stale event running around? Skip it.
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final RouteInfo route = (RouteInfo) item;
|
public void setExtendedSettingsClickListener(View.OnClickListener listener) {
|
||||||
if (type == VIEW_ROUTE) {
|
if (listener != mExtendedSettingsClickListener) {
|
||||||
mRouter.selectRouteInt(mRouteTypes, route, true);
|
mExtendedSettingsClickListener = listener;
|
||||||
dismiss();
|
|
||||||
} else if (type == VIEW_GROUPING_ROUTE) {
|
|
||||||
final Checkable c = (Checkable) view;
|
|
||||||
final boolean wasChecked = c.isChecked();
|
|
||||||
|
|
||||||
mIgnoreUpdates = true;
|
|
||||||
RouteGroup oldGroup = route.getGroup();
|
|
||||||
if (!wasChecked && oldGroup != mEditingGroup) {
|
|
||||||
// Assumption: in a groupable category oldGroup will never be null.
|
|
||||||
if (mRouter.getSelectedRoute(mRouteTypes) == oldGroup) {
|
|
||||||
// Old group was selected but is now empty. Select the group
|
|
||||||
// we're manipulating since that's where the last route went.
|
|
||||||
mRouter.selectRouteInt(mRouteTypes, mEditingGroup, true);
|
|
||||||
}
|
|
||||||
oldGroup.removeRoute(route);
|
|
||||||
mEditingGroup.addRoute(route);
|
|
||||||
c.setChecked(true);
|
|
||||||
} else if (wasChecked && mEditingGroup.getRouteCount() > 1) {
|
|
||||||
mEditingGroup.removeRoute(route);
|
|
||||||
|
|
||||||
// In a groupable category this will add
|
|
||||||
// the route into its own new group.
|
|
||||||
mRouter.addRouteInt(route);
|
|
||||||
}
|
|
||||||
mIgnoreUpdates = false;
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isGrouping() {
|
|
||||||
return mCategoryEditingGroups != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
void finishGrouping() {
|
|
||||||
mCategoryEditingGroups = null;
|
|
||||||
mEditingGroup = null;
|
|
||||||
getDialog().setCanceledOnTouchOutside(true);
|
|
||||||
update();
|
|
||||||
scrollToSelectedItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
class ExpandGroupListener implements View.OnClickListener {
|
|
||||||
int position;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
// Assumption: this is only available for the user to click if we're presenting
|
|
||||||
// a groupable category, where every top-level route in the category is a group.
|
|
||||||
final RouteGroup group = (RouteGroup) getItem(position);
|
|
||||||
mEditingGroup = group;
|
|
||||||
mCategoryEditingGroups = group.getCategory();
|
|
||||||
getDialog().setCanceledOnTouchOutside(false);
|
|
||||||
mRouter.selectRouteInt(mRouteTypes, mEditingGroup, true);
|
|
||||||
update();
|
|
||||||
scrollToEditingGroup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MediaRouterCallback extends MediaRouter.Callback {
|
|
||||||
@Override
|
|
||||||
public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
|
|
||||||
mAdapter.update();
|
|
||||||
updateVolume();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
|
|
||||||
mAdapter.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRouteAdded(MediaRouter router, RouteInfo info) {
|
|
||||||
mAdapter.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRouteRemoved(MediaRouter router, RouteInfo info) {
|
|
||||||
if (info == mAdapter.mEditingGroup) {
|
|
||||||
mAdapter.finishGrouping();
|
|
||||||
}
|
|
||||||
mAdapter.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRouteChanged(MediaRouter router, RouteInfo info) {
|
|
||||||
mAdapter.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRouteGrouped(MediaRouter router, RouteInfo info,
|
|
||||||
RouteGroup group, int index) {
|
|
||||||
mAdapter.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
|
|
||||||
mAdapter.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRouteVolumeChanged(MediaRouter router, RouteInfo info) {
|
|
||||||
if (!mIgnoreCallbackVolumeChanges) {
|
|
||||||
updateVolume();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RouteComparator implements Comparator<RouteInfo> {
|
|
||||||
@Override
|
|
||||||
public int compare(RouteInfo lhs, RouteInfo rhs) {
|
|
||||||
return lhs.getName(getActivity()).toString()
|
|
||||||
.compareTo(rhs.getName(getActivity()).toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RouteChooserDialog extends Dialog {
|
|
||||||
public RouteChooserDialog(Context context, int theme) {
|
|
||||||
super(context, theme);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
if (mAdapter != null && mAdapter.isGrouping()) {
|
|
||||||
mAdapter.finishGrouping();
|
|
||||||
} else {
|
|
||||||
super.onBackPressed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
|
||||||
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN && mVolumeSlider.isEnabled()) {
|
|
||||||
final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
|
|
||||||
if (selectedRoute != null) {
|
|
||||||
selectedRoute.requestUpdateVolume(-1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mVolumeSlider.isEnabled()) {
|
|
||||||
final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
|
|
||||||
if (selectedRoute != null) {
|
|
||||||
mRouter.getSelectedRoute(mRouteTypes).requestUpdateVolume(1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.onKeyDown(keyCode, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
MediaRouteChooserDialog dialog = (MediaRouteChooserDialog)getDialog();
|
||||||
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN && mVolumeSlider.isEnabled()) {
|
if (dialog != null) {
|
||||||
return true;
|
dialog.setExtendedSettingsClickListener(listener);
|
||||||
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mVolumeSlider.isEnabled()) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return super.onKeyUp(keyCode, event);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implemented by the MediaRouteButton that launched this dialog
|
* Called when the chooser dialog is being created.
|
||||||
|
* <p>
|
||||||
|
* Subclasses may override this method to customize the dialog.
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
public interface LauncherListener {
|
public MediaRouteChooserDialog onCreateChooserDialog(
|
||||||
public void onDetached(MediaRouteChooserDialogFragment detachedFragment);
|
Context context, Bundle savedInstanceState) {
|
||||||
}
|
return new MediaRouteChooserDialog(context, getTheme());
|
||||||
|
|
||||||
class VolumeSliderChangeListener implements SeekBar.OnSeekBarChangeListener {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
|
||||||
changeVolume(progress);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
mIgnoreCallbackVolumeChanges = true;
|
MediaRouteChooserDialog dialog = onCreateChooserDialog(getActivity(), savedInstanceState);
|
||||||
}
|
dialog.setRouteTypes(getRouteTypes());
|
||||||
|
dialog.setExtendedSettingsClickListener(mExtendedSettingsClickListener);
|
||||||
@Override
|
return dialog;
|
||||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
|
||||||
mIgnoreCallbackVolumeChanges = false;
|
|
||||||
updateVolume();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,318 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.internal.app;
|
||||||
|
|
||||||
|
import com.android.internal.R;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.app.MediaRouteActionProvider;
|
||||||
|
import android.app.MediaRouteButton;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.media.MediaRouter;
|
||||||
|
import android.media.MediaRouter.RouteGroup;
|
||||||
|
import android.media.MediaRouter.RouteInfo;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.SeekBar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class implements the route controller dialog for {@link MediaRouter}.
|
||||||
|
* <p>
|
||||||
|
* This dialog allows the user to control or disconnect from the currently selected route.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @see MediaRouteButton
|
||||||
|
* @see MediaRouteActionProvider
|
||||||
|
*
|
||||||
|
* TODO: Move this back into the API, as in the support library media router.
|
||||||
|
*/
|
||||||
|
public class MediaRouteControllerDialog extends Dialog {
|
||||||
|
// Time to wait before updating the volume when the user lets go of the seek bar
|
||||||
|
// to allow the route provider time to propagate the change and publish a new
|
||||||
|
// route descriptor.
|
||||||
|
private static final int VOLUME_UPDATE_DELAY_MILLIS = 250;
|
||||||
|
|
||||||
|
private final MediaRouter mRouter;
|
||||||
|
private final MediaRouterCallback mCallback;
|
||||||
|
private final MediaRouter.RouteInfo mRoute;
|
||||||
|
|
||||||
|
private boolean mCreated;
|
||||||
|
private Drawable mMediaRouteConnectingDrawable;
|
||||||
|
private Drawable mMediaRouteOnDrawable;
|
||||||
|
private Drawable mCurrentIconDrawable;
|
||||||
|
|
||||||
|
private boolean mVolumeControlEnabled = true;
|
||||||
|
private LinearLayout mVolumeLayout;
|
||||||
|
private SeekBar mVolumeSlider;
|
||||||
|
private boolean mVolumeSliderTouched;
|
||||||
|
|
||||||
|
private View mControlView;
|
||||||
|
|
||||||
|
private Button mDisconnectButton;
|
||||||
|
|
||||||
|
public MediaRouteControllerDialog(Context context, int theme) {
|
||||||
|
super(context, theme);
|
||||||
|
|
||||||
|
mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
|
||||||
|
mCallback = new MediaRouterCallback();
|
||||||
|
mRoute = mRouter.getSelectedRoute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the route that this dialog is controlling.
|
||||||
|
*/
|
||||||
|
public MediaRouter.RouteInfo getRoute() {
|
||||||
|
return mRoute;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the subclass an opportunity to create a view that will
|
||||||
|
* be included within the body of the dialog to offer additional media controls
|
||||||
|
* for the currently playing content.
|
||||||
|
*
|
||||||
|
* @param savedInstanceState The dialog's saved instance state.
|
||||||
|
* @return The media control view, or null if none.
|
||||||
|
*/
|
||||||
|
public View onCreateMediaControlView(Bundle savedInstanceState) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the media control view that was created by {@link #onCreateMediaControlView(Bundle)}.
|
||||||
|
*
|
||||||
|
* @return The media control view, or null if none.
|
||||||
|
*/
|
||||||
|
public View getMediaControlView() {
|
||||||
|
return mControlView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether to enable the volume slider and volume control using the volume keys
|
||||||
|
* when the route supports it.
|
||||||
|
* <p>
|
||||||
|
* The default value is true.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public void setVolumeControlEnabled(boolean enable) {
|
||||||
|
if (mVolumeControlEnabled != enable) {
|
||||||
|
mVolumeControlEnabled = enable;
|
||||||
|
if (mCreated) {
|
||||||
|
updateVolume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether to enable the volume slider and volume control using the volume keys
|
||||||
|
* when the route supports it.
|
||||||
|
*/
|
||||||
|
public boolean isVolumeControlEnabled() {
|
||||||
|
return mVolumeControlEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
getWindow().requestFeature(Window.FEATURE_LEFT_ICON);
|
||||||
|
|
||||||
|
setContentView(R.layout.media_route_controller_dialog);
|
||||||
|
|
||||||
|
mVolumeLayout = (LinearLayout)findViewById(R.id.media_route_volume_layout);
|
||||||
|
mVolumeSlider = (SeekBar)findViewById(R.id.media_route_volume_slider);
|
||||||
|
mVolumeSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||||
|
private final Runnable mStopTrackingTouch = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (mVolumeSliderTouched) {
|
||||||
|
mVolumeSliderTouched = false;
|
||||||
|
updateVolume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||||
|
if (mVolumeSliderTouched) {
|
||||||
|
mVolumeSlider.removeCallbacks(mStopTrackingTouch);
|
||||||
|
} else {
|
||||||
|
mVolumeSliderTouched = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||||
|
// Defer resetting mVolumeSliderTouched to allow the media route provider
|
||||||
|
// a little time to settle into its new state and publish the final
|
||||||
|
// volume update.
|
||||||
|
mVolumeSlider.postDelayed(mStopTrackingTouch, VOLUME_UPDATE_DELAY_MILLIS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||||
|
if (fromUser) {
|
||||||
|
mRoute.requestSetVolume(progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mDisconnectButton = (Button)findViewById(R.id.media_route_disconnect_button);
|
||||||
|
mDisconnectButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (mRoute.isSelected()) {
|
||||||
|
mRouter.getDefaultRoute().select();
|
||||||
|
}
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mCreated = true;
|
||||||
|
if (update()) {
|
||||||
|
mControlView = onCreateMediaControlView(savedInstanceState);
|
||||||
|
FrameLayout controlFrame =
|
||||||
|
(FrameLayout)findViewById(R.id.media_route_control_frame);
|
||||||
|
if (mControlView != null) {
|
||||||
|
controlFrame.addView(mControlView);
|
||||||
|
controlFrame.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
controlFrame.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttachedToWindow() {
|
||||||
|
super.onAttachedToWindow();
|
||||||
|
|
||||||
|
mRouter.addCallback(0, mCallback, MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetachedFromWindow() {
|
||||||
|
mRouter.removeCallback(mCallback);
|
||||||
|
|
||||||
|
super.onDetachedFromWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
|
||||||
|
|| keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
|
||||||
|
mRoute.requestUpdateVolume(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ? -1 : 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onKeyDown(keyCode, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
|
||||||
|
|| keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onKeyUp(keyCode, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean update() {
|
||||||
|
if (!mRoute.isSelected() || mRoute.isDefault()) {
|
||||||
|
dismiss();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTitle(mRoute.getName());
|
||||||
|
updateVolume();
|
||||||
|
|
||||||
|
Drawable icon = getIconDrawable();
|
||||||
|
if (icon != mCurrentIconDrawable) {
|
||||||
|
mCurrentIconDrawable = icon;
|
||||||
|
getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, icon);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Drawable getIconDrawable() {
|
||||||
|
if (mRoute.isConnecting()) {
|
||||||
|
if (mMediaRouteConnectingDrawable == null) {
|
||||||
|
mMediaRouteConnectingDrawable = getContext().getResources().getDrawable(
|
||||||
|
R.drawable.ic_media_route_connecting_holo_dark);
|
||||||
|
}
|
||||||
|
return mMediaRouteConnectingDrawable;
|
||||||
|
} else {
|
||||||
|
if (mMediaRouteOnDrawable == null) {
|
||||||
|
mMediaRouteOnDrawable = getContext().getResources().getDrawable(
|
||||||
|
R.drawable.ic_media_route_on_holo_dark);
|
||||||
|
}
|
||||||
|
return mMediaRouteOnDrawable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateVolume() {
|
||||||
|
if (!mVolumeSliderTouched) {
|
||||||
|
if (isVolumeControlAvailable()) {
|
||||||
|
mVolumeLayout.setVisibility(View.VISIBLE);
|
||||||
|
mVolumeSlider.setMax(mRoute.getVolumeMax());
|
||||||
|
mVolumeSlider.setProgress(mRoute.getVolume());
|
||||||
|
} else {
|
||||||
|
mVolumeLayout.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isVolumeControlAvailable() {
|
||||||
|
return mVolumeControlEnabled && mRoute.getVolumeHandling() ==
|
||||||
|
MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class MediaRouterCallback extends MediaRouter.SimpleCallback {
|
||||||
|
@Override
|
||||||
|
public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRouteVolumeChanged(MediaRouter router, MediaRouter.RouteInfo route) {
|
||||||
|
if (route == mRoute) {
|
||||||
|
updateVolume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
|
||||||
|
int index) {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.internal.app;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.app.DialogFragment;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Media route controller dialog fragment.
|
||||||
|
* <p>
|
||||||
|
* Creates a {@link MediaRouteControllerDialog}. The application may subclass
|
||||||
|
* this dialog fragment to customize the media route controller dialog.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* TODO: Move this back into the API, as in the support library media router.
|
||||||
|
*/
|
||||||
|
public class MediaRouteControllerDialogFragment extends DialogFragment {
|
||||||
|
/**
|
||||||
|
* Creates a media route controller dialog fragment.
|
||||||
|
* <p>
|
||||||
|
* All subclasses of this class must also possess a default constructor.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public MediaRouteControllerDialogFragment() {
|
||||||
|
setCancelable(true);
|
||||||
|
setStyle(STYLE_NORMAL, android.R.style.Theme_DeviceDefault_Dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the controller dialog is being created.
|
||||||
|
* <p>
|
||||||
|
* Subclasses may override this method to customize the dialog.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public MediaRouteControllerDialog onCreateControllerDialog(
|
||||||
|
Context context, Bundle savedInstanceState) {
|
||||||
|
return new MediaRouteControllerDialog(context, getTheme());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
return onCreateControllerDialog(getActivity(), savedInstanceState);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.internal.app;
|
||||||
|
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.app.DialogFragment;
|
||||||
|
import android.app.FragmentManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.media.MediaRouter;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows media route dialog as appropriate.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public abstract class MediaRouteDialogPresenter {
|
||||||
|
private static final String TAG = "MediaRouter";
|
||||||
|
|
||||||
|
private static final String CHOOSER_FRAGMENT_TAG =
|
||||||
|
"android.app.MediaRouteButton:MediaRouteChooserDialogFragment";
|
||||||
|
private static final String CONTROLLER_FRAGMENT_TAG =
|
||||||
|
"android.app.MediaRouteButton:MediaRouteControllerDialogFragment";
|
||||||
|
|
||||||
|
public static DialogFragment showDialogFragment(Activity activity,
|
||||||
|
int routeTypes, View.OnClickListener extendedSettingsClickListener) {
|
||||||
|
final MediaRouter router = (MediaRouter)activity.getSystemService(
|
||||||
|
Context.MEDIA_ROUTER_SERVICE);
|
||||||
|
final FragmentManager fm = activity.getFragmentManager();
|
||||||
|
|
||||||
|
MediaRouter.RouteInfo route = router.getSelectedRoute();
|
||||||
|
if (route.isDefault() || !route.matchesTypes(routeTypes)) {
|
||||||
|
if (fm.findFragmentByTag(CHOOSER_FRAGMENT_TAG) != null) {
|
||||||
|
Log.w(TAG, "showDialog(): Route chooser dialog already showing!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
MediaRouteChooserDialogFragment f = new MediaRouteChooserDialogFragment();
|
||||||
|
f.setRouteTypes(routeTypes);
|
||||||
|
f.setExtendedSettingsClickListener(extendedSettingsClickListener);
|
||||||
|
f.show(fm, CHOOSER_FRAGMENT_TAG);
|
||||||
|
return f;
|
||||||
|
} else {
|
||||||
|
if (fm.findFragmentByTag(CONTROLLER_FRAGMENT_TAG) != null) {
|
||||||
|
Log.w(TAG, "showDialog(): Route controller dialog already showing!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
MediaRouteControllerDialogFragment f = new MediaRouteControllerDialogFragment();
|
||||||
|
f.show(fm, CONTROLLER_FRAGMENT_TAG);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dialog createDialog(Context context,
|
||||||
|
int routeTypes, View.OnClickListener extendedSettingsClickListener) {
|
||||||
|
final MediaRouter router = (MediaRouter)context.getSystemService(
|
||||||
|
Context.MEDIA_ROUTER_SERVICE);
|
||||||
|
|
||||||
|
MediaRouter.RouteInfo route = router.getSelectedRoute();
|
||||||
|
if (route.isDefault() || !route.matchesTypes(routeTypes)) {
|
||||||
|
final MediaRouteChooserDialog d = new MediaRouteChooserDialog(
|
||||||
|
context, android.R.style.Theme_DeviceDefault_Dialog);
|
||||||
|
d.setRouteTypes(routeTypes);
|
||||||
|
d.setExtendedSettingsClickListener(extendedSettingsClickListener);
|
||||||
|
return d;
|
||||||
|
} else {
|
||||||
|
MediaRouteControllerDialog d = new MediaRouteControllerDialog(
|
||||||
|
context, android.R.style.Theme_DeviceDefault_Dialog);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,65 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2012 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.internal.view;
|
|
||||||
|
|
||||||
import com.android.internal.R;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.widget.Checkable;
|
|
||||||
import android.widget.CheckBox;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
|
|
||||||
public class CheckableLinearLayout extends LinearLayout implements Checkable {
|
|
||||||
private CheckBox mCheckBox;
|
|
||||||
|
|
||||||
public CheckableLinearLayout(Context context) {
|
|
||||||
super(context);
|
|
||||||
// TODO Auto-generated constructor stub
|
|
||||||
}
|
|
||||||
|
|
||||||
public CheckableLinearLayout(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
// TODO Auto-generated constructor stub
|
|
||||||
}
|
|
||||||
|
|
||||||
public CheckableLinearLayout(Context context, AttributeSet attrs, int defStyle) {
|
|
||||||
super(context, attrs, defStyle);
|
|
||||||
// TODO Auto-generated constructor stub
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onFinishInflate() {
|
|
||||||
super.onFinishInflate();
|
|
||||||
mCheckBox = (CheckBox) findViewById(R.id.check);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setChecked(boolean checked) {
|
|
||||||
mCheckBox.setChecked(checked);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isChecked() {
|
|
||||||
return mCheckBox.isChecked();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void toggle() {
|
|
||||||
mCheckBox.toggle();
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 933 B |
Binary file not shown.
Before Width: | Height: | Size: 984 B |
Binary file not shown.
Before Width: | Height: | Size: 720 B |
Binary file not shown.
Before Width: | Height: | Size: 751 B |
Binary file not shown.
Before Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.3 KiB |
58
core/res/res/layout/media_route_chooser_dialog.xml
Normal file
58
core/res/res/layout/media_route_chooser_dialog.xml
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2013 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:divider="?android:attr/dividerHorizontal"
|
||||||
|
android:showDividers="middle">
|
||||||
|
<!-- List of routes. -->
|
||||||
|
<ListView android:id="@+id/media_route_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<!-- Content to show when list is empty. -->
|
||||||
|
<LinearLayout android:id="@android:id/empty"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:visibility="gone">
|
||||||
|
<ProgressBar android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center" />
|
||||||
|
<TextView android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:text="@string/media_route_chooser_searching" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Settings button. -->
|
||||||
|
<LinearLayout android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="?attr/buttonBarStyle">
|
||||||
|
<Button android:id="@+id/media_route_extended_settings_button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
style="?attr/buttonBarButtonStyle"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/media_route_chooser_extended_settings"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
@ -1,48 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Copyright (C) 2012 The Android Open Source Project
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
-->
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:showDividers="middle"
|
|
||||||
android:divider="?android:attr/dividerHorizontal">
|
|
||||||
<LinearLayout android:layout_width="match_parent"
|
|
||||||
android:layout_height="?android:attr/listPreferredItemHeight"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:padding="8dp">
|
|
||||||
<ImageView android:id="@+id/volume_icon"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:src="@android:drawable/ic_audio_vol"
|
|
||||||
android:gravity="center"
|
|
||||||
android:scaleType="center" />
|
|
||||||
<SeekBar android:id="@+id/volume_slider"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginEnd="8dp" />
|
|
||||||
<ImageButton android:id="@+id/extended_settings"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:background="?android:attr/selectableItemBackground"
|
|
||||||
android:src="@android:drawable/ic_sysbar_quicksettings"
|
|
||||||
android:visibility="gone" />
|
|
||||||
</LinearLayout>
|
|
||||||
<ListView android:id="@id/list"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
</LinearLayout>
|
|
60
core/res/res/layout/media_route_controller_dialog.xml
Normal file
60
core/res/res/layout/media_route_controller_dialog.xml
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2013 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:divider="?android:attr/dividerHorizontal"
|
||||||
|
android:showDividers="middle">
|
||||||
|
<!-- Optional volume slider section. -->
|
||||||
|
<LinearLayout android:id="@+id/media_route_volume_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:visibility="gone">
|
||||||
|
<ImageView android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:src="@drawable/ic_audio_vol"
|
||||||
|
android:gravity="center"
|
||||||
|
android:scaleType="center" />
|
||||||
|
<SeekBar android:id="@+id/media_route_volume_slider"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginRight="8dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Optional content view section. -->
|
||||||
|
<FrameLayout android:id="@+id/media_route_control_frame"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<!-- Disconnect button. -->
|
||||||
|
<LinearLayout android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="?attr/buttonBarStyle">
|
||||||
|
<Button android:id="@+id/media_route_disconnect_button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
style="?attr/buttonBarButtonStyle"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/media_route_controller_disconnect" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
@ -20,13 +20,6 @@
|
|||||||
android:background="@drawable/item_background_activated_holo_dark"
|
android:background="@drawable/item_background_activated_holo_dark"
|
||||||
android:gravity="center_vertical">
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
<ImageView android:layout_width="56dp"
|
|
||||||
android:layout_height="56dp"
|
|
||||||
android:scaleType="center"
|
|
||||||
android:id="@+id/icon"
|
|
||||||
android:visibility="gone"
|
|
||||||
android:duplicateParentState="true" />
|
|
||||||
|
|
||||||
<LinearLayout android:layout_width="0dp"
|
<LinearLayout android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
@ -53,14 +46,4 @@
|
|||||||
android:duplicateParentState="true" />
|
android:duplicateParentState="true" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:layout_width="56dp"
|
|
||||||
android:layout_height="56dp"
|
|
||||||
android:id="@+id/expand_button"
|
|
||||||
android:background="?android:attr/selectableItemBackground"
|
|
||||||
android:src="@drawable/ic_media_group_expand"
|
|
||||||
android:scaleType="center"
|
|
||||||
android:visibility="gone"
|
|
||||||
android:duplicateParentState="true" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Copyright (C) 2012 The Android Open Source Project
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<com.android.internal.view.CheckableLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?android:attr/listPreferredItemHeight"
|
|
||||||
android:background="?android:attr/selectableItemBackground"
|
|
||||||
android:gravity="center_vertical">
|
|
||||||
|
|
||||||
<ImageView android:layout_width="56dp"
|
|
||||||
android:layout_height="56dp"
|
|
||||||
android:scaleType="center"
|
|
||||||
android:id="@+id/icon"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<LinearLayout android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:gravity="start|center_vertical"
|
|
||||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
|
||||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
|
|
||||||
|
|
||||||
<TextView android:id="@android:id/text1"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:ellipsize="marquee"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
|
||||||
|
|
||||||
<TextView android:id="@android:id/text2"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:ellipsize="marquee"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<CheckBox
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:id="@+id/check"
|
|
||||||
android:focusable="false"
|
|
||||||
android:clickable="false" />
|
|
||||||
|
|
||||||
</com.android.internal.view.CheckableLinearLayout>
|
|
@ -1,44 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Copyright (C) 2012 The Android Open Source Project
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?android:attr/selectableItemBackground">
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?android:attr/listPreferredItemHeightSmall"
|
|
||||||
android:background="#19ffffff"
|
|
||||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
|
||||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
|
||||||
android:gravity="center_vertical">
|
|
||||||
|
|
||||||
<TextView android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:ellipsize="marquee"
|
|
||||||
android:text="@string/media_route_chooser_grouping_done"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:src="@drawable/ic_media_group_collapse"
|
|
||||||
android:scaleType="center" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</FrameLayout>
|
|
@ -1,34 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Copyright (C) 2012 The Android Open Source Project
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingTop="16dp">
|
|
||||||
<TextView
|
|
||||||
android:id="@android:id/text1"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
|
||||||
android:background="#19ffffff"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
|
||||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
|
||||||
android:minHeight="24dp"
|
|
||||||
/>
|
|
||||||
</FrameLayout>
|
|
@ -1,29 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Copyright (C) 2012 The Android Open Source Project
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@android:id/text1"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
|
||||||
android:background="#19ffffff"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
|
||||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
|
||||||
android:minHeight="24dp"
|
|
||||||
/>
|
|
@ -4096,12 +4096,21 @@
|
|||||||
<!-- Description of a wireless display route. [CHAR LIMIT=50] -->
|
<!-- Description of a wireless display route. [CHAR LIMIT=50] -->
|
||||||
<string name="wireless_display_route_description">Wireless display</string>
|
<string name="wireless_display_route_description">Wireless display</string>
|
||||||
|
|
||||||
<!-- "Done" button for MediaRouter chooser dialog when grouping routes. [CHAR LIMIT=NONE] -->
|
|
||||||
<string name="media_route_chooser_grouping_done">Done</string>
|
|
||||||
|
|
||||||
<!-- Content description of a MediaRouteButton for accessibility support -->
|
<!-- Content description of a MediaRouteButton for accessibility support -->
|
||||||
<string name="media_route_button_content_description">Media output</string>
|
<string name="media_route_button_content_description">Media output</string>
|
||||||
|
|
||||||
|
<!-- Title of the media route chooser dialog. [CHAR LIMIT=30] -->
|
||||||
|
<string name="media_route_chooser_title">Connect to device</string>
|
||||||
|
|
||||||
|
<!-- Placeholder text to show when no devices have been found. [CHAR LIMIT=50] -->
|
||||||
|
<string name="media_route_chooser_searching">Searching for devices\u2026</string>
|
||||||
|
|
||||||
|
<!-- Button to access extended settings. [CHAR LIMIT=30] -->
|
||||||
|
<string name="media_route_chooser_extended_settings">Settings</string>
|
||||||
|
|
||||||
|
<!-- Button to disconnect from a media route. [CHAR LIMIT=30] -->
|
||||||
|
<string name="media_route_controller_disconnect">Disconnect</string>
|
||||||
|
|
||||||
<!-- Status message for remote routes attempting to scan/determine availability -->
|
<!-- Status message for remote routes attempting to scan/determine availability -->
|
||||||
<string name="media_route_status_scanning">Scanning...</string>
|
<string name="media_route_status_scanning">Scanning...</string>
|
||||||
|
|
||||||
|
@ -1255,17 +1255,16 @@
|
|||||||
|
|
||||||
<java-symbol type="attr" name="mediaRouteButtonStyle" />
|
<java-symbol type="attr" name="mediaRouteButtonStyle" />
|
||||||
<java-symbol type="attr" name="externalRouteEnabledDrawable" />
|
<java-symbol type="attr" name="externalRouteEnabledDrawable" />
|
||||||
<java-symbol type="id" name="extended_settings" />
|
<java-symbol type="layout" name="media_route_chooser_dialog" />
|
||||||
<java-symbol type="id" name="check" />
|
<java-symbol type="layout" name="media_route_controller_dialog" />
|
||||||
<java-symbol type="id" name="volume_slider" />
|
|
||||||
<java-symbol type="id" name="volume_icon" />
|
|
||||||
<java-symbol type="drawable" name="ic_media_route_on_holo_dark" />
|
|
||||||
<java-symbol type="layout" name="media_route_chooser_layout" />
|
|
||||||
<java-symbol type="layout" name="media_route_list_item_top_header" />
|
|
||||||
<java-symbol type="layout" name="media_route_list_item_section_header" />
|
|
||||||
<java-symbol type="layout" name="media_route_list_item" />
|
<java-symbol type="layout" name="media_route_list_item" />
|
||||||
<java-symbol type="layout" name="media_route_list_item_checkable" />
|
<java-symbol type="id" name="media_route_list" />
|
||||||
<java-symbol type="layout" name="media_route_list_item_collapse_group" />
|
<java-symbol type="id" name="media_route_volume_layout" />
|
||||||
|
<java-symbol type="id" name="media_route_volume_slider" />
|
||||||
|
<java-symbol type="id" name="media_route_control_frame" />
|
||||||
|
<java-symbol type="id" name="media_route_disconnect_button" />
|
||||||
|
<java-symbol type="id" name="media_route_extended_settings_button" />
|
||||||
|
<java-symbol type="string" name="media_route_chooser_title" />
|
||||||
<java-symbol type="string" name="bluetooth_a2dp_audio_route_name" />
|
<java-symbol type="string" name="bluetooth_a2dp_audio_route_name" />
|
||||||
|
|
||||||
<java-symbol type="dimen" name="config_minScalingSpan" />
|
<java-symbol type="dimen" name="config_minScalingSpan" />
|
||||||
|
@ -666,6 +666,19 @@ public class MediaRouter {
|
|||||||
*/
|
*/
|
||||||
public static final int CALLBACK_FLAG_PASSIVE_DISCOVERY = 1 << 3;
|
public static final int CALLBACK_FLAG_PASSIVE_DISCOVERY = 1 << 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag for {@link #isRouteAvailable}: Ignore the default route.
|
||||||
|
* <p>
|
||||||
|
* This flag is used to determine whether a matching non-default route is available.
|
||||||
|
* This constraint may be used to decide whether to offer the route chooser dialog
|
||||||
|
* to the user. There is no point offering the chooser if there are no
|
||||||
|
* non-default choices.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @hide Future API ported from support library. Revisit this later.
|
||||||
|
*/
|
||||||
|
public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0;
|
||||||
|
|
||||||
// Maps application contexts
|
// Maps application contexts
|
||||||
static final HashMap<Context, MediaRouter> sRouters = new HashMap<Context, MediaRouter>();
|
static final HashMap<Context, MediaRouter> sRouters = new HashMap<Context, MediaRouter>();
|
||||||
|
|
||||||
@ -716,6 +729,11 @@ public class MediaRouter {
|
|||||||
return sStatic.mSystemCategory;
|
return sStatic.mSystemCategory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public RouteInfo getSelectedRoute() {
|
||||||
|
return getSelectedRoute(ROUTE_TYPE_ANY);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the currently selected route for any of the given types
|
* Return the currently selected route for any of the given types
|
||||||
*
|
*
|
||||||
@ -738,6 +756,38 @@ public class MediaRouter {
|
|||||||
return sStatic.mDefaultAudioVideo;
|
return sStatic.mDefaultAudioVideo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there is a route that matches the specified types.
|
||||||
|
* <p>
|
||||||
|
* This method returns true if there are any available routes that match the types
|
||||||
|
* regardless of whether they are enabled or disabled. If the
|
||||||
|
* {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then
|
||||||
|
* the method will only consider non-default routes.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param types The types to match.
|
||||||
|
* @param flags Flags to control the determination of whether a route may be available.
|
||||||
|
* May be zero or {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE}.
|
||||||
|
* @return True if a matching route may be available.
|
||||||
|
*
|
||||||
|
* @hide Future API ported from support library. Revisit this later.
|
||||||
|
*/
|
||||||
|
public boolean isRouteAvailable(int types, int flags) {
|
||||||
|
final int count = sStatic.mRoutes.size();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
RouteInfo route = sStatic.mRoutes.get(i);
|
||||||
|
if (route.matchesTypes(types)) {
|
||||||
|
if ((flags & AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE) == 0
|
||||||
|
|| route != sStatic.mDefaultAudioVideo) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// It doesn't look like we can find a matching route right now.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a callback to listen to events about specific kinds of media routes.
|
* Add a callback to listen to events about specific kinds of media routes.
|
||||||
* If the specified callback is already registered, its registration will be updated for any
|
* If the specified callback is already registered, its registration will be updated for any
|
||||||
@ -836,7 +886,7 @@ public class MediaRouter {
|
|||||||
static void selectRouteStatic(int types, RouteInfo route, boolean explicit) {
|
static void selectRouteStatic(int types, RouteInfo route, boolean explicit) {
|
||||||
final RouteInfo oldRoute = sStatic.mSelectedRoute;
|
final RouteInfo oldRoute = sStatic.mSelectedRoute;
|
||||||
if (oldRoute == route) return;
|
if (oldRoute == route) return;
|
||||||
if ((route.getSupportedTypes() & types) == 0) {
|
if (!route.matchesTypes(types)) {
|
||||||
Log.w(TAG, "selectRoute ignored; cannot select route with supported types " +
|
Log.w(TAG, "selectRoute ignored; cannot select route with supported types " +
|
||||||
typesToString(route.getSupportedTypes()) + " into route types " +
|
typesToString(route.getSupportedTypes()) + " into route types " +
|
||||||
typesToString(types));
|
typesToString(types));
|
||||||
@ -986,7 +1036,7 @@ public class MediaRouter {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (info == sStatic.mSelectedRoute) {
|
if (info.isSelected()) {
|
||||||
// Removing the currently selected route? Select the default before we remove it.
|
// Removing the currently selected route? Select the default before we remove it.
|
||||||
selectDefaultRouteStatic();
|
selectDefaultRouteStatic();
|
||||||
}
|
}
|
||||||
@ -1319,7 +1369,7 @@ public class MediaRouter {
|
|||||||
dispatchRouteChanged(route);
|
dispatchRouteChanged(route);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!enabled && route == sStatic.mSelectedRoute) {
|
if (!enabled && route.isSelected()) {
|
||||||
// Oops, no longer available. Reselect the default.
|
// Oops, no longer available. Reselect the default.
|
||||||
selectDefaultRouteStatic();
|
selectDefaultRouteStatic();
|
||||||
}
|
}
|
||||||
@ -1526,6 +1576,11 @@ public class MediaRouter {
|
|||||||
return mSupportedTypes;
|
return mSupportedTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public boolean matchesTypes(int types) {
|
||||||
|
return (mSupportedTypes & types) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The group that this route belongs to.
|
* @return The group that this route belongs to.
|
||||||
*/
|
*/
|
||||||
@ -1769,7 +1824,7 @@ public class MediaRouter {
|
|||||||
// then report it as connecting even though it has not yet had a chance
|
// then report it as connecting even though it has not yet had a chance
|
||||||
// to move into the CONNECTING state. Note that routes in the NONE state
|
// to move into the CONNECTING state. Note that routes in the NONE state
|
||||||
// are assumed to not require an explicit connection lifecycle.
|
// are assumed to not require an explicit connection lifecycle.
|
||||||
if (this == sStatic.mSelectedRoute) {
|
if (isSelected()) {
|
||||||
switch (mStatusCode) {
|
switch (mStatusCode) {
|
||||||
case STATUS_AVAILABLE:
|
case STATUS_AVAILABLE:
|
||||||
case STATUS_SCANNING:
|
case STATUS_SCANNING:
|
||||||
@ -1780,6 +1835,21 @@ public class MediaRouter {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public boolean isSelected() {
|
||||||
|
return this == sStatic.mSelectedRoute;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public boolean isDefault() {
|
||||||
|
return this == sStatic.mDefaultAudioVideo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public void select() {
|
||||||
|
selectRouteStatic(mSupportedTypes, this, true);
|
||||||
|
}
|
||||||
|
|
||||||
void setStatusInt(CharSequence status) {
|
void setStatusInt(CharSequence status) {
|
||||||
if (!status.equals(mStatus)) {
|
if (!status.equals(mStatus)) {
|
||||||
mStatus = status;
|
mStatus = status;
|
||||||
|
@ -37,8 +37,8 @@ import android.database.Cursor;
|
|||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.LevelListDrawable;
|
|
||||||
import android.hardware.display.DisplayManager;
|
import android.hardware.display.DisplayManager;
|
||||||
|
import android.media.MediaRouter;
|
||||||
import android.net.wifi.WifiManager;
|
import android.net.wifi.WifiManager;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@ -61,6 +61,7 @@ import android.view.WindowManagerGlobal;
|
|||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.android.internal.app.MediaRouteDialogPresenter;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.statusbar.phone.QuickSettingsModel.ActivityState;
|
import com.android.systemui.statusbar.phone.QuickSettingsModel.ActivityState;
|
||||||
import com.android.systemui.statusbar.phone.QuickSettingsModel.BluetoothState;
|
import com.android.systemui.statusbar.phone.QuickSettingsModel.BluetoothState;
|
||||||
@ -676,7 +677,21 @@ class QuickSettings {
|
|||||||
remoteDisplayTile.setOnClickListener(new View.OnClickListener() {
|
remoteDisplayTile.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
startSettingsActivity(android.provider.Settings.ACTION_WIFI_DISPLAY_SETTINGS);
|
collapsePanels();
|
||||||
|
|
||||||
|
final Dialog[] dialog = new Dialog[1];
|
||||||
|
dialog[0] = MediaRouteDialogPresenter.createDialog(mContext,
|
||||||
|
MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
|
||||||
|
new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
dialog[0].dismiss();
|
||||||
|
startSettingsActivity(
|
||||||
|
android.provider.Settings.ACTION_WIFI_DISPLAY_SETTINGS);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dialog[0].getWindow().setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
|
||||||
|
dialog[0].show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
mModel.addRemoteDisplayTile(remoteDisplayTile,
|
mModel.addRemoteDisplayTile(remoteDisplayTile,
|
||||||
|
@ -684,8 +684,8 @@ class QuickSettingsModel implements BluetoothStateChangeCallback,
|
|||||||
private void updateRemoteDisplays() {
|
private void updateRemoteDisplays() {
|
||||||
MediaRouter.RouteInfo connectedRoute = mMediaRouter.getSelectedRoute(
|
MediaRouter.RouteInfo connectedRoute = mMediaRouter.getSelectedRoute(
|
||||||
MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
|
MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
|
||||||
boolean enabled = connectedRoute != null && (connectedRoute.getSupportedTypes()
|
boolean enabled = connectedRoute != null
|
||||||
& MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY) != 0;
|
&& connectedRoute.matchesTypes(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
|
||||||
boolean connecting;
|
boolean connecting;
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
connecting = connectedRoute.isConnecting();
|
connecting = connectedRoute.isConnecting();
|
||||||
@ -695,7 +695,7 @@ class QuickSettingsModel implements BluetoothStateChangeCallback,
|
|||||||
final int count = mMediaRouter.getRouteCount();
|
final int count = mMediaRouter.getRouteCount();
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
MediaRouter.RouteInfo route = mMediaRouter.getRouteAt(i);
|
MediaRouter.RouteInfo route = mMediaRouter.getRouteAt(i);
|
||||||
if ((route.getSupportedTypes() & MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY) != 0) {
|
if (route.matchesTypes(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)) {
|
||||||
enabled = true;
|
enabled = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user