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 17 package com.example.android.apis.app; 18 19 // Need the following import to get access to the app resources, since this 20 // class is in a sub-package. 21 import com.example.android.apis.R; 22 23 import android.app.Activity; 24 import android.app.AlertDialog; 25 import android.app.Presentation; 26 import android.content.Context; 27 import android.content.DialogInterface; 28 import android.content.res.Resources; 29 import android.graphics.Point; 30 import android.graphics.drawable.GradientDrawable; 31 import android.hardware.display.DisplayManager; 32 import android.os.Bundle; 33 import android.os.Parcel; 34 import android.os.Parcelable; 35 import android.os.Parcelable.Creator; 36 import android.util.Log; 37 import android.util.SparseArray; 38 import android.view.Display; 39 import android.view.View; 40 import android.view.View.OnClickListener; 41 import android.view.ViewGroup; 42 import android.widget.CheckBox; 43 import android.widget.CompoundButton; 44 import android.widget.CompoundButton.OnCheckedChangeListener; 45 import android.widget.ArrayAdapter; 46 import android.widget.Button; 47 import android.widget.ImageView; 48 import android.widget.ListView; 49 import android.widget.TextView; 50 51 //BEGIN_INCLUDE(activity) 52 /** 53 * <h3>Presentation Activity</h3> 54 * 55 * <p> 56 * This demonstrates how to create an activity that shows some content 57 * on a secondary display using a {@link Presentation}. 58 * </p><p> 59 * The activity uses the {@link DisplayManager} API to enumerate displays. 60 * When the user selects a display, the activity opens a {@link Presentation} 61 * on that display. We show a different photograph in each presentation 62 * on a unique background along with a label describing the display. 63 * We also write information about displays and display-related events to 64 * the Android log which you can read using <code>adb logcat</code>. 65 * </p><p> 66 * You can try this out using an HDMI or Wifi display or by using the 67 * "Simulate secondary displays" feature in Development Settings to create a few 68 * simulated secondary displays. Each display will appear in the list along with a 69 * checkbox to show a presentation on that display. 70 * </p><p> 71 * See also the {@link PresentationWithMediaRouterActivity} sample which 72 * uses the media router to automatically select a secondary display 73 * on which to show content based on the currently selected route. 74 * </p> 75 */ 76 public class PresentationActivity extends Activity 77 implements OnCheckedChangeListener, OnClickListener { 78 private final String TAG = "PresentationActivity"; 79 80 // Key for storing saved instance state. 81 private static final String PRESENTATION_KEY = "presentation"; 82 83 // The content that we want to show on the presentation. 84 private static final int[] PHOTOS = new int[] { 85 R.drawable.frantic, 86 R.drawable.photo1, R.drawable.photo2, R.drawable.photo3, 87 R.drawable.photo4, R.drawable.photo5, R.drawable.photo6, 88 R.drawable.sample_4, 89 }; 90 91 private DisplayManager mDisplayManager; 92 private DisplayListAdapter mDisplayListAdapter; 93 private CheckBox mShowAllDisplaysCheckbox; 94 private ListView mListView; 95 private int mNextImageNumber; 96 97 // List of presentation contents indexed by displayId. 98 // This state persists so that we can restore the old presentation 99 // contents when the activity is paused or resumed. 100 private SparseArray<PresentationContents> mSavedPresentationContents; 101 102 // List of all currently visible presentations indexed by display id. 103 private final SparseArray<DemoPresentation> mActivePresentations = 104 new SparseArray<DemoPresentation>(); 105 106 /** 107 * Initialization of the Activity after it is first created. Must at least 108 * call {@link android.app.Activity#setContentView setContentView()} to 109 * describe what is to be displayed in the screen. 110 */ 111 @Override 112 protected void onCreate(Bundle savedInstanceState) { 113 // Be sure to call the super class. 114 super.onCreate(savedInstanceState); 115 116 // Restore saved instance state. 117 if (savedInstanceState != null) { 118 mSavedPresentationContents = 119 savedInstanceState.getSparseParcelableArray(PRESENTATION_KEY); 120 } else { 121 mSavedPresentationContents = new SparseArray<PresentationContents>(); 122 } 123 124 // Get the display manager service. 125 mDisplayManager = (DisplayManager)getSystemService(Context.DISPLAY_SERVICE); 126 127 // See assets/res/any/layout/presentation_activity.xml for this 128 // view layout definition, which is being set here as 129 // the content of our screen. 130 setContentView(R.layout.presentation_activity); 131 132 // Set up checkbox to toggle between showing all displays or only presentation displays. 133 mShowAllDisplaysCheckbox = (CheckBox)findViewById(R.id.show_all_displays); 134 mShowAllDisplaysCheckbox.setOnCheckedChangeListener(this); 135 136 // Set up the list of displays. 137 mDisplayListAdapter = new DisplayListAdapter(this); 138 mListView = (ListView)findViewById(R.id.display_list); 139 mListView.setAdapter(mDisplayListAdapter); 140 } 141 142 @Override 143 protected void onResume() { 144 // Be sure to call the super class. 145 super.onResume(); 146 147 // Update our list of displays on resume. 148 mDisplayListAdapter.updateContents(); 149 150 // Restore presentations from before the activity was paused. 151 final int numDisplays = mDisplayListAdapter.getCount(); 152 for (int i = 0; i < numDisplays; i++) { 153 final Display display = mDisplayListAdapter.getItem(i); 154 final PresentationContents contents = 155 mSavedPresentationContents.get(display.getDisplayId()); 156 if (contents != null) { 157 showPresentation(display, contents); 158 } 159 } 160 mSavedPresentationContents.clear(); 161 162 // Register to receive events from the display manager. 163 mDisplayManager.registerDisplayListener(mDisplayListener, null); 164 } 165 166 @Override 167 protected void onPause() { 168 // Be sure to call the super class. 169 super.onPause(); 170 171 // Unregister from the display manager. 172 mDisplayManager.unregisterDisplayListener(mDisplayListener); 173 174 // Dismiss all of our presentations but remember their contents. 175 Log.d(TAG, "Activity is being paused. Dismissing all active presentation."); 176 for (int i = 0; i < mActivePresentations.size(); i++) { 177 DemoPresentation presentation = mActivePresentations.valueAt(i); 178 int displayId = mActivePresentations.keyAt(i); 179 mSavedPresentationContents.put(displayId, presentation.mContents); 180 presentation.dismiss(); 181 } 182 mActivePresentations.clear(); 183 } 184 185 @Override 186 protected void onSaveInstanceState(Bundle outState) { 187 // Be sure to call the super class. 188 super.onSaveInstanceState(outState); 189 outState.putSparseParcelableArray(PRESENTATION_KEY, mSavedPresentationContents); 190 } 191 192 /** 193 * Shows a {@link Presentation} on the specified display. 194 */ 195 private void showPresentation(Display display, PresentationContents contents) { 196 final int displayId = display.getDisplayId(); 197 if (mActivePresentations.get(displayId) != null) { 198 return; 199 } 200 201 Log.d(TAG, "Showing presentation photo #" + contents.photo 202 + " on display #" + displayId + "."); 203 204 DemoPresentation presentation = new DemoPresentation(this, display, contents); 205 presentation.show(); 206 presentation.setOnDismissListener(mOnDismissListener); 207 mActivePresentations.put(displayId, presentation); 208 } 209 210 /** 211 * Hides a {@link Presentation} on the specified display. 212 */ 213 private void hidePresentation(Display display) { 214 final int displayId = display.getDisplayId(); 215 DemoPresentation presentation = mActivePresentations.get(displayId); 216 if (presentation == null) { 217 return; 218 } 219 220 Log.d(TAG, "Dismissing presentation on display #" + displayId + "."); 221 222 presentation.dismiss(); 223 mActivePresentations.delete(displayId); 224 } 225 226 private int getNextPhoto() { 227 final int photo = mNextImageNumber; 228 mNextImageNumber = (mNextImageNumber + 1) % PHOTOS.length; 229 return photo; 230 } 231 232 /** 233 * Called when the show all displays checkbox is toggled or when 234 * an item in the list of displays is checked or unchecked. 235 */ 236 @Override 237 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 238 if (buttonView == mShowAllDisplaysCheckbox) { 239 // Show all displays checkbox was toggled. 240 mDisplayListAdapter.updateContents(); 241 } else { 242 // Display item checkbox was toggled. 243 final Display display = (Display)buttonView.getTag(); 244 if (isChecked) { 245 PresentationContents contents = new PresentationContents(getNextPhoto()); 246 showPresentation(display, contents); 247 } else { 248 hidePresentation(display); 249 } 250 } 251 } 252 253 /** 254 * Called when the Info button next to a display is clicked to show information 255 * about the display. 256 */ 257 @Override 258 public void onClick(View v) { 259 Context context = v.getContext(); 260 AlertDialog.Builder builder = new AlertDialog.Builder(context); 261 final Display display = (Display)v.getTag(); 262 Resources r = context.getResources(); 263 AlertDialog alert = builder 264 .setTitle(r.getString( 265 R.string.presentation_alert_info_text, display.getDisplayId())) 266 .setMessage(display.toString()) 267 .setNeutralButton(R.string.presentation_alert_dismiss_text, 268 new DialogInterface.OnClickListener() { 269 @Override 270 public void onClick(DialogInterface dialog, int which) { 271 dialog.dismiss(); 272 } 273 }) 274 .create(); 275 alert.show(); 276 } 277 278 /** 279 * Listens for displays to be added, changed or removed. 280 * We use it to update the list and show a new {@link Presentation} when a 281 * display is connected. 282 * 283 * Note that we don't bother dismissing the {@link Presentation} when a 284 * display is removed, although we could. The presentation API takes care 285 * of doing that automatically for us. 286 */ 287 private final DisplayManager.DisplayListener mDisplayListener = 288 new DisplayManager.DisplayListener() { 289 @Override 290 public void onDisplayAdded(int displayId) { 291 Log.d(TAG, "Display #" + displayId + " added."); 292 mDisplayListAdapter.updateContents(); 293 } 294 295 @Override 296 public void onDisplayChanged(int displayId) { 297 Log.d(TAG, "Display #" + displayId + " changed."); 298 mDisplayListAdapter.updateContents(); 299 } 300 301 @Override 302 public void onDisplayRemoved(int displayId) { 303 Log.d(TAG, "Display #" + displayId + " removed."); 304 mDisplayListAdapter.updateContents(); 305 } 306 }; 307 308 /** 309 * Listens for when presentations are dismissed. 310 */ 311 private final DialogInterface.OnDismissListener mOnDismissListener = 312 new DialogInterface.OnDismissListener() { 313 @Override 314 public void onDismiss(DialogInterface dialog) { 315 DemoPresentation presentation = (DemoPresentation)dialog; 316 int displayId = presentation.getDisplay().getDisplayId(); 317 Log.d(TAG, "Presentation on display #" + displayId + " was dismissed."); 318 mActivePresentations.delete(displayId); 319 mDisplayListAdapter.notifyDataSetChanged(); 320 } 321 }; 322 323 /** 324 * List adapter. 325 * Shows information about all displays. 326 */ 327 private final class DisplayListAdapter extends ArrayAdapter<Display> { 328 final Context mContext; 329 330 public DisplayListAdapter(Context context) { 331 super(context, R.layout.presentation_list_item); 332 mContext = context; 333 } 334 335 @Override 336 public View getView(int position, View convertView, ViewGroup parent) { 337 final View v; 338 if (convertView == null) { 339 v = ((Activity) mContext).getLayoutInflater().inflate( 340 R.layout.presentation_list_item, null); 341 } else { 342 v = convertView; 343 } 344 345 final Display display = getItem(position); 346 final int displayId = display.getDisplayId(); 347 348 CheckBox cb = (CheckBox)v.findViewById(R.id.checkbox_presentation); 349 cb.setTag(display); 350 cb.setOnCheckedChangeListener(PresentationActivity.this); 351 cb.setChecked(mActivePresentations.indexOfKey(displayId) >= 0 352 || mSavedPresentationContents.indexOfKey(displayId) >= 0); 353 354 TextView tv = (TextView)v.findViewById(R.id.display_id); 355 tv.setText(v.getContext().getResources().getString( 356 R.string.presentation_display_id_text, displayId, display.getName())); 357 358 Button b = (Button)v.findViewById(R.id.info); 359 b.setTag(display); 360 b.setOnClickListener(PresentationActivity.this); 361 362 return v; 363 } 364 365 /** 366 * Update the contents of the display list adapter to show 367 * information about all current displays. 368 */ 369 public void updateContents() { 370 clear(); 371 372 String displayCategory = getDisplayCategory(); 373 Display[] displays = mDisplayManager.getDisplays(displayCategory); 374 addAll(displays); 375 376 Log.d(TAG, "There are currently " + displays.length + " displays connected."); 377 for (Display display : displays) { 378 Log.d(TAG, " " + display); 379 } 380 } 381 382 private String getDisplayCategory() { 383 return mShowAllDisplaysCheckbox.isChecked() ? null : 384 DisplayManager.DISPLAY_CATEGORY_PRESENTATION; 385 } 386 } 387 388 /** 389 * The presentation to show on the secondary display. 390 * 391 * Note that the presentation display may have different metrics from the display on which 392 * the main activity is showing so we must be careful to use the presentation's 393 * own {@link Context} whenever we load resources. 394 */ 395 private final class DemoPresentation extends Presentation { 396 397 final PresentationContents mContents; 398 399 public DemoPresentation(Context context, Display display, PresentationContents contents) { 400 super(context, display); 401 mContents = contents; 402 } 403 404 @Override 405 protected void onCreate(Bundle savedInstanceState) { 406 // Be sure to call the super class. 407 super.onCreate(savedInstanceState); 408 409 // Get the resources for the context of the presentation. 410 // Notice that we are getting the resources from the context of the presentation. 411 Resources r = getContext().getResources(); 412 413 // Inflate the layout. 414 setContentView(R.layout.presentation_content); 415 416 final Display display = getDisplay(); 417 final int displayId = display.getDisplayId(); 418 final int photo = mContents.photo; 419 420 // Show a caption to describe what's going on. 421 TextView text = (TextView)findViewById(R.id.text); 422 text.setText(r.getString(R.string.presentation_photo_text, 423 photo, displayId, display.getName())); 424 425 // Show a n image for visual interest. 426 ImageView image = (ImageView)findViewById(R.id.image); 427 image.setImageDrawable(r.getDrawable(PHOTOS[photo])); 428 429 GradientDrawable drawable = new GradientDrawable(); 430 drawable.setShape(GradientDrawable.RECTANGLE); 431 drawable.setGradientType(GradientDrawable.RADIAL_GRADIENT); 432 433 // Set the background to a random gradient. 434 Point p = new Point(); 435 getDisplay().getSize(p); 436 drawable.setGradientRadius(Math.max(p.x, p.y) / 2); 437 drawable.setColors(mContents.colors); 438 findViewById(android.R.id.content).setBackground(drawable); 439 } 440 } 441 442 /** 443 * Information about the content we want to show in a presentation. 444 */ 445 private final static class PresentationContents implements Parcelable { 446 final int photo; 447 final int[] colors; 448 449 public static final Creator<PresentationContents> CREATOR = 450 new Creator<PresentationContents>() { 451 @Override 452 public PresentationContents createFromParcel(Parcel in) { 453 return new PresentationContents(in); 454 } 455 456 @Override 457 public PresentationContents[] newArray(int size) { 458 return new PresentationContents[size]; 459 } 460 }; 461 462 public PresentationContents(int photo) { 463 this.photo = photo; 464 colors = new int[] { 465 ((int) (Math.random() * Integer.MAX_VALUE)) | 0xFF000000, 466 ((int) (Math.random() * Integer.MAX_VALUE)) | 0xFF000000 }; 467 } 468 469 private PresentationContents(Parcel in) { 470 photo = in.readInt(); 471 colors = new int[] { in.readInt(), in.readInt() }; 472 } 473 474 @Override 475 public int describeContents() { 476 return 0; 477 } 478 479 @Override 480 public void writeToParcel(Parcel dest, int flags) { 481 dest.writeInt(photo); 482 dest.writeInt(colors[0]); 483 dest.writeInt(colors[1]); 484 } 485 } 486 } 487 //END_INCLUDE(activity) 488