summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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/android/app/Notification.java4
-rw-r--r--core/java/android/text/Layout.java14
-rw-r--r--core/java/android/text/StaticLayout.java6
-rw-r--r--core/java/android/webkit/WebViewClassic.java4
-rw-r--r--core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java690
-rw-r--r--core/java/com/android/internal/view/CheckableLinearLayout.java65
-rw-r--r--core/java/com/android/internal/view/ImageButtonNoParentPress.java44
-rw-r--r--core/res/res/drawable-hdpi/ic_media_group_collapse.pngbin0 -> 933 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_media_group_expand.pngbin0 -> 984 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_group_collapse.pngbin0 -> 720 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_group_expand.pngbin0 -> 751 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_media_group_collapse.pngbin0 -> 1358 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_media_group_expand.pngbin0 -> 1356 bytes
-rw-r--r--core/res/res/drawable/item_background_activated_holo_dark.xml27
-rw-r--r--core/res/res/layout/media_route_chooser_layout.xml48
-rw-r--r--core/res/res/layout/media_route_list_item.xml61
-rw-r--r--core/res/res/layout/media_route_list_item_checkable.xml59
-rw-r--r--core/res/res/layout/media_route_list_item_collapse_group.xml39
-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/layout/notification_template_big_text.xml3
-rw-r--r--core/res/res/values/public.xml9
-rwxr-xr-xcore/res/res/values/strings.xml17
-rw-r--r--media/java/android/media/MediaRouter.java97
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/videoproc/BackDropperFilter.java28
-rw-r--r--packages/SystemUI/res/drawable/system_bar_notification_header_bg.xml20
-rw-r--r--packages/SystemUI/res/layout/system_bar_notification_panel_title.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/ExpandHelper.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java3
-rw-r--r--policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java2
-rw-r--r--policy/src/com/android/internal/policy/impl/LockPatternKeyguardViewProperties.java1
-rw-r--r--services/java/com/android/server/LocationManagerService.java4
-rw-r--r--services/java/com/android/server/accessibility/TouchExplorer.java55
-rw-r--r--services/java/com/android/server/wm/WindowAnimator.java3
-rwxr-xr-xservices/java/com/android/server/wm/WindowManagerService.java40
-rw-r--r--services/java/com/android/server/wm/WindowStateAnimator.java3
42 files changed, 1491 insertions, 83 deletions
diff --git a/api/current.txt b/api/current.txt
index 9f1e152b46b0..a62698ef351b 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 5fe08ecdb545..4860182aa4d5 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 385241ced8c4..a4eebda0ca0e 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/android/app/Notification.java b/core/java/android/app/Notification.java
index ec35a3f5214a..bb497c02719e 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1670,6 +1670,9 @@ public class Notification implements Parcelable
contentView.setTextViewText(R.id.text, overflowText);
contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE);
contentView.setViewVisibility(R.id.line3, View.VISIBLE);
+ } else {
+ contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
+ contentView.setViewVisibility(R.id.line3, View.GONE);
}
return contentView;
@@ -1812,6 +1815,7 @@ public class Notification implements Parcelable
// Remove the content text so line3 only shows if you have a summary
final boolean hadThreeLines = (mBuilder.mContentText != null && mBuilder.mSubText != null);
mBuilder.mContentText = null;
+
RemoteViews contentView = getStandardView(R.layout.notification_template_big_text);
if (hadThreeLines) {
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 12f16fd3fae4..d2bed489e23d 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1696,8 +1696,14 @@ public abstract class Layout {
return text.getSpans(start, end, type);
}
+ private char getEllipsisChar(TextUtils.TruncateAt method) {
+ return (method == TextUtils.TruncateAt.END_SMALL) ?
+ ELLIPSIS_TWO_DOTS[0] :
+ ELLIPSIS_NORMAL[0];
+ }
+
private void ellipsize(int start, int end, int line,
- char[] dest, int destoff) {
+ char[] dest, int destoff, TextUtils.TruncateAt method) {
int ellipsisCount = getEllipsisCount(line);
if (ellipsisCount == 0) {
@@ -1711,7 +1717,7 @@ public abstract class Layout {
char c;
if (i == ellipsisStart) {
- c = '\u2026'; // ellipsis
+ c = getEllipsisChar(method); // ellipsis
} else {
c = '\uFEFF'; // 0-width space
}
@@ -1785,7 +1791,7 @@ public abstract class Layout {
TextUtils.getChars(mText, start, end, dest, destoff);
for (int i = line1; i <= line2; i++) {
- mLayout.ellipsize(start, end, i, dest, destoff);
+ mLayout.ellipsize(start, end, i, dest, destoff, mMethod);
}
}
@@ -1890,4 +1896,6 @@ public abstract class Layout {
/* package */ static final Directions DIRS_ALL_RIGHT_TO_LEFT =
new Directions(new int[] { 0, RUN_LENGTH_MASK | RUN_RTL_FLAG });
+ /* package */ static final char[] ELLIPSIS_NORMAL = { '\u2026' }; // this is "..."
+ /* package */ static final char[] ELLIPSIS_TWO_DOTS = { '\u2025' }; // this is ".."
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 299e1159c27a..6973b2e39ada 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -745,7 +745,8 @@ public class StaticLayout extends Layout {
}
float ellipsisWidth = paint.measureText(
- (where == TextUtils.TruncateAt.END_SMALL) ? ELLIPSIS_TWO_DOTS : ELLIPSIS_NORMAL);
+ (where == TextUtils.TruncateAt.END_SMALL) ?
+ ELLIPSIS_TWO_DOTS : ELLIPSIS_NORMAL, 0, 1);
int ellipsisStart = 0;
int ellipsisCount = 0;
int len = lineEnd - lineStart;
@@ -985,9 +986,6 @@ public class StaticLayout extends Layout {
private static final double EXTRA_ROUNDING = 0.5;
- private static final String ELLIPSIS_NORMAL = "\u2026"; // this is "..."
- private static final String ELLIPSIS_TWO_DOTS = "\u2025"; // this is ".."
-
private static final int CHAR_FIRST_HIGH_SURROGATE = 0xD800;
private static final int CHAR_LAST_LOW_SURROGATE = 0xDFFF;
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 611742b113a3..da61e7492a94 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -1316,7 +1316,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
case WebViewInputDispatcher.EVENT_TYPE_LONG_PRESS:
HitTestResult hitTest = getHitTestResult();
if (hitTest != null) {
- performLongClick();
+ mWebView.performLongClick();
}
break;
case WebViewInputDispatcher.EVENT_TYPE_DOUBLE_TAP:
@@ -7265,7 +7265,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
// the states
mGotCenterDown = false;
mTrackballDown = false;
- performLongClick();
+ mWebView.performLongClick();
break;
case WEBCORE_NEED_TOUCH_EVENTS:
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 000000000000..bfcfdfa86a9b
--- /dev/null
+++ b/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java
@@ -0,0 +1,690 @@
+/*
+ * 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.Dialog;
+import android.app.DialogFragment;
+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.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.ImageButton;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+/**
+ * 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";
+
+ 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 static final int[] GROUP_ITEM_LAYOUTS = new int[] {
+ R.layout.media_route_list_item_top_header,
+ 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 GroupAdapter mGroupAdapter;
+ private ListView mListView;
+
+ static final RouteComparator sComparator = new RouteComparator();
+
+ 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 (mGroupAdapter != null) {
+ mRouter.removeCallback(mGroupAdapter);
+ mGroupAdapter = null;
+ }
+ if (mAdapter != null) {
+ mRouter.removeCallback(mAdapter);
+ mAdapter = null;
+ }
+ mInflater = 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) {
+ mInflater = inflater;
+ 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.setItemsCanFocus(true);
+ list.setAdapter(mAdapter = new RouteAdapter());
+ list.setItemChecked(mAdapter.getSelectedRoutePosition(), true);
+ list.setOnItemClickListener(mAdapter);
+
+ mListView = list;
+ mRouter.addCallback(mRouteTypes, mAdapter);
+
+ return layout;
+ }
+
+ void onExpandGroup(RouteGroup info) {
+ mGroupAdapter = new GroupAdapter(info);
+ mRouter.addCallback(mRouteTypes, mGroupAdapter);
+ mListView.setAdapter(mGroupAdapter);
+ mListView.setOnItemClickListener(mGroupAdapter);
+ mListView.setItemsCanFocus(false);
+ mListView.clearChoices();
+ mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+ mGroupAdapter.initCheckedItems();
+
+ getDialog().setCanceledOnTouchOutside(false);
+ }
+
+ void onDoneGrouping() {
+ mListView.setAdapter(mAdapter);
+ mListView.setOnItemClickListener(mAdapter);
+ mListView.setItemsCanFocus(true);
+ mListView.clearChoices();
+ mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+ mListView.setItemChecked(mAdapter.getSelectedRoutePosition(), true);
+
+ mRouter.removeCallback(mGroupAdapter);
+ mGroupAdapter = null;
+
+ getDialog().setCanceledOnTouchOutside(true);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return new RouteChooserDialog(getActivity(), getTheme());
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (mListView != null) {
+ if (mGroupAdapter != null) {
+ mGroupAdapter.initCheckedItems();
+ } else {
+ mListView.setItemChecked(mAdapter.getSelectedRoutePosition(), true);
+ }
+ }
+ }
+
+ private static class ViewHolder {
+ public TextView text1;
+ public TextView text2;
+ public ImageView icon;
+ public ImageButton expandGroupButton;
+ public RouteAdapter.ExpandGroupListener expandGroupListener;
+ public int position;
+ }
+
+ 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>();
+
+ RouteAdapter() {
+ 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.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.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;
+ }
+
+ 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);
+ }
+ 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.isGroupable()) {
+ final RouteGroup group = (RouteGroup) info;
+ canGroup = group.getRouteCount() > 1 ||
+ getItemViewType(position - 1) == VIEW_ROUTE ||
+ (position < getCount() - 1 && getItemViewType(position + 1) == VIEW_ROUTE);
+ }
+ 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());
+ }
+
+ 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();
+ }
+
+ 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.
+ onExpandGroup((RouteGroup) getItem(position));
+ }
+ }
+ }
+
+ private class GroupAdapter extends BaseAdapter implements MediaRouter.Callback,
+ ListView.OnItemClickListener {
+ private static final int VIEW_HEADER = 0;
+ private static final int VIEW_ROUTE = 1;
+ private static final int VIEW_DONE = 2;
+
+ private RouteGroup mPrimary;
+ private RouteCategory mCategory;
+ private final ArrayList<RouteInfo> mTempList = new ArrayList<RouteInfo>();
+ private final ArrayList<RouteInfo> mFlatRoutes = new ArrayList<RouteInfo>();
+ private boolean mIgnoreUpdates;
+
+ public GroupAdapter(RouteGroup primary) {
+ mPrimary = primary;
+ mCategory = primary.getCategory();
+ update();
+ }
+
+ @Override
+ public int getCount() {
+ return mFlatRoutes.size() + 2;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 3;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (position == 0) {
+ return VIEW_HEADER;
+ } else if (position == getCount() - 1) {
+ return VIEW_DONE;
+ }
+ return VIEW_ROUTE;
+ }
+
+ void update() {
+ if (mIgnoreUpdates) return;
+ mFlatRoutes.clear();
+ mCategory.getRoutes(mTempList);
+
+ // Unpack groups and flatten for presentation
+ final int topCount = mTempList.size();
+ for (int i = 0; i < topCount; i++) {
+ final RouteInfo route = mTempList.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);
+ mFlatRoutes.add(innerRoute);
+ }
+ } else {
+ mFlatRoutes.add(route);
+ }
+ }
+ mTempList.clear();
+
+ // Sort by name. This will keep the route positions relatively stable even though they
+ // will be repeatedly added and removed.
+ Collections.sort(mFlatRoutes, sComparator);
+ notifyDataSetChanged();
+ }
+
+ void initCheckedItems() {
+ if (mIgnoreUpdates) return;
+ mListView.clearChoices();
+ int count = mFlatRoutes.size();
+ for (int i = 0; i < count; i++){
+ final RouteInfo route = mFlatRoutes.get(i);
+ if (route.getGroup() == mPrimary) {
+ mListView.setItemChecked(i + 1, true);
+ }
+ }
+ }
+
+ @Override
+ public Object getItem(int position) {
+ if (position == 0) {
+ return mCategory;
+ } else if (position == getCount() - 1) {
+ return null; // Done
+ }
+ return mFlatRoutes.get(position - 1);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return position > 0;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final int viewType = getItemViewType(position);
+
+ ViewHolder holder;
+ if (convertView == null) {
+ convertView = mInflater.inflate(GROUP_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);
+ convertView.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ holder.position = position;
+ }
+
+ if (viewType == VIEW_ROUTE) {
+ bindItemView(position, holder);
+ } else if (viewType == VIEW_HEADER) {
+ bindHeaderView(position, holder);
+ }
+
+ return convertView;
+ }
+
+ void bindItemView(int position, ViewHolder holder) {
+ RouteInfo info = (RouteInfo) getItem(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) {
+ holder.text1.setText(mCategory.getName());
+ }
+
+ @Override
+ public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
+ }
+
+ @Override
+ public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
+ }
+
+ @Override
+ public void onRouteAdded(MediaRouter router, RouteInfo info) {
+ update();
+ initCheckedItems();
+ }
+
+ @Override
+ public void onRouteRemoved(MediaRouter router, RouteInfo info) {
+ if (info == mPrimary) {
+ // Can't keep grouping, clean it up.
+ onDoneGrouping();
+ } else {
+ update();
+ initCheckedItems();
+ }
+ }
+
+ @Override
+ public void onRouteChanged(MediaRouter router, RouteInfo info) {
+ update();
+ }
+
+ @Override
+ public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group, int index) {
+ update();
+ initCheckedItems();
+ }
+
+ @Override
+ public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
+ update();
+ initCheckedItems();
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ if (getItemViewType(position) == VIEW_DONE) {
+ onDoneGrouping();
+ return;
+ }
+
+ final ListView lv = (ListView) parent;
+ final RouteInfo route = mFlatRoutes.get(position - 1);
+ final boolean checked = lv.isItemChecked(position);
+
+ mIgnoreUpdates = true;
+ RouteGroup oldGroup = route.getGroup();
+ if (checked && oldGroup != mPrimary) {
+ // Assumption: in a groupable category oldGroup will never be null.
+ oldGroup.removeRoute(route);
+
+ // If the group is now empty, remove the group too.
+ if (oldGroup.getRouteCount() == 0) {
+ 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.selectRoute(mRouteTypes, mPrimary);
+ }
+ mRouter.removeRouteInt(oldGroup);
+ }
+
+ mPrimary.addRoute(route);
+ } else if (!checked) {
+ if (mPrimary.getRouteCount() > 1) {
+ mPrimary.removeRoute(route);
+
+ // In a groupable category this will add the route into its own new group.
+ mRouter.addRouteInt(route);
+ } else {
+ // We're about to remove the last route.
+ // Don't let this happen, as it would be silly.
+ // Turn the checkmark back on again. Silly user!
+ lv.setItemChecked(position, true);
+ }
+ }
+ mIgnoreUpdates = false;
+ update();
+ initCheckedItems();
+ }
+ }
+
+ static class RouteComparator implements Comparator<RouteInfo> {
+ @Override
+ public int compare(RouteInfo lhs, RouteInfo rhs) {
+ return lhs.getName().toString().compareTo(rhs.getName().toString());
+ }
+ }
+
+ class RouteChooserDialog extends Dialog {
+ public RouteChooserDialog(Context context, int theme) {
+ super(context, theme);
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (mGroupAdapter != null) {
+ onDoneGrouping();
+ } else {
+ super.onBackPressed();
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/view/CheckableLinearLayout.java b/core/java/com/android/internal/view/CheckableLinearLayout.java
new file mode 100644
index 000000000000..3fb7cec0e689
--- /dev/null
+++ b/core/java/com/android/internal/view/CheckableLinearLayout.java
@@ -0,0 +1,65 @@
+/*
+ * 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();
+ }
+}
diff --git a/core/java/com/android/internal/view/ImageButtonNoParentPress.java b/core/java/com/android/internal/view/ImageButtonNoParentPress.java
new file mode 100644
index 000000000000..a6cfd8612191
--- /dev/null
+++ b/core/java/com/android/internal/view/ImageButtonNoParentPress.java
@@ -0,0 +1,44 @@
+/*
+ * 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 android.content.Context;
+import android.util.AttributeSet;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+
+public class ImageButtonNoParentPress extends ImageButton {
+
+ public ImageButtonNoParentPress(Context context) {
+ super(context);
+ }
+
+ public ImageButtonNoParentPress(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public ImageButtonNoParentPress(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ public void setPressed(boolean pressed) {
+ // Normally parents propagate pressed state to their children.
+ // We don't want that to happen here; only press if our parent isn't.
+ super.setPressed(((ViewGroup) getParent()).isPressed() ? false : pressed);
+ }
+}
diff --git a/core/res/res/drawable-hdpi/ic_media_group_collapse.png b/core/res/res/drawable-hdpi/ic_media_group_collapse.png
new file mode 100644
index 000000000000..89abf2c21ac5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_group_collapse.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_group_expand.png b/core/res/res/drawable-hdpi/ic_media_group_expand.png
new file mode 100644
index 000000000000..d9470b2ef098
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_group_expand.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_group_collapse.png b/core/res/res/drawable-mdpi/ic_media_group_collapse.png
new file mode 100644
index 000000000000..34454ac3e7e8
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_group_collapse.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_group_expand.png b/core/res/res/drawable-mdpi/ic_media_group_expand.png
new file mode 100644
index 000000000000..8ce5a448d813
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_group_expand.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_group_collapse.png b/core/res/res/drawable-xhdpi/ic_media_group_collapse.png
new file mode 100644
index 000000000000..2fb7428f266f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_group_collapse.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_group_expand.png b/core/res/res/drawable-xhdpi/ic_media_group_expand.png
new file mode 100644
index 000000000000..5755b9da2f03
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_group_expand.png
Binary files differ
diff --git a/core/res/res/drawable/item_background_activated_holo_dark.xml b/core/res/res/drawable/item_background_activated_holo_dark.xml
new file mode 100644
index 000000000000..9cbf6abfac35
--- /dev/null
+++ b/core/res/res/drawable/item_background_activated_holo_dark.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
+ <item android:state_focused="true" android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/list_selector_disabled_holo_light" />
+ <item android:state_focused="true" android:state_enabled="false" android:drawable="@drawable/list_selector_disabled_holo_light" />
+ <item android:state_focused="true" android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_holo_light" />
+ <item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_holo_light" />
+ <item android:state_focused="true" android:drawable="@drawable/list_focused_holo" />
+ <item android:state_activated="true" android:drawable="@android:drawable/list_activated_holo" />
+ <item android:drawable="@color/transparent" />
+</selector>
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 000000000000..731c0d057630
--- /dev/null
+++ b/core/res/res/layout/media_route_chooser_layout.xml
@@ -0,0 +1,48 @@
+<?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" />
+</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 000000000000..ba1aaf2a2171
--- /dev/null
+++ b/core/res/res/layout/media_route_list_item.xml
@@ -0,0 +1,61 @@
+<?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="@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" />
+
+ <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: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>
+
+ <com.android.internal.view.ImageButtonNoParentPress
+ 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" />
+
+</LinearLayout>
diff --git a/core/res/res/layout/media_route_list_item_checkable.xml b/core/res/res/layout/media_route_list_item_checkable.xml
new file mode 100644
index 000000000000..f6ba09e1e7f8
--- /dev/null
+++ b/core/res/res/layout/media_route_list_item_checkable.xml
@@ -0,0 +1,59 @@
+<?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: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: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_marginRight="16dp"
+ android:id="@+id/check"
+ android:focusable="false"
+ android:clickable="false" />
+
+</com.android.internal.view.CheckableLinearLayout>
diff --git a/core/res/res/layout/media_route_list_item_collapse_group.xml b/core/res/res/layout/media_route_list_item_collapse_group.xml
new file mode 100644
index 000000000000..3f4b1c0843bb
--- /dev/null
+++ b/core/res/res/layout/media_route_list_item_collapse_group.xml
@@ -0,0 +1,39 @@
+<?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/listPreferredItemHeightSmall"
+ android:background="#19ffffff"
+ android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+ android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+ 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>
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 000000000000..04bd0ea58e4b
--- /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 000000000000..75decd38dc26
--- /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/layout/notification_template_big_text.xml b/core/res/res/layout/notification_template_big_text.xml
index d37788203294..0b3386b6ec8f 100644
--- a/core/res/res/layout/notification_template_big_text.xml
+++ b/core/res/res/layout/notification_template_big_text.xml
@@ -108,9 +108,8 @@
android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_marginBottom="8dp"
+ android:layout_marginBottom="10dp"
android:layout_marginRight="8dp"
- android:layout_marginTop="2dp"
android:singleLine="false"
android:visibility="gone"
android:maxLines="8"
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e1f15cff85a2..a6f2f49c66ae 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1166,6 +1166,15 @@
<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="layout" name="media_route_chooser_layout" />
+ <java-symbol type="layout" name="media_route_list_item_top_header" />
+ <java-symbol type="layout" name="media_route_list_item_section_header" />
+ <java-symbol type="layout" name="media_route_list_item" />
+ <java-symbol type="layout" name="media_route_list_item_checkable" />
+ <java-symbol type="layout" name="media_route_list_item_collapse_group" />
+ <java-symbol type="string" name="bluetooth_a2dp_audio_route_name" />
<!-- 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 53914a8bd531..da4d37a8c109 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3551,24 +3551,29 @@
<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>
+ <!-- Default name of the bluetooth a2dp audio route. [CHAR LIMIT=50] -->
+ <string name="bluetooth_a2dp_audio_route_name">Bluetooth audio</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 38fe3631886d..8488cd2058fb 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -237,6 +237,13 @@ public class MediaRouter {
addRoute(info);
}
+ /**
+ * @hide Framework use only
+ */
+ public void addRouteInt(RouteInfo info) {
+ addRoute(info);
+ }
+
static void addRoute(RouteInfo info) {
final RouteCategory cat = info.getCategory();
if (!sStatic.mCategories.contains(cat)) {
@@ -246,13 +253,10 @@ public class MediaRouter {
if (cat.isGroupable() && !(info instanceof RouteGroup)) {
// Enforce that any added route in a groupable category must be in a group.
final RouteGroup group = new RouteGroup(info.getCategory());
+ group.addRoute(info);
sStatic.mRoutes.add(group);
dispatchRouteAdded(group);
- final int at = group.getRouteCount();
- group.addRoute(info);
- dispatchRouteGrouped(info, group, at);
-
info = group;
} else {
sStatic.mRoutes.add(info);
@@ -282,13 +286,22 @@ public class MediaRouter {
public void clearUserRoutes() {
for (int i = 0; i < sStatic.mRoutes.size(); i++) {
final RouteInfo info = sStatic.mRoutes.get(i);
- if (info instanceof UserRouteInfo) {
+ // TODO Right now, RouteGroups only ever contain user routes.
+ // The code below will need to change if this assumption does.
+ if (info instanceof UserRouteInfo || info instanceof RouteGroup) {
removeRouteAt(i);
i--;
}
}
}
+ /**
+ * @hide internal use only
+ */
+ public void removeRouteInt(RouteInfo info) {
+ removeRoute(info);
+ }
+
static void removeRoute(RouteInfo info) {
if (sStatic.mRoutes.remove(info)) {
final RouteCategory removingCat = info.getCategory();
@@ -301,6 +314,11 @@ public class MediaRouter {
break;
}
}
+ if (info == sStatic.mSelectedRoute) {
+ // Removing the currently selected route? Select the default before we remove it.
+ // TODO: Be smarter about the route types here; this selects for all valid.
+ selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_USER, sStatic.mDefaultAudio);
+ }
if (!found) {
sStatic.mCategories.remove(removingCat);
}
@@ -321,6 +339,11 @@ public class MediaRouter {
break;
}
}
+ if (info == sStatic.mSelectedRoute) {
+ // Removing the currently selected route? Select the default before we remove it.
+ // TODO: Be smarter about the route types here; this selects for all valid.
+ selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_USER, sStatic.mDefaultAudio);
+ }
if (!found) {
sStatic.mCategories.remove(removingCat);
}
@@ -478,7 +501,8 @@ public class MediaRouter {
static void onA2dpDeviceConnected() {
final RouteInfo info = new RouteInfo(sStatic.mSystemCategory);
- info.mName = "Bluetooth"; // TODO Fetch the real name of the device
+ info.mName = sStatic.mResources.getString(
+ com.android.internal.R.string.bluetooth_a2dp_audio_route_name);
sStatic.mBluetoothA2dpRoute = info;
addRoute(sStatic.mBluetoothA2dpRoute);
}
@@ -567,9 +591,9 @@ public class MediaRouter {
@Override
public String toString() {
- String supportedTypes = typesToString(mSupportedTypes);
- return "RouteInfo{ name=" + mName + ", status=" + mStatus +
- " category=" + mCategory +
+ String supportedTypes = typesToString(getSupportedTypes());
+ return getClass().getSimpleName() + "{ name=" + getName() + ", status=" + getStatus() +
+ " category=" + getCategory() +
" supportedTypes=" + supportedTypes + "}";
}
}
@@ -581,6 +605,7 @@ public class MediaRouter {
*/
public static class UserRouteInfo extends RouteInfo {
RemoteControlClient mRcc;
+ private Object mTag;
UserRouteInfo(RouteCategory category) {
super(category);
@@ -638,6 +663,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;
+ }
}
/**
@@ -674,6 +722,7 @@ public class MediaRouter {
}
final int at = mRoutes.size();
mRoutes.add(route);
+ route.mGroup = this;
mUpdateName = true;
dispatchRouteGrouped(route, this, at);
routeUpdated();
@@ -696,6 +745,7 @@ public class MediaRouter {
" group category=" + mCategory + ")");
}
mRoutes.add(insertAt, route);
+ route.mGroup = this;
mUpdateName = true;
dispatchRouteGrouped(route, this, insertAt);
routeUpdated();
@@ -712,6 +762,7 @@ public class MediaRouter {
" is not a member of this group.");
}
mRoutes.remove(route);
+ route.mGroup = null;
mUpdateName = true;
dispatchRouteUngrouped(route, this);
routeUpdated();
@@ -724,6 +775,7 @@ public class MediaRouter {
*/
public void removeRoute(int index) {
RouteInfo route = mRoutes.remove(index);
+ route.mGroup = null;
mUpdateName = true;
dispatchRouteUngrouped(route, this);
routeUpdated();
@@ -775,6 +827,18 @@ public class MediaRouter {
setStatusInt(status);
}
+ @Override
+ void routeUpdated() {
+ int types = 0;
+ final int count = mRoutes.size();
+ for (int i = 0; i < count; i++) {
+ types |= mRoutes.get(i).mSupportedTypes;
+ }
+ mSupportedTypes = types;
+ mIcon = count == 1 ? mRoutes.get(0).getIconDrawable() : null;
+ super.routeUpdated();
+ }
+
void updateName() {
final StringBuilder sb = new StringBuilder();
final int count = mRoutes.size();
@@ -786,6 +850,19 @@ public class MediaRouter {
mName = sb.toString();
mUpdateName = false;
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(super.toString());
+ sb.append('[');
+ final int count = mRoutes.size();
+ for (int i = 0; i < count; i++) {
+ if (i > 0) sb.append(", ");
+ sb.append(mRoutes.get(i));
+ }
+ sb.append(']');
+ return sb.toString();
+ }
}
/**
@@ -860,7 +937,7 @@ public class MediaRouter {
public String toString() {
return "RouteCategory{ name=" + mName + " types=" + typesToString(mTypes) +
- " groupable=" + mGroupable + " routes=" + sStatic.mRoutes.size() + " }";
+ " groupable=" + mGroupable + " }";
}
}
diff --git a/media/mca/filterpacks/java/android/filterpacks/videoproc/BackDropperFilter.java b/media/mca/filterpacks/java/android/filterpacks/videoproc/BackDropperFilter.java
index 52c9fda0333d..78f7f3ee1d94 100644
--- a/media/mca/filterpacks/java/android/filterpacks/videoproc/BackDropperFilter.java
+++ b/media/mca/filterpacks/java/android/filterpacks/videoproc/BackDropperFilter.java
@@ -29,6 +29,7 @@ import android.filterfw.core.ShaderProgram;
import android.filterfw.format.ImageFormat;
import android.opengl.GLES20;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.util.Log;
import java.lang.ArrayIndexOutOfBoundsException;
@@ -510,6 +511,20 @@ public class BackDropperFilter extends Filter {
super(name);
mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+
+ String adjStr = SystemProperties.get("ro.media.effect.bgdropper.adj");
+ if (adjStr.length() > 0) {
+ try {
+ mAcceptStddev += Float.parseFloat(adjStr);
+ if (mLogVerbose) {
+ Log.v(TAG, "Adjusting accept threshold by " + adjStr +
+ ", now " + mAcceptStddev);
+ }
+ } catch (NumberFormatException e) {
+ Log.e(TAG,
+ "Badly formatted property ro.media.effect.bgdropper.adj: " + adjStr);
+ }
+ }
}
@Override
@@ -695,7 +710,6 @@ public class BackDropperFilter extends Filter {
mBgUpdateVarianceProgram.setHostValue("bg_adapt_rate", mAdaptRateLearning);
mBgUpdateVarianceProgram.setHostValue("fg_adapt_rate", mAdaptRateLearning);
mFrameCount = 0;
- mStartLearning = false;
}
// Select correct pingpong buffers
@@ -720,6 +734,11 @@ public class BackDropperFilter extends Filter {
mBgInput.setTextureParameter(GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_LINEAR_MIPMAP_NEAREST);
+ if (mStartLearning) {
+ copyShaderProgram.process(mVideoInput, mBgMean[inputIndex]);
+ mStartLearning = false;
+ }
+
// Process shaders
Frame[] distInputs = { mVideoInput, mBgMean[inputIndex], mBgVariance[inputIndex] };
mBgDistProgram.process(distInputs, mDistance);
@@ -765,7 +784,12 @@ public class BackDropperFilter extends Filter {
ByteBuffer mMaskAverageByteBuffer = mMaskAverage.getData();
byte[] mask_average = mMaskAverageByteBuffer.array();
int bi = (int)(mask_average[3] & 0xFF);
- if (mLogVerbose) Log.v(TAG, String.format("Mask_average is %d", bi));
+
+ if (mLogVerbose) {
+ Log.v(TAG,
+ String.format("Mask_average is %d, threshold is %d",
+ bi, DEFAULT_LEARNING_DONE_THRESHOLD));
+ }
if (bi >= DEFAULT_LEARNING_DONE_THRESHOLD) {
mStartLearning = true; // Restart learning
diff --git a/packages/SystemUI/res/drawable/system_bar_notification_header_bg.xml b/packages/SystemUI/res/drawable/system_bar_notification_header_bg.xml
new file mode 100644
index 000000000000..85f1ea215849
--- /dev/null
+++ b/packages/SystemUI/res/drawable/system_bar_notification_header_bg.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true" android:drawable="@*android:drawable/list_selector_pressed_holo_dark" />
+ <item android:drawable="@*android:drawable/list_selector_disabled_holo_dark" />
+</selector>
diff --git a/packages/SystemUI/res/layout/system_bar_notification_panel_title.xml b/packages/SystemUI/res/layout/system_bar_notification_panel_title.xml
index afe3b49aef1a..480d979cd488 100644
--- a/packages/SystemUI/res/layout/system_bar_notification_panel_title.xml
+++ b/packages/SystemUI/res/layout/system_bar_notification_panel_title.xml
@@ -18,7 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
android:id="@+id/title_area"
- android:background="@color/notification_panel_solid_background"
+ android:background="@drawable/system_bar_notification_header_bg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index df41d255fa89..48efbc7217e6 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -23,11 +23,14 @@ import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.util.Log;
+import android.view.Gravity;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
+import android.widget.ScrollView;
+import android.widget.FrameLayout;
public class ExpandHelper implements Gefingerpoken, OnClickListener {
public interface Callback {
@@ -38,7 +41,7 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
}
private static final String TAG = "ExpandHelper";
- protected static final boolean DEBUG = false;
+ protected static final boolean DEBUG = true;
private static final long EXPAND_DURATION = 250;
private static final long GLOW_DURATION = 150;
@@ -82,8 +85,11 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
private int mLargeSize;
private float mMaximumStretch;
+ private int mGravity;
+
private class ViewScaler {
View mView;
+
public ViewScaler() {}
public void setView(View v) {
mView = v;
@@ -119,6 +125,15 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
}
}
+ /**
+ * Handle expansion gestures to expand and contract children of the callback.
+ *
+ * @param context application context
+ * @param callback the container that holds the items to be manipulated
+ * @param small the smallest allowable size for the manuipulated items.
+ * @param large the largest allowable size for the manuipulated items.
+ * @param scoller if non-null also manipulate the scroll position to obey the gravity.
+ */
public ExpandHelper(Context context, Callback callback, int small, int large) {
mSmallSize = small;
mMaximumStretch = mSmallSize * STRETCH_INTERVAL;
@@ -126,7 +141,7 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
mContext = context;
mCallback = callback;
mScaler = new ViewScaler();
-
+ mGravity = Gravity.TOP;
mScaleAnimation = ObjectAnimator.ofFloat(mScaler, "height", 0f);
mScaleAnimation.setDuration(EXPAND_DURATION);
@@ -194,6 +209,7 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
span *= USE_SPAN ? 1f : 0f;
float drag = detector.getFocusY() - mInitialTouchFocusY;
drag *= USE_DRAG ? 1f : 0f;
+ drag *= mGravity == Gravity.BOTTOM ? -1f : 1f;
float pull = Math.abs(drag) + Math.abs(span) + 1f;
float hand = drag * Math.abs(drag) / pull + span * Math.abs(span) / pull;
if (DEBUG) Log.d(TAG, "current span handle is: " + hand);
@@ -227,6 +243,10 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
mEventSource = eventSource;
}
+ public void setGravity(int gravity) {
+ mGravity = gravity;
+ }
+
public void setGlow(float glow) {
if (!mGlowAnimationSet.isRunning() || glow == 0f) {
if (mGlowAnimationSet.isRunning()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 9c773a58623d..00d6d6f28c18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -225,11 +225,28 @@ public class NavigationBarView extends LinearLayout {
final boolean disableRecent = ((disabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0);
final boolean disableBack = ((disabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0);
+ setSlippery(disableHome && disableRecent && disableBack);
+
getBackButton() .setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE);
getHomeButton() .setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
}
+ public void setSlippery(boolean newSlippery) {
+ WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams();
+ if (lp != null) {
+ boolean oldSlippery = (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) != 0;
+ if (!oldSlippery && newSlippery) {
+ lp.flags |= WindowManager.LayoutParams.FLAG_SLIPPERY;
+ } else if (oldSlippery && !newSlippery) {
+ lp.flags &= ~WindowManager.LayoutParams.FLAG_SLIPPERY;
+ } else {
+ return;
+ }
+ WindowManagerImpl.getDefault().updateViewLayout(this, lp);
+ }
+ }
+
public void setMenuVisibility(final boolean show) {
setMenuVisibility(show, false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 56de506c3380..baf86f397646 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -171,6 +171,7 @@ public class PhoneStatusBar extends BaseStatusBar {
final Rect mNotificationPanelBackgroundPadding = new Rect();
int mNotificationPanelGravity;
int mNotificationPanelMinHeight;
+ boolean mNotificationPanelIsFullScreenWidth;
// top bar
View mClearButton;
@@ -361,9 +362,11 @@ public class PhoneStatusBar extends BaseStatusBar {
return true;
}
});
+ mNotificationPanelIsFullScreenWidth =
+ (mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT);
mNotificationPanel.setSystemUiVisibility(
View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER
- | View.STATUS_BAR_DISABLE_SYSTEM_INFO);
+ | (mNotificationPanelIsFullScreenWidth ? 0 : View.STATUS_BAR_DISABLE_SYSTEM_INFO));
if (!ActivityManager.isHighEndGfx(mDisplay)) {
mStatusBarWindow.setBackground(null);
@@ -376,6 +379,8 @@ public class PhoneStatusBar extends BaseStatusBar {
mIntruderAlertView.setBar(this);
}
+ updateShowSearchHoldoff();
+
mStatusBarView.mService = this;
mChoreographer = Choreographer.getInstance();
@@ -663,7 +668,6 @@ public class PhoneStatusBar extends BaseStatusBar {
lp.setTitle("NavigationBar");
lp.windowAnimations = 0;
-
return lp;
}
@@ -808,8 +812,12 @@ public class PhoneStatusBar extends BaseStatusBar {
@Override
protected void onConfigurationChanged(Configuration newConfig) {
updateRecentsPanel();
- mShowSearchHoldoff = mContext.getResources().getInteger(
- R.integer.config_show_search_delay);
+ updateShowSearchHoldoff();
+ }
+
+ private void updateShowSearchHoldoff() {
+ mShowSearchHoldoff = mContext.getResources().getInteger(
+ R.integer.config_show_search_delay);
}
private void loadNotificationShade() {
@@ -1174,7 +1182,8 @@ public class PhoneStatusBar extends BaseStatusBar {
mExpandedVisible = true;
mPile.setLayoutTransitionsEnabled(true);
- makeSlippery(mNavigationBarView, true);
+ if (mNavigationBarView != null)
+ mNavigationBarView.setSlippery(true);
updateCarrierLabelVisibility(true);
@@ -1198,19 +1207,6 @@ public class PhoneStatusBar extends BaseStatusBar {
visibilityChanged(true);
}
- private static void makeSlippery(View view, boolean slippery) {
- if (view == null) {
- return;
- }
- WindowManager.LayoutParams lp = (WindowManager.LayoutParams) view.getLayoutParams();
- if (slippery) {
- lp.flags |= WindowManager.LayoutParams.FLAG_SLIPPERY;
- } else {
- lp.flags &= ~WindowManager.LayoutParams.FLAG_SLIPPERY;
- }
- WindowManagerImpl.getDefault().updateViewLayout(view, lp);
- }
-
public void animateExpand() {
if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
@@ -1295,8 +1291,9 @@ public class PhoneStatusBar extends BaseStatusBar {
}
mExpandedVisible = false;
mPile.setLayoutTransitionsEnabled(false);
+ if (mNavigationBarView != null)
+ mNavigationBarView.setSlippery(false);
visibilityChanged(false);
- makeSlippery(mNavigationBarView, false);
// Shrink the window to the size of the status bar only
WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index ed1b2f5c7faf..931756197c23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -22,6 +22,7 @@ import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.widget.FrameLayout;
+import android.widget.ScrollView;
import android.widget.TextSwitcher;
import com.android.systemui.ExpandHelper;
@@ -47,6 +48,7 @@ public class StatusBarWindowView extends FrameLayout
protected void onAttachedToWindow () {
super.onAttachedToWindow();
latestItems = (NotificationRowLayout) findViewById(R.id.latestItems);
+ ScrollView scroller = (ScrollView) findViewById(R.id.scroll);
int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_min_height);
int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_max_height);
mExpandHelper = new ExpandHelper(mContext, latestItems, minHeight, maxHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
index d8166f1bae01..af0f9d3496dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
@@ -24,6 +24,7 @@ import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Slog;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -33,6 +34,7 @@ import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.RelativeLayout;
+import android.widget.ScrollView;
import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
@@ -114,6 +116,7 @@ public class NotificationPanel extends RelativeLayout implements StatusBarPanel,
int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_max_height);
mExpandHelper = new ExpandHelper(mContext, latestItems, minHeight, maxHeight);
mExpandHelper.setEventSource(this);
+ mExpandHelper.setGravity(Gravity.BOTTOM);
}
private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
index 18b804217904..87ec16b23419 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
@@ -81,7 +81,7 @@ public class KeyguardUpdateMonitor {
private int mFailedAttempts = 0;
private int mFailedBiometricUnlockAttempts = 0;
- private static final int FAILED_BIOMETRIC_UNLOCK_ATTEMPTS_BEFORE_BACKUP = 5;
+ private static final int FAILED_BIOMETRIC_UNLOCK_ATTEMPTS_BEFORE_BACKUP = 3;
private boolean mClockVisible;
diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardViewProperties.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardViewProperties.java
index d7fb19a3f785..58d14eddb79a 100644
--- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardViewProperties.java
+++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardViewProperties.java
@@ -58,7 +58,6 @@ public class LockPatternKeyguardViewProperties implements KeyguardViewProperties
final IccCard.State simState = mUpdateMonitor.getSimState();
return (simState == IccCard.State.PIN_REQUIRED
|| simState == IccCard.State.PUK_REQUIRED
- || simState == IccCard.State.ABSENT
|| simState == IccCard.State.PERM_DISABLED);
}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 985249d8d6bc..2918dbcdb635 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -1702,7 +1702,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
mProximityAlerts.remove(intent);
if (mProximityAlerts.size() == 0) {
- removeUpdatesLocked(mProximityReceiver);
+ if (mProximityReceiver != null) {
+ removeUpdatesLocked(mProximityReceiver);
+ }
mProximityReceiver = null;
mProximityListener = null;
}
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index a36c67305d35..152e1889e60d 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -98,6 +98,9 @@ public class TouchExplorer {
// the two dragging pointers as opposed to use the location of the primary one.
private static final int MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP = 200;
+ // The timeout after which we are no longer trying to detect a gesture.
+ private static final int EXIT_GESTURE_DETECTION_TIMEOUT = 2000;
+
// Temporary array for storing pointer IDs.
private final int[] mTempPointerIds = new int[MAX_POINTER_COUNT];
@@ -138,6 +141,9 @@ public class TouchExplorer {
// Command for delayed sending of a long press.
private final PerformLongPressDelayed mPerformLongPressDelayed;
+ // Command for exiting gesture detection mode after a timeout.
+ private final ExitGestureDetectionModeDelayed mExitGestureDetectionModeDelayed;
+
// Helper to detect and react to double tap in touch explore mode.
private final DoubleTapDetector mDoubleTapDetector;
@@ -212,6 +218,7 @@ public class TouchExplorer {
mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
mHandler = new Handler(context.getMainLooper());
mPerformLongPressDelayed = new PerformLongPressDelayed();
+ mExitGestureDetectionModeDelayed = new ExitGestureDetectionModeDelayed();
mGestureLibrary = GestureLibraries.fromRawResource(context, R.raw.accessibility_gestures);
mGestureLibrary.setOrientationStyle(4);
mGestureLibrary.load();
@@ -257,6 +264,7 @@ public class TouchExplorer {
mSendHoverEnterDelayed.remove();
mSendHoverExitDelayed.remove();
mPerformLongPressDelayed.remove();
+ mExitGestureDetectionModeDelayed.remove();
// Reset the pointer trackers.
mReceivedPointerTracker.clear();
mInjectedPointerTracker.clear();
@@ -304,9 +312,9 @@ public class TouchExplorer {
switch (eventType) {
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
- if (mInjectedPointerTracker.mLastInjectedHoverEvent != null) {
- mInjectedPointerTracker.mLastInjectedHoverEvent.recycle();
- mInjectedPointerTracker.mLastInjectedHoverEvent = null;
+ if (mInjectedPointerTracker.mLastInjectedHoverEventForClick != null) {
+ mInjectedPointerTracker.mLastInjectedHoverEventForClick.recycle();
+ mInjectedPointerTracker.mLastInjectedHoverEventForClick = null;
}
mLastTouchedWindowId = -1;
} break;
@@ -420,6 +428,7 @@ public class TouchExplorer {
mSendHoverEnterDelayed.remove();
mSendHoverExitDelayed.remove();
mPerformLongPressDelayed.remove();
+ mExitGestureDetectionModeDelayed.post();
} else {
// We have just decided that the user is touch,
// exploring so start sending events.
@@ -727,6 +736,7 @@ public class TouchExplorer {
}
mStrokeBuffer.clear();
+ mExitGestureDetectionModeDelayed.remove();
mCurrentState = STATE_TOUCH_EXPLORING;
} break;
case MotionEvent.ACTION_CANCEL: {
@@ -1067,7 +1077,8 @@ public class TouchExplorer {
final int pointerId = secondTapUp.getPointerId(secondTapUp.getActionIndex());
final int pointerIndex = secondTapUp.findPointerIndex(pointerId);
- MotionEvent lastExploreEvent = mInjectedPointerTracker.getLastInjectedHoverEvent();
+ MotionEvent lastExploreEvent =
+ mInjectedPointerTracker.getLastInjectedHoverEventForClick();
if (lastExploreEvent == null) {
// No last touch explored event but there is accessibility focus in
// the active window. We click in the middle of the focus bounds.
@@ -1263,6 +1274,25 @@ public class TouchExplorer {
}
/**
+ * Class for delayed exiting from gesture detecting mode.
+ */
+ private final class ExitGestureDetectionModeDelayed implements Runnable {
+
+ public void post() {
+ mHandler.postDelayed(this, EXIT_GESTURE_DETECTION_TIMEOUT);
+ }
+
+ public void remove() {
+ mHandler.removeCallbacks(this);
+ }
+
+ @Override
+ public void run() {
+ clear();
+ }
+ }
+
+ /**
* Class for delayed sending of long press.
*/
private final class PerformLongPressDelayed implements Runnable {
@@ -1299,7 +1329,8 @@ public class TouchExplorer {
final int pointerId = mEvent.getPointerId(mEvent.getActionIndex());
final int pointerIndex = mEvent.findPointerIndex(pointerId);
- MotionEvent lastExploreEvent = mInjectedPointerTracker.getLastInjectedHoverEvent();
+ MotionEvent lastExploreEvent =
+ mInjectedPointerTracker.getLastInjectedHoverEventForClick();
if (lastExploreEvent == null) {
// No last touch explored event but there is accessibility focus in
// the active window. We click in the middle of the focus bounds.
@@ -1453,6 +1484,9 @@ public class TouchExplorer {
// The last injected hover event.
private MotionEvent mLastInjectedHoverEvent;
+ // The last injected hover event used for performing clicks.
+ private MotionEvent mLastInjectedHoverEventForClick;
+
/**
* Processes an injected {@link MotionEvent} event.
*
@@ -1484,6 +1518,10 @@ public class TouchExplorer {
mLastInjectedHoverEvent.recycle();
}
mLastInjectedHoverEvent = MotionEvent.obtain(event);
+ if (mLastInjectedHoverEventForClick != null) {
+ mLastInjectedHoverEventForClick.recycle();
+ }
+ mLastInjectedHoverEventForClick = MotionEvent.obtain(event);
} break;
}
if (DEBUG) {
@@ -1537,6 +1575,13 @@ public class TouchExplorer {
return mLastInjectedHoverEvent;
}
+ /**
+ * @return The the last injected hover event.
+ */
+ public MotionEvent getLastInjectedHoverEventForClick() {
+ return mLastInjectedHoverEventForClick;
+ }
+
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index efed0a4a3721..267bf828d326 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -93,6 +93,7 @@ public class WindowAnimator {
final WindowStateAnimator winAnimator = wallpaper.mWinAnimator;
if (!winAnimator.mLastHidden) {
winAnimator.hide();
+ mService.dispatchWallpaperVisibility(wallpaper, false);
mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
}
@@ -292,7 +293,7 @@ public class WindowAnimator {
+ " anim=" + win.mWinAnimator.mAnimation);
} else if (mPolicy.canBeForceHidden(win, win.mAttrs)) {
final boolean changed;
- if (mForceHiding) {
+ if (mForceHiding && !winAnimator.isAnimating()) {
changed = win.hideLw(false, false);
if (WindowManagerService.DEBUG_VISIBILITY && changed) Slog.v(TAG,
"Now policy hidden: " + win);
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index b85573f82849..6d5ae71427bd 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -1870,16 +1870,7 @@ public class WindowManagerService extends IWindowManager.Stub
// First, make sure the client has the current visibility
// state.
- if (wallpaper.mWallpaperVisible != visible) {
- wallpaper.mWallpaperVisible = visible;
- try {
- if (DEBUG_VISIBILITY || DEBUG_WALLPAPER) Slog.v(TAG,
- "Setting visibility of wallpaper " + wallpaper
- + ": " + visible);
- wallpaper.mClient.dispatchAppVisibility(visible);
- } catch (RemoteException e) {
- }
- }
+ dispatchWallpaperVisibility(wallpaper, visible);
wallpaper.mWinAnimator.mAnimLayer = wallpaper.mLayer + mWallpaperAnimLayerAdjustment;
if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG, "adjustWallpaper win "
@@ -2089,6 +2080,24 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ /**
+ * Check wallpaper for visiblity change and notify window if so.
+ * @param wallpaper The wallpaper to test and notify.
+ * @param visible Current visibility.
+ */
+ void dispatchWallpaperVisibility(final WindowState wallpaper, final boolean visible) {
+ if (wallpaper.mWallpaperVisible != visible) {
+ wallpaper.mWallpaperVisible = visible;
+ try {
+ if (DEBUG_VISIBILITY || DEBUG_WALLPAPER) Slog.v(TAG,
+ "Updating visibility of wallpaper " + wallpaper
+ + ": " + visible + " Callers=" + Debug.getCallers(2));
+ wallpaper.mClient.dispatchAppVisibility(visible);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
void updateWallpaperVisibilityLocked() {
final boolean visible = isWallpaperVisible(mWallpaperTarget);
final int dw = mAppDisplayWidth;
@@ -2113,16 +2122,7 @@ public class WindowManagerService extends IWindowManager.Stub
updateWallpaperOffsetLocked(wallpaper, dw, dh, false);
}
- if (wallpaper.mWallpaperVisible != visible) {
- wallpaper.mWallpaperVisible = visible;
- try {
- if (DEBUG_VISIBILITY || DEBUG_WALLPAPER) Slog.v(TAG,
- "Updating visibility of wallpaper " + wallpaper
- + ": " + visible);
- wallpaper.mClient.dispatchAppVisibility(visible);
- } catch (RemoteException e) {
- }
- }
+ dispatchWallpaperVisibility(wallpaper, visible);
}
}
}
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index bdacb6e23dac..579cbb7b5119 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -1122,6 +1122,9 @@ class WindowStateAnimator {
+ " during relayout");
if (showSurfaceRobustlyLocked()) {
mLastHidden = false;
+ if (w.mIsWallpaper) {
+ mService.dispatchWallpaperVisibility(w, true);
+ }
} else {
w.mOrientationChanging = false;
}