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.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