• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
onCreate(Bundle savedInstanceState)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
onResume()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
onPause()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
onSaveInstanceState(Bundle outState)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      */
showPresentation(Display display, PresentationContents contents)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      */
hidePresentation(Display display)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 
getNextPhoto()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
onCheckedChanged(CompoundButton buttonView, boolean isChecked)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
onClick(View v)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 
DisplayListAdapter(Context context)330         public DisplayListAdapter(Context context) {
331             super(context, R.layout.presentation_list_item);
332             mContext = context;
333         }
334 
335         @Override
getView(int position, View convertView, ViewGroup parent)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          */
updateContents()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 
getDisplayCategory()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 
DemoPresentation(Context context, Display display, PresentationContents contents)399         public DemoPresentation(Context context, Display display, PresentationContents contents) {
400             super(context, display);
401             mContents = contents;
402         }
403 
404         @Override
onCreate(Bundle savedInstanceState)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 
PresentationContents(int photo)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 
PresentationContents(Parcel in)469         private PresentationContents(Parcel in) {
470             photo = in.readInt();
471             colors = new int[] { in.readInt(), in.readInt() };
472         }
473 
474         @Override
describeContents()475         public int describeContents() {
476             return 0;
477         }
478 
479         @Override
writeToParcel(Parcel dest, int flags)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