/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.android.apis.app; // Need the following import to get access to the app resources, since this // class is in a sub-package. import com.example.android.apis.R; import android.app.Activity; import android.app.AlertDialog; import android.app.Presentation; import android.content.Context; import android.content.DialogInterface; import android.content.res.Resources; import android.graphics.Point; import android.graphics.drawable.GradientDrawable; import android.hardware.display.DisplayManager; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.os.Parcelable.Creator; import android.util.Log; import android.util.SparseArray; import android.view.Display; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ImageView; import android.widget.ListView; import android.widget.Spinner; import android.widget.TextView; //BEGIN_INCLUDE(activity) /** *

Presentation Activity

* *

* This demonstrates how to create an activity that shows some content * on a secondary display using a {@link Presentation}. *

* The activity uses the {@link DisplayManager} API to enumerate displays. * When the user selects a display, the activity opens a {@link Presentation} * on that display. We show a different photograph in each presentation * on a unique background along with a label describing the display. * We also write information about displays and display-related events to * the Android log which you can read using adb logcat. *

* You can try this out using an HDMI or Wifi display or by using the * "Simulate secondary displays" feature in Development Settings to create a few * simulated secondary displays. Each display will appear in the list along with a * checkbox to show a presentation on that display. *

* See also the {@link PresentationWithMediaRouterActivity} sample which * uses the media router to automatically select a secondary display * on which to show content based on the currently selected route. *

*/ public class PresentationActivity extends Activity implements OnCheckedChangeListener, OnClickListener, OnItemSelectedListener { private final String TAG = "PresentationActivity"; // Key for storing saved instance state. private static final String PRESENTATION_KEY = "presentation"; // The content that we want to show on the presentation. private static final int[] PHOTOS = new int[] { R.drawable.frantic, R.drawable.photo1, R.drawable.photo2, R.drawable.photo3, R.drawable.photo4, R.drawable.photo5, R.drawable.photo6, R.drawable.sample_4, }; private DisplayManager mDisplayManager; private DisplayListAdapter mDisplayListAdapter; private CheckBox mShowAllDisplaysCheckbox; private ListView mListView; private int mNextImageNumber; // List of presentation contents indexed by displayId. // This state persists so that we can restore the old presentation // contents when the activity is paused or resumed. private SparseArray mSavedPresentationContents; // List of all currently visible presentations indexed by display id. private final SparseArray mActivePresentations = new SparseArray(); /** * Initialization of the Activity after it is first created. Must at least * call {@link android.app.Activity#setContentView setContentView()} to * describe what is to be displayed in the screen. */ @Override protected void onCreate(Bundle savedInstanceState) { // Be sure to call the super class. super.onCreate(savedInstanceState); // Restore saved instance state. if (savedInstanceState != null) { mSavedPresentationContents = savedInstanceState.getSparseParcelableArray(PRESENTATION_KEY); } else { mSavedPresentationContents = new SparseArray(); } // Get the display manager service. mDisplayManager = (DisplayManager)getSystemService(Context.DISPLAY_SERVICE); // See assets/res/any/layout/presentation_activity.xml for this // view layout definition, which is being set here as // the content of our screen. setContentView(R.layout.presentation_activity); // Set up checkbox to toggle between showing all displays or only presentation displays. mShowAllDisplaysCheckbox = (CheckBox)findViewById(R.id.show_all_displays); mShowAllDisplaysCheckbox.setOnCheckedChangeListener(this); // Set up the list of displays. mDisplayListAdapter = new DisplayListAdapter(this); mListView = (ListView)findViewById(R.id.display_list); mListView.setAdapter(mDisplayListAdapter); } @Override protected void onResume() { // Be sure to call the super class. super.onResume(); // Update our list of displays on resume. mDisplayListAdapter.updateContents(); // Restore presentations from before the activity was paused. final int numDisplays = mDisplayListAdapter.getCount(); for (int i = 0; i < numDisplays; i++) { final Display display = mDisplayListAdapter.getItem(i); final DemoPresentationContents contents = mSavedPresentationContents.get(display.getDisplayId()); if (contents != null) { showPresentation(display, contents); } } mSavedPresentationContents.clear(); // Register to receive events from the display manager. mDisplayManager.registerDisplayListener(mDisplayListener, null); } @Override protected void onPause() { // Be sure to call the super class. super.onPause(); // Unregister from the display manager. mDisplayManager.unregisterDisplayListener(mDisplayListener); // Dismiss all of our presentations but remember their contents. Log.d(TAG, "Activity is being paused. Dismissing all active presentation."); for (int i = 0; i < mActivePresentations.size(); i++) { DemoPresentation presentation = mActivePresentations.valueAt(i); int displayId = mActivePresentations.keyAt(i); mSavedPresentationContents.put(displayId, presentation.mContents); presentation.dismiss(); } mActivePresentations.clear(); } @Override protected void onSaveInstanceState(Bundle outState) { // Be sure to call the super class. super.onSaveInstanceState(outState); outState.putSparseParcelableArray(PRESENTATION_KEY, mSavedPresentationContents); } /** * Shows a {@link Presentation} on the specified display. */ private void showPresentation(Display display, DemoPresentationContents contents) { final int displayId = display.getDisplayId(); if (mActivePresentations.get(displayId) != null) { return; } Log.d(TAG, "Showing presentation photo #" + contents.photo + " on display #" + displayId + "."); DemoPresentation presentation = new DemoPresentation(this, display, contents); presentation.show(); presentation.setOnDismissListener(mOnDismissListener); mActivePresentations.put(displayId, presentation); } /** * Hides a {@link Presentation} on the specified display. */ private void hidePresentation(Display display) { final int displayId = display.getDisplayId(); DemoPresentation presentation = mActivePresentations.get(displayId); if (presentation == null) { return; } Log.d(TAG, "Dismissing presentation on display #" + displayId + "."); presentation.dismiss(); mActivePresentations.delete(displayId); } /** * Sets the display mode of the {@link Presentation} on the specified display * if it is already shown. */ private void setPresentationDisplayMode(Display display, int displayModeId) { final int displayId = display.getDisplayId(); DemoPresentation presentation = mActivePresentations.get(displayId); if (presentation == null) { return; } presentation.setPreferredDisplayMode(displayModeId); } private int getNextPhoto() { final int photo = mNextImageNumber; mNextImageNumber = (mNextImageNumber + 1) % PHOTOS.length; return photo; } /** * Called when the show all displays checkbox is toggled or when * an item in the list of displays is checked or unchecked. */ @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (buttonView == mShowAllDisplaysCheckbox) { // Show all displays checkbox was toggled. mDisplayListAdapter.updateContents(); } else { // Display item checkbox was toggled. final Display display = (Display)buttonView.getTag(); if (isChecked) { DemoPresentationContents contents = new DemoPresentationContents(getNextPhoto()); showPresentation(display, contents); } else { hidePresentation(display); } mDisplayListAdapter.updateContents(); } } /** * Called when the Info button next to a display is clicked to show information * about the display. */ @Override public void onClick(View v) { Context context = v.getContext(); AlertDialog.Builder builder = new AlertDialog.Builder(context); final Display display = (Display)v.getTag(); Resources r = context.getResources(); AlertDialog alert = builder .setTitle(r.getString( R.string.presentation_alert_info_text, display.getDisplayId())) .setMessage(display.toString()) .setNeutralButton(R.string.presentation_alert_dismiss_text, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }) .create(); alert.show(); } /** * Called when a display mode has been selected. */ @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { final Display display = (Display)parent.getTag(); final Display.Mode[] modes = display.getSupportedModes(); setPresentationDisplayMode(display, position >= 1 && position <= modes.length ? modes[position - 1].getModeId() : 0); } /** * Called when a display mode has been unselected. */ @Override public void onNothingSelected(AdapterView parent) { final Display display = (Display)parent.getTag(); setPresentationDisplayMode(display, 0); } /** * Listens for displays to be added, changed or removed. * We use it to update the list and show a new {@link Presentation} when a * display is connected. * * Note that we don't bother dismissing the {@link Presentation} when a * display is removed, although we could. The presentation API takes care * of doing that automatically for us. */ private final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() { @Override public void onDisplayAdded(int displayId) { Log.d(TAG, "Display #" + displayId + " added."); mDisplayListAdapter.updateContents(); } @Override public void onDisplayChanged(int displayId) { Log.d(TAG, "Display #" + displayId + " changed."); mDisplayListAdapter.updateContents(); } @Override public void onDisplayRemoved(int displayId) { Log.d(TAG, "Display #" + displayId + " removed."); mDisplayListAdapter.updateContents(); } }; /** * Listens for when presentations are dismissed. */ private final DialogInterface.OnDismissListener mOnDismissListener = new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { DemoPresentation presentation = (DemoPresentation)dialog; int displayId = presentation.getDisplay().getDisplayId(); Log.d(TAG, "Presentation on display #" + displayId + " was dismissed."); mActivePresentations.delete(displayId); mDisplayListAdapter.notifyDataSetChanged(); } }; /** * List adapter. * Shows information about all displays. */ private final class DisplayListAdapter extends ArrayAdapter { final Context mContext; public DisplayListAdapter(Context context) { super(context, R.layout.presentation_list_item); mContext = context; } @Override public View getView(int position, View convertView, ViewGroup parent) { final View v; if (convertView == null) { v = ((Activity) mContext).getLayoutInflater().inflate( R.layout.presentation_list_item, null); } else { v = convertView; } final Display display = getItem(position); final int displayId = display.getDisplayId(); DemoPresentation presentation = mActivePresentations.get(displayId); DemoPresentationContents contents = presentation != null ? presentation.mContents : null; if (contents == null) { contents = mSavedPresentationContents.get(displayId); } CheckBox cb = (CheckBox)v.findViewById(R.id.checkbox_presentation); cb.setTag(display); cb.setOnCheckedChangeListener(PresentationActivity.this); cb.setChecked(contents != null); TextView tv = (TextView)v.findViewById(R.id.display_id); tv.setText(v.getContext().getResources().getString( R.string.presentation_display_id_text, displayId, display.getName())); Button b = (Button)v.findViewById(R.id.info); b.setTag(display); b.setOnClickListener(PresentationActivity.this); Spinner s = (Spinner)v.findViewById(R.id.modes); Display.Mode[] modes = display.getSupportedModes(); if (contents == null || modes.length == 1) { s.setVisibility(View.GONE); s.setAdapter(null); } else { ArrayAdapter modeAdapter = new ArrayAdapter(mContext, android.R.layout.simple_list_item_1); s.setVisibility(View.VISIBLE); s.setAdapter(modeAdapter); s.setTag(display); s.setOnItemSelectedListener(PresentationActivity.this); modeAdapter.add(""); for (Display.Mode mode : modes) { modeAdapter.add(String.format("Mode %d: %dx%d/%.1ffps", mode.getModeId(), mode.getPhysicalWidth(), mode.getPhysicalHeight(), mode.getRefreshRate())); if (contents.displayModeId == mode.getModeId()) { s.setSelection(modeAdapter.getCount() - 1); } } } return v; } /** * Update the contents of the display list adapter to show * information about all current displays. */ public void updateContents() { clear(); String displayCategory = getDisplayCategory(); Display[] displays = mDisplayManager.getDisplays(displayCategory); addAll(displays); Log.d(TAG, "There are currently " + displays.length + " displays connected."); for (Display display : displays) { Log.d(TAG, " " + display); } } private String getDisplayCategory() { return mShowAllDisplaysCheckbox.isChecked() ? null : DisplayManager.DISPLAY_CATEGORY_PRESENTATION; } } /** * The presentation to show on the secondary display. * * Note that the presentation display may have different metrics from the display on which * the main activity is showing so we must be careful to use the presentation's * own {@link Context} whenever we load resources. */ private final class DemoPresentation extends Presentation { final DemoPresentationContents mContents; public DemoPresentation(Context context, Display display, DemoPresentationContents contents) { super(context, display); mContents = contents; } /** * Sets the preferred display mode id for the presentation. */ public void setPreferredDisplayMode(int modeId) { mContents.displayModeId = modeId; WindowManager.LayoutParams params = getWindow().getAttributes(); params.preferredDisplayModeId = modeId; getWindow().setAttributes(params); } @Override protected void onCreate(Bundle savedInstanceState) { // Be sure to call the super class. super.onCreate(savedInstanceState); // Get the resources for the context of the presentation. // Notice that we are getting the resources from the context of the presentation. Resources r = getContext().getResources(); // Inflate the layout. setContentView(R.layout.presentation_content); final Display display = getDisplay(); final int displayId = display.getDisplayId(); final int photo = mContents.photo; // Show a caption to describe what's going on. TextView text = (TextView)findViewById(R.id.text); text.setText(r.getString(R.string.presentation_photo_text, photo, displayId, display.getName())); // Show a n image for visual interest. ImageView image = (ImageView)findViewById(R.id.image); image.setImageDrawable(r.getDrawable(PHOTOS[photo])); GradientDrawable drawable = new GradientDrawable(); drawable.setShape(GradientDrawable.RECTANGLE); drawable.setGradientType(GradientDrawable.RADIAL_GRADIENT); // Set the background to a random gradient. Point p = new Point(); getDisplay().getSize(p); drawable.setGradientRadius(Math.max(p.x, p.y) / 2); drawable.setColors(mContents.colors); findViewById(android.R.id.content).setBackground(drawable); } } /** * Information about the content we want to show in the presentation. */ private final static class DemoPresentationContents implements Parcelable { final int photo; final int[] colors; int displayModeId; public static final Creator CREATOR = new Creator() { @Override public DemoPresentationContents createFromParcel(Parcel in) { return new DemoPresentationContents(in); } @Override public DemoPresentationContents[] newArray(int size) { return new DemoPresentationContents[size]; } }; public DemoPresentationContents(int photo) { this.photo = photo; colors = new int[] { ((int) (Math.random() * Integer.MAX_VALUE)) | 0xFF000000, ((int) (Math.random() * Integer.MAX_VALUE)) | 0xFF000000 }; } private DemoPresentationContents(Parcel in) { photo = in.readInt(); colors = new int[] { in.readInt(), in.readInt() }; displayModeId = in.readInt(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(photo); dest.writeInt(colors[0]); dest.writeInt(colors[1]); dest.writeInt(displayModeId); } } } //END_INCLUDE(activity)