}
private RemoteViews makeBigContentView() {
- RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(R.layout.notification_template_base);
+ RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(R.layout.notification_template_big_text);
contentView.setTextViewText(R.id.big_text, mBigText);
contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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:textAppearance="?android:attr/dropDownHintAppearance"
+ android:singleLine="true"
+ android:layout_marginLeft="3dip"
+ android:layout_marginTop="3dip"
+ android:layout_marginRight="3dip"
+ android:layout_marginBottom="3dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:internal="http://schemas.android.com/apk/prv/res/android"
+ android:background="@android:color/background_dark"
android:id="@+id/status_bar_latest_event_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ internal:layout_minHeight="64dp"
+ internal:layout_maxHeight="64dp"
>
<ImageView android:id="@+id/icon"
android:layout_width="@dimen/notification_large_icon_width"
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:internal="http://schemas.android.com/apk/prv/res/android"
+ android:background="@android:color/background_dark"
android:id="@+id/status_bar_latest_event_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ internal:layout_minHeight="65dp"
+ internal:layout_maxHeight="unbounded"
>
<ImageView
android:id="@+id/big_picture"
android:layout_height="wrap_content"
android:layout_marginTop="192dp"
/>
-</FrameLayout>
\ No newline at end of file
+</FrameLayout>
--- /dev/null
+<?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"
+ xmlns:internal="http://schemas.android.com/apk/prv/res/android"
+ android:background="@android:color/background_dark"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ internal:layout_minHeight="65dp"
+ internal:layout_maxHeight="unbounded"
+ >
+ <ImageView android:id="@+id/icon"
+ android:layout_width="@dimen/notification_large_icon_width"
+ android:layout_height="@dimen/notification_large_icon_height"
+ android:background="@android:drawable/notify_panel_notification_icon_bg_tile"
+ android:scaleType="center"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="fill_vertical"
+ android:layout_marginLeft="@dimen/notification_large_icon_width"
+ android:minHeight="@dimen/notification_large_icon_height"
+ android:orientation="vertical"
+ android:paddingLeft="12dp"
+ android:paddingRight="12dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:gravity="center_vertical"
+ >
+ <LinearLayout
+ android:id="@+id/line1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView android:id="@+id/title"
+ android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:layout_weight="1"
+ />
+ <ViewStub android:id="@+id/time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="0"
+ android:visibility="gone"
+ android:layout="@layout/notification_template_part_time"
+ />
+ <ViewStub android:id="@+id/chronometer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="0"
+ android:visibility="gone"
+ android:layout="@layout/notification_template_part_chronometer"
+ />
+ </LinearLayout>
+ <TextView android:id="@+id/text2"
+ android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Line2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-2dp"
+ android:layout_mgarginBottom="-2dp"
+ android:singleLine="true"
+ android:fadingEdge="horizontal"
+ android:ellipsize="marquee"
+ android:visibility="gone"
+ />
+ <TextView android:id="@+id/big_text"
+ android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="false"
+ android:visibility="gone"
+ />
+ <LinearLayout
+ android:id="@+id/line3"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView android:id="@+id/text"
+ android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="center"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ />
+ <TextView android:id="@+id/info"
+ android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="0"
+ android:singleLine="true"
+ android:gravity="center"
+ android:paddingLeft="8dp"
+ />
+ <ImageView android:id="@+id/right_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="0"
+ android:scaleType="center"
+ android:paddingLeft="8dp"
+ android:visibility="gone"
+ android:drawableAlpha="180"
+ />
+ </LinearLayout>
+ <ProgressBar
+ android:id="@android:id/progress"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ style="?android:attr/progressBarStyleHorizontal"
+ />
+ <LinearLayout
+ android:id="@+id/actions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ >
+ <!-- actions will be added here -->
+ </LinearLayout>
+ </LinearLayout>
+</FrameLayout>
<java-symbol type="layout" name="notification_intruder_content" />
<java-symbol type="layout" name="notification_template_base" />
<java-symbol type="layout" name="notification_template_big_picture" />
+ <java-symbol type="layout" name="notification_template_big_text" />
<java-symbol type="layout" name="notification_template_part_time" />
<java-symbol type="layout" name="notification_template_part_chronometer" />
<java-symbol type="layout" name="notification_template_inbox" />
--- /dev/null
+<?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.
+-->
+<SizeAdaptiveLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:background="@android:color/background_dark"
+ android:id="@+id/notification_adaptive_wrapper"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="@dimen/notification_height"
+ android:layout_height="wrap_content"
>
<Button
android:focusable="true"
android:clickable="true"
android:background="@drawable/notification_row_bg"
- />
+ >
+
+ <com.android.internal.widget.SizeAdaptiveLayout android:id="@+id/adaptive"
+ android:background="@android:color/background_dark"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </com.android.systemui.statusbar.LatestItemView>
<View
android:layout_width="match_parent"
<!-- Height of notification icons in the status bar -->
<dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen>
+ <!-- Height of a small notification in the status bar -->
+ <dimen name="notification_min_height">@android:dimen/notification_large_icon_height</dimen>
+
+ <!-- Height of a small notification in the status bar -->
+ <!-- TODO: change this back to 256dp once we deal with actions. -->
+ <dimen name="notification_max_height">320dp</dimen>
+
<!-- size at which Notification icons will be drawn in the status bar -->
<dimen name="status_bar_icon_drawing_size">18dip</dimen>
--- /dev/null
+/*
+ * 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.systemui;
+
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.RectF;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+import com.android.internal.widget.SizeAdaptiveLayout;
+
+public class ExpandHelper implements Gefingerpoken, OnClickListener {
+ public interface Callback {
+ View getChildAtPosition(MotionEvent ev);
+ View getChildAtPosition(float x, float y);
+ }
+
+ private static final String TAG = "ExpandHelper";
+ protected static final boolean DEBUG = false;
+ private static final long EXPAND_DURATION = 250;
+
+ @SuppressWarnings("unused")
+ private Context mContext;
+
+ private boolean mStretching;
+ private View mCurrView;
+ private float mOldHeight;
+ private float mNaturalHeight;
+ private float mInitialTouchSpan;
+ private Callback mCallback;
+ private ScaleGestureDetector mDetector;
+ private ViewScaler mScaler;
+ private ObjectAnimator mAnimation;
+
+ private int mSmallSize;
+ private int mLargeSize;
+
+
+ private class ViewScaler {
+ View mView;
+ public ViewScaler() {}
+ public void setView(View v) {
+ mView = v;
+ }
+ public void setHeight(float h) {
+ Log.v(TAG, "SetHeight: setting to " + h);
+ ViewGroup.LayoutParams lp = mView.getLayoutParams();
+ lp.height = (int)h;
+ mView.setLayoutParams(lp);
+ mView.requestLayout();
+ }
+ public float getHeight() {
+ int height = mView.getLayoutParams().height;
+ if (height < 0) {
+ height = mView.getMeasuredHeight();
+ }
+ return (float) height;
+ }
+ public int getNaturalHeight(int maximum) {
+ ViewGroup.LayoutParams lp = mView.getLayoutParams();
+ if (DEBUG) Log.v(TAG, "Inspecting a child of type: " + mView.getClass().getName());
+ int oldHeight = lp.height;
+ lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ mView.setLayoutParams(lp);
+ mView.measure(
+ View.MeasureSpec.makeMeasureSpec(mView.getMeasuredWidth(),
+ View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(maximum,
+ View.MeasureSpec.AT_MOST));
+ lp.height = oldHeight;
+ mView.setLayoutParams(lp);
+ return mView.getMeasuredHeight();
+ }
+ }
+
+ public ExpandHelper(Context context, Callback callback, int small, int large) {
+ mSmallSize = small;
+ mLargeSize = large;
+ mContext = context;
+ mCallback = callback;
+ mScaler = new ViewScaler();
+ mDetector =
+ new ScaleGestureDetector(context,
+ new ScaleGestureDetector.SimpleOnScaleGestureListener() {
+ @Override
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
+ if (DEBUG) Log.v(TAG, "onscalebegin()");
+ View v = mCallback.getChildAtPosition(detector.getFocusX(), detector.getFocusY());
+
+ // your fingers have to be somewhat close to the bounds of the view in question
+ mInitialTouchSpan = Math.abs(detector.getCurrentSpanY());
+ if (DEBUG) Log.d(TAG, "got mInitialTouchSpan: " + mInitialTouchSpan);
+
+ mStretching = initScale(v);
+ return mStretching;
+ }
+
+ @Override
+ public boolean onScale(ScaleGestureDetector detector) {
+ if (DEBUG) Log.v(TAG, "onscale() on " + mCurrView);
+ float h = Math.abs(detector.getCurrentSpanY());
+ if (DEBUG) Log.d(TAG, "current span is: " + h);
+ h = h + mOldHeight - mInitialTouchSpan;
+ h = h<mSmallSize?mSmallSize:(h>mLargeSize?mLargeSize:h);
+ h = h>mNaturalHeight?mNaturalHeight:h;
+ if (DEBUG) Log.d(TAG, "scale continues: h=" + h);
+ mScaler.setHeight(h);
+ return true;
+ }
+
+ @Override
+ public void onScaleEnd(ScaleGestureDetector detector) {
+ if (DEBUG) Log.v(TAG, "onscaleend()");
+ // I guess we're alone now
+ if (DEBUG) Log.d(TAG, "scale end");
+ finishScale(false);
+ }
+ });
+ }
+
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (DEBUG) Log.d(TAG, "interceptTouch: act=" + (ev.getAction()) +
+ " stretching=" + mStretching);
+ mDetector.onTouchEvent(ev);
+ return mStretching;
+ }
+
+ public boolean onTouchEvent(MotionEvent ev) {
+ final int action = ev.getAction();
+ if (DEBUG) Log.d(TAG, "touch: act=" + (action) + " stretching=" + mStretching);
+ if (mStretching) {
+ mDetector.onTouchEvent(ev);
+ }
+ switch (action) {
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mStretching = false;
+ mCurrView = null;
+ break;
+ }
+ return true;
+ }
+ private boolean initScale(View v) {
+ if (v != null) {
+ if (DEBUG) Log.d(TAG, "scale begins on view: " + v);
+ mStretching = true;
+ mCurrView = v;
+ mScaler.setView(v);
+ mOldHeight = mScaler.getHeight();
+ mNaturalHeight = mScaler.getNaturalHeight(mLargeSize);
+ if (DEBUG) Log.d(TAG, "got mOldHeight: " + mOldHeight +
+ " mNaturalHeight: " + mNaturalHeight);
+ v.getParent().requestDisallowInterceptTouchEvent(true);
+ if (DEBUG) v.setBackgroundColor(0xFFFFFF00);
+ }
+ return mStretching;
+ }
+
+ private void finishScale(boolean force) {
+ float h = mScaler.getHeight();
+ final boolean wasClosed = (mOldHeight == mSmallSize);
+ if (wasClosed) {
+ h = (force || h > mSmallSize) ? mNaturalHeight : mSmallSize;
+ } else {
+ h = (force || h < mNaturalHeight) ? mSmallSize : mNaturalHeight;
+ }
+ if (DEBUG) mCurrView.setBackgroundColor(0);
+ mAnimation = ObjectAnimator.ofFloat(mScaler, "height", h).setDuration(EXPAND_DURATION);
+ mAnimation.start();
+ mStretching = false;
+ mCurrView = null;
+ }
+
+ @Override
+ public void onClick(View v) {
+ initScale(v);
+ finishScale(true);
+
+ }
+}
--- /dev/null
+/*
+ * 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.systemui;
+
+import android.view.MotionEvent;
+
+// ACHTUNG!
+public interface Gefingerpoken {
+ boolean onInterceptTouchEvent(MotionEvent ev);
+ boolean onTouchEvent(MotionEvent ev);
+}
import android.view.VelocityTracker;
import android.view.View;
-public class SwipeHelper {
+public class SwipeHelper implements Gefingerpoken {
static final String TAG = "com.android.systemui.SwipeHelper";
private static final boolean DEBUG = false;
private static final boolean DEBUG_INVALIDATE = false;
import java.util.ArrayList;
+import android.app.ActivityManagerNative;
+import android.app.KeyguardManager;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
import android.widget.LinearLayout;
+import android.widget.RemoteViews;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
import com.android.internal.statusbar.StatusBarNotification;
+import com.android.internal.widget.SizeAdaptiveLayout;
import com.android.systemui.SystemUI;
import com.android.systemui.recent.RecentsPanelView;
import com.android.systemui.recent.RecentTasksLoader;
protected IStatusBarService mBarService;
protected H mHandler = createHandler();
+ // used to notify status bar for suppressing notification LED
+ protected boolean mPanelSlightlyVisible;
+
// Recent apps
protected RecentsPanelView mRecentsPanel;
protected RecentTasksLoader mRecentTasksLoader;
// UI-specific methods
-
+
/**
* Create all windows necessary for the status bar (including navigation, overlay panels, etc)
* and add them to the window manager.
protected Display mDisplay;
private IWindowManager mWindowManager;
-
+
public IWindowManager getWindowManager() {
return mWindowManager;
}
-
+
public Display getDisplay() {
return mDisplay;
}
-
+
public IStatusBarService getStatusBarService() {
return mBarService;
}
ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
mCommandQueue = new CommandQueue(this, iconList);
-
+
int[] switches = new int[7];
ArrayList<IBinder> binders = new ArrayList<IBinder>();
try {
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
-
+
createAndAddWindows();
disable(switches[0]);
if (DEBUG) {
Slog.d(TAG, String.format(
- "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
+ "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
iconList.size(),
switches[0],
switches[1],
));
}
}
-
+
protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
View vetoButton = row.findViewById(R.id.veto);
if (n.isClearable()) {
}
return vetoButton;
}
-
+
protected void applyLegacyRowBackground(StatusBarNotification sbn, View content) {
if (sbn.notification.contentView.getLayoutId() !=
return false;
}
}
+
+ protected void workAroundBadLayerDrawableOpacity(View v) {
+ }
+
+ protected boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
+ int minHeight =
+ mContext.getResources().getDimensionPixelSize(R.dimen.notification_min_height);
+ int maxHeight =
+ mContext.getResources().getDimensionPixelSize(R.dimen.notification_max_height);
+ StatusBarNotification sbn = entry.notification;
+ RemoteViews oneU = sbn.notification.contentView;
+ RemoteViews large = sbn.notification.bigContentView;
+ if (oneU == null) {
+ return false;
+ }
+
+ // create the row view
+ LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false);
+ // XXX: temporary: while testing big notifications, auto-expand all of them
+ ViewGroup.LayoutParams lp = row.getLayoutParams();
+ if (sbn.notification.bigContentView != null) {
+ lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ } else {
+ lp.height = minHeight;
+ }
+ row.setLayoutParams(lp);
+ workAroundBadLayerDrawableOpacity(row);
+ View vetoButton = updateNotificationVetoButton(row, sbn);
+ vetoButton.setContentDescription(mContext.getString(
+ R.string.accessibility_remove_notification));
+
+ // NB: the large icon is now handled entirely by the template
+
+ // bind the click event to the content area
+ ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
+ ViewGroup adaptive = (ViewGroup)row.findViewById(R.id.adaptive);
+ // XXX: update to allow controls within notification views
+ content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+// content.setOnFocusChangeListener(mFocusChangeListener);
+ PendingIntent contentIntent = sbn.notification.contentIntent;
+ if (contentIntent != null) {
+ final View.OnClickListener listener = new NotificationClicker(contentIntent,
+ sbn.pkg, sbn.tag, sbn.id);
+ content.setOnClickListener(listener);
+ } else {
+ content.setOnClickListener(null);
+ }
+
+ View expandedOneU = null;
+ View expandedLarge = null;
+ Exception exception = null;
+ try {
+ expandedOneU = oneU.apply(mContext, adaptive);
+ if (large != null) {
+ expandedLarge = large.apply(mContext, adaptive);
+ }
+ }
+ catch (RuntimeException e) {
+ exception = e;
+ }
+ if (expandedOneU == null && expandedLarge == null) {
+ final String ident = sbn.pkg + "/0x" + Integer.toHexString(sbn.id);
+ Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
+ return false;
+ } else {
+ if (expandedOneU != null) {
+ SizeAdaptiveLayout.LayoutParams params =
+ new SizeAdaptiveLayout.LayoutParams(expandedOneU.getLayoutParams());
+ params.minHeight = minHeight;
+ params.maxHeight = minHeight;
+ adaptive.addView(expandedOneU, params);
+ }
+ if (expandedLarge != null) {
+ SizeAdaptiveLayout.LayoutParams params =
+ new SizeAdaptiveLayout.LayoutParams(expandedLarge.getLayoutParams());
+ params.minHeight = minHeight+1;
+ params.maxHeight = SizeAdaptiveLayout.LayoutParams.UNBOUNDED;
+ adaptive.addView(expandedLarge, params);
+ }
+ row.setDrawingCacheEnabled(true);
+ }
+
+ applyLegacyRowBackground(sbn, content);
+
+ entry.row = row;
+ entry.content = content;
+ entry.expanded = expandedOneU;
+ entry.expandedLarge = expandedOneU;
+
+ return true;
+ }
+
+ public NotificationClicker makeClicker(PendingIntent intent, String pkg, String tag, int id) {
+ return new NotificationClicker(intent, pkg, tag, id);
+ }
+
+ private class NotificationClicker implements View.OnClickListener {
+ private PendingIntent mIntent;
+ private String mPkg;
+ private String mTag;
+ private int mId;
+
+ NotificationClicker(PendingIntent intent, String pkg, String tag, int id) {
+ mIntent = intent;
+ mPkg = pkg;
+ mTag = tag;
+ mId = id;
+ }
+
+ public void onClick(View v) {
+ try {
+ // The intent we are sending is for the application, which
+ // won't have permission to immediately start an activity after
+ // the user switches to home. We know it is safe to do at this
+ // point, so make sure new activity switches are now allowed.
+ ActivityManagerNative.getDefault().resumeAppSwitches();
+ // Also, notifications can be launched from the lock screen,
+ // so dismiss the lock screen when the activity starts.
+ ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+ } catch (RemoteException e) {
+ }
+
+ if (mIntent != null) {
+ int[] pos = new int[2];
+ v.getLocationOnScreen(pos);
+ Intent overlay = new Intent();
+ overlay.setSourceBounds(
+ new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
+ try {
+ mIntent.send(mContext, 0, overlay);
+ } catch (PendingIntent.CanceledException e) {
+ // the stack trace isn't very helpful here. Just log the exception message.
+ Slog.w(TAG, "Sending contentIntent failed: " + e);
+ }
+
+ KeyguardManager kgm =
+ (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ if (kgm != null) kgm.exitKeyguardSecurely(null);
+ }
+
+ try {
+ mBarService.onNotificationClick(mPkg, mTag, mId);
+ } catch (RemoteException ex) {
+ // system process is dead if we're here.
+ }
+
+ // close the shade if it was open
+ animateCollapse();
+ visibilityChanged(false);
+
+ // If this click was on the intruder alert, hide that instead
+// mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
+ }
+ }
+ /**
+ * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
+ * This was added last-minute and is inconsistent with the way the rest of the notifications
+ * are handled, because the notification isn't really cancelled. The lights are just
+ * turned off. If any other notifications happen, the lights will turn back on. Steve says
+ * this is what he wants. (see bug 1131461)
+ */
+ protected void visibilityChanged(boolean visible) {
+ if (mPanelSlightlyVisible != visible) {
+ mPanelSlightlyVisible = visible;
+ try {
+ mBarService.onPanelRevealed();
+ } catch (RemoteException ex) {
+ // Won't fail unless the world has ended.
+ }
+ }
+ }
+
}
public View content; // takes the click events and sends the PendingIntent
public View expanded; // the inflated RemoteViews
public ImageView largeIcon;
+ public View expandedLarge;
public Entry() {}
public Entry(IBinder key, StatusBarNotification n, StatusBarIconView ic) {
this.key = key;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
-import android.os.Build;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.view.Gravity;
import android.view.IWindowManager;
import android.view.KeyEvent;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
-import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.widget.ScrollView;
import android.widget.TextView;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarNotification;
import com.android.systemui.R;
-import com.android.systemui.SwipeHelper;
import com.android.systemui.recent.RecentTasksLoader;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.SignalClusterView;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.IntruderAlertView;
import com.android.systemui.statusbar.policy.DateView;
+import com.android.systemui.statusbar.policy.IntruderAlertView;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NotificationRowLayout;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
public class PhoneStatusBar extends BaseStatusBar {
static final String TAG = "PhoneStatusBar";
public static final boolean DEBUG = false;
}
}
- private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
- StatusBarNotification sbn = entry.notification;
- // XXX: temporary: while testing big notifications, auto-expand all of them
- final boolean big = (sbn.notification.bigContentView != null);
- RemoteViews remoteViews = big ? sbn.notification.bigContentView
- : sbn.notification.contentView;
- if (remoteViews == null) {
- return false;
- }
-
- // create the row view
- LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false);
- ViewGroup.LayoutParams lp = row.getLayoutParams();
- if (big) {
- lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
- } else {
- lp.height = mContext.getResources().getDimensionPixelSize(R.dimen.notification_height);
- }
- row.setLayoutParams(lp);
- View vetoButton = updateNotificationVetoButton(row, sbn);
- vetoButton.setContentDescription(mContext.getString(
- R.string.accessibility_remove_notification));
-
- // NB: the large icon is now handled entirely by the template
-
- // bind the click event to the content area
- ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
- // XXX: update to allow controls within notification views
- content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
-// content.setOnFocusChangeListener(mFocusChangeListener);
- PendingIntent contentIntent = sbn.notification.contentIntent;
- if (contentIntent != null) {
- final View.OnClickListener listener = new NotificationClicker(contentIntent,
- sbn.pkg, sbn.tag, sbn.id);
- content.setOnClickListener(listener);
- } else {
- content.setOnClickListener(null);
- }
-
- View expanded = null;
- Exception exception = null;
- try {
- expanded = remoteViews.apply(mContext, content);
- }
- catch (RuntimeException e) {
- exception = e;
- }
- if (expanded == null) {
- final String ident = sbn.pkg + "/0x" + Integer.toHexString(sbn.id);
- Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
- return false;
- } else {
- content.addView(expanded);
- row.setDrawingCacheEnabled(true);
- }
-
- applyLegacyRowBackground(sbn, content);
-
- entry.row = row;
- entry.content = content;
- entry.expanded = expanded;
-
- return true;
- }
-
StatusBarNotification removeNotificationViews(IBinder key) {
NotificationData.Entry entry = mNotificationData.remove(key);
if (entry == null) {
@Override
public void setHardKeyboardStatus(boolean available, boolean enabled) { }
- public NotificationClicker makeClicker(PendingIntent intent, String pkg, String tag, int id) {
- return new NotificationClicker(intent, pkg, tag, id);
- }
-
private class NotificationClicker implements View.OnClickListener {
private PendingIntent mIntent;
private String mPkg;
}
public void onClick(View v) {
- if (DEBUG) {
- Slog.v(TAG, "NotificationClicker: intent=" + mIntent
- + " pkg=" + mPkg
- + " tag=" + mTag
- + " id=" + mId);
- }
try {
// The intent we are sending is for the application, which
// won't have permission to immediately start an activity after
}
}
- /**
- * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
- * This was added last-minute and is inconsistent with the way the rest of the notifications
- * are handled, because the notification isn't really cancelled. The lights are just
- * turned off. If any other notifications happen, the lights will turn back on. Steve says
- * this is what he wants. (see bug 1131461)
- */
- void visibilityChanged(boolean visible) {
- if (mPanelSlightlyVisible != visible) {
- mPanelSlightlyVisible = visible;
- try {
- mBarService.onPanelRevealed();
- } catch (RemoteException ex) {
- // Won't fail unless the world has ended.
- }
- }
- }
-
void performDisableActions(int net) {
int old = mDisabled;
int diff = net ^ old;
import android.view.ViewGroup;
import android.widget.LinearLayout;
+import com.android.systemui.ExpandHelper;
+import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
import java.util.HashMap;
-public class NotificationRowLayout extends LinearLayout implements SwipeHelper.Callback {
+public class NotificationRowLayout
+ extends LinearLayout
+ implements SwipeHelper.Callback, ExpandHelper.Callback
+{
private static final String TAG = "NotificationRowLayout";
private static final boolean DEBUG = false;
private static final boolean SLOW_ANIMATIONS = DEBUG;
HashMap<View, ValueAnimator> mDisappearingViews = new HashMap<View, ValueAnimator>();
private SwipeHelper mSwipeHelper;
+ private ExpandHelper mExpandHelper;
+
+ private Gefingerpoken mCurrentHelper;
// Flag set during notification removal animation to avoid causing too much work until
// animation is done
setOrientation(LinearLayout.VERTICAL);
+ setMotionEventSplittingEnabled(false);
+
if (DEBUG) {
setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
@Override
float densityScale = getResources().getDisplayMetrics().density;
float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
+ int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
+ int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_max_height);
+ mExpandHelper = new ExpandHelper(mContext, this, minHeight, maxHeight);
}
public void setAnimateBounds(boolean anim) {
mAnimateBounds = anim;
}
+ private void logLayoutTransition() {
+ Log.v(TAG, "layout " +
+ (getLayoutTransition().isChangingLayout() ? "is " : "is not ") +
+ "in transition and animations " +
+ (getLayoutTransition().isRunning() ? "are " : "are not ") +
+ "running.");
+ }
+
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
- return mSwipeHelper.onInterceptTouchEvent(ev) ||
- super.onInterceptTouchEvent(ev);
+ if (DEBUG) logLayoutTransition();
+
+ MotionEvent cancellation = MotionEvent.obtain(ev);
+ cancellation.setAction(MotionEvent.ACTION_CANCEL);
+
+ if (mSwipeHelper.onInterceptTouchEvent(ev)) {
+ if (DEBUG) Log.v(TAG, "will swipe");
+ mCurrentHelper = mSwipeHelper;
+ mExpandHelper.onInterceptTouchEvent(cancellation);
+ return true;
+ } else if (mExpandHelper.onInterceptTouchEvent(ev)) {
+ if (DEBUG) Log.v(TAG, "will stretch");
+ mCurrentHelper = mExpandHelper;
+ mSwipeHelper.onInterceptTouchEvent(cancellation);
+ return true;
+ } else {
+ mCurrentHelper = null;
+ if (super.onInterceptTouchEvent(ev)) {
+ if (DEBUG) Log.v(TAG, "intercepting ourselves");
+ mSwipeHelper.onInterceptTouchEvent(cancellation);
+ mExpandHelper.onInterceptTouchEvent(cancellation);
+ return true;
+ }
+ }
+ return false;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
- return mSwipeHelper.onTouchEvent(ev) ||
- super.onTouchEvent(ev);
+ if (DEBUG) Log.v(TAG, "onTouchEvent()");
+ if (DEBUG) logLayoutTransition();
+ if (mCurrentHelper != null) {
+ return mCurrentHelper.onTouchEvent(ev);
+ }
+ return super.onTouchEvent(ev);
}
public boolean canChildBeDismissed(View v) {
}
public View getChildAtPosition(MotionEvent ev) {
+ return getChildAtPosition(ev.getX(), ev.getY());
+ }
+ public View getChildAtPosition(float touchX, float touchY) {
// find the view under the pointer, accounting for GONE views
final int count = getChildCount();
int y = 0;
- int touchY = (int) ev.getY();
int childIdx = 0;
View slidingChild;
for (; childIdx < count; childIdx++) {
@Override
public void onDraw(android.graphics.Canvas c) {
super.onDraw(c);
+ if (DEBUG) logLayoutTransition();
if (DEBUG) {
//Slog.d(TAG, "onDraw: canvas height: " + c.getHeight() + "px; measured height: "
// + getMeasuredHeight() + "px");
package com.android.systemui.statusbar.tablet;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.app.ActivityManagerNative;
-import android.app.KeyguardManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Point;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.inputmethodservice.InputMethodService;
-import android.os.Build;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.view.Gravity;
import android.view.IWindowManager;
import android.view.KeyEvent;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.VelocityTracker;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.Prefs;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
public class TabletStatusBar extends BaseStatusBar implements
InputMethodsPanel.OnHardKeyboardEnabledChangeListener,
RecentsPanelView.OnRecentsPanelVisibilityChangedListener {
private CompatModePanel mCompatModePanel;
private int mSystemUiVisibility = 0;
- // used to notify status bar for suppressing notification LED
- private boolean mPanelSlightlyVisible;
private int mNavigationIconHints = 0;
// update the contentIntent
final PendingIntent contentIntent = notification.notification.contentIntent;
if (contentIntent != null) {
- final View.OnClickListener listener = new NotificationClicker(contentIntent,
+ final View.OnClickListener listener = makeClicker(contentIntent,
notification.pkg, notification.tag, notification.id);
oldEntry.content.setOnClickListener(listener);
} else {
}
}
- /**
- * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
- * This was added last-minute and is inconsistent with the way the rest of the notifications
- * are handled, because the notification isn't really cancelled. The lights are just
- * turned off. If any other notifications happen, the lights will turn back on. Steve says
- * this is what he wants. (see bug 1131461)
- */
- void visibilityChanged(boolean visible) {
- if (mPanelSlightlyVisible != visible) {
- mPanelSlightlyVisible = visible;
- try {
- mBarService.onPanelRevealed();
- } catch (RemoteException ex) {
- // Won't fail unless the world has ended.
- }
- }
- }
-
@Override // CommandQueue
public void setNavigationIconHints(int hints) {
if (hints == mNavigationIconHints) return;
mHandler.sendEmptyMessage(msg);
}
- public NotificationClicker makeClicker(PendingIntent intent, String pkg, String tag, int id) {
- return new NotificationClicker(intent, pkg, tag, id);
- }
-
- private class NotificationClicker implements View.OnClickListener {
- private PendingIntent mIntent;
- private String mPkg;
- private String mTag;
- private int mId;
-
- NotificationClicker(PendingIntent intent, String pkg, String tag, int id) {
- mIntent = intent;
- mPkg = pkg;
- mTag = tag;
- mId = id;
- }
-
- public void onClick(View v) {
- try {
- // The intent we are sending is for the application, which
- // won't have permission to immediately start an activity after
- // the user switches to home. We know it is safe to do at this
- // point, so make sure new activity switches are now allowed.
- ActivityManagerNative.getDefault().resumeAppSwitches();
- // Also, notifications can be launched from the lock screen,
- // so dismiss the lock screen when the activity starts.
- ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
- } catch (RemoteException e) {
- }
-
- if (mIntent != null) {
- int[] pos = new int[2];
- v.getLocationOnScreen(pos);
- Intent overlay = new Intent();
- overlay.setSourceBounds(
- new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
- try {
- mIntent.send(mContext, 0, overlay);
-
- } catch (PendingIntent.CanceledException e) {
- // the stack trace isn't very helpful here. Just log the exception message.
- Slog.w(TAG, "Sending contentIntent failed: " + e);
- }
-
- KeyguardManager kgm =
- (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- if (kgm != null) kgm.exitKeyguardSecurely(null);
- }
-
- try {
- mBarService.onNotificationClick(mPkg, mTag, mId);
- } catch (RemoteException ex) {
- // system process is dead if we're here.
- }
-
- // close the shade if it was open
- animateCollapse();
- visibilityChanged(false);
-
- // If this click was on the intruder alert, hide that instead
-// mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
- }
- }
-
StatusBarNotification removeNotificationViews(IBinder key) {
NotificationData.Entry entry = mNotificationData.remove(key);
if (entry == null) {
mNotificationPanel.setNotificationCount(N);
}
- void workAroundBadLayerDrawableOpacity(View v) {
+ @Override
+ protected void workAroundBadLayerDrawableOpacity(View v) {
Drawable bgd = v.getBackground();
if (!(bgd instanceof LayerDrawable)) return;
v.setBackgroundDrawable(d);
}
- private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
- StatusBarNotification sbn = entry.notification;
- RemoteViews remoteViews = sbn.notification.contentView;
- if (remoteViews == null) {
- return false;
- }
-
- // create the row view
- LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false);
- workAroundBadLayerDrawableOpacity(row);
- View vetoButton = updateNotificationVetoButton(row, entry.notification);
- vetoButton.setContentDescription(mContext.getString(
- R.string.accessibility_remove_notification));
-
- // NB: the large icon is now handled entirely by the template
-
- // bind the click event to the content area
- ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
- // XXX: update to allow controls within notification views
- content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
-// content.setOnFocusChangeListener(mFocusChangeListener);
- PendingIntent contentIntent = sbn.notification.contentIntent;
- if (contentIntent != null) {
- final View.OnClickListener listener = new NotificationClicker(
- contentIntent, sbn.pkg, sbn.tag, sbn.id);
- content.setOnClickListener(listener);
- } else {
- content.setOnClickListener(null);
- }
-
- View expanded = null;
- Exception exception = null;
- try {
- expanded = remoteViews.apply(mContext, content);
- }
- catch (RuntimeException e) {
- exception = e;
- }
- if (expanded == null) {
- final String ident = sbn.pkg + "/0x" + Integer.toHexString(sbn.id);
- Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
- return false;
- } else {
- content.addView(expanded);
- row.setDrawingCacheEnabled(true);
- }
-
- applyLegacyRowBackground(sbn, content);
-
- entry.row = row;
- entry.content = content;
- entry.expanded = expanded;
-
- return true;
- }
-
public void clearAll() {
try {
mBarService.onClearAllNotifications();