summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Powell <adamp@google.com>2012-06-12 16:59:45 -0700
committerAdam Powell <adamp@google.com>2012-06-13 11:42:10 -0700
commit70e11e50eecfc8f1dfb76316d099e4331ebd28f7 (patch)
treec2a2a912ec851734e73de72d0b04a0b744cebafb
parentd772c4878e2207795e92b26a462fd02bca7e3c2e (diff)
downloadframeworks_base-70e11e50eecfc8f1dfb76316d099e4331ebd28f7.zip
frameworks_base-70e11e50eecfc8f1dfb76316d099e4331ebd28f7.tar.gz
frameworks_base-70e11e50eecfc8f1dfb76316d099e4331ebd28f7.tar.bz2
MediaRouter dialog
Add the dialog behavior for MediaRouteActionProvider/MediaRouteButton. Still TODO: * Switch audio icon based on source; speaker/bt/user * Rig up volume slider * Rig up item icons * Rig up group button for groupable categories * Make grouping work Change-Id: I3f992516b184d5ae940ddb7bbb7f94ff58914589
-rw-r--r--api/current.txt3
-rw-r--r--core/java/android/app/MediaRouteActionProvider.java32
-rw-r--r--core/java/android/app/MediaRouteButton.java50
-rw-r--r--core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java341
-rw-r--r--core/res/res/layout/media_route_chooser_layout.xml54
-rw-r--r--core/res/res/layout/media_route_list_item.xml56
-rw-r--r--core/res/res/layout/media_route_list_item_section_header.xml34
-rw-r--r--core/res/res/layout/media_route_list_item_top_header.xml29
-rw-r--r--core/res/res/values/public.xml7
-rwxr-xr-xcore/res/res/values/strings.xml14
-rw-r--r--media/java/android/media/MediaRouter.java24
11 files changed, 635 insertions, 9 deletions
diff --git a/api/current.txt b/api/current.txt
index 55c9e28..f5bc504 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3690,6 +3690,7 @@ package android.app {
method public int getRouteTypes();
method public void setExtendedSettingsClickListener(android.view.View.OnClickListener);
method public void setRouteTypes(int);
+ method public void showDialog();
}
public class NativeActivity extends android.app.Activity implements android.view.InputQueue.Callback android.view.SurfaceHolder.Callback2 android.view.ViewTreeObserver.OnGlobalLayoutListener {
@@ -11575,11 +11576,13 @@ package android.media {
}
public static class MediaRouter.UserRouteInfo extends android.media.MediaRouter.RouteInfo {
+ method public java.lang.Object getTag();
method public void setIconDrawable(android.graphics.drawable.Drawable);
method public void setIconResource(int);
method public void setName(java.lang.CharSequence);
method public void setRemoteControlClient(android.media.RemoteControlClient);
method public void setStatus(java.lang.CharSequence);
+ method public void setTag(java.lang.Object);
}
public class MediaScannerConnection implements android.content.ServiceConnection {
diff --git a/core/java/android/app/MediaRouteActionProvider.java b/core/java/android/app/MediaRouteActionProvider.java
index 5fe08ec..4860182 100644
--- a/core/java/android/app/MediaRouteActionProvider.java
+++ b/core/java/android/app/MediaRouteActionProvider.java
@@ -16,7 +16,10 @@
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;
@@ -83,10 +86,37 @@ public class MediaRouteActionProvider extends ActionProvider {
@Override
public boolean onPerformDefaultAction() {
- // Show routing dialog
+ 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!");
+ 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) {
diff --git a/core/java/android/app/MediaRouteButton.java b/core/java/android/app/MediaRouteButton.java
index 385241c..a4eebda 100644
--- a/core/java/android/app/MediaRouteButton.java
+++ b/core/java/android/app/MediaRouteButton.java
@@ -17,8 +17,10 @@
package android.app;
import com.android.internal.R;
+import com.android.internal.app.MediaRouteChooserDialogFragment;
import android.content.Context;
+import android.content.ContextWrapper;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
@@ -44,6 +46,7 @@ public class MediaRouteButton extends View {
private int mMinHeight;
private OnClickListener mExtendedSettingsClickListener;
+ private MediaRouteChooserDialogFragment mDialogFragment;
private static final int[] ACTIVATED_STATE_SET = {
R.attr.state_activated
@@ -112,7 +115,7 @@ public class MediaRouteButton extends View {
}
}
} else {
- Log.d(TAG, "TODO: Implement the dialog!");
+ showDialog();
}
return handled;
@@ -263,8 +266,51 @@ public class MediaRouteButton extends View {
}
public void setExtendedSettingsClickListener(OnClickListener listener) {
- // TODO: if dialog is already open, propagate so that it updates live.
mExtendedSettingsClickListener = listener;
+ if (mDialogFragment != null) {
+ mDialogFragment.setExtendedSettingsClickListener(listener);
+ }
+ }
+
+ /**
+ * 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 {
diff --git a/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java b/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java
new file mode 100644
index 0000000..000eee7
--- /dev/null
+++ b/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java
@@ -0,0 +1,341 @@
+/*
+ * 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.app;
+
+import com.android.internal.R;
+
+import android.app.Activity;
+import android.app.DialogFragment;
+import android.app.MediaRouteActionProvider;
+import android.app.MediaRouteButton;
+import android.content.Context;
+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.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * This class implements the route chooser dialog for {@link MediaRouter}.
+ *
+ * @see MediaRouteButton
+ * @see MediaRouteActionProvider
+ */
+public class MediaRouteChooserDialogFragment extends DialogFragment {
+ private static final String TAG = "MediaRouteChooserDialogFragment";
+ public static final String FRAGMENT_TAG = "android:MediaRouteChooserDialogFragment";
+
+ MediaRouter mRouter;
+ private int mRouteTypes;
+
+ private LauncherListener mLauncherListener;
+ private View.OnClickListener mExtendedSettingsListener;
+ private RouteAdapter mAdapter;
+ private ListView mListView;
+
+ public MediaRouteChooserDialogFragment() {
+ setStyle(STYLE_NO_TITLE, 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);
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ if (mLauncherListener != null) {
+ mLauncherListener.onDetached(this);
+ }
+ if (mAdapter != null) {
+ mRouter.removeCallback(mAdapter);
+ mAdapter = null;
+ }
+ mRouter = null;
+ }
+
+ /**
+ * Implemented by the MediaRouteButton that launched this dialog
+ */
+ public interface LauncherListener {
+ public void onDetached(MediaRouteChooserDialogFragment detachedFragment);
+ }
+
+ public void setExtendedSettingsClickListener(View.OnClickListener listener) {
+ mExtendedSettingsListener = listener;
+ }
+
+ public void setRouteTypes(int types) {
+ mRouteTypes = types;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ final View layout = inflater.inflate(R.layout.media_route_chooser_layout, container, false);
+ final View extendedSettingsButton = layout.findViewById(R.id.extended_settings);
+
+ if (mExtendedSettingsListener != null) {
+ extendedSettingsButton.setVisibility(View.VISIBLE);
+ extendedSettingsButton.setOnClickListener(mExtendedSettingsListener);
+ }
+
+ final ListView list = (ListView) layout.findViewById(R.id.list);
+ list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+ list.setAdapter(mAdapter = new RouteAdapter(inflater));
+ list.setItemChecked(mAdapter.getSelectedRoutePosition(), true);
+ list.setOnItemClickListener(mAdapter);
+
+ mListView = list;
+ mRouter.addCallback(mRouteTypes, mAdapter);
+
+ return layout;
+ }
+
+ 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
+ };
+
+ private class RouteAdapter extends BaseAdapter implements MediaRouter.Callback,
+ 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 int mSelectedItemPosition;
+ private final ArrayList<Object> mItems = new ArrayList<Object>();
+ private final LayoutInflater mInflater;
+
+ RouteAdapter(LayoutInflater inflater) {
+ mInflater = inflater;
+ update();
+ }
+
+ void update() {
+ // TODO this is kind of naive, but our data sets are going to be
+ // fairly small on average.
+ mItems.clear();
+
+ final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
+
+ final ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
+ final int catCount = mRouter.getCategoryCount();
+ for (int i = 0; i < catCount; i++) {
+ final RouteCategory cat = mRouter.getCategoryAt(i);
+ cat.getRoutes(routes);
+
+ mItems.add(cat);
+
+ final int routeCount = routes.size();
+ for (int j = 0; j < routeCount; j++) {
+ final RouteInfo info = routes.get(j);
+ if (info == selectedRoute) {
+ mSelectedItemPosition = mItems.size();
+ }
+ mItems.add(info);
+ }
+ }
+
+ notifyDataSetChanged();
+ if (mListView != null) {
+ mListView.setItemChecked(mSelectedItemPosition, true);
+ }
+ }
+
+ @Override
+ public int getCount() {
+ return mItems.size();
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 3;
+ }
+
+ @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 {
+ return VIEW_ROUTE;
+ }
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return getItemViewType(position) == VIEW_ROUTE;
+ }
+
+ @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.text1 = (TextView) convertView.findViewById(R.id.text1);
+ holder.text2 = (TextView) convertView.findViewById(R.id.text2);
+ convertView.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
+
+ if (viewType == VIEW_ROUTE) {
+ bindItemView(position, holder);
+ } else {
+ bindHeaderView(position, holder);
+ }
+
+ return convertView;
+ }
+
+ void bindItemView(int position, ViewHolder holder) {
+ RouteInfo info = (RouteInfo) mItems.get(position);
+ holder.text1.setText(info.getName());
+ final CharSequence status = info.getStatus();
+ if (TextUtils.isEmpty(status)) {
+ holder.text2.setVisibility(View.GONE);
+ } else {
+ holder.text2.setVisibility(View.VISIBLE);
+ holder.text2.setText(status);
+ }
+ }
+
+ void bindHeaderView(int position, ViewHolder holder) {
+ RouteCategory cat = (RouteCategory) mItems.get(position);
+ holder.text1.setText(cat.getName());
+ }
+
+ public int getSelectedRoutePosition() {
+ return mSelectedItemPosition;
+ }
+
+ @Override
+ public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
+ update();
+ }
+
+ @Override
+ public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
+ update();
+ }
+
+ @Override
+ public void onRouteAdded(MediaRouter router, RouteInfo info) {
+ update();
+ }
+
+ @Override
+ public void onRouteRemoved(MediaRouter router, RouteInfo info) {
+ update();
+ }
+
+ @Override
+ public void onRouteChanged(MediaRouter router, RouteInfo info) {
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void onRouteGrouped(MediaRouter router, RouteInfo info,
+ RouteGroup group, int index) {
+ update();
+ }
+
+ @Override
+ public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
+ update();
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ ListView lv = (ListView) parent;
+ final Object item = getItem(lv.getCheckedItemPosition());
+ if (!(item instanceof RouteInfo)) {
+ // Oops. Stale event running around? Skip it.
+ return;
+ }
+ mRouter.selectRoute(mRouteTypes, (RouteInfo) item);
+ dismiss();
+ }
+ }
+
+ private static class ViewHolder {
+ public TextView text1;
+ public TextView text2;
+ }
+
+ private class GroupAdapter extends BaseAdapter {
+ @Override
+ public int getCount() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+ }
+}
diff --git a/core/res/res/layout/media_route_chooser_layout.xml b/core/res/res/layout/media_route_chooser_layout.xml
new file mode 100644
index 0000000..320d6de
--- /dev/null
+++ b/core/res/res/layout/media_route_chooser_layout.xml
@@ -0,0 +1,54 @@
+<?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_marginLeft="8dp"
+ android:layout_marginRight="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" />
+ <Button android:id="@+id/done"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="?android:attr/borderlessButtonStyle"
+ android:text="@string/media_route_chooser_grouping_done"
+ android:visibility="gone" />
+</LinearLayout>
diff --git a/core/res/res/layout/media_route_list_item.xml b/core/res/res/layout/media_route_list_item.xml
new file mode 100644
index 0000000..d457c6c
--- /dev/null
+++ b/core/res/res/layout/media_route_list_item.xml
@@ -0,0 +1,56 @@
+<?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="?android:attr/listPreferredItemHeight"
+ android:background="?android:attr/activatedBackgroundIndicator"
+ 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="left|center_vertical"
+ android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+ android:paddingRight="?android:attr/listPreferredItemPaddingRight">
+
+ <TextView android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView android:id="@android:id/text2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </LinearLayout>
+
+ <!-- TODO Make this not glow when pressed from above, and give it a divider. -->
+ <ImageButton android:layout_width="56dp"
+ android:layout_height="56dp"
+ android:id="@+id/group_button"
+ android:background="?android:attr/selectableItemBackground"
+ android:scaleType="center"
+ android:visibility="gone" />
+
+</LinearLayout>
diff --git a/core/res/res/layout/media_route_list_item_section_header.xml b/core/res/res/layout/media_route_list_item_section_header.xml
new file mode 100644
index 0000000..04bd0ea
--- /dev/null
+++ b/core/res/res/layout/media_route_list_item_section_header.xml
@@ -0,0 +1,34 @@
+<?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:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+ android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+ android:minHeight="24dp"
+ />
+</FrameLayout>
diff --git a/core/res/res/layout/media_route_list_item_top_header.xml b/core/res/res/layout/media_route_list_item_top_header.xml
new file mode 100644
index 0000000..75decd3
--- /dev/null
+++ b/core/res/res/layout/media_route_list_item_top_header.xml
@@ -0,0 +1,29 @@
+<?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:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+ android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+ android:minHeight="24dp"
+/>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e1f15cf..0af8534 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1166,6 +1166,13 @@
<java-symbol type="attr" name="mediaRouteButtonStyle" />
<java-symbol type="attr" name="externalRouteEnabledDrawable" />
+ <java-symbol type="layout" name="media_route_chooser_layout" />
+ <java-symbol type="id" name="extended_settings" />
+ <java-symbol type="id" name="done" />
+ <java-symbol type="layout" name="media_route_list_item_top_header" />
+ <java-symbol type="layout" name="media_route_list_item_section_header" />
+ <java-symbol type="layout" name="media_route_list_item" />
+ <java-symbol type="id" name="group_button" />
<!-- From android.policy -->
<java-symbol type="anim" name="app_starting_exit" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 53914a8..967d700 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3551,24 +3551,26 @@
<string name="activity_resolver_use_once">Just once</string>
<!-- Name of the default audio route for tablets when nothing
- is connected to a headphone or other wired audio output jack. [CHAR LIMIT=25] -->
+ is connected to a headphone or other wired audio output jack. [CHAR LIMIT=50] -->
<string name="default_audio_route_name" product="tablet">Tablet speakers</string>
<!-- Name of the default audio route when nothing is connected to
- a headphone or other wired audio output jack. [CHAR LIMIT=25] -->
+ a headphone or other wired audio output jack. [CHAR LIMIT=50] -->
<string name="default_audio_route_name" product="default">Phone speaker</string>
<!-- Name of the default audio route when wired headphones are
- connected. [CHAR LIMIT=25] -->
+ connected. [CHAR LIMIT=50] -->
<string name="default_audio_route_name_headphones">Headphones</string>
- <!-- Name of the default audio route when an audio dock is connected. [CHAR LIMIT=25] -->
+ <!-- Name of the default audio route when an audio dock is connected. [CHAR LIMIT=50] -->
<string name="default_audio_route_name_dock_speakers">Dock speakers</string>
- <!-- Name of the default audio route when HDMI is connected. [CHAR LIMIT=25] -->
+ <!-- Name of the default audio route when HDMI is connected. [CHAR LIMIT=50] -->
<string name="default_audio_route_name_hdmi">HDMI audio</string>
- <!-- Name of the default audio route category. [CHAR LIMIT=25] -->
+ <!-- Name of the default audio route category. [CHAR LIMIT=50] -->
<string name="default_audio_route_category_name">System</string>
+ <!-- "Done" button for MediaRouter chooser dialog when grouping routes. [CHAR LIMIT=NONE] -->
+ <string name="media_route_chooser_grouping_done">Done</string>
</resources>
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 38fe363..b0657ff 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -581,6 +581,7 @@ public class MediaRouter {
*/
public static class UserRouteInfo extends RouteInfo {
RemoteControlClient mRcc;
+ private Object mTag;
UserRouteInfo(RouteCategory category) {
super(category);
@@ -638,6 +639,29 @@ public class MediaRouter {
public void setIconResource(int resId) {
setIconDrawable(sStatic.mResources.getDrawable(resId));
}
+
+ /**
+ * Set an application-specific tag object for this route.
+ * The application may use this to store arbitrary data associated with the
+ * route for internal tracking.
+ *
+ * <p>Note that the lifespan of a route may be well past the lifespan of
+ * an Activity or other Context; take care that objects you store here
+ * will not keep more data in memory alive than you intend.</p>
+ *
+ * @param tag Arbitrary, app-specific data for this route to hold for later use
+ */
+ public void setTag(Object tag) {
+ mTag = tag;
+ }
+
+ /**
+ * @return The tag object previously set by the application
+ * @see #setTag(Object)
+ */
+ public Object getTag() {
+ return mTag;
+ }
}
/**