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