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