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