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;
|
||||
|
||||
import com.android.internal.app.MediaRouteChooserDialogFragment;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.media.MediaRouter;
|
||||
import android.media.MediaRouter.RouteInfo;
|
||||
import android.util.Log;
|
||||
@ -30,22 +27,38 @@ import android.view.ViewGroup;
|
||||
|
||||
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 {
|
||||
private static final String TAG = "MediaRouteActionProvider";
|
||||
|
||||
private Context mContext;
|
||||
private MediaRouter mRouter;
|
||||
private MenuItem mMenuItem;
|
||||
private MediaRouteButton mView;
|
||||
private final Context mContext;
|
||||
private final MediaRouter mRouter;
|
||||
private final MediaRouterCallback mCallback;
|
||||
|
||||
private int mRouteTypes;
|
||||
private MediaRouteButton mButton;
|
||||
private View.OnClickListener mExtendedSettingsListener;
|
||||
private RouterCallback mCallback;
|
||||
|
||||
public MediaRouteActionProvider(Context context) {
|
||||
super(context);
|
||||
|
||||
mContext = context;
|
||||
mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
|
||||
mCallback = new RouterCallback(this);
|
||||
mCallback = new MediaRouterCallback(this);
|
||||
|
||||
// Start with live audio by default.
|
||||
// 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) 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) {
|
||||
mRouter.removeCallback(mCallback);
|
||||
}
|
||||
mRouteTypes = types;
|
||||
if (types != 0) {
|
||||
mRouter.addCallback(types, mCallback, MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
|
||||
mRouter.addCallback(types, mCallback,
|
||||
MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
|
||||
}
|
||||
if (mView != null) {
|
||||
mView.setRouteTypes(mRouteTypes);
|
||||
refreshRoute();
|
||||
|
||||
if (mButton != null) {
|
||||
mButton.setRouteTypes(mRouteTypes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setExtendedSettingsClickListener(View.OnClickListener listener) {
|
||||
mExtendedSettingsListener = listener;
|
||||
if (mButton != null) {
|
||||
mButton.setExtendedSettingsClickListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public View onCreateActionView() {
|
||||
throw new UnsupportedOperationException("Use onCreateActionView(MenuItem) instead.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateActionView(MenuItem item) {
|
||||
if (mMenuItem != null || mView != null) {
|
||||
if (mButton != null) {
|
||||
Log.e(TAG, "onCreateActionView: this ActionProvider is already associated " +
|
||||
"with a menu item. Don't reuse MediaRouteActionProvider instances! " +
|
||||
"Abandoning the old one...");
|
||||
}
|
||||
mMenuItem = item;
|
||||
mView = new MediaRouteButton(mContext);
|
||||
mView.setCheatSheetEnabled(true);
|
||||
mView.setRouteTypes(mRouteTypes);
|
||||
mView.setExtendedSettingsClickListener(mExtendedSettingsListener);
|
||||
mView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
|
||||
mButton = new MediaRouteButton(mContext);
|
||||
mButton.setCheatSheetEnabled(true);
|
||||
mButton.setRouteTypes(mRouteTypes);
|
||||
mButton.setExtendedSettingsClickListener(mExtendedSettingsListener);
|
||||
mButton.setLayoutParams(new ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
return mView;
|
||||
return mButton;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPerformDefaultAction() {
|
||||
final FragmentManager fm = getActivity().getFragmentManager();
|
||||
// See if one is already attached to this activity.
|
||||
MediaRouteChooserDialogFragment dialogFragment =
|
||||
(MediaRouteChooserDialogFragment) fm.findFragmentByTag(
|
||||
MediaRouteChooserDialogFragment.FRAGMENT_TAG);
|
||||
if (dialogFragment != null) {
|
||||
Log.w(TAG, "onPerformDefaultAction(): Chooser dialog already showing!");
|
||||
if (mButton != null) {
|
||||
return mButton.showDialogInternal();
|
||||
}
|
||||
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
|
||||
public boolean overridesItemVisibility() {
|
||||
return true;
|
||||
@ -136,36 +143,43 @@ public class MediaRouteActionProvider extends ActionProvider {
|
||||
|
||||
@Override
|
||||
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 WeakReference<MediaRouteActionProvider> mAp;
|
||||
private void refreshRoute() {
|
||||
refreshVisibility();
|
||||
}
|
||||
|
||||
RouterCallback(MediaRouteActionProvider ap) {
|
||||
mAp = new WeakReference<MediaRouteActionProvider>(ap);
|
||||
private static class MediaRouterCallback extends MediaRouter.SimpleCallback {
|
||||
private final WeakReference<MediaRouteActionProvider> mProviderWeak;
|
||||
|
||||
public MediaRouterCallback(MediaRouteActionProvider provider) {
|
||||
mProviderWeak = new WeakReference<MediaRouteActionProvider>(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRouteAdded(MediaRouter router, RouteInfo info) {
|
||||
final MediaRouteActionProvider ap = mAp.get();
|
||||
if (ap == null) {
|
||||
router.removeCallback(this);
|
||||
return;
|
||||
}
|
||||
|
||||
ap.refreshVisibility();
|
||||
refreshRoute(router);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRouteRemoved(MediaRouter router, RouteInfo info) {
|
||||
final MediaRouteActionProvider ap = mAp.get();
|
||||
if (ap == null) {
|
||||
router.removeCallback(this);
|
||||
return;
|
||||
refreshRoute(router);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.app.MediaRouteChooserDialogFragment;
|
||||
import com.android.internal.app.MediaRouteDialogPresenter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
@ -30,7 +30,6 @@ import android.media.MediaRouter.RouteGroup;
|
||||
import android.media.MediaRouter.RouteInfo;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.SoundEffectConstants;
|
||||
@ -38,17 +37,15 @@ import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
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 boolean mAttachedToWindow;
|
||||
|
||||
private Drawable mRemoteIndicator;
|
||||
private boolean mRemoteActive;
|
||||
private boolean mToggleMode;
|
||||
private boolean mCheatSheetEnabled;
|
||||
private boolean mIsConnecting;
|
||||
|
||||
@ -56,12 +53,13 @@ public class MediaRouteButton extends View {
|
||||
private int mMinHeight;
|
||||
|
||||
private OnClickListener mExtendedSettingsClickListener;
|
||||
private MediaRouteChooserDialogFragment mDialogFragment;
|
||||
|
||||
// The checked state is used when connected to a remote route.
|
||||
private static final int[] CHECKED_STATE_SET = {
|
||||
R.attr.state_checked
|
||||
};
|
||||
|
||||
// The activated state is used while connecting to a remote route.
|
||||
private static final int[] ACTIVATED_STATE_SET = {
|
||||
R.attr.state_activated
|
||||
};
|
||||
@ -78,6 +76,7 @@ public class MediaRouteButton extends View {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
mRouter = (MediaRouter)context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
|
||||
mCallback = new MediaRouterCallback();
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs,
|
||||
com.android.internal.R.styleable.MediaRouteButton, defStyleAttr, 0);
|
||||
@ -98,19 +97,87 @@ public class MediaRouteButton extends View {
|
||||
setRouteTypes(routeTypes);
|
||||
}
|
||||
|
||||
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);
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
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
|
||||
@ -120,29 +187,7 @@ public class MediaRouteButton extends View {
|
||||
if (!handled) {
|
||||
playSoundEffect(SoundEffectConstants.CLICK);
|
||||
}
|
||||
|
||||
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;
|
||||
return showDialogInternal() || handled;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -183,87 +228,9 @@ public class MediaRouteButton extends View {
|
||||
}
|
||||
cheatSheet.show();
|
||||
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||
|
||||
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
|
||||
protected int[] onCreateDrawableState(int extraSpace) {
|
||||
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
|
||||
protected boolean verifyDrawable(Drawable who) {
|
||||
return super.verifyDrawable(who) || who == mRemoteIndicator;
|
||||
@ -299,12 +281,16 @@ public class MediaRouteButton extends View {
|
||||
@Override
|
||||
public void jumpDrawablesToCurrentState() {
|
||||
super.jumpDrawablesToCurrentState();
|
||||
if (mRemoteIndicator != null) mRemoteIndicator.jumpToCurrentState();
|
||||
|
||||
if (mRemoteIndicator != null) {
|
||||
mRemoteIndicator.jumpToCurrentState();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisibility(int visibility) {
|
||||
super.setVisibility(visibility);
|
||||
|
||||
if (mRemoteIndicator != null) {
|
||||
mRemoteIndicator.setVisible(getVisibility() == VISIBLE, false);
|
||||
}
|
||||
@ -313,20 +299,22 @@ public class MediaRouteButton extends View {
|
||||
@Override
|
||||
public void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
|
||||
mAttachedToWindow = true;
|
||||
if (mRouteTypes != 0) {
|
||||
mRouter.addCallback(mRouteTypes, mRouterCallback,
|
||||
mRouter.addCallback(mRouteTypes, mCallback,
|
||||
MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
|
||||
updateRouteInfo();
|
||||
}
|
||||
refreshRoute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromWindow() {
|
||||
if (mRouteTypes != 0) {
|
||||
mRouter.removeCallback(mRouterCallback);
|
||||
}
|
||||
mAttachedToWindow = false;
|
||||
if (mRouteTypes != 0) {
|
||||
mRouter.removeCallback(mCallback);
|
||||
}
|
||||
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
@ -389,93 +377,71 @@ public class MediaRouteButton extends View {
|
||||
final int drawLeft = left + (right - left - drawWidth) / 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);
|
||||
}
|
||||
|
||||
public void setExtendedSettingsClickListener(OnClickListener listener) {
|
||||
mExtendedSettingsClickListener = listener;
|
||||
if (mDialogFragment != null) {
|
||||
mDialogFragment.setExtendedSettingsClickListener(listener);
|
||||
private void refreshRoute() {
|
||||
if (mAttachedToWindow) {
|
||||
final MediaRouter.RouteInfo route = mRouter.getSelectedRoute();
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
private final class MediaRouterCallback extends MediaRouter.SimpleCallback {
|
||||
@Override
|
||||
public void onRouteAdded(MediaRouter router, RouteInfo info) {
|
||||
updateRouteCount();
|
||||
refreshRoute();
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
|
||||
int index) {
|
||||
updateRouteCount();
|
||||
refreshRoute();
|
||||
}
|
||||
|
||||
@Override
|
||||
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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,675 +16,86 @@
|
||||
|
||||
package com.android.internal.app;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.app.MediaRouteActionProvider;
|
||||
import android.app.MediaRouteButton;
|
||||
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.text.TextUtils;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
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;
|
||||
import android.view.View.OnClickListener;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @see MediaRouteActionProvider
|
||||
* TODO: Move this back into the API, as in the support library media router.
|
||||
*/
|
||||
public class MediaRouteChooserDialogFragment extends DialogFragment {
|
||||
private static final String TAG = "MediaRouteChooserDialogFragment";
|
||||
public static final String FRAGMENT_TAG = "android:MediaRouteChooserDialogFragment";
|
||||
private final String ARGUMENT_ROUTE_TYPES = "routeTypes";
|
||||
|
||||
private static final int[] ITEM_LAYOUTS = new int[] {
|
||||
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;
|
||||
private View.OnClickListener mExtendedSettingsClickListener;
|
||||
|
||||
/**
|
||||
* Creates a media route chooser dialog fragment.
|
||||
* <p>
|
||||
* All subclasses of this class must also possess a default constructor.
|
||||
* </p>
|
||||
*/
|
||||
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) {
|
||||
mLauncherListener = listener;
|
||||
}
|
||||
|
||||
@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 int getRouteTypes() {
|
||||
Bundle args = getArguments();
|
||||
return args != null ? args.getInt(ARGUMENT_ROUTE_TYPES) : 0;
|
||||
}
|
||||
|
||||
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() {
|
||||
if (mRouter == null) return;
|
||||
|
||||
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;
|
||||
MediaRouteChooserDialog dialog = (MediaRouteChooserDialog)getDialog();
|
||||
if (dialog != null) {
|
||||
dialog.setRouteTypes(types);
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
if (type == VIEW_ROUTE) {
|
||||
mRouter.selectRouteInt(mRouteTypes, route, true);
|
||||
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 void setExtendedSettingsClickListener(View.OnClickListener listener) {
|
||||
if (listener != mExtendedSettingsClickListener) {
|
||||
mExtendedSettingsClickListener = listener;
|
||||
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN && mVolumeSlider.isEnabled()) {
|
||||
return true;
|
||||
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mVolumeSlider.isEnabled()) {
|
||||
return true;
|
||||
} else {
|
||||
return super.onKeyUp(keyCode, event);
|
||||
MediaRouteChooserDialog dialog = (MediaRouteChooserDialog)getDialog();
|
||||
if (dialog != null) {
|
||||
dialog.setExtendedSettingsClickListener(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 void onDetached(MediaRouteChooserDialogFragment detachedFragment);
|
||||
}
|
||||
|
||||
class VolumeSliderChangeListener implements SeekBar.OnSeekBarChangeListener {
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
changeVolume(progress);
|
||||
public MediaRouteChooserDialog onCreateChooserDialog(
|
||||
Context context, Bundle savedInstanceState) {
|
||||
return new MediaRouteChooserDialog(context, getTheme());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
mIgnoreCallbackVolumeChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
mIgnoreCallbackVolumeChanges = false;
|
||||
updateVolume();
|
||||
}
|
||||
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
MediaRouteChooserDialog dialog = onCreateChooserDialog(getActivity(), savedInstanceState);
|
||||
dialog.setRouteTypes(getRouteTypes());
|
||||
dialog.setExtendedSettingsClickListener(mExtendedSettingsClickListener);
|
||||
return dialog;
|
||||
}
|
||||
}
|
||||
|
@ -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: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"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
@ -53,14 +46,4 @@
|
||||
android:duplicateParentState="true" />
|
||||
</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>
|
||||
|
@ -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] -->
|
||||
<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 -->
|
||||
<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 -->
|
||||
<string name="media_route_status_scanning">Scanning...</string>
|
||||
|
||||
|
@ -1255,17 +1255,16 @@
|
||||
|
||||
<java-symbol type="attr" name="mediaRouteButtonStyle" />
|
||||
<java-symbol type="attr" name="externalRouteEnabledDrawable" />
|
||||
<java-symbol type="id" name="extended_settings" />
|
||||
<java-symbol type="id" name="check" />
|
||||
<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_chooser_dialog" />
|
||||
<java-symbol type="layout" name="media_route_controller_dialog" />
|
||||
<java-symbol type="layout" name="media_route_list_item" />
|
||||
<java-symbol type="layout" name="media_route_list_item_checkable" />
|
||||
<java-symbol type="layout" name="media_route_list_item_collapse_group" />
|
||||
<java-symbol type="id" name="media_route_list" />
|
||||
<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="dimen" name="config_minScalingSpan" />
|
||||
|
@ -666,6 +666,19 @@ public class MediaRouter {
|
||||
*/
|
||||
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
|
||||
static final HashMap<Context, MediaRouter> sRouters = new HashMap<Context, MediaRouter>();
|
||||
|
||||
@ -716,6 +729,11 @@ public class MediaRouter {
|
||||
return sStatic.mSystemCategory;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public RouteInfo getSelectedRoute() {
|
||||
return getSelectedRoute(ROUTE_TYPE_ANY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the currently selected route for any of the given types
|
||||
*
|
||||
@ -738,6 +756,38 @@ public class MediaRouter {
|
||||
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.
|
||||
* 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) {
|
||||
final RouteInfo oldRoute = sStatic.mSelectedRoute;
|
||||
if (oldRoute == route) return;
|
||||
if ((route.getSupportedTypes() & types) == 0) {
|
||||
if (!route.matchesTypes(types)) {
|
||||
Log.w(TAG, "selectRoute ignored; cannot select route with supported types " +
|
||||
typesToString(route.getSupportedTypes()) + " into route types " +
|
||||
typesToString(types));
|
||||
@ -986,7 +1036,7 @@ public class MediaRouter {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (info == sStatic.mSelectedRoute) {
|
||||
if (info.isSelected()) {
|
||||
// Removing the currently selected route? Select the default before we remove it.
|
||||
selectDefaultRouteStatic();
|
||||
}
|
||||
@ -1319,7 +1369,7 @@ public class MediaRouter {
|
||||
dispatchRouteChanged(route);
|
||||
}
|
||||
|
||||
if (!enabled && route == sStatic.mSelectedRoute) {
|
||||
if (!enabled && route.isSelected()) {
|
||||
// Oops, no longer available. Reselect the default.
|
||||
selectDefaultRouteStatic();
|
||||
}
|
||||
@ -1526,6 +1576,11 @@ public class MediaRouter {
|
||||
return mSupportedTypes;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public boolean matchesTypes(int types) {
|
||||
return (mSupportedTypes & types) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
// to move into the CONNECTING state. Note that routes in the NONE state
|
||||
// are assumed to not require an explicit connection lifecycle.
|
||||
if (this == sStatic.mSelectedRoute) {
|
||||
if (isSelected()) {
|
||||
switch (mStatusCode) {
|
||||
case STATUS_AVAILABLE:
|
||||
case STATUS_SCANNING:
|
||||
@ -1780,6 +1835,21 @@ public class MediaRouter {
|
||||
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) {
|
||||
if (!status.equals(mStatus)) {
|
||||
mStatus = status;
|
||||
|
@ -37,8 +37,8 @@ import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.LevelListDrawable;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.media.MediaRouter;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Handler;
|
||||
@ -61,6 +61,7 @@ import android.view.WindowManagerGlobal;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.internal.app.MediaRouteDialogPresenter;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.phone.QuickSettingsModel.ActivityState;
|
||||
import com.android.systemui.statusbar.phone.QuickSettingsModel.BluetoothState;
|
||||
@ -676,7 +677,21 @@ class QuickSettings {
|
||||
remoteDisplayTile.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
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,
|
||||
|
@ -684,8 +684,8 @@ class QuickSettingsModel implements BluetoothStateChangeCallback,
|
||||
private void updateRemoteDisplays() {
|
||||
MediaRouter.RouteInfo connectedRoute = mMediaRouter.getSelectedRoute(
|
||||
MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
|
||||
boolean enabled = connectedRoute != null && (connectedRoute.getSupportedTypes()
|
||||
& MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY) != 0;
|
||||
boolean enabled = connectedRoute != null
|
||||
&& connectedRoute.matchesTypes(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
|
||||
boolean connecting;
|
||||
if (enabled) {
|
||||
connecting = connectedRoute.isConnecting();
|
||||
@ -695,7 +695,7 @@ class QuickSettingsModel implements BluetoothStateChangeCallback,
|
||||
final int count = mMediaRouter.getRouteCount();
|
||||
for (int i = 0; i < count; 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;
|
||||
break;
|
||||
}
|
||||
|
Reference in New Issue
Block a user