1 /** 2 * Copyright (C) 2012 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 package android.service.dreams; 17 18 import java.io.FileDescriptor; 19 import java.io.PrintWriter; 20 21 import android.annotation.SdkConstant; 22 import android.annotation.SdkConstant.SdkConstantType; 23 import android.app.Service; 24 import android.content.Intent; 25 import android.graphics.PixelFormat; 26 import android.graphics.drawable.ColorDrawable; 27 import android.os.Handler; 28 import android.os.IBinder; 29 import android.os.ServiceManager; 30 import android.util.Slog; 31 import android.view.ActionMode; 32 import android.view.KeyEvent; 33 import android.view.Menu; 34 import android.view.MenuItem; 35 import android.view.MotionEvent; 36 import android.view.View; 37 import android.view.ViewGroup; 38 import android.view.Window; 39 import android.view.WindowManager; 40 import android.view.WindowManagerGlobal; 41 import android.view.WindowManager.LayoutParams; 42 import android.view.accessibility.AccessibilityEvent; 43 44 import com.android.internal.policy.PolicyManager; 45 46 /** 47 * Extend this class to implement a custom dream (available to the user as a "Daydream"). 48 * 49 * <p>Dreams are interactive screensavers launched when a charging device is idle, or docked in a 50 * desk dock. Dreams provide another modality for apps to express themselves, tailored for 51 * an exhibition/lean-back experience.</p> 52 * 53 * <p>The {@code DreamService} lifecycle is as follows:</p> 54 * <ol> 55 * <li>{@link #onAttachedToWindow} 56 * <p>Use this for initial setup, such as calling {@link #setContentView setContentView()}.</li> 57 * <li>{@link #onDreamingStarted} 58 * <p>Your dream has started, so you should begin animations or other behaviors here.</li> 59 * <li>{@link #onDreamingStopped} 60 * <p>Use this to stop the things you started in {@link #onDreamingStarted}.</li> 61 * <li>{@link #onDetachedFromWindow} 62 * <p>Use this to dismantle resources (for example, detach from handlers 63 * and listeners).</li> 64 * </ol> 65 * 66 * <p>In addition, onCreate and onDestroy (from the Service interface) will also be called, but 67 * initialization and teardown should be done by overriding the hooks above.</p> 68 * 69 * <p>To be available to the system, your {@code DreamService} should be declared in the 70 * manifest as follows:</p> 71 * <pre> 72 * <service 73 * android:name=".MyDream" 74 * android:exported="true" 75 * android:icon="@drawable/my_icon" 76 * android:label="@string/my_dream_label" > 77 * 78 * <intent-filter> 79 * <action android:name="android.service.dreams.DreamService" /> 80 * <category android:name="android.intent.category.DEFAULT" /> 81 * </intent-filter> 82 * 83 * <!-- Point to additional information for this dream (optional) --> 84 * <meta-data 85 * android:name="android.service.dream" 86 * android:resource="@xml/my_dream" /> 87 * </service> 88 * </pre> 89 * 90 * <p>If specified with the {@code <meta-data>} element, 91 * additional information for the dream is defined using the 92 * {@link android.R.styleable#Dream <dream>} element in a separate XML file. 93 * Currently, the only addtional 94 * information you can provide is for a settings activity that allows the user to configure 95 * the dream behavior. For example:</p> 96 * <p class="code-caption">res/xml/my_dream.xml</p> 97 * <pre> 98 * <dream xmlns:android="http://schemas.android.com/apk/res/android" 99 * android:settingsActivity="com.example.app/.MyDreamSettingsActivity" /> 100 * </pre> 101 * <p>This makes a Settings button available alongside your dream's listing in the 102 * system settings, which when pressed opens the specified activity.</p> 103 * 104 * 105 * <p>To specify your dream layout, call {@link #setContentView}, typically during the 106 * {@link #onAttachedToWindow} callback. For example:</p> 107 * <pre> 108 * public class MyDream extends DreamService { 109 * 110 * @Override 111 * public void onAttachedToWindow() { 112 * super.onAttachedToWindow(); 113 * 114 * // Exit dream upon user touch 115 * setInteractive(false); 116 * // Hide system UI 117 * setFullscreen(true); 118 * // Set the dream layout 119 * setContentView(R.layout.dream); 120 * } 121 * } 122 * </pre> 123 */ 124 public class DreamService extends Service implements Window.Callback { 125 private final String TAG = DreamService.class.getSimpleName() + "[" + getClass().getSimpleName() + "]"; 126 127 /** 128 * The name of the dream manager service. 129 * @hide 130 */ 131 public static final String DREAM_SERVICE = "dreams"; 132 133 /** 134 * The {@link Intent} that must be declared as handled by the service. 135 */ 136 @SdkConstant(SdkConstantType.SERVICE_ACTION) 137 public static final String SERVICE_INTERFACE = 138 "android.service.dreams.DreamService"; 139 140 /** 141 * Name under which a Dream publishes information about itself. 142 * This meta-data must reference an XML resource containing 143 * a <code><{@link android.R.styleable#Dream dream}></code> 144 * tag. 145 */ 146 public static final String DREAM_META_DATA = "android.service.dream"; 147 148 private final Handler mHandler = new Handler(); 149 private IBinder mWindowToken; 150 private Window mWindow; 151 private WindowManager mWindowManager; 152 private IDreamManager mSandman; 153 private boolean mInteractive = false; 154 private boolean mLowProfile = true; 155 private boolean mFullscreen = false; 156 private boolean mScreenBright = true; 157 private boolean mFinished; 158 159 private boolean mDebug = false; 160 161 /** 162 * @hide 163 */ 164 public void setDebug(boolean dbg) { 165 mDebug = dbg; 166 } 167 168 // begin Window.Callback methods 169 /** {@inheritDoc} */ 170 @Override 171 public boolean dispatchKeyEvent(KeyEvent event) { 172 // TODO: create more flexible version of mInteractive that allows use of KEYCODE_BACK 173 if (!mInteractive) { 174 if (mDebug) Slog.v(TAG, "Finishing on keyEvent"); 175 safelyFinish(); 176 return true; 177 } else if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { 178 if (mDebug) Slog.v(TAG, "Finishing on back key"); 179 safelyFinish(); 180 return true; 181 } 182 return mWindow.superDispatchKeyEvent(event); 183 } 184 185 /** {@inheritDoc} */ 186 @Override 187 public boolean dispatchKeyShortcutEvent(KeyEvent event) { 188 if (!mInteractive) { 189 if (mDebug) Slog.v(TAG, "Finishing on keyShortcutEvent"); 190 safelyFinish(); 191 return true; 192 } 193 return mWindow.superDispatchKeyShortcutEvent(event); 194 } 195 196 /** {@inheritDoc} */ 197 @Override 198 public boolean dispatchTouchEvent(MotionEvent event) { 199 // TODO: create more flexible version of mInteractive that allows clicks 200 // but finish()es on any other kind of activity 201 if (!mInteractive) { 202 if (mDebug) Slog.v(TAG, "Finishing on touchEvent"); 203 safelyFinish(); 204 return true; 205 } 206 return mWindow.superDispatchTouchEvent(event); 207 } 208 209 /** {@inheritDoc} */ 210 @Override 211 public boolean dispatchTrackballEvent(MotionEvent event) { 212 if (!mInteractive) { 213 if (mDebug) Slog.v(TAG, "Finishing on trackballEvent"); 214 safelyFinish(); 215 return true; 216 } 217 return mWindow.superDispatchTrackballEvent(event); 218 } 219 220 /** {@inheritDoc} */ 221 @Override 222 public boolean dispatchGenericMotionEvent(MotionEvent event) { 223 if (!mInteractive) { 224 if (mDebug) Slog.v(TAG, "Finishing on genericMotionEvent"); 225 safelyFinish(); 226 return true; 227 } 228 return mWindow.superDispatchGenericMotionEvent(event); 229 } 230 231 /** {@inheritDoc} */ 232 @Override 233 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 234 return false; 235 } 236 237 /** {@inheritDoc} */ 238 @Override 239 public View onCreatePanelView(int featureId) { 240 return null; 241 } 242 243 /** {@inheritDoc} */ 244 @Override 245 public boolean onCreatePanelMenu(int featureId, Menu menu) { 246 return false; 247 } 248 249 /** {@inheritDoc} */ 250 @Override 251 public boolean onPreparePanel(int featureId, View view, Menu menu) { 252 return false; 253 } 254 255 /** {@inheritDoc} */ 256 @Override 257 public boolean onMenuOpened(int featureId, Menu menu) { 258 return false; 259 } 260 261 /** {@inheritDoc} */ 262 @Override 263 public boolean onMenuItemSelected(int featureId, MenuItem item) { 264 return false; 265 } 266 267 /** {@inheritDoc} */ 268 @Override 269 public void onWindowAttributesChanged(LayoutParams attrs) { 270 } 271 272 /** {@inheritDoc} */ 273 @Override 274 public void onContentChanged() { 275 } 276 277 /** {@inheritDoc} */ 278 @Override 279 public void onWindowFocusChanged(boolean hasFocus) { 280 } 281 282 /** {@inheritDoc} */ 283 @Override 284 public void onAttachedToWindow() { 285 } 286 287 /** {@inheritDoc} */ 288 @Override 289 public void onDetachedFromWindow() { 290 } 291 292 /** {@inheritDoc} */ 293 @Override 294 public void onPanelClosed(int featureId, Menu menu) { 295 } 296 297 /** {@inheritDoc} */ 298 @Override 299 public boolean onSearchRequested() { 300 return false; 301 } 302 303 /** {@inheritDoc} */ 304 @Override 305 public ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback callback) { 306 return null; 307 } 308 309 /** {@inheritDoc} */ 310 @Override 311 public void onActionModeStarted(ActionMode mode) { 312 } 313 314 /** {@inheritDoc} */ 315 @Override 316 public void onActionModeFinished(ActionMode mode) { 317 } 318 // end Window.Callback methods 319 320 // begin public api 321 /** 322 * Retrieves the current {@link android.view.WindowManager} for the dream. 323 * Behaves similarly to {@link android.app.Activity#getWindowManager()}. 324 * 325 * @return The current window manager, or null if the dream is not started. 326 */ 327 public WindowManager getWindowManager() { 328 return mWindowManager; 329 } 330 331 /** 332 * Retrieves the current {@link android.view.Window} for the dream. 333 * Behaves similarly to {@link android.app.Activity#getWindow()}. 334 * 335 * @return The current window, or null if the dream is not started. 336 */ 337 public Window getWindow() { 338 return mWindow; 339 } 340 341 /** 342 * Inflates a layout resource and set it to be the content view for this Dream. 343 * Behaves similarly to {@link android.app.Activity#setContentView(int)}. 344 * 345 * <p>Note: Requires a window, do not call before {@link #onAttachedToWindow()}</p> 346 * 347 * @param layoutResID Resource ID to be inflated. 348 * 349 * @see #setContentView(android.view.View) 350 * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams) 351 */ 352 public void setContentView(int layoutResID) { 353 getWindow().setContentView(layoutResID); 354 } 355 356 /** 357 * Sets a view to be the content view for this Dream. 358 * Behaves similarly to {@link android.app.Activity#setContentView(android.view.View)} in an activity, 359 * including using {@link ViewGroup.LayoutParams#MATCH_PARENT} as the layout height and width of the view. 360 * 361 * <p>Note: This requires a window, so you should usually call it during 362 * {@link #onAttachedToWindow()} and never earlier (you <strong>cannot</strong> call it 363 * during {@link #onCreate}).</p> 364 * 365 * @see #setContentView(int) 366 * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams) 367 */ 368 public void setContentView(View view) { 369 getWindow().setContentView(view); 370 } 371 372 /** 373 * Sets a view to be the content view for this Dream. 374 * Behaves similarly to 375 * {@link android.app.Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams)} 376 * in an activity. 377 * 378 * <p>Note: This requires a window, so you should usually call it during 379 * {@link #onAttachedToWindow()} and never earlier (you <strong>cannot</strong> call it 380 * during {@link #onCreate}).</p> 381 * 382 * @param view The desired content to display. 383 * @param params Layout parameters for the view. 384 * 385 * @see #setContentView(android.view.View) 386 * @see #setContentView(int) 387 */ 388 public void setContentView(View view, ViewGroup.LayoutParams params) { 389 getWindow().setContentView(view, params); 390 } 391 392 /** 393 * Adds a view to the Dream's window, leaving other content views in place. 394 * 395 * <p>Note: Requires a window, do not call before {@link #onAttachedToWindow()}</p> 396 * 397 * @param view The desired content to display. 398 * @param params Layout parameters for the view. 399 */ 400 public void addContentView(View view, ViewGroup.LayoutParams params) { 401 getWindow().addContentView(view, params); 402 } 403 404 /** 405 * Finds a view that was identified by the id attribute from the XML that 406 * was processed in {@link #onCreate}. 407 * 408 * <p>Note: Requires a window, do not call before {@link #onAttachedToWindow()}</p> 409 * 410 * @return The view if found or null otherwise. 411 */ 412 public View findViewById(int id) { 413 return getWindow().findViewById(id); 414 } 415 416 /** 417 * Marks this dream as interactive to receive input events. 418 * 419 * <p>Non-interactive dreams (default) will dismiss on the first input event.</p> 420 * 421 * <p>Interactive dreams should call {@link #finish()} to dismiss themselves.</p> 422 * 423 * @param interactive True if this dream will handle input events. 424 */ 425 public void setInteractive(boolean interactive) { 426 mInteractive = interactive; 427 } 428 429 /** 430 * Returns whether or not this dream is interactive. Defaults to false. 431 * 432 * @see #setInteractive(boolean) 433 */ 434 public boolean isInteractive() { 435 return mInteractive; 436 } 437 438 /** 439 * Sets View.SYSTEM_UI_FLAG_LOW_PROFILE on the content view. 440 * 441 * @param lowProfile True to set View.SYSTEM_UI_FLAG_LOW_PROFILE 442 * @hide There is no reason to have this -- dreams can set this flag 443 * on their own content view, and from there can actually do the 444 * correct interactions with it (seeing when it is cleared etc). 445 */ 446 public void setLowProfile(boolean lowProfile) { 447 mLowProfile = lowProfile; 448 int flag = View.SYSTEM_UI_FLAG_LOW_PROFILE; 449 applySystemUiVisibilityFlags(mLowProfile ? flag : 0, flag); 450 } 451 452 /** 453 * Returns whether or not this dream is in low profile mode. Defaults to true. 454 * 455 * @see #setLowProfile(boolean) 456 * @hide 457 */ 458 public boolean isLowProfile() { 459 return getSystemUiVisibilityFlagValue(View.SYSTEM_UI_FLAG_LOW_PROFILE, mLowProfile); 460 } 461 462 /** 463 * Controls {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN} 464 * on the dream's window. 465 * 466 * @param fullscreen If true, the fullscreen flag will be set; else it 467 * will be cleared. 468 */ 469 public void setFullscreen(boolean fullscreen) { 470 mFullscreen = fullscreen; 471 int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN; 472 applyWindowFlags(mFullscreen ? flag : 0, flag); 473 } 474 475 /** 476 * Returns whether or not this dream is in fullscreen mode. Defaults to false. 477 * 478 * @see #setFullscreen(boolean) 479 */ 480 public boolean isFullscreen() { 481 return mFullscreen; 482 } 483 484 /** 485 * Marks this dream as keeping the screen bright while dreaming. 486 * 487 * @param screenBright True to keep the screen bright while dreaming. 488 */ 489 public void setScreenBright(boolean screenBright) { 490 mScreenBright = screenBright; 491 int flag = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; 492 applyWindowFlags(mScreenBright ? flag : 0, flag); 493 } 494 495 /** 496 * Returns whether or not this dream keeps the screen bright while dreaming. Defaults to false, 497 * allowing the screen to dim if necessary. 498 * 499 * @see #setScreenBright(boolean) 500 */ 501 public boolean isScreenBright() { 502 return getWindowFlagValue(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, mScreenBright); 503 } 504 505 /** 506 * Called when this Dream is constructed. 507 */ 508 @Override 509 public void onCreate() { 510 if (mDebug) Slog.v(TAG, "onCreate() on thread " + Thread.currentThread().getId()); 511 super.onCreate(); 512 } 513 514 /** 515 * Called when the dream's window has been created and is visible and animation may now begin. 516 */ 517 public void onDreamingStarted() { 518 if (mDebug) Slog.v(TAG, "onDreamingStarted()"); 519 // hook for subclasses 520 } 521 522 /** 523 * Called when this Dream is stopped, either by external request or by calling finish(), 524 * before the window has been removed. 525 */ 526 public void onDreamingStopped() { 527 if (mDebug) Slog.v(TAG, "onDreamingStopped()"); 528 // hook for subclasses 529 } 530 531 /** {@inheritDoc} */ 532 @Override 533 public final IBinder onBind(Intent intent) { 534 if (mDebug) Slog.v(TAG, "onBind() intent = " + intent); 535 return new DreamServiceWrapper(); 536 } 537 538 /** 539 * Stops the dream, detaches from the window, and wakes up. 540 */ 541 public final void finish() { 542 if (mDebug) Slog.v(TAG, "finish()"); 543 finishInternal(); 544 } 545 546 /** {@inheritDoc} */ 547 @Override 548 public void onDestroy() { 549 if (mDebug) Slog.v(TAG, "onDestroy()"); 550 // hook for subclasses 551 552 // Just in case destroy came in before detach, let's take care of that now 553 detach(); 554 555 super.onDestroy(); 556 } 557 558 // end public api 559 560 private void loadSandman() { 561 mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE)); 562 } 563 564 /** 565 * Called by DreamController.stopDream() when the Dream is about to be unbound and destroyed. 566 * 567 * Must run on mHandler. 568 */ 569 private final void detach() { 570 if (mWindow == null) { 571 // already detached! 572 return; 573 } 574 575 try { 576 onDreamingStopped(); 577 } catch (Throwable t) { 578 Slog.w(TAG, "Crashed in onDreamingStopped()", t); 579 // we were going to stop anyway 580 } 581 582 if (mDebug) Slog.v(TAG, "detach(): Removing window from window manager"); 583 try { 584 // force our window to be removed synchronously 585 mWindowManager.removeViewImmediate(mWindow.getDecorView()); 586 // the following will print a log message if it finds any other leaked windows 587 WindowManagerGlobal.getInstance().closeAll(mWindowToken, 588 this.getClass().getName(), "Dream"); 589 } catch (Throwable t) { 590 Slog.w(TAG, "Crashed removing window view", t); 591 } 592 593 mWindow = null; 594 mWindowToken = null; 595 } 596 597 /** 598 * Called when the Dream is ready to be shown. 599 * 600 * Must run on mHandler. 601 * 602 * @param windowToken A window token that will allow a window to be created in the correct layer. 603 */ 604 private final void attach(IBinder windowToken) { 605 if (mWindowToken != null) { 606 Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken); 607 return; 608 } 609 610 if (mDebug) Slog.v(TAG, "Attached on thread " + Thread.currentThread().getId()); 611 612 if (mSandman == null) { 613 loadSandman(); 614 } 615 mWindowToken = windowToken; 616 mWindow = PolicyManager.makeNewWindow(this); 617 mWindow.setCallback(this); 618 mWindow.requestFeature(Window.FEATURE_NO_TITLE); 619 mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000)); 620 mWindow.setFormat(PixelFormat.OPAQUE); 621 622 if (mDebug) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s", 623 windowToken, WindowManager.LayoutParams.TYPE_DREAM)); 624 625 WindowManager.LayoutParams lp = mWindow.getAttributes(); 626 lp.type = WindowManager.LayoutParams.TYPE_DREAM; 627 lp.token = windowToken; 628 lp.windowAnimations = com.android.internal.R.style.Animation_Dream; 629 lp.flags |= ( WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 630 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR 631 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED 632 | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD 633 | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON 634 | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0) 635 | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0) 636 ); 637 mWindow.setAttributes(lp); 638 639 if (mDebug) Slog.v(TAG, "Created and attached window: " + mWindow); 640 641 mWindow.setWindowManager(null, windowToken, "dream", true); 642 mWindowManager = mWindow.getWindowManager(); 643 644 if (mDebug) Slog.v(TAG, "Window added on thread " + Thread.currentThread().getId()); 645 try { 646 applySystemUiVisibilityFlags( 647 (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0), 648 View.SYSTEM_UI_FLAG_LOW_PROFILE); 649 getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); 650 } catch (Throwable t) { 651 Slog.w(TAG, "Crashed adding window view", t); 652 safelyFinish(); 653 return; 654 } 655 656 // start it up 657 mHandler.post(new Runnable() { 658 @Override 659 public void run() { 660 try { 661 onDreamingStarted(); 662 } catch (Throwable t) { 663 Slog.w(TAG, "Crashed in onDreamingStarted()", t); 664 safelyFinish(); 665 } 666 } 667 }); 668 } 669 670 private void safelyFinish() { 671 if (mDebug) Slog.v(TAG, "safelyFinish()"); 672 try { 673 finish(); 674 } catch (Throwable t) { 675 Slog.w(TAG, "Crashed in safelyFinish()", t); 676 finishInternal(); 677 return; 678 } 679 680 if (!mFinished) { 681 Slog.w(TAG, "Bad dream, did not call super.finish()"); 682 finishInternal(); 683 } 684 } 685 686 private void finishInternal() { 687 if (mDebug) Slog.v(TAG, "finishInternal() mFinished = " + mFinished); 688 if (mFinished) return; 689 try { 690 mFinished = true; 691 692 if (mSandman != null) { 693 mSandman.finishSelf(mWindowToken); 694 } else { 695 Slog.w(TAG, "No dream manager found"); 696 } 697 stopSelf(); // if launched via any other means 698 699 } catch (Throwable t) { 700 Slog.w(TAG, "Crashed in finishInternal()", t); 701 } 702 } 703 704 private boolean getWindowFlagValue(int flag, boolean defaultValue) { 705 return mWindow == null ? defaultValue : (mWindow.getAttributes().flags & flag) != 0; 706 } 707 708 private void applyWindowFlags(int flags, int mask) { 709 if (mWindow != null) { 710 WindowManager.LayoutParams lp = mWindow.getAttributes(); 711 lp.flags = applyFlags(lp.flags, flags, mask); 712 mWindow.setAttributes(lp); 713 mWindowManager.updateViewLayout(mWindow.getDecorView(), lp); 714 } 715 } 716 717 private boolean getSystemUiVisibilityFlagValue(int flag, boolean defaultValue) { 718 View v = mWindow == null ? null : mWindow.getDecorView(); 719 return v == null ? defaultValue : (v.getSystemUiVisibility() & flag) != 0; 720 } 721 722 private void applySystemUiVisibilityFlags(int flags, int mask) { 723 View v = mWindow == null ? null : mWindow.getDecorView(); 724 if (v != null) { 725 v.setSystemUiVisibility(applyFlags(v.getSystemUiVisibility(), flags, mask)); 726 } 727 } 728 729 private int applyFlags(int oldFlags, int flags, int mask) { 730 return (oldFlags&~mask) | (flags&mask); 731 } 732 733 @Override 734 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 735 super.dump(fd, pw, args); 736 737 pw.print(TAG + ": "); 738 if (mWindowToken == null) { 739 pw.println("stopped"); 740 } else { 741 pw.println("running (token=" + mWindowToken + ")"); 742 } 743 pw.println(" window: " + mWindow); 744 pw.print(" flags:"); 745 if (isInteractive()) pw.print(" interactive"); 746 if (isLowProfile()) pw.print(" lowprofile"); 747 if (isFullscreen()) pw.print(" fullscreen"); 748 if (isScreenBright()) pw.print(" bright"); 749 pw.println(); 750 } 751 752 private class DreamServiceWrapper extends IDreamService.Stub { 753 public void attach(final IBinder windowToken) { 754 mHandler.post(new Runnable() { 755 @Override 756 public void run() { 757 DreamService.this.attach(windowToken); 758 } 759 }); 760 } 761 public void detach() { 762 mHandler.post(new Runnable() { 763 @Override 764 public void run() { 765 DreamService.this.detach(); 766 } 767 }); 768 } 769 } 770 771 } 772