PhoneStatusBar.java revision df89e65bf0fcc651d20b208c8d8d0b848fb43418
1afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project/*
2afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * Copyright (C) 2010 The Android Open Source Project
3afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project *
4afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * you may not use this file except in compliance with the License.
6afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * You may obtain a copy of the License at
7afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project *
8afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project *
10afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * See the License for the specific language governing permissions and
14afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * limitations under the License.
15afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project */
16afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
17afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectpackage com.android.systemui.statusbar.phone;
18afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
19afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.animation.ObjectAnimator;
20afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.app.ActivityManager;
21afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.app.ActivityManagerNative;
22afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.app.Dialog;
23afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.app.KeyguardManager;
24afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.app.Notification;
25afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.app.PendingIntent;
2683bcc98af19597a213a8d174e8f1b47453d513ceMike Lockwoodimport android.app.StatusBarManager;
27afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.content.BroadcastReceiver;
28afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.content.Context;
29afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.content.Intent;
30afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.content.IntentFilter;
31afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.content.res.Resources;
32afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.content.res.Configuration;
33afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.graphics.PixelFormat;
34afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.graphics.Rect;
35afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.graphics.drawable.Drawable;
3683bcc98af19597a213a8d174e8f1b47453d513ceMike Lockwoodimport android.os.IBinder;
37afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.os.RemoteException;
38afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.os.Handler;
39afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.os.Message;
40afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.os.ServiceManager;
41afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.os.SystemClock;
42afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.provider.Settings;
43afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.text.TextUtils;
44afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.util.DisplayMetrics;
45afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.util.Slog;
46afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.util.Log;
47afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.Display;
48afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.Gravity;
49afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.IWindowManager;
50afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.KeyEvent;
51afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.LayoutInflater;
52afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.MotionEvent;
53afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.Surface;
54afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.VelocityTracker;
55afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.View;
56afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.ViewGroup;
57afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.ViewGroup.LayoutParams;
58afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.Window;
59afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.WindowManager;
60afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.WindowManagerImpl;
61afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.animation.Animation;
62afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.animation.AnimationUtils;
63afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.widget.ImageView;
64afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.widget.LinearLayout;
65afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.widget.RemoteViews;
66afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.widget.ScrollView;
67afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.widget.TextView;
68afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
69afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport java.io.FileDescriptor;
70afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport java.io.PrintWriter;
71afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport java.util.ArrayList;
72afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
73afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport com.android.internal.statusbar.StatusBarIcon;
74afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport com.android.internal.statusbar.StatusBarNotification;
75afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
76afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport com.android.systemui.R;
77afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport com.android.systemui.recent.RecentsPanelView;
78afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport com.android.systemui.statusbar.NotificationData;
79afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport com.android.systemui.statusbar.StatusBar;
80afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport com.android.systemui.statusbar.StatusBarIconView;
81afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport com.android.systemui.statusbar.SignalClusterView;
82afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport com.android.systemui.statusbar.policy.DateView;
83afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport com.android.systemui.statusbar.policy.BatteryController;
84afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport com.android.systemui.statusbar.policy.LocationController;
85afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport com.android.systemui.statusbar.policy.NetworkController;
86afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport com.android.systemui.statusbar.policy.NotificationRowLayout;
87afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
88afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectpublic class PhoneStatusBar extends StatusBar {
89afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    static final String TAG = "PhoneStatusBar";
90afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public static final boolean DEBUG = false;
91afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public static final boolean SPEW = false;
92afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public static final boolean DUMPTRUCK = true; // extra dumpsys info
93afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
94afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    // additional instrumentation for testing purposes; intended to be left on during development
95afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public static final boolean CHATTY = DEBUG || true;
96afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
97afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public static final String ACTION_STATUSBAR_START
98afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            = "com.android.internal.policy.statusbar.START";
99afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
100afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    static final int EXPANDED_LEAVE_ALONE = -10000;
101afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    static final int EXPANDED_FULL_OPEN = -10001;
102afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
103afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final int MSG_ANIMATE = 100;
104afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final int MSG_ANIMATE_REVEAL = 101;
105afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
106afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001;
107afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final int MSG_SHOW_INTRUDER = 1002;
108afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final int MSG_HIDE_INTRUDER = 1003;
109afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final int MSG_OPEN_RECENTS_PANEL = 1020;
110afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final int MSG_CLOSE_RECENTS_PANEL = 1021;
111afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
112afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    // will likely move to a resource or other tunable param at some point
113afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final int INTRUDER_ALERT_DECAY_MS = 10000;
114afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
115afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    // fling gesture tuning parameters, scaled to display density
116afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private float mSelfExpandVelocityPx; // classic value: 2000px/s
117afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up")
118afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private float mFlingExpandMinVelocityPx; // classic value: 200px/s
119afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private float mFlingCollapseMinVelocityPx; // classic value: 200px/s
120afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private float mCollapseMinDisplayFraction; // classic value: 0.08 (25px/min(320px,480px) on G1)
121afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private float mExpandMinDisplayFraction; // classic value: 0.5 (drag open halfway to expand)
122afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private float mFlingGestureMaxXVelocityPx; // classic value: 150px/s
123afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
124afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private float mExpandAccelPx; // classic value: 2000px/s/s
125afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private float mCollapseAccelPx; // classic value: 2000px/s/s (will be negated to collapse "up")
12683bcc98af19597a213a8d174e8f1b47453d513ceMike Lockwood
127afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    PhoneStatusBarPolicy mIconPolicy;
128afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
129afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    // These are no longer handled by the policy, because we need custom strategies for them
130afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    BatteryController mBatteryController;
131afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    LocationController mLocationController;
132afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    NetworkController mNetworkController;
133afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
134afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    int mNaturalBarHeight = -1;
135afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    int mIconSize = -1;
136afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    int mIconHPadding = -1;
137afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    Display mDisplay;
138afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
139afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    IWindowManager mWindowManager;
140afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
141afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    PhoneStatusBarView mStatusBarView;
142afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    int mPixelFormat;
143afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    H mHandler = new H();
144afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    Object mQueueLock = new Object();
145afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
146afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    // icons
147afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    LinearLayout mIcons;
148afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    IconMerger mNotificationIcons;
149afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    LinearLayout mStatusIcons;
150afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
151afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    // expanded notifications
152afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    Dialog mExpandedDialog;
153afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    ExpandedView mExpandedView;
154afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    WindowManager.LayoutParams mExpandedParams;
155afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    ScrollView mScrollView;
15683bcc98af19597a213a8d174e8f1b47453d513ceMike Lockwood    View mExpandedContents;
15783bcc98af19597a213a8d174e8f1b47453d513ceMike Lockwood    // top bar
15883bcc98af19597a213a8d174e8f1b47453d513ceMike Lockwood    TextView mNoNotificationsTitle;
159afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    View mClearButton;
160afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    View mSettingsButton;
16183bcc98af19597a213a8d174e8f1b47453d513ceMike Lockwood
16283bcc98af19597a213a8d174e8f1b47453d513ceMike Lockwood    // drag bar
16383bcc98af19597a213a8d174e8f1b47453d513ceMike Lockwood    CloseDragHandle mCloseView;
16483bcc98af19597a213a8d174e8f1b47453d513ceMike Lockwood
16583bcc98af19597a213a8d174e8f1b47453d513ceMike Lockwood    // all notifications
166afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    NotificationData mNotificationData = new NotificationData();
16783bcc98af19597a213a8d174e8f1b47453d513ceMike Lockwood    NotificationRowLayout mPile;
16883bcc98af19597a213a8d174e8f1b47453d513ceMike Lockwood
16983bcc98af19597a213a8d174e8f1b47453d513ceMike Lockwood    // position
17083bcc98af19597a213a8d174e8f1b47453d513ceMike Lockwood    int[] mPositionTmp = new int[2];
171afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    boolean mExpanded;
17283bcc98af19597a213a8d174e8f1b47453d513ceMike Lockwood    boolean mExpandedVisible;
173afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
174afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    // the date view
175afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    DateView mDateView;
176afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
177afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    // for immersive activities
178afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private View mIntruderAlertView;
179afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
180afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    // on-screen navigation buttons
181afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private NavigationBarView mNavigationBarView = null;
182afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
183afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    // the tracker view
184afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    TrackingView mTrackingView;
185afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    WindowManager.LayoutParams mTrackingParams;
186afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    int mTrackingPosition; // the position of the top of the tracking view.
187afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private boolean mPanelSlightlyVisible;
188afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
189afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    // ticker
190    private Ticker mTicker;
191    private View mTickerView;
192    private boolean mTicking;
193
194    // Recent apps
195    private RecentsPanelView mRecentsPanel;
196
197    // Tracking finger for opening/closing.
198    int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
199    boolean mTracking;
200    VelocityTracker mVelocityTracker;
201
202    static final int ANIM_FRAME_DURATION = (1000/60);
203
204    boolean mAnimating;
205    long mCurAnimationTime;
206    float mAnimY;
207    float mAnimVel;
208    float mAnimAccel;
209    long mAnimLastTime;
210    boolean mAnimatingReveal = false;
211    int mViewDelta;
212    int[] mAbsPos = new int[2];
213
214    // for disabling the status bar
215    int mDisabled = 0;
216
217    // tracking calls to View.setSystemUiVisibility()
218    int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
219
220    DisplayMetrics mDisplayMetrics = new DisplayMetrics();
221
222    private class ExpandedDialog extends Dialog {
223        ExpandedDialog(Context context) {
224            super(context, com.android.internal.R.style.Theme_Translucent_NoTitleBar);
225        }
226
227        @Override
228        public boolean dispatchKeyEvent(KeyEvent event) {
229            boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
230            switch (event.getKeyCode()) {
231            case KeyEvent.KEYCODE_BACK:
232                if (!down) {
233                    animateCollapse();
234                }
235                return true;
236            }
237            return super.dispatchKeyEvent(event);
238        }
239    }
240
241    @Override
242    public void start() {
243        mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
244                .getDefaultDisplay();
245
246        mWindowManager = IWindowManager.Stub.asInterface(
247                ServiceManager.getService(Context.WINDOW_SERVICE));
248
249        super.start();
250
251        addNavigationBar();
252
253        //addIntruderView();
254
255        // Lastly, call to the icon policy to install/update all the icons.
256        mIconPolicy = new PhoneStatusBarPolicy(mContext);
257    }
258
259    // ================================================================================
260    // Constructing the view
261    // ================================================================================
262    protected View makeStatusBarView() {
263        final Context context = mContext;
264
265        Resources res = context.getResources();
266
267        mDisplay.getMetrics(mDisplayMetrics);
268        if (DEBUG) {
269            Slog.d(TAG, "makeStatusBarView: mDisplayMetrics=" + mDisplayMetrics);
270            mDisplayMetrics = res.getDisplayMetrics();
271            Slog.d(TAG, "makeStatusBarView: mDisplayMetrics2=" + mDisplayMetrics);
272        }
273        loadDimens();
274
275        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
276
277        ExpandedView expanded = (ExpandedView)View.inflate(context,
278                R.layout.status_bar_expanded, null);
279        if (DEBUG) {
280            expanded.setBackgroundColor(0x6000FF80);
281        }
282        expanded.mService = this;
283
284        mIntruderAlertView = View.inflate(context, R.layout.intruder_alert, null);
285        mIntruderAlertView.setVisibility(View.GONE);
286        mIntruderAlertView.setClickable(true);
287
288        PhoneStatusBarView sb = (PhoneStatusBarView)View.inflate(context,
289                R.layout.status_bar, null);
290        sb.mService = this;
291        mStatusBarView = sb;
292
293        try {
294            boolean showNav = res.getBoolean(com.android.internal.R.bool.config_showNavigationBar);
295            if (showNav) {
296                mNavigationBarView =
297                    (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
298
299                mNavigationBarView.setDisabledFlags(mDisabled);
300
301                sb.setOnSystemUiVisibilityChangeListener(
302                    new View.OnSystemUiVisibilityChangeListener() {
303                        @Override
304                        public void onSystemUiVisibilityChange(int visibility) {
305                            if (DEBUG) {
306                                Slog.d(TAG, "systemUi: " + visibility);
307                            }
308                            boolean hide = (0 != (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION));
309                            mNavigationBarView.setHidden(hide);
310                        }
311                    });
312            }
313        } catch (Resources.NotFoundException ex) {
314            // no nav bar for you
315        }
316
317        // figure out which pixel-format to use for the status bar.
318        mPixelFormat = PixelFormat.OPAQUE;
319        mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons);
320        mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons);
321        mIcons = (LinearLayout)sb.findViewById(R.id.icons);
322        mTickerView = sb.findViewById(R.id.ticker);
323
324        mExpandedDialog = new ExpandedDialog(context);
325        mExpandedView = expanded;
326        mPile = (NotificationRowLayout)expanded.findViewById(R.id.latestItems);
327        mExpandedContents = mPile; // was: expanded.findViewById(R.id.notificationLinearLayout);
328        mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
329        mNoNotificationsTitle.setVisibility(View.GONE); // disabling for now
330
331        mClearButton = expanded.findViewById(R.id.clear_all_button);
332        mClearButton.setOnClickListener(mClearButtonListener);
333        mClearButton.setAlpha(0f);
334        mClearButton.setEnabled(false);
335        mDateView = (DateView)expanded.findViewById(R.id.date);
336        mSettingsButton = expanded.findViewById(R.id.settings_button);
337        mSettingsButton.setOnClickListener(mSettingsButtonListener);
338        mScrollView = (ScrollView)expanded.findViewById(R.id.scroll);
339
340        mTicker = new MyTicker(context, sb);
341
342        TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText);
343        tickerView.mTicker = mTicker;
344
345        mTrackingView = (TrackingView)View.inflate(context, R.layout.status_bar_tracking, null);
346        mTrackingView.mService = this;
347        mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close);
348        mCloseView.mService = this;
349
350        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
351
352        // set the inital view visibility
353        setAreThereNotifications();
354
355        // Other icons
356        mLocationController = new LocationController(mContext); // will post a notification
357        mBatteryController = new BatteryController(mContext);
358        mBatteryController.addIconView((ImageView)sb.findViewById(R.id.battery));
359        mNetworkController = new NetworkController(mContext);
360        final SignalClusterView signalCluster =
361                (SignalClusterView)sb.findViewById(R.id.signal_cluster);
362        mNetworkController.addSignalCluster(signalCluster);
363        signalCluster.setNetworkController(mNetworkController);
364
365        // Recents Panel
366        updateRecentsPanel();
367
368        // receive broadcasts
369        IntentFilter filter = new IntentFilter();
370        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
371        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
372        filter.addAction(Intent.ACTION_SCREEN_OFF);
373        context.registerReceiver(mBroadcastReceiver, filter);
374
375        return sb;
376    }
377
378    protected WindowManager.LayoutParams getRecentsLayoutParams(LayoutParams layoutParams) {
379        boolean opaque = false;
380        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
381                layoutParams.width,
382                layoutParams.height,
383                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
384                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
385                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
386                | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
387                (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
388        if (ActivityManager.isHighEndGfx(mDisplay)) {
389            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
390        }
391        lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
392        lp.setTitle("RecentsPanel");
393        lp.windowAnimations = R.style.Animation_RecentPanel;
394        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
395        | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
396        return lp;
397    }
398
399    protected void updateRecentsPanel() {
400        // Recents Panel
401        boolean visible = false;
402        if (mRecentsPanel != null) {
403            visible = mRecentsPanel.isShowing();
404            WindowManagerImpl.getDefault().removeView(mRecentsPanel);
405        }
406
407        // Provide RecentsPanelView with a temporary parent to allow layout params to work.
408        LinearLayout tmpRoot = new LinearLayout(mContext);
409        mRecentsPanel = (RecentsPanelView) LayoutInflater.from(mContext).inflate(
410                R.layout.status_bar_recent_panel, tmpRoot, false);
411
412        mRecentsPanel.setOnTouchListener(new TouchOutsideListener(MSG_CLOSE_RECENTS_PANEL,
413                mRecentsPanel));
414        mRecentsPanel.setVisibility(View.GONE);
415        WindowManager.LayoutParams lp = getRecentsLayoutParams(mRecentsPanel.getLayoutParams());
416
417        WindowManagerImpl.getDefault().addView(mRecentsPanel, lp);
418        mRecentsPanel.setBar(this);
419        if (visible) {
420            mRecentsPanel.show(true, false);
421        }
422
423    }
424
425    protected int getStatusBarGravity() {
426        return Gravity.TOP | Gravity.FILL_HORIZONTAL;
427    }
428
429    public int getStatusBarHeight() {
430        final Resources res = mContext.getResources();
431        return res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
432    }
433
434    private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
435        public void onClick(View v) {
436            toggleRecentApps();
437        }
438    };
439
440    // For small-screen devices (read: phones) that lack hardware navigation buttons
441    private void addNavigationBar() {
442        if (mNavigationBarView == null) return;
443
444        mNavigationBarView.reorient();
445
446        mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
447
448        WindowManagerImpl.getDefault().addView(
449                mNavigationBarView, getNavigationBarLayoutParams());
450    }
451
452    private void repositionNavigationBar() {
453        if (mNavigationBarView == null) return;
454
455        mNavigationBarView.reorient();
456
457        mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
458
459        WindowManagerImpl.getDefault().updateViewLayout(
460                mNavigationBarView, getNavigationBarLayoutParams());
461    }
462
463    private WindowManager.LayoutParams getNavigationBarLayoutParams() {
464        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
465                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
466                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
467                    0
468                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
469                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
470                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
471                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
472                    | WindowManager.LayoutParams.FLAG_SLIPPERY,
473                PixelFormat.OPAQUE);
474        // this will allow the navbar to run in an overlay on devices that support this
475        if (ActivityManager.isHighEndGfx(mDisplay)) {
476            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
477        }
478
479        lp.setTitle("NavigationBar");
480        lp.windowAnimations = 0;
481
482        return lp;
483    }
484
485    private void addIntruderView() {
486        final int height = getStatusBarHeight();
487
488        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
489                ViewGroup.LayoutParams.MATCH_PARENT,
490                ViewGroup.LayoutParams.WRAP_CONTENT,
491                WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL,
492                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
493                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
494                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
495                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
496                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
497                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
498                PixelFormat.TRANSLUCENT);
499        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
500        lp.y += height * 1.5; // FIXME
501        lp.setTitle("IntruderAlert");
502        lp.packageName = mContext.getPackageName();
503        lp.windowAnimations = R.style.Animation_StatusBar_IntruderAlert;
504
505        WindowManagerImpl.getDefault().addView(mIntruderAlertView, lp);
506    }
507
508    public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
509        if (SPEW) Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
510                + " icon=" + icon);
511        StatusBarIconView view = new StatusBarIconView(mContext, slot, null);
512        view.set(icon);
513        mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize));
514    }
515
516    public void updateIcon(String slot, int index, int viewIndex,
517            StatusBarIcon old, StatusBarIcon icon) {
518        if (SPEW) Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
519                + " old=" + old + " icon=" + icon);
520        StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex);
521        view.set(icon);
522    }
523
524    public void removeIcon(String slot, int index, int viewIndex) {
525        if (SPEW) Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
526        mStatusIcons.removeViewAt(viewIndex);
527    }
528
529    public void addNotification(IBinder key, StatusBarNotification notification) {
530        StatusBarIconView iconView = addNotificationViews(key, notification);
531        if (iconView == null) return;
532
533        boolean immersive = false;
534        try {
535            immersive = ActivityManagerNative.getDefault().isTopActivityImmersive();
536            if (DEBUG) {
537                Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
538            }
539        } catch (RemoteException ex) {
540        }
541        if (immersive) {
542            if ((notification.notification.flags & Notification.FLAG_HIGH_PRIORITY) != 0) {
543                Slog.d(TAG, "Presenting high-priority notification in immersive activity");
544                // special new transient ticker mode
545                // 1. Populate mIntruderAlertView
546
547                ImageView alertIcon = (ImageView) mIntruderAlertView.findViewById(R.id.alertIcon);
548                TextView alertText = (TextView) mIntruderAlertView.findViewById(R.id.alertText);
549                alertIcon.setImageDrawable(StatusBarIconView.getIcon(
550                    alertIcon.getContext(),
551                    iconView.getStatusBarIcon()));
552                alertText.setText(notification.notification.tickerText);
553
554                View button = mIntruderAlertView.findViewById(R.id.intruder_alert_content);
555                button.setOnClickListener(
556                    new NotificationClicker(notification.notification.contentIntent,
557                        notification.pkg, notification.tag, notification.id));
558
559                // 2. Animate mIntruderAlertView in
560                mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER);
561
562                // 3. Set alarm to age the notification off (TODO)
563                mHandler.removeMessages(MSG_HIDE_INTRUDER);
564                mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS);
565            }
566        } else if (notification.notification.fullScreenIntent != null) {
567            // not immersive & a full-screen alert should be shown
568            Slog.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
569            try {
570                notification.notification.fullScreenIntent.send();
571            } catch (PendingIntent.CanceledException e) {
572            }
573        } else {
574            // usual case: status bar visible & not immersive
575
576            // show the ticker
577            tick(notification);
578        }
579
580        // Recalculate the position of the sliding windows and the titles.
581        setAreThereNotifications();
582        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
583    }
584
585    public void updateNotification(IBinder key, StatusBarNotification notification) {
586        if (DEBUG) Slog.d(TAG, "updateNotification(" + key + " -> " + notification + ")");
587
588        final NotificationData.Entry oldEntry = mNotificationData.findByKey(key);
589        if (oldEntry == null) {
590            Slog.w(TAG, "updateNotification for unknown key: " + key);
591            return;
592        }
593
594        final StatusBarNotification oldNotification = oldEntry.notification;
595        final RemoteViews oldContentView = oldNotification.notification.contentView;
596
597        final RemoteViews contentView = notification.notification.contentView;
598
599
600        if (DEBUG) {
601            Slog.d(TAG, "old notification: when=" + oldNotification.notification.when
602                    + " ongoing=" + oldNotification.isOngoing()
603                    + " expanded=" + oldEntry.expanded
604                    + " contentView=" + oldContentView
605                    + " rowParent=" + oldEntry.row.getParent());
606            Slog.d(TAG, "new notification: when=" + notification.notification.when
607                    + " ongoing=" + oldNotification.isOngoing()
608                    + " contentView=" + contentView);
609        }
610
611
612        // Can we just reapply the RemoteViews in place?  If when didn't change, the order
613        // didn't change.
614        boolean contentsUnchanged = oldEntry.expanded != null
615                && contentView != null && oldContentView != null
616                && contentView.getPackage() != null
617                && oldContentView.getPackage() != null
618                && oldContentView.getPackage().equals(contentView.getPackage())
619                && oldContentView.getLayoutId() == contentView.getLayoutId();
620        ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent();
621        boolean orderUnchanged = notification.notification.when==oldNotification.notification.when
622                && notification.priority == oldNotification.priority;
623                // priority now encompasses isOngoing()
624        boolean isFirstAnyway = rowParent.indexOfChild(oldEntry.row) == 0;
625        if (contentsUnchanged && (orderUnchanged || isFirstAnyway)) {
626            if (DEBUG) Slog.d(TAG, "reusing notification for key: " + key);
627            oldEntry.notification = notification;
628            try {
629                // Reapply the RemoteViews
630                contentView.reapply(mContext, oldEntry.content);
631                // update the contentIntent
632                final PendingIntent contentIntent = notification.notification.contentIntent;
633                if (contentIntent != null) {
634                    final View.OnClickListener listener = new NotificationClicker(contentIntent,
635                            notification.pkg, notification.tag, notification.id);
636                    oldEntry.largeIcon.setOnClickListener(listener);
637                    oldEntry.content.setOnClickListener(listener);
638                } else {
639                    oldEntry.largeIcon.setOnClickListener(null);
640                    oldEntry.content.setOnClickListener(null);
641                }
642                // Update the icon.
643                final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
644                        notification.notification.icon, notification.notification.iconLevel,
645                        notification.notification.number,
646                        notification.notification.tickerText);
647                if (!oldEntry.icon.set(ic)) {
648                    handleNotificationError(key, notification, "Couldn't update icon: " + ic);
649                    return;
650                }
651                // Update the large icon
652                if (notification.notification.largeIcon != null) {
653                    oldEntry.largeIcon.setImageBitmap(notification.notification.largeIcon);
654                } else {
655                    oldEntry.largeIcon.getLayoutParams().width = 0;
656                    oldEntry.largeIcon.setVisibility(View.INVISIBLE);
657                }
658            }
659            catch (RuntimeException e) {
660                // It failed to add cleanly.  Log, and remove the view from the panel.
661                Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
662                removeNotificationViews(key);
663                addNotificationViews(key, notification);
664            }
665        } else {
666            if (SPEW) Slog.d(TAG, "not reusing notification");
667            removeNotificationViews(key);
668            addNotificationViews(key, notification);
669        }
670
671        // Restart the ticker if it's still running
672        if (notification.notification.tickerText != null
673                && !TextUtils.equals(notification.notification.tickerText,
674                    oldEntry.notification.notification.tickerText)) {
675            tick(notification);
676        }
677
678        // Recalculate the position of the sliding windows and the titles.
679        setAreThereNotifications();
680        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
681    }
682
683    public void removeNotification(IBinder key) {
684        if (SPEW) Slog.d(TAG, "removeNotification key=" + key);
685        StatusBarNotification old = removeNotificationViews(key);
686
687        if (old != null) {
688            // Cancel the ticker if it's still running
689            mTicker.removeEntry(old);
690
691            // Recalculate the position of the sliding windows and the titles.
692            updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
693        }
694
695        setAreThereNotifications();
696    }
697
698    @Override
699    protected void onConfigurationChanged(Configuration newConfig) {
700        updateRecentsPanel();
701    }
702
703
704    View[] makeNotificationView(StatusBarNotification notification, ViewGroup parent) {
705        Notification n = notification.notification;
706        RemoteViews remoteViews = n.contentView;
707        if (remoteViews == null) {
708            return null;
709        }
710
711        // create the row view
712        LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
713                Context.LAYOUT_INFLATER_SERVICE);
714        View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false);
715
716        // wire up the veto button
717        View vetoButton = row.findViewById(R.id.veto);
718        if (notification.isClearable()) {
719            final String _pkg = notification.pkg;
720            final String _tag = notification.tag;
721            final int _id = notification.id;
722            vetoButton.setOnClickListener(new View.OnClickListener() {
723                    public void onClick(View v) {
724                        try {
725                            mBarService.onNotificationClear(_pkg, _tag, _id);
726                        } catch (RemoteException ex) {
727                            // system process is dead if we're here.
728                        }
729                    }
730                });
731        } else {
732            vetoButton.setVisibility(View.GONE);
733        }
734        vetoButton.setContentDescription(mContext.getString(
735                R.string.accessibility_remove_notification));
736
737        // the large icon
738        ImageView largeIcon = (ImageView)row.findViewById(R.id.large_icon);
739        if (notification.notification.largeIcon != null) {
740            largeIcon.setImageBitmap(notification.notification.largeIcon);
741        } else {
742            largeIcon.getLayoutParams().width = 0;
743            largeIcon.setVisibility(View.INVISIBLE);
744        }
745
746        // bind the click event to the content area
747        ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
748        content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
749        content.setOnFocusChangeListener(mFocusChangeListener);
750        PendingIntent contentIntent = n.contentIntent;
751        if (contentIntent != null) {
752            final View.OnClickListener listener = new NotificationClicker(contentIntent,
753                    notification.pkg, notification.tag, notification.id);
754            largeIcon.setOnClickListener(listener);
755            content.setOnClickListener(listener);
756        } else {
757            largeIcon.setOnClickListener(null);
758            content.setOnClickListener(null);
759        }
760
761        View expanded = null;
762        Exception exception = null;
763        try {
764            expanded = remoteViews.apply(mContext, content);
765        }
766        catch (RuntimeException e) {
767            exception = e;
768        }
769        if (expanded == null) {
770            String ident = notification.pkg + "/0x" + Integer.toHexString(notification.id);
771            Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
772            return null;
773        } else {
774            content.addView(expanded);
775            row.setDrawingCacheEnabled(true);
776        }
777
778        return new View[] { row, content, expanded };
779    }
780
781    StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
782        if (DEBUG) {
783            Slog.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification);
784        }
785        // Construct the icon.
786        final StatusBarIconView iconView = new StatusBarIconView(mContext,
787                notification.pkg + "/0x" + Integer.toHexString(notification.id),
788                notification.notification);
789        iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
790
791        final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
792                    notification.notification.icon,
793                    notification.notification.iconLevel,
794                    notification.notification.number,
795                    notification.notification.tickerText);
796        if (!iconView.set(ic)) {
797            handleNotificationError(key, notification, "Couldn't create icon: " + ic);
798            return null;
799        }
800        // Construct the expanded view.
801        NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
802        if (!inflateViews(entry, mPile)) {
803            handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
804                    + notification);
805            return null;
806        }
807
808        // Add the expanded view and icon.
809        int pos = mNotificationData.add(entry);
810        if (DEBUG) {
811            Slog.d(TAG, "addNotificationViews: added at " + pos);
812        }
813        updateNotificationIcons();
814
815        return iconView;
816    }
817
818    private void loadNotificationShade() {
819        int N = mNotificationData.size();
820
821        ArrayList<View> toShow = new ArrayList<View>();
822
823        for (int i=0; i<N; i++) {
824            View row = mNotificationData.get(N-i-1).row;
825            toShow.add(row);
826        }
827
828        ArrayList<View> toRemove = new ArrayList<View>();
829        for (int i=0; i<mPile.getChildCount(); i++) {
830            View child = mPile.getChildAt(i);
831            if (!toShow.contains(child)) {
832                toRemove.add(child);
833            }
834        }
835
836        for (View remove : toRemove) {
837            mPile.removeView(remove);
838        }
839
840        for (int i=0; i<toShow.size(); i++) {
841            View v = toShow.get(i);
842            if (v.getParent() == null) {
843                mPile.addView(v, 0); // the notification shade has newest at the top
844            }
845        }
846    }
847
848    private void reloadAllNotificationIcons() {
849        if (mNotificationIcons == null) return;
850        mNotificationIcons.removeAllViews();
851        updateNotificationIcons();
852    }
853
854    private void updateNotificationIcons() {
855        loadNotificationShade();
856
857        final LinearLayout.LayoutParams params
858            = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
859
860        int N = mNotificationData.size();
861
862        if (DEBUG) {
863            Slog.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + mNotificationIcons);
864        }
865
866        ArrayList<View> toShow = new ArrayList<View>();
867
868        for (int i=0; i<N; i++) {
869            toShow.add(mNotificationData.get(N-i-1).icon);
870        }
871
872        ArrayList<View> toRemove = new ArrayList<View>();
873        for (int i=0; i<mNotificationIcons.getChildCount(); i++) {
874            View child = mNotificationIcons.getChildAt(i);
875            if (!toShow.contains(child)) {
876                toRemove.add(child);
877            }
878        }
879
880        for (View remove : toRemove) {
881            mNotificationIcons.removeView(remove);
882        }
883
884        for (int i=0; i<toShow.size(); i++) {
885            View v = toShow.get(i);
886            if (v.getParent() == null) {
887                mNotificationIcons.addView(v, i, params);
888            }
889        }
890    }
891
892    private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
893        StatusBarNotification sbn = entry.notification;
894        RemoteViews remoteViews = sbn.notification.contentView;
895        if (remoteViews == null) {
896            return false;
897        }
898
899        // create the row view
900        LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
901                Context.LAYOUT_INFLATER_SERVICE);
902        View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false);
903        View vetoButton = row.findViewById(R.id.veto);
904        if (entry.notification.isClearable()) {
905            final String _pkg = sbn.pkg;
906            final String _tag = sbn.tag;
907            final int _id = sbn.id;
908            vetoButton.setOnClickListener(new View.OnClickListener() {
909                    public void onClick(View v) {
910                        try {
911                            mBarService.onNotificationClear(_pkg, _tag, _id);
912                        } catch (RemoteException ex) {
913                            // system process is dead if we're here.
914                        }
915                    }
916                });
917        } else {
918            vetoButton.setVisibility(View.GONE);
919        }
920        vetoButton.setContentDescription(mContext.getString(
921                R.string.accessibility_remove_notification));
922
923        // the large icon
924        ImageView largeIcon = (ImageView)row.findViewById(R.id.large_icon);
925        if (sbn.notification.largeIcon != null) {
926            largeIcon.setImageBitmap(sbn.notification.largeIcon);
927            largeIcon.setContentDescription(sbn.notification.tickerText);
928        } else {
929            largeIcon.getLayoutParams().width = 0;
930            largeIcon.setVisibility(View.INVISIBLE);
931        }
932        largeIcon.setContentDescription(sbn.notification.tickerText);
933
934        // bind the click event to the content area
935        ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
936        // XXX: update to allow controls within notification views
937        content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
938//        content.setOnFocusChangeListener(mFocusChangeListener);
939        PendingIntent contentIntent = sbn.notification.contentIntent;
940        if (contentIntent != null) {
941            final View.OnClickListener listener = new NotificationClicker(contentIntent,
942                    sbn.pkg, sbn.tag, sbn.id);
943            largeIcon.setOnClickListener(listener);
944            content.setOnClickListener(listener);
945        } else {
946            largeIcon.setOnClickListener(null);
947            content.setOnClickListener(null);
948        }
949
950        View expanded = null;
951        Exception exception = null;
952        try {
953            expanded = remoteViews.apply(mContext, content);
954        }
955        catch (RuntimeException e) {
956            exception = e;
957        }
958        if (expanded == null) {
959            final String ident = sbn.pkg + "/0x" + Integer.toHexString(sbn.id);
960            Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
961            return false;
962        } else {
963            content.addView(expanded);
964            row.setDrawingCacheEnabled(true);
965        }
966
967        entry.row = row;
968        entry.content = content;
969        entry.expanded = expanded;
970        entry.largeIcon = largeIcon;
971
972        return true;
973    }
974
975    StatusBarNotification removeNotificationViews(IBinder key) {
976        NotificationData.Entry entry = mNotificationData.remove(key);
977        if (entry == null) {
978            Slog.w(TAG, "removeNotification for unknown key: " + key);
979            return null;
980        }
981        // Remove the expanded view.
982        ViewGroup rowParent = (ViewGroup)entry.row.getParent();
983        if (rowParent != null) rowParent.removeView(entry.row);
984        updateNotificationIcons();
985
986        return entry.notification;
987    }
988
989    private void setAreThereNotifications() {
990        final boolean any = mNotificationData.size() > 0;
991
992        final boolean clearable = any && mNotificationData.hasClearableItems();
993
994        if (DEBUG) {
995            Slog.d(TAG, "setAreThereNotifications: N=" + mNotificationData.size()
996                    + " any=" + any + " clearable=" + clearable);
997        }
998
999        if (mClearButton.isShown()) {
1000            if (clearable != (mClearButton.getAlpha() == 1.0f)) {
1001                ObjectAnimator.ofFloat(mClearButton, "alpha",
1002                        clearable ? 1.0f : 0.0f)
1003                    .setDuration(250)
1004                    .start();
1005            }
1006        } else {
1007            mClearButton.setAlpha(clearable ? 1.0f : 0.0f);
1008        }
1009        mClearButton.setEnabled(clearable);
1010
1011        /*
1012        if (mNoNotificationsTitle.isShown()) {
1013            if (any != (mNoNotificationsTitle.getAlpha() == 0.0f)) {
1014                ObjectAnimator a = ObjectAnimator.ofFloat(mNoNotificationsTitle, "alpha",
1015                            (any ? 0.0f : 0.75f));
1016                a.setDuration(any ? 0 : 500);
1017                a.setStartDelay(any ? 250 : 1000);
1018                a.start();
1019            }
1020        } else {
1021            mNoNotificationsTitle.setAlpha(any ? 0.0f : 0.75f);
1022        }
1023        */
1024    }
1025
1026    public void showClock(boolean show) {
1027        View clock = mStatusBarView.findViewById(R.id.clock);
1028        if (clock != null) {
1029            clock.setVisibility(show ? View.VISIBLE : View.GONE);
1030        }
1031    }
1032
1033    /**
1034     * State is one or more of the DISABLE constants from StatusBarManager.
1035     */
1036    public void disable(int state) {
1037        final int old = mDisabled;
1038        final int diff = state ^ old;
1039        mDisabled = state;
1040
1041        if (DEBUG) {
1042            Slog.d(TAG, String.format("disable: 0x%08x -> 0x%08x (diff: 0x%08x)",
1043                old, state, diff));
1044        }
1045
1046        StringBuilder flagdbg = new StringBuilder();
1047        flagdbg.append("disable: < ");
1048        flagdbg.append(((state & StatusBarManager.DISABLE_EXPAND) != 0) ? "EXPAND" : "expand");
1049        flagdbg.append(((diff  & StatusBarManager.DISABLE_EXPAND) != 0) ? "* " : " ");
1050        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "ICONS" : "icons");
1051        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " ");
1052        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts");
1053        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " ");
1054        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) ? "TICKER" : "ticker");
1055        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) ? "* " : " ");
1056        flagdbg.append(((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info");
1057        flagdbg.append(((diff  & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " ");
1058        flagdbg.append(((state & StatusBarManager.DISABLE_NAVIGATION) != 0) ? "NAVIGATION" : "navigation");
1059        flagdbg.append(((diff  & StatusBarManager.DISABLE_NAVIGATION) != 0) ? "* " : " ");
1060        flagdbg.append(((state & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back");
1061        flagdbg.append(((diff  & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " ");
1062        flagdbg.append(((state & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock");
1063        flagdbg.append(((diff  & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " ");
1064        flagdbg.append(">");
1065        Slog.d(TAG, flagdbg.toString());
1066
1067        if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) {
1068            boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0;
1069            showClock(show);
1070        }
1071        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
1072            if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
1073                animateCollapse();
1074            }
1075        }
1076
1077        if ((diff & (StatusBarManager.DISABLE_NAVIGATION | StatusBarManager.DISABLE_BACK)) != 0) {
1078            // the nav bar will take care of DISABLE_NAVIGATION and DISABLE_BACK
1079            if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state);
1080
1081            if ((state & StatusBarManager.DISABLE_NAVIGATION) != 0) {
1082                // close recents if it's visible
1083                mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
1084                mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
1085            }
1086        }
1087
1088        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1089            if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1090                if (mTicking) {
1091                    mTicker.halt();
1092                } else {
1093                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
1094                }
1095            } else {
1096                if (!mExpandedVisible) {
1097                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1098                }
1099            }
1100        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1101            if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1102                mTicker.halt();
1103            }
1104        }
1105    }
1106
1107    /**
1108     * All changes to the status bar and notifications funnel through here and are batched.
1109     */
1110    private class H extends Handler {
1111        public void handleMessage(Message m) {
1112            switch (m.what) {
1113                case MSG_ANIMATE:
1114                    doAnimation();
1115                    break;
1116                case MSG_ANIMATE_REVEAL:
1117                    doRevealAnimation();
1118                    break;
1119                case MSG_OPEN_NOTIFICATION_PANEL:
1120                    animateExpand();
1121                    break;
1122                case MSG_CLOSE_NOTIFICATION_PANEL:
1123                    animateCollapse();
1124                    break;
1125                case MSG_SHOW_INTRUDER:
1126                    setIntruderAlertVisibility(true);
1127                    break;
1128                case MSG_HIDE_INTRUDER:
1129                    setIntruderAlertVisibility(false);
1130                    break;
1131                case MSG_OPEN_RECENTS_PANEL:
1132                    if (DEBUG) Slog.d(TAG, "opening recents panel");
1133                    if (mRecentsPanel != null) {
1134                        mRecentsPanel.show(true, true);
1135                    }
1136                    break;
1137                case MSG_CLOSE_RECENTS_PANEL:
1138                    if (DEBUG) Slog.d(TAG, "closing recents panel");
1139                    if (mRecentsPanel != null && mRecentsPanel.isShowing()) {
1140                        mRecentsPanel.show(false, true);
1141                    }
1142                    break;
1143            }
1144        }
1145    }
1146
1147    View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
1148        public void onFocusChange(View v, boolean hasFocus) {
1149            // Because 'v' is a ViewGroup, all its children will be (un)selected
1150            // too, which allows marqueeing to work.
1151            v.setSelected(hasFocus);
1152        }
1153    };
1154
1155    private void makeExpandedVisible() {
1156        if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
1157        if (mExpandedVisible) {
1158            return;
1159        }
1160        mExpandedVisible = true;
1161        visibilityChanged(true);
1162
1163        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1164        mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1165        mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1166        if (DEBUG) {
1167            Slog.d(TAG, "makeExpandedVisible: expanded params = " + mExpandedParams);
1168        }
1169        mExpandedDialog.getWindow().setAttributes(mExpandedParams);
1170        mExpandedView.requestFocus(View.FOCUS_FORWARD);
1171        mTrackingView.setVisibility(View.VISIBLE);
1172    }
1173
1174    public void animateExpand() {
1175        if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
1176        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
1177            return ;
1178        }
1179        if (mExpanded) {
1180            return;
1181        }
1182
1183        prepareTracking(0, true);
1184        performFling(0, mSelfExpandVelocityPx, true);
1185    }
1186
1187    public void animateCollapse() {
1188        animateCollapse(false);
1189    }
1190
1191    public void animateCollapse(boolean excludeRecents) {
1192        animateCollapse(excludeRecents, 1.0f);
1193    }
1194
1195    public void animateCollapse(boolean excludeRecents, float velocityMultiplier) {
1196        if (SPEW) {
1197            Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
1198                    + " mExpandedVisible=" + mExpandedVisible
1199                    + " mExpanded=" + mExpanded
1200                    + " mAnimating=" + mAnimating
1201                    + " mAnimY=" + mAnimY
1202                    + " mAnimVel=" + mAnimVel);
1203        }
1204
1205        if (!excludeRecents) {
1206            mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
1207            mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
1208        }
1209
1210        if (!mExpandedVisible) {
1211            return;
1212        }
1213
1214        int y;
1215        if (mAnimating) {
1216            y = (int)mAnimY;
1217        } else {
1218            y = mDisplayMetrics.heightPixels-1;
1219        }
1220        // Let the fling think that we're open so it goes in the right direction
1221        // and doesn't try to re-open the windowshade.
1222        mExpanded = true;
1223        prepareTracking(y, false);
1224        performFling(y, -mSelfCollapseVelocityPx*velocityMultiplier, true);
1225    }
1226
1227    void performExpand() {
1228        if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded);
1229        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
1230            return ;
1231        }
1232        if (mExpanded) {
1233            return;
1234        }
1235
1236        mExpanded = true;
1237        makeExpandedVisible();
1238        updateExpandedViewPos(EXPANDED_FULL_OPEN);
1239
1240        if (false) postStartTracing();
1241    }
1242
1243    void performCollapse() {
1244        if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
1245                + " mExpandedVisible=" + mExpandedVisible);
1246
1247        if (!mExpandedVisible) {
1248            return;
1249        }
1250        mExpandedVisible = false;
1251        visibilityChanged(false);
1252        mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1253        mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1254        mExpandedDialog.getWindow().setAttributes(mExpandedParams);
1255        mTrackingView.setVisibility(View.GONE);
1256
1257        if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
1258            setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1259        }
1260
1261        if (!mExpanded) {
1262            return;
1263        }
1264        mExpanded = false;
1265    }
1266
1267    void doAnimation() {
1268        if (mAnimating) {
1269            if (SPEW) Slog.d(TAG, "doAnimation");
1270            if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
1271            incrementAnim();
1272            if (SPEW) Slog.d(TAG, "doAnimation after  mAnimY=" + mAnimY);
1273            if (mAnimY >= mDisplayMetrics.heightPixels-1) {
1274                if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
1275                mAnimating = false;
1276                updateExpandedViewPos(EXPANDED_FULL_OPEN);
1277                performExpand();
1278            }
1279            else if (mAnimY < mStatusBarView.getHeight()) {
1280                if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
1281                mAnimating = false;
1282                updateExpandedViewPos(0);
1283                performCollapse();
1284            }
1285            else {
1286                updateExpandedViewPos((int)mAnimY);
1287                mCurAnimationTime += ANIM_FRAME_DURATION;
1288                mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
1289            }
1290        }
1291    }
1292
1293    void stopTracking() {
1294        mTracking = false;
1295        mVelocityTracker.recycle();
1296        mVelocityTracker = null;
1297    }
1298
1299    void incrementAnim() {
1300        long now = SystemClock.uptimeMillis();
1301        float t = ((float)(now - mAnimLastTime)) / 1000;            // ms -> s
1302        final float y = mAnimY;
1303        final float v = mAnimVel;                                   // px/s
1304        final float a = mAnimAccel;                                 // px/s/s
1305        mAnimY = y + (v*t) + (0.5f*a*t*t);                          // px
1306        mAnimVel = v + (a*t);                                       // px/s
1307        mAnimLastTime = now;                                        // ms
1308        //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
1309        //        + " mAnimAccel=" + mAnimAccel);
1310    }
1311
1312    void doRevealAnimation() {
1313        final int h = mCloseView.getHeight() + mStatusBarView.getHeight();
1314        if (mAnimatingReveal && mAnimating && mAnimY < h) {
1315            incrementAnim();
1316            if (mAnimY >= h) {
1317                mAnimY = h;
1318                updateExpandedViewPos((int)mAnimY);
1319            } else {
1320                updateExpandedViewPos((int)mAnimY);
1321                mCurAnimationTime += ANIM_FRAME_DURATION;
1322                mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
1323                        mCurAnimationTime);
1324            }
1325        }
1326    }
1327
1328    void prepareTracking(int y, boolean opening) {
1329        if (CHATTY) {
1330            Slog.d(TAG, "panel: beginning to track the user's touch, y=" + y + " opening=" + opening);
1331        }
1332
1333        // there are some race conditions that cause this to be inaccurate; let's recalculate it any
1334        // time we're about to drag the panel
1335        updateExpandedSize();
1336
1337        mTracking = true;
1338        mVelocityTracker = VelocityTracker.obtain();
1339        if (opening) {
1340            mAnimAccel = mExpandAccelPx;
1341            mAnimVel = mFlingExpandMinVelocityPx;
1342            mAnimY = mStatusBarView.getHeight();
1343            updateExpandedViewPos((int)mAnimY);
1344            mAnimating = true;
1345            mAnimatingReveal = true;
1346            mHandler.removeMessages(MSG_ANIMATE);
1347            mHandler.removeMessages(MSG_ANIMATE_REVEAL);
1348            long now = SystemClock.uptimeMillis();
1349            mAnimLastTime = now;
1350            mCurAnimationTime = now + ANIM_FRAME_DURATION;
1351            mAnimating = true;
1352            mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
1353                    mCurAnimationTime);
1354            makeExpandedVisible();
1355        } else {
1356            // it's open, close it?
1357            if (mAnimating) {
1358                mAnimating = false;
1359                mHandler.removeMessages(MSG_ANIMATE);
1360            }
1361            updateExpandedViewPos(y + mViewDelta);
1362        }
1363    }
1364
1365    void performFling(int y, float vel, boolean always) {
1366        if (CHATTY) {
1367            Slog.d(TAG, "panel: will fling, y=" + y + " vel=" + vel);
1368        }
1369
1370        mAnimatingReveal = false;
1371
1372        mAnimY = y;
1373        mAnimVel = vel;
1374
1375        //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
1376
1377        if (mExpanded) {
1378            if (!always && (
1379                    vel > mFlingCollapseMinVelocityPx
1380                    || (y > (mDisplayMetrics.heightPixels*(1f-mCollapseMinDisplayFraction)) &&
1381                        vel > -mFlingExpandMinVelocityPx))) {
1382                // We are expanded, but they didn't move sufficiently to cause
1383                // us to retract.  Animate back to the expanded position.
1384                mAnimAccel = mExpandAccelPx;
1385                if (vel < 0) {
1386                    mAnimVel = 0;
1387                }
1388            }
1389            else {
1390                // We are expanded and are now going to animate away.
1391                mAnimAccel = -mCollapseAccelPx;
1392                if (vel > 0) {
1393                    mAnimVel = 0;
1394                }
1395            }
1396        } else {
1397            if (always || (
1398                    vel > mFlingExpandMinVelocityPx
1399                    || (y > (mDisplayMetrics.heightPixels*(1f-mExpandMinDisplayFraction)) &&
1400                        vel > -mFlingCollapseMinVelocityPx))) {
1401                // We are collapsed, and they moved enough to allow us to
1402                // expand.  Animate in the notifications.
1403                mAnimAccel = mExpandAccelPx;
1404                if (vel < 0) {
1405                    mAnimVel = 0;
1406                }
1407            }
1408            else {
1409                // We are collapsed, but they didn't move sufficiently to cause
1410                // us to retract.  Animate back to the collapsed position.
1411                mAnimAccel = -mCollapseAccelPx;
1412                if (vel > 0) {
1413                    mAnimVel = 0;
1414                }
1415            }
1416        }
1417        //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
1418        //        + " mAnimAccel=" + mAnimAccel);
1419
1420        long now = SystemClock.uptimeMillis();
1421        mAnimLastTime = now;
1422        mCurAnimationTime = now + ANIM_FRAME_DURATION;
1423        mAnimating = true;
1424        mHandler.removeMessages(MSG_ANIMATE);
1425        mHandler.removeMessages(MSG_ANIMATE_REVEAL);
1426        mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
1427        stopTracking();
1428    }
1429
1430    boolean interceptTouchEvent(MotionEvent event) {
1431        if (SPEW) {
1432            Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
1433                + mDisabled);
1434        } else if (CHATTY) {
1435            if (event.getAction() != MotionEvent.ACTION_MOVE) {
1436                Slog.d(TAG, String.format(
1437                            "panel: %s at (%f, %f) mDisabled=0x%08x",
1438                            MotionEvent.actionToString(event.getAction()),
1439                            event.getRawX(), event.getRawY(), mDisabled));
1440            }
1441        }
1442
1443        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
1444            return false;
1445        }
1446
1447        final int action = event.getAction();
1448        final int statusBarSize = mStatusBarView.getHeight();
1449        final int hitSize = statusBarSize*2;
1450        final int y = (int)event.getRawY();
1451        if (action == MotionEvent.ACTION_DOWN) {
1452            if (!mExpanded) {
1453                mViewDelta = statusBarSize - y;
1454            } else {
1455                mTrackingView.getLocationOnScreen(mAbsPos);
1456                mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y;
1457            }
1458            if ((!mExpanded && y < hitSize) ||
1459                    (mExpanded && y > (mDisplayMetrics.heightPixels-hitSize))) {
1460
1461                // We drop events at the edge of the screen to make the windowshade come
1462                // down by accident less, especially when pushing open a device with a keyboard
1463                // that rotates (like g1 and droid)
1464                int x = (int)event.getRawX();
1465                final int edgeBorder = mEdgeBorder;
1466                if (x >= edgeBorder && x < mDisplayMetrics.widthPixels - edgeBorder) {
1467                    prepareTracking(y, !mExpanded);// opening if we're not already fully visible
1468                    trackMovement(event);
1469                }
1470            }
1471        } else if (mTracking) {
1472            trackMovement(event);
1473            final int minY = statusBarSize + mCloseView.getHeight();
1474            if (action == MotionEvent.ACTION_MOVE) {
1475                if (mAnimatingReveal && y < minY) {
1476                    // nothing
1477                } else  {
1478                    mAnimatingReveal = false;
1479                    updateExpandedViewPos(y + mViewDelta);
1480                }
1481            } else if (action == MotionEvent.ACTION_UP
1482                    || action == MotionEvent.ACTION_CANCEL) {
1483                mVelocityTracker.computeCurrentVelocity(1000);
1484
1485                float yVel = mVelocityTracker.getYVelocity();
1486                boolean negative = yVel < 0;
1487
1488                float xVel = mVelocityTracker.getXVelocity();
1489                if (xVel < 0) {
1490                    xVel = -xVel;
1491                }
1492                if (xVel > mFlingGestureMaxXVelocityPx) {
1493                    xVel = mFlingGestureMaxXVelocityPx; // limit how much we care about the x axis
1494                }
1495
1496                float vel = (float)Math.hypot(yVel, xVel);
1497                if (negative) {
1498                    vel = -vel;
1499                }
1500
1501                if (CHATTY) {
1502                    Slog.d(TAG, String.format("gesture: vraw=(%f,%f) vnorm=(%f,%f) vlinear=%f",
1503                        mVelocityTracker.getXVelocity(),
1504                        mVelocityTracker.getYVelocity(),
1505                        xVel, yVel,
1506                        vel));
1507                }
1508
1509                performFling(y + mViewDelta, vel, false);
1510            }
1511
1512        }
1513        return false;
1514    }
1515
1516    private void trackMovement(MotionEvent event) {
1517        // Add movement to velocity tracker using raw screen X and Y coordinates instead
1518        // of window coordinates because the window frame may be moving at the same time.
1519        float deltaX = event.getRawX() - event.getX();
1520        float deltaY = event.getRawY() - event.getY();
1521        event.offsetLocation(deltaX, deltaY);
1522        mVelocityTracker.addMovement(event);
1523        event.offsetLocation(-deltaX, -deltaY);
1524    }
1525
1526    @Override // CommandQueue
1527    public void setSystemUiVisibility(int vis) {
1528        final int old = mSystemUiVisibility;
1529        final int diff = vis ^ old;
1530
1531        if (diff != 0) {
1532            mSystemUiVisibility = vis;
1533
1534            if (0 != (diff & View.SYSTEM_UI_FLAG_LOW_PROFILE)) {
1535                final boolean lightsOut = (0 != (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE));
1536                if (lightsOut) {
1537                    animateCollapse();
1538                }
1539                if (mNavigationBarView != null) {
1540                    mNavigationBarView.setLowProfile(lightsOut);
1541                }
1542            }
1543
1544            notifyUiVisibilityChanged();
1545        }
1546    }
1547
1548    public void setLightsOn(boolean on) {
1549        Log.v(TAG, "setLightsOn(" + on + ")");
1550        if (on) {
1551            setSystemUiVisibility(mSystemUiVisibility & ~View.SYSTEM_UI_FLAG_LOW_PROFILE);
1552        } else {
1553            setSystemUiVisibility(mSystemUiVisibility | View.SYSTEM_UI_FLAG_LOW_PROFILE);
1554        }
1555    }
1556
1557    private void notifyUiVisibilityChanged() {
1558        try {
1559            mWindowManager.statusBarVisibilityChanged(mSystemUiVisibility);
1560        } catch (RemoteException ex) {
1561        }
1562    }
1563
1564    public void topAppWindowChanged(boolean showMenu) {
1565        if (DEBUG) {
1566            Slog.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
1567        }
1568        if (mNavigationBarView != null) {
1569            mNavigationBarView.setMenuVisibility(showMenu);
1570        }
1571
1572        // See above re: lights-out policy for legacy apps.
1573        if (showMenu) setLightsOn(true);
1574    }
1575
1576    // Not supported
1577    public void setImeWindowStatus(IBinder token, int vis, int backDisposition) { }
1578    @Override
1579    public void setHardKeyboardStatus(boolean available, boolean enabled) { }
1580
1581    public NotificationClicker makeClicker(PendingIntent intent, String pkg, String tag, int id) {
1582        return new NotificationClicker(intent, pkg, tag, id);
1583    }
1584
1585    private class NotificationClicker implements View.OnClickListener {
1586        private PendingIntent mIntent;
1587        private String mPkg;
1588        private String mTag;
1589        private int mId;
1590
1591        NotificationClicker(PendingIntent intent, String pkg, String tag, int id) {
1592            mIntent = intent;
1593            mPkg = pkg;
1594            mTag = tag;
1595            mId = id;
1596        }
1597
1598        public void onClick(View v) {
1599            try {
1600                // The intent we are sending is for the application, which
1601                // won't have permission to immediately start an activity after
1602                // the user switches to home.  We know it is safe to do at this
1603                // point, so make sure new activity switches are now allowed.
1604                ActivityManagerNative.getDefault().resumeAppSwitches();
1605                // Also, notifications can be launched from the lock screen,
1606                // so dismiss the lock screen when the activity starts.
1607                ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
1608            } catch (RemoteException e) {
1609            }
1610
1611            if (mIntent != null) {
1612                int[] pos = new int[2];
1613                v.getLocationOnScreen(pos);
1614                Intent overlay = new Intent();
1615                overlay.setSourceBounds(
1616                        new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
1617                try {
1618                    mIntent.send(mContext, 0, overlay);
1619                } catch (PendingIntent.CanceledException e) {
1620                    // the stack trace isn't very helpful here.  Just log the exception message.
1621                    Slog.w(TAG, "Sending contentIntent failed: " + e);
1622                }
1623
1624                KeyguardManager kgm =
1625                    (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
1626                if (kgm != null) kgm.exitKeyguardSecurely(null);
1627            }
1628
1629            try {
1630                mBarService.onNotificationClick(mPkg, mTag, mId);
1631            } catch (RemoteException ex) {
1632                // system process is dead if we're here.
1633            }
1634
1635            // close the shade if it was open
1636            animateCollapse();
1637
1638            // If this click was on the intruder alert, hide that instead
1639            mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
1640        }
1641    }
1642
1643    private void tick(StatusBarNotification n) {
1644        // Show the ticker if one is requested. Also don't do this
1645        // until status bar window is attached to the window manager,
1646        // because...  well, what's the point otherwise?  And trying to
1647        // run a ticker without being attached will crash!
1648        if (n.notification.tickerText != null && mStatusBarView.getWindowToken() != null) {
1649            if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
1650                            | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
1651                mTicker.addEntry(n);
1652            }
1653        }
1654    }
1655
1656    /**
1657     * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
1658     * about the failure.
1659     *
1660     * WARNING: this will call back into us.  Don't hold any locks.
1661     */
1662    void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
1663        removeNotification(key);
1664        try {
1665            mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message);
1666        } catch (RemoteException ex) {
1667            // The end is nigh.
1668        }
1669    }
1670
1671    private class MyTicker extends Ticker {
1672        MyTicker(Context context, View sb) {
1673            super(context, sb);
1674        }
1675
1676        @Override
1677        public void tickerStarting() {
1678            mTicking = true;
1679            mIcons.setVisibility(View.GONE);
1680            mTickerView.setVisibility(View.VISIBLE);
1681            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
1682            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
1683        }
1684
1685        @Override
1686        public void tickerDone() {
1687            mIcons.setVisibility(View.VISIBLE);
1688            mTickerView.setVisibility(View.GONE);
1689            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
1690            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
1691                        mTickingDoneListener));
1692        }
1693
1694        public void tickerHalting() {
1695            mIcons.setVisibility(View.VISIBLE);
1696            mTickerView.setVisibility(View.GONE);
1697            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
1698            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
1699                        mTickingDoneListener));
1700        }
1701    }
1702
1703    Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
1704        public void onAnimationEnd(Animation animation) {
1705            mTicking = false;
1706        }
1707        public void onAnimationRepeat(Animation animation) {
1708        }
1709        public void onAnimationStart(Animation animation) {
1710        }
1711    };
1712
1713    private Animation loadAnim(int id, Animation.AnimationListener listener) {
1714        Animation anim = AnimationUtils.loadAnimation(mContext, id);
1715        if (listener != null) {
1716            anim.setAnimationListener(listener);
1717        }
1718        return anim;
1719    }
1720
1721    public String viewInfo(View v) {
1722        return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
1723                + " " + v.getWidth() + "x" + v.getHeight() + ")";
1724    }
1725
1726    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1727        synchronized (mQueueLock) {
1728            pw.println("Current Status Bar state:");
1729            pw.println("  mExpanded=" + mExpanded
1730                    + ", mExpandedVisible=" + mExpandedVisible);
1731            pw.println("  mTicking=" + mTicking);
1732            pw.println("  mTracking=" + mTracking);
1733            pw.println("  mAnimating=" + mAnimating
1734                    + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
1735                    + ", mAnimAccel=" + mAnimAccel);
1736            pw.println("  mCurAnimationTime=" + mCurAnimationTime
1737                    + " mAnimLastTime=" + mAnimLastTime);
1738            pw.println("  mAnimatingReveal=" + mAnimatingReveal
1739                    + " mViewDelta=" + mViewDelta);
1740            pw.println("  mDisplayMetrics=" + mDisplayMetrics);
1741            pw.println("  mExpandedParams: " + mExpandedParams);
1742            pw.println("  mExpandedView: " + viewInfo(mExpandedView));
1743            pw.println("  mExpandedDialog: " + mExpandedDialog);
1744            pw.println("  mTrackingParams: " + mTrackingParams);
1745            pw.println("  mTrackingView: " + viewInfo(mTrackingView));
1746            pw.println("  mPile: " + viewInfo(mPile));
1747            pw.println("  mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle));
1748            pw.println("  mCloseView: " + viewInfo(mCloseView));
1749            pw.println("  mTickerView: " + viewInfo(mTickerView));
1750            pw.println("  mScrollView: " + viewInfo(mScrollView)
1751                    + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
1752        }
1753
1754        if (DUMPTRUCK) {
1755            synchronized (mNotificationData) {
1756                int N = mNotificationData.size();
1757                pw.println("  notification icons: " + N);
1758                for (int i=0; i<N; i++) {
1759                    NotificationData.Entry e = mNotificationData.get(i);
1760                    pw.println("    [" + i + "] key=" + e.key + " icon=" + e.icon);
1761                    StatusBarNotification n = e.notification;
1762                    pw.println("         pkg=" + n.pkg + " id=" + n.id + " priority=" + n.priority);
1763                    pw.println("         notification=" + n.notification);
1764                    pw.println("         tickerText=\"" + n.notification.tickerText + "\"");
1765                }
1766            }
1767
1768            int N = mStatusIcons.getChildCount();
1769            pw.println("  system icons: " + N);
1770            for (int i=0; i<N; i++) {
1771                StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i);
1772                pw.println("    [" + i + "] icon=" + ic);
1773            }
1774
1775            if (false) {
1776                pw.println("see the logcat for a dump of the views we have created.");
1777                // must happen on ui thread
1778                mHandler.post(new Runnable() {
1779                        public void run() {
1780                            mStatusBarView.getLocationOnScreen(mAbsPos);
1781                            Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
1782                                    + ") " + mStatusBarView.getWidth() + "x"
1783                                    + mStatusBarView.getHeight());
1784                            mStatusBarView.debug();
1785
1786                            mExpandedView.getLocationOnScreen(mAbsPos);
1787                            Slog.d(TAG, "mExpandedView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
1788                                    + ") " + mExpandedView.getWidth() + "x"
1789                                    + mExpandedView.getHeight());
1790                            mExpandedView.debug();
1791
1792                            mTrackingView.getLocationOnScreen(mAbsPos);
1793                            Slog.d(TAG, "mTrackingView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
1794                                    + ") " + mTrackingView.getWidth() + "x"
1795                                    + mTrackingView.getHeight());
1796                            mTrackingView.debug();
1797                        }
1798                    });
1799            }
1800        }
1801
1802        mNetworkController.dump(fd, pw, args);
1803    }
1804
1805    void onBarViewAttached() {
1806        WindowManager.LayoutParams lp;
1807        int pixelFormat;
1808        Drawable bg;
1809
1810        /// ---------- Tracking View --------------
1811        bg = mTrackingView.getBackground();
1812        if (bg != null) {
1813            pixelFormat = bg.getOpacity();
1814        }
1815
1816        lp = new WindowManager.LayoutParams(
1817                ViewGroup.LayoutParams.MATCH_PARENT,
1818                ViewGroup.LayoutParams.MATCH_PARENT,
1819                WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL,
1820                0
1821                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
1822                | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
1823                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
1824                PixelFormat.TRANSLUCENT);
1825        if (ActivityManager.isHighEndGfx(mDisplay)) {
1826            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
1827        }
1828//        lp.token = mStatusBarView.getWindowToken();
1829        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
1830        lp.setTitle("TrackingView");
1831        lp.y = mTrackingPosition;
1832        mTrackingParams = lp;
1833
1834        WindowManagerImpl.getDefault().addView(mTrackingView, lp);
1835    }
1836
1837    void onTrackingViewAttached() {
1838        WindowManager.LayoutParams lp;
1839        int pixelFormat;
1840
1841        /// ---------- Expanded View --------------
1842        pixelFormat = PixelFormat.TRANSLUCENT;
1843
1844        lp = mExpandedDialog.getWindow().getAttributes();
1845        lp.x = 0;
1846        mTrackingPosition = lp.y = mDisplayMetrics.heightPixels; // sufficiently large negative
1847        lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
1848        lp.flags = 0
1849                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
1850                | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
1851                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
1852                | WindowManager.LayoutParams.FLAG_DITHER
1853                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1854        if (ActivityManager.isHighEndGfx(mDisplay)) {
1855            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
1856        }
1857        lp.format = pixelFormat;
1858        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
1859        lp.setTitle("StatusBarExpanded");
1860        mExpandedParams = lp;
1861        updateExpandedSize();
1862        mExpandedDialog.getWindow().setFormat(pixelFormat);
1863
1864        mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
1865        mExpandedDialog.setContentView(mExpandedView,
1866                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
1867                                           ViewGroup.LayoutParams.MATCH_PARENT));
1868        mExpandedDialog.getWindow().setBackgroundDrawable(null);
1869        mExpandedDialog.show();
1870    }
1871
1872    void setNotificationIconVisibility(boolean visible, int anim) {
1873        int old = mNotificationIcons.getVisibility();
1874        int v = visible ? View.VISIBLE : View.INVISIBLE;
1875        if (old != v) {
1876            mNotificationIcons.setVisibility(v);
1877            mNotificationIcons.startAnimation(loadAnim(anim, null));
1878        }
1879    }
1880
1881    void updateExpandedInvisiblePosition() {
1882        if (mTrackingView != null) {
1883            mTrackingPosition = -mDisplayMetrics.heightPixels;
1884            if (mTrackingParams != null) {
1885                mTrackingParams.y = mTrackingPosition;
1886                WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
1887            }
1888        }
1889        if (mExpandedParams != null) {
1890            mExpandedParams.y = -mDisplayMetrics.heightPixels;
1891            mExpandedDialog.getWindow().setAttributes(mExpandedParams);
1892        }
1893    }
1894
1895    void updateExpandedViewPos(int expandedPosition) {
1896        if (SPEW) {
1897            Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
1898                    + " mTrackingParams.y=" + ((mTrackingParams == null) ? "?" : mTrackingParams.y)
1899                    + " mTrackingPosition=" + mTrackingPosition);
1900        }
1901
1902        int h = mStatusBarView.getHeight();
1903        int disph = mDisplayMetrics.heightPixels;
1904
1905        // If the expanded view is not visible, make sure they're still off screen.
1906        // Maybe the view was resized.
1907        if (!mExpandedVisible) {
1908            updateExpandedInvisiblePosition();
1909            return;
1910        }
1911
1912        // tracking view...
1913        int pos;
1914        if (expandedPosition == EXPANDED_FULL_OPEN) {
1915            pos = h;
1916        }
1917        else if (expandedPosition == EXPANDED_LEAVE_ALONE) {
1918            pos = mTrackingPosition;
1919        }
1920        else {
1921            if (expandedPosition <= disph) {
1922                pos = expandedPosition;
1923            } else {
1924                pos = disph;
1925            }
1926            pos -= disph-h;
1927        }
1928        mTrackingPosition = mTrackingParams.y = pos;
1929        mTrackingParams.height = disph-h;
1930        WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
1931
1932        if (mExpandedParams != null) {
1933            if (mCloseView.getWindowVisibility() == View.VISIBLE) {
1934                mCloseView.getLocationInWindow(mPositionTmp);
1935                final int closePos = mPositionTmp[1];
1936
1937                mExpandedContents.getLocationInWindow(mPositionTmp);
1938                final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight();
1939
1940                mExpandedParams.y = pos + mTrackingView.getHeight()
1941                        - (mTrackingParams.height-closePos) - contentsBottom;
1942
1943                if (SPEW) {
1944                    Slog.d(PhoneStatusBar.TAG,
1945                            "pos=" + pos +
1946                            " trackingHeight=" + mTrackingView.getHeight() +
1947                            " (trackingParams.height - closePos)=" +
1948                                (mTrackingParams.height - closePos) +
1949                            " contentsBottom=" + contentsBottom);
1950                }
1951
1952            } else {
1953                // If the tracking view is not yet visible, then we can't have
1954                // a good value of the close view location.  We need to wait for
1955                // it to be visible to do a layout.
1956                mExpandedParams.y = -mDisplayMetrics.heightPixels;
1957            }
1958            int max = h;
1959            if (mExpandedParams.y > max) {
1960                mExpandedParams.y = max;
1961            }
1962            int min = mTrackingPosition;
1963            if (mExpandedParams.y < min) {
1964                mExpandedParams.y = min;
1965            }
1966
1967            boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h;
1968            if (!visible) {
1969                // if the contents aren't visible, move the expanded view way off screen
1970                // because the window itself extends below the content view.
1971                mExpandedParams.y = -disph;
1972            }
1973            mExpandedDialog.getWindow().setAttributes(mExpandedParams);
1974
1975            // As long as this isn't just a repositioning that's not supposed to affect
1976            // the user's perception of what's showing, call to say that the visibility
1977            // has changed. (Otherwise, someone else will call to do that).
1978            if (expandedPosition != EXPANDED_LEAVE_ALONE) {
1979                if (SPEW) Slog.d(TAG, "updateExpandedViewPos visibilityChanged(" + visible + ")");
1980                visibilityChanged(visible);
1981            }
1982        }
1983
1984        if (SPEW) {
1985            Slog.d(TAG, "updateExpandedViewPos after  expandedPosition=" + expandedPosition
1986                    + " mTrackingParams.y=" + mTrackingParams.y
1987                    + " mTrackingPosition=" + mTrackingPosition
1988                    + " mExpandedParams.y=" + mExpandedParams.y
1989                    + " mExpandedParams.height=" + mExpandedParams.height);
1990        }
1991    }
1992
1993    int getExpandedHeight(int disph) {
1994        if (DEBUG) {
1995            Slog.d(TAG, "getExpandedHeight(" + disph + "): sbView="
1996                    + mStatusBarView.getHeight() + " closeView=" + mCloseView.getHeight());
1997        }
1998        return disph - mStatusBarView.getHeight() - mCloseView.getHeight();
1999    }
2000
2001    void updateDisplaySize() {
2002        mDisplay.getMetrics(mDisplayMetrics);
2003        if (DEBUG) {
2004            Slog.d(TAG, "updateDisplaySize: " + mDisplayMetrics);
2005        }
2006        updateExpandedSize();
2007    }
2008
2009    void updateExpandedSize() {
2010        if (DEBUG) {
2011            Slog.d(TAG, "updateExpandedSize()");
2012        }
2013        if (mExpandedDialog != null && mExpandedParams != null && mDisplayMetrics != null) {
2014            mExpandedParams.width = mDisplayMetrics.widthPixels;
2015            mExpandedParams.height = getExpandedHeight(mDisplayMetrics.heightPixels);
2016            if (!mExpandedVisible) {
2017                updateExpandedInvisiblePosition();
2018            } else {
2019                mExpandedDialog.getWindow().setAttributes(mExpandedParams);
2020            }
2021            if (DEBUG) {
2022                Slog.d(TAG, "updateExpandedSize: height=" + mExpandedParams.height + " " +
2023                    (mExpandedVisible ? "VISIBLE":"INVISIBLE"));
2024            }
2025        }
2026    }
2027
2028    public void toggleRecentApps() {
2029        int msg = (mRecentsPanel.getVisibility() == View.GONE)
2030                ? MSG_OPEN_RECENTS_PANEL : MSG_CLOSE_RECENTS_PANEL;
2031        mHandler.removeMessages(msg);
2032        mHandler.sendEmptyMessage(msg);
2033    }
2034
2035    /**
2036     * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
2037     * This was added last-minute and is inconsistent with the way the rest of the notifications
2038     * are handled, because the notification isn't really cancelled.  The lights are just
2039     * turned off.  If any other notifications happen, the lights will turn back on.  Steve says
2040     * this is what he wants. (see bug 1131461)
2041     */
2042    void visibilityChanged(boolean visible) {
2043        if (mPanelSlightlyVisible != visible) {
2044            mPanelSlightlyVisible = visible;
2045            try {
2046                mBarService.onPanelRevealed();
2047            } catch (RemoteException ex) {
2048                // Won't fail unless the world has ended.
2049            }
2050        }
2051    }
2052
2053    void performDisableActions(int net) {
2054        int old = mDisabled;
2055        int diff = net ^ old;
2056        mDisabled = net;
2057
2058        // act accordingly
2059        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
2060            if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
2061                Slog.d(TAG, "DISABLE_EXPAND: yes");
2062                animateCollapse();
2063            }
2064        }
2065        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
2066            if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
2067                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
2068                if (mTicking) {
2069                    mNotificationIcons.setVisibility(View.INVISIBLE);
2070                    mTicker.halt();
2071                } else {
2072                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
2073                }
2074            } else {
2075                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
2076                if (!mExpandedVisible) {
2077                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
2078                }
2079            }
2080        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
2081            if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
2082                mTicker.halt();
2083            }
2084        }
2085    }
2086
2087    private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
2088        final int mini(int a, int b) {
2089            return (b>a?a:b);
2090        }
2091        public void onClick(View v) {
2092            synchronized (mNotificationData) {
2093                // let's also queue up 400ms worth of animated dismissals
2094                final int N = mini(5, mPile.getChildCount());
2095
2096                final ArrayList<View> snapshot = new ArrayList<View>(N);
2097                for (int i=0; i<N; i++) {
2098                    final View child = mPile.getChildAt(i);
2099                    if (mPile.canChildBeDismissed(child)) snapshot.add(child);
2100                }
2101                new Thread(new Runnable() {
2102                    @Override
2103                    public void run() {
2104                        final int ROW_DELAY = 100;
2105
2106                        mHandler.postDelayed(new Runnable() {
2107                            public void run() {
2108                                animateCollapse(false, 0f);
2109                            }
2110                        }, (N-1) * ROW_DELAY);
2111
2112                        mHandler.postDelayed(new Runnable() {
2113                            public void run() {
2114                                try {
2115                                    mBarService.onClearAllNotifications();
2116                                } catch (RemoteException ex) { }
2117                            }
2118                        }, N * ROW_DELAY + 500);
2119
2120                        mPile.setAnimateBounds(false); // temporarily disable some re-layouts
2121
2122                        for (View v : snapshot) {
2123                            final View _v = v;
2124                            mHandler.post(new Runnable() {
2125                                @Override
2126                                public void run() {
2127                                    mPile.dismissRowAnimated(_v, (int)(ROW_DELAY*0.25f));
2128                                }
2129                            });
2130                            try {
2131                                Thread.sleep(ROW_DELAY);
2132                            } catch (InterruptedException ex) { }
2133                        }
2134
2135                        mPile.setAnimateBounds(true); // reenable layout animation
2136                    }
2137                }).start();
2138            }
2139        }
2140    };
2141
2142    private View.OnClickListener mSettingsButtonListener = new View.OnClickListener() {
2143        public void onClick(View v) {
2144            v.getContext().startActivity(new Intent(Settings.ACTION_SETTINGS)
2145                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
2146            animateCollapse();
2147        }
2148    };
2149
2150    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
2151        public void onReceive(Context context, Intent intent) {
2152            String action = intent.getAction();
2153            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
2154                    || Intent.ACTION_SCREEN_OFF.equals(action)) {
2155                boolean excludeRecents = false;
2156                if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
2157                    String reason = intent.getStringExtra("reason");
2158                    if (reason != null) {
2159                        excludeRecents = reason.equals("recentapps");
2160                    }
2161                }
2162                animateCollapse(excludeRecents);
2163            }
2164            else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
2165                repositionNavigationBar();
2166                updateResources();
2167            }
2168        }
2169    };
2170
2171    private void setIntruderAlertVisibility(boolean vis) {
2172        mIntruderAlertView.setVisibility(vis ? View.VISIBLE : View.GONE);
2173    }
2174
2175    /**
2176     * Reload some of our resources when the configuration changes.
2177     *
2178     * We don't reload everything when the configuration changes -- we probably
2179     * should, but getting that smooth is tough.  Someday we'll fix that.  In the
2180     * meantime, just update the things that we know change.
2181     */
2182    void updateResources() {
2183        final Context context = mContext;
2184        final Resources res = context.getResources();
2185
2186        if (mClearButton instanceof TextView) {
2187            ((TextView)mClearButton).setText(context.getText(R.string.status_bar_clear_all_button));
2188        }
2189        mNoNotificationsTitle.setText(context.getText(R.string.status_bar_no_notifications_title));
2190
2191        loadDimens();
2192    }
2193
2194    protected void loadDimens() {
2195        final Resources res = mContext.getResources();
2196
2197        mNaturalBarHeight = res.getDimensionPixelSize(
2198                com.android.internal.R.dimen.status_bar_height);
2199
2200        int newIconSize = res.getDimensionPixelSize(
2201            com.android.internal.R.dimen.status_bar_icon_size);
2202        int newIconHPadding = res.getDimensionPixelSize(
2203            R.dimen.status_bar_icon_padding);
2204
2205        if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
2206//            Slog.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
2207            mIconHPadding = newIconHPadding;
2208            mIconSize = newIconSize;
2209            //reloadAllNotificationIcons(); // reload the tray
2210        }
2211
2212        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
2213
2214        mSelfExpandVelocityPx = res.getDimension(R.dimen.self_expand_velocity);
2215        mSelfCollapseVelocityPx = res.getDimension(R.dimen.self_collapse_velocity);
2216        mFlingExpandMinVelocityPx = res.getDimension(R.dimen.fling_expand_min_velocity);
2217        mFlingCollapseMinVelocityPx = res.getDimension(R.dimen.fling_collapse_min_velocity);
2218
2219        mCollapseMinDisplayFraction = res.getFraction(R.dimen.collapse_min_display_fraction, 1, 1);
2220        mExpandMinDisplayFraction = res.getFraction(R.dimen.expand_min_display_fraction, 1, 1);
2221
2222        mExpandAccelPx = res.getDimension(R.dimen.expand_accel);
2223        mCollapseAccelPx = res.getDimension(R.dimen.collapse_accel);
2224
2225        mFlingGestureMaxXVelocityPx = res.getDimension(R.dimen.fling_gesture_max_x_velocity);
2226
2227        if (false) Slog.v(TAG, "updateResources");
2228    }
2229
2230    //
2231    // tracing
2232    //
2233
2234    void postStartTracing() {
2235        mHandler.postDelayed(mStartTracing, 3000);
2236    }
2237
2238    void vibrate() {
2239        android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
2240                Context.VIBRATOR_SERVICE);
2241        vib.vibrate(250);
2242    }
2243
2244    Runnable mStartTracing = new Runnable() {
2245        public void run() {
2246            vibrate();
2247            SystemClock.sleep(250);
2248            Slog.d(TAG, "startTracing");
2249            android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
2250            mHandler.postDelayed(mStopTracing, 10000);
2251        }
2252    };
2253
2254    Runnable mStopTracing = new Runnable() {
2255        public void run() {
2256            android.os.Debug.stopMethodTracing();
2257            Slog.d(TAG, "stopTracing");
2258            vibrate();
2259        }
2260    };
2261
2262    public class TouchOutsideListener implements View.OnTouchListener {
2263        private int mMsg;
2264        private RecentsPanelView mPanel;
2265
2266        public TouchOutsideListener(int msg, RecentsPanelView panel) {
2267            mMsg = msg;
2268            mPanel = panel;
2269        }
2270
2271        public boolean onTouch(View v, MotionEvent ev) {
2272            final int action = ev.getAction();
2273            if (action == MotionEvent.ACTION_OUTSIDE
2274                || (action == MotionEvent.ACTION_DOWN
2275                    && !mPanel.isInContentArea((int)ev.getX(), (int)ev.getY()))) {
2276                mHandler.removeMessages(mMsg);
2277                mHandler.sendEmptyMessage(mMsg);
2278                return true;
2279            }
2280            return false;
2281        }
2282    }
2283}
2284
2285