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