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