• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.statusbar.phone;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.LayoutTransition;
22 import android.app.StatusBarManager;
23 import android.content.Context;
24 import android.content.res.Resources;
25 import android.graphics.Point;
26 import android.graphics.Rect;
27 import android.graphics.drawable.Drawable;
28 import android.os.Handler;
29 import android.os.Message;
30 import android.os.ServiceManager;
31 import android.util.AttributeSet;
32 import android.util.Slog;
33 import android.view.animation.AccelerateInterpolator;
34 import android.view.Display;
35 import android.view.MotionEvent;
36 import android.view.View;
37 import android.view.Surface;
38 import android.view.ViewGroup;
39 import android.view.WindowManager;
40 import android.widget.ImageView;
41 import android.widget.LinearLayout;
42 
43 import java.io.FileDescriptor;
44 import java.io.PrintWriter;
45 
46 import com.android.internal.statusbar.IStatusBarService;
47 import com.android.systemui.R;
48 import com.android.systemui.statusbar.BaseStatusBar;
49 import com.android.systemui.statusbar.DelegateViewHelper;
50 import com.android.systemui.statusbar.policy.DeadZone;
51 
52 public class NavigationBarView extends LinearLayout {
53     final static boolean DEBUG = false;
54     final static String TAG = "PhoneStatusBar/NavigationBarView";
55 
56     final static boolean NAVBAR_ALWAYS_AT_RIGHT = true;
57 
58     // slippery nav bar when everything is disabled, e.g. during setup
59     final static boolean SLIPPERY_WHEN_DISABLED= true;
60 
61     final static boolean ANIMATE_HIDE_TRANSITION = false; // turned off because it introduces unsightly delay when videos goes to full screen
62 
63     protected IStatusBarService mBarService;
64     final Display mDisplay;
65     View mCurrentView = null;
66     View[] mRotatedViews = new View[4];
67 
68     int mBarSize;
69     boolean mVertical;
70     boolean mScreenOn;
71 
72     boolean mHidden, mLowProfile, mShowMenu;
73     int mDisabledFlags = 0;
74     int mNavigationIconHints = 0;
75 
76     private Drawable mBackIcon, mBackLandIcon, mBackAltIcon, mBackAltLandIcon;
77     private Drawable mRecentIcon;
78     private Drawable mRecentLandIcon;
79 
80     private DelegateViewHelper mDelegateHelper;
81     private DeadZone mDeadZone;
82 
83     // workaround for LayoutTransitions leaving the nav buttons in a weird state (bug 5549288)
84     final static boolean WORKAROUND_INVALID_LAYOUT = true;
85     final static int MSG_CHECK_INVALID_LAYOUT = 8686;
86 
87     private class H extends Handler {
handleMessage(Message m)88         public void handleMessage(Message m) {
89             switch (m.what) {
90                 case MSG_CHECK_INVALID_LAYOUT:
91                     final String how = "" + m.obj;
92                     final int w = getWidth();
93                     final int h = getHeight();
94                     final int vw = mCurrentView.getWidth();
95                     final int vh = mCurrentView.getHeight();
96 
97                     if (h != vh || w != vw) {
98                         Slog.w(TAG, String.format(
99                             "*** Invalid layout in navigation bar (%s this=%dx%d cur=%dx%d)",
100                             how, w, h, vw, vh));
101                         if (WORKAROUND_INVALID_LAYOUT) {
102                             requestLayout();
103                         }
104                     }
105                     break;
106             }
107         }
108     }
109 
setDelegateView(View view)110     public void setDelegateView(View view) {
111         mDelegateHelper.setDelegateView(view);
112     }
113 
setBar(BaseStatusBar phoneStatusBar)114     public void setBar(BaseStatusBar phoneStatusBar) {
115         mDelegateHelper.setBar(phoneStatusBar);
116     }
117 
118     @Override
onTouchEvent(MotionEvent event)119     public boolean onTouchEvent(MotionEvent event) {
120         if (mDeadZone != null && event.getAction() == MotionEvent.ACTION_OUTSIDE) {
121             mDeadZone.poke(event);
122         }
123         if (mDelegateHelper != null) {
124             boolean ret = mDelegateHelper.onInterceptTouchEvent(event);
125             if (ret) return true;
126         }
127         return super.onTouchEvent(event);
128     }
129 
130     @Override
onInterceptTouchEvent(MotionEvent event)131     public boolean onInterceptTouchEvent(MotionEvent event) {
132         return mDelegateHelper.onInterceptTouchEvent(event);
133     }
134 
135     private H mHandler = new H();
136 
getRecentsButton()137     public View getRecentsButton() {
138         return mCurrentView.findViewById(R.id.recent_apps);
139     }
140 
getMenuButton()141     public View getMenuButton() {
142         return mCurrentView.findViewById(R.id.menu);
143     }
144 
getBackButton()145     public View getBackButton() {
146         return mCurrentView.findViewById(R.id.back);
147     }
148 
getHomeButton()149     public View getHomeButton() {
150         return mCurrentView.findViewById(R.id.home);
151     }
152 
153     // for when home is disabled, but search isn't
getSearchLight()154     public View getSearchLight() {
155         return mCurrentView.findViewById(R.id.search_light);
156     }
157 
NavigationBarView(Context context, AttributeSet attrs)158     public NavigationBarView(Context context, AttributeSet attrs) {
159         super(context, attrs);
160 
161         mHidden = false;
162 
163         mDisplay = ((WindowManager)context.getSystemService(
164                 Context.WINDOW_SERVICE)).getDefaultDisplay();
165         mBarService = IStatusBarService.Stub.asInterface(
166                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
167 
168         final Resources res = mContext.getResources();
169         mBarSize = res.getDimensionPixelSize(R.dimen.navigation_bar_size);
170         mVertical = false;
171         mShowMenu = false;
172         mDelegateHelper = new DelegateViewHelper(this);
173 
174         getIcons(res);
175     }
176 
getIcons(Resources res)177     private void getIcons(Resources res) {
178         mBackIcon = res.getDrawable(R.drawable.ic_sysbar_back);
179         mBackLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_land);
180         mBackAltIcon = res.getDrawable(R.drawable.ic_sysbar_back_ime);
181         mBackAltLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_ime);
182         mRecentIcon = res.getDrawable(R.drawable.ic_sysbar_recent);
183         mRecentLandIcon = res.getDrawable(R.drawable.ic_sysbar_recent_land);
184     }
185 
186     @Override
setLayoutDirection(int layoutDirection)187     public void setLayoutDirection(int layoutDirection) {
188         getIcons(mContext.getResources());
189 
190         super.setLayoutDirection(layoutDirection);
191     }
192 
notifyScreenOn(boolean screenOn)193     public void notifyScreenOn(boolean screenOn) {
194         mScreenOn = screenOn;
195         setDisabledFlags(mDisabledFlags, true);
196     }
197 
198     View.OnTouchListener mLightsOutListener = new View.OnTouchListener() {
199         @Override
200         public boolean onTouch(View v, MotionEvent ev) {
201             if (ev.getAction() == MotionEvent.ACTION_DOWN) {
202                 // even though setting the systemUI visibility below will turn these views
203                 // on, we need them to come up faster so that they can catch this motion
204                 // event
205                 setLowProfile(false, false, false);
206 
207                 try {
208                     mBarService.setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
209                 } catch (android.os.RemoteException ex) {
210                 }
211             }
212             return false;
213         }
214     };
215 
setNavigationIconHints(int hints)216     public void setNavigationIconHints(int hints) {
217         setNavigationIconHints(hints, false);
218     }
219 
setNavigationIconHints(int hints, boolean force)220     public void setNavigationIconHints(int hints, boolean force) {
221         if (!force && hints == mNavigationIconHints) return;
222 
223         if (DEBUG) {
224             android.widget.Toast.makeText(mContext,
225                 "Navigation icon hints = " + hints,
226                 500).show();
227         }
228 
229         mNavigationIconHints = hints;
230 
231         getBackButton().setAlpha(
232             (0 != (hints & StatusBarManager.NAVIGATION_HINT_BACK_NOP)) ? 0.5f : 1.0f);
233         getHomeButton().setAlpha(
234             (0 != (hints & StatusBarManager.NAVIGATION_HINT_HOME_NOP)) ? 0.5f : 1.0f);
235         getRecentsButton().setAlpha(
236             (0 != (hints & StatusBarManager.NAVIGATION_HINT_RECENT_NOP)) ? 0.5f : 1.0f);
237 
238         ((ImageView)getBackButton()).setImageDrawable(
239             (0 != (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT))
240                 ? (mVertical ? mBackAltLandIcon : mBackAltIcon)
241                 : (mVertical ? mBackLandIcon : mBackIcon));
242 
243         ((ImageView)getRecentsButton()).setImageDrawable(mVertical ? mRecentLandIcon : mRecentIcon);
244 
245         setDisabledFlags(mDisabledFlags, true);
246     }
247 
setDisabledFlags(int disabledFlags)248     public void setDisabledFlags(int disabledFlags) {
249         setDisabledFlags(disabledFlags, false);
250     }
251 
setDisabledFlags(int disabledFlags, boolean force)252     public void setDisabledFlags(int disabledFlags, boolean force) {
253         if (!force && mDisabledFlags == disabledFlags) return;
254 
255         mDisabledFlags = disabledFlags;
256 
257         final boolean disableHome = ((disabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
258         final boolean disableRecent = ((disabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0);
259         final boolean disableBack = ((disabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0)
260                 && ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) == 0);
261         final boolean disableSearch = ((disabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0);
262 
263         if (SLIPPERY_WHEN_DISABLED) {
264             setSlippery(disableHome && disableRecent && disableBack && disableSearch);
265         }
266 
267         if (!mScreenOn && mCurrentView != null) {
268             ViewGroup navButtons = (ViewGroup) mCurrentView.findViewById(R.id.nav_buttons);
269             LayoutTransition lt = navButtons == null ? null : navButtons.getLayoutTransition();
270             if (lt != null) {
271                 lt.disableTransitionType(
272                         LayoutTransition.CHANGE_APPEARING | LayoutTransition.CHANGE_DISAPPEARING |
273                         LayoutTransition.APPEARING | LayoutTransition.DISAPPEARING);
274             }
275         }
276 
277         getBackButton()   .setVisibility(disableBack       ? View.INVISIBLE : View.VISIBLE);
278         getHomeButton()   .setVisibility(disableHome       ? View.INVISIBLE : View.VISIBLE);
279         getRecentsButton().setVisibility(disableRecent     ? View.INVISIBLE : View.VISIBLE);
280 
281         getSearchLight().setVisibility((disableHome && !disableSearch) ? View.VISIBLE : View.GONE);
282     }
283 
setSlippery(boolean newSlippery)284     public void setSlippery(boolean newSlippery) {
285         WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams();
286         if (lp != null) {
287             boolean oldSlippery = (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) != 0;
288             if (!oldSlippery && newSlippery) {
289                 lp.flags |= WindowManager.LayoutParams.FLAG_SLIPPERY;
290             } else if (oldSlippery && !newSlippery) {
291                 lp.flags &= ~WindowManager.LayoutParams.FLAG_SLIPPERY;
292             } else {
293                 return;
294             }
295             WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE);
296             wm.updateViewLayout(this, lp);
297         }
298     }
299 
setMenuVisibility(final boolean show)300     public void setMenuVisibility(final boolean show) {
301         setMenuVisibility(show, false);
302     }
303 
setMenuVisibility(final boolean show, final boolean force)304     public void setMenuVisibility(final boolean show, final boolean force) {
305         if (!force && mShowMenu == show) return;
306 
307         mShowMenu = show;
308 
309         getMenuButton().setVisibility(mShowMenu ? View.VISIBLE : View.INVISIBLE);
310     }
311 
setLowProfile(final boolean lightsOut)312     public void setLowProfile(final boolean lightsOut) {
313         setLowProfile(lightsOut, true, false);
314     }
315 
setLowProfile(final boolean lightsOut, final boolean animate, final boolean force)316     public void setLowProfile(final boolean lightsOut, final boolean animate, final boolean force) {
317         if (!force && lightsOut == mLowProfile) return;
318 
319         mLowProfile = lightsOut;
320 
321         if (DEBUG) Slog.d(TAG, "setting lights " + (lightsOut?"out":"on"));
322 
323         final View navButtons = mCurrentView.findViewById(R.id.nav_buttons);
324         final View lowLights = mCurrentView.findViewById(R.id.lights_out);
325 
326         // ok, everyone, stop it right there
327         navButtons.animate().cancel();
328         lowLights.animate().cancel();
329 
330         if (!animate) {
331             navButtons.setAlpha(lightsOut ? 0f : 1f);
332 
333             lowLights.setAlpha(lightsOut ? 1f : 0f);
334             lowLights.setVisibility(lightsOut ? View.VISIBLE : View.GONE);
335         } else {
336             navButtons.animate()
337                 .alpha(lightsOut ? 0f : 1f)
338                 .setDuration(lightsOut ? 750 : 250)
339                 .start();
340 
341             lowLights.setOnTouchListener(mLightsOutListener);
342             if (lowLights.getVisibility() == View.GONE) {
343                 lowLights.setAlpha(0f);
344                 lowLights.setVisibility(View.VISIBLE);
345             }
346             lowLights.animate()
347                 .alpha(lightsOut ? 1f : 0f)
348                 .setDuration(lightsOut ? 750 : 250)
349                 .setInterpolator(new AccelerateInterpolator(2.0f))
350                 .setListener(lightsOut ? null : new AnimatorListenerAdapter() {
351                     @Override
352                     public void onAnimationEnd(Animator _a) {
353                         lowLights.setVisibility(View.GONE);
354                     }
355                 })
356                 .start();
357         }
358     }
359 
setHidden(final boolean hide)360     public void setHidden(final boolean hide) {
361         if (hide == mHidden) return;
362 
363         mHidden = hide;
364         Slog.d(TAG,
365             (hide ? "HIDING" : "SHOWING") + " navigation bar");
366 
367         // bring up the lights no matter what
368         setLowProfile(false);
369     }
370 
371     @Override
onFinishInflate()372     public void onFinishInflate() {
373         mRotatedViews[Surface.ROTATION_0] =
374         mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
375 
376         mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90);
377 
378         mRotatedViews[Surface.ROTATION_270] = NAVBAR_ALWAYS_AT_RIGHT
379                                                 ? findViewById(R.id.rot90)
380                                                 : findViewById(R.id.rot270);
381 
382         mCurrentView = mRotatedViews[Surface.ROTATION_0];
383     }
384 
reorient()385     public void reorient() {
386         final int rot = mDisplay.getRotation();
387         for (int i=0; i<4; i++) {
388             mRotatedViews[i].setVisibility(View.GONE);
389         }
390         mCurrentView = mRotatedViews[rot];
391         mCurrentView.setVisibility(View.VISIBLE);
392 
393         mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone);
394 
395         // force the low profile & disabled states into compliance
396         setLowProfile(mLowProfile, false, true /* force */);
397         setDisabledFlags(mDisabledFlags, true /* force */);
398         setMenuVisibility(mShowMenu, true /* force */);
399 
400         if (DEBUG) {
401             Slog.d(TAG, "reorient(): rot=" + mDisplay.getRotation());
402         }
403 
404         setNavigationIconHints(mNavigationIconHints, true);
405     }
406 
407     @Override
onLayout(boolean changed, int l, int t, int r, int b)408     protected void onLayout(boolean changed, int l, int t, int r, int b) {
409         super.onLayout(changed, l, t, r, b);
410         mDelegateHelper.setInitialTouchRegion(getHomeButton(), getBackButton(), getRecentsButton());
411     }
412 
413     @Override
onSizeChanged(int w, int h, int oldw, int oldh)414     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
415         if (DEBUG) Slog.d(TAG, String.format(
416                     "onSizeChanged: (%dx%d) old: (%dx%d)", w, h, oldw, oldh));
417 
418         final boolean newVertical = w > 0 && h > w;
419         if (newVertical != mVertical) {
420             mVertical = newVertical;
421             //Slog.v(TAG, String.format("onSizeChanged: h=%d, w=%d, vert=%s", h, w, mVertical?"y":"n"));
422             reorient();
423         }
424 
425         postCheckForInvalidLayout("sizeChanged");
426         super.onSizeChanged(w, h, oldw, oldh);
427     }
428 
429     /*
430     @Override
431     protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
432         if (DEBUG) Slog.d(TAG, String.format(
433                     "onLayout: %s (%d,%d,%d,%d)",
434                     changed?"changed":"notchanged", left, top, right, bottom));
435         super.onLayout(changed, left, top, right, bottom);
436     }
437 
438     // uncomment this for extra defensiveness in WORKAROUND_INVALID_LAYOUT situations: if all else
439     // fails, any touch on the display will fix the layout.
440     @Override
441     public boolean onInterceptTouchEvent(MotionEvent ev) {
442         if (DEBUG) Slog.d(TAG, "onInterceptTouchEvent: " + ev.toString());
443         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
444             postCheckForInvalidLayout("touch");
445         }
446         return super.onInterceptTouchEvent(ev);
447     }
448     */
449 
450 
getResourceName(int resId)451     private String getResourceName(int resId) {
452         if (resId != 0) {
453             final android.content.res.Resources res = mContext.getResources();
454             try {
455                 return res.getResourceName(resId);
456             } catch (android.content.res.Resources.NotFoundException ex) {
457                 return "(unknown)";
458             }
459         } else {
460             return "(null)";
461         }
462     }
463 
postCheckForInvalidLayout(final String how)464     private void postCheckForInvalidLayout(final String how) {
465         mHandler.obtainMessage(MSG_CHECK_INVALID_LAYOUT, 0, 0, how).sendToTarget();
466     }
467 
visibilityToString(int vis)468     private static String visibilityToString(int vis) {
469         switch (vis) {
470             case View.INVISIBLE:
471                 return "INVISIBLE";
472             case View.GONE:
473                 return "GONE";
474             default:
475                 return "VISIBLE";
476         }
477     }
478 
dump(FileDescriptor fd, PrintWriter pw, String[] args)479     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
480         pw.println("NavigationBarView {");
481         final Rect r = new Rect();
482         final Point size = new Point();
483         mDisplay.getRealSize(size);
484 
485         pw.println(String.format("      this: " + PhoneStatusBar.viewInfo(this)
486                         + " " + visibilityToString(getVisibility())));
487 
488         getWindowVisibleDisplayFrame(r);
489         final boolean offscreen = r.right > size.x || r.bottom > size.y;
490         pw.println("      window: "
491                 + r.toShortString()
492                 + " " + visibilityToString(getWindowVisibility())
493                 + (offscreen ? " OFFSCREEN!" : ""));
494 
495         pw.println(String.format("      mCurrentView: id=%s (%dx%d) %s",
496                         getResourceName(mCurrentView.getId()),
497                         mCurrentView.getWidth(), mCurrentView.getHeight(),
498                         visibilityToString(mCurrentView.getVisibility())));
499 
500         pw.println(String.format("      disabled=0x%08x vertical=%s hidden=%s low=%s menu=%s",
501                         mDisabledFlags,
502                         mVertical ? "true" : "false",
503                         mHidden ? "true" : "false",
504                         mLowProfile ? "true" : "false",
505                         mShowMenu ? "true" : "false"));
506 
507         final View back = getBackButton();
508         final View home = getHomeButton();
509         final View recent = getRecentsButton();
510         final View menu = getMenuButton();
511 
512         pw.println("      back: "
513                 + PhoneStatusBar.viewInfo(back)
514                 + " " + visibilityToString(back.getVisibility())
515                 );
516         pw.println("      home: "
517                 + PhoneStatusBar.viewInfo(home)
518                 + " " + visibilityToString(home.getVisibility())
519                 );
520         pw.println("      rcnt: "
521                 + PhoneStatusBar.viewInfo(recent)
522                 + " " + visibilityToString(recent.getVisibility())
523                 );
524         pw.println("      menu: "
525                 + PhoneStatusBar.viewInfo(menu)
526                 + " " + visibilityToString(menu.getVisibility())
527                 );
528         pw.println("    }");
529     }
530 
531 }
532