1 /* 2 * Copyright (C) 2019 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.google.android.car.multidisplaytest.present; 18 19 import android.app.Activity; 20 import android.app.AlertDialog; 21 import android.app.Presentation; 22 import android.content.Context; 23 import android.content.DialogInterface; 24 import android.content.res.Resources; 25 import android.graphics.Color; 26 import android.hardware.display.DisplayManager; 27 import android.os.Bundle; 28 import android.os.Parcel; 29 import android.os.Parcelable; 30 import android.os.Parcelable.Creator; 31 import android.util.Log; 32 import android.util.SparseArray; 33 import android.view.Display; 34 import android.view.LayoutInflater; 35 import android.view.View; 36 import android.view.ViewGroup; 37 import android.view.WindowManager; 38 import android.widget.ArrayAdapter; 39 import android.widget.Button; 40 import android.widget.CheckBox; 41 import android.widget.CompoundButton; 42 import android.widget.ImageView; 43 import android.widget.ListView; 44 import android.widget.TextView; 45 46 import androidx.fragment.app.Fragment; 47 48 import com.google.android.car.multidisplaytest.R; 49 50 import java.util.Arrays; 51 52 /** 53 * Modified from 54 * //development/samples/ApiDemos/src/com/example/android/apis/app/PresentationActivity.java 55 * Show content on selected displays with Presentation and DisplayManager APIs 56 */ 57 public class PresentTestFragment extends Fragment { 58 private static final String TAG = PresentTestFragment.class.getSimpleName(); 59 private static final int[] PHOTOS = new int[] { 60 R.drawable.photo_landscape, R.drawable.photo_portrait 61 }; 62 private Display mCurrentDisplay; 63 private DisplayManager mDisplayManager; 64 private DisplayListAdapter mDisplayListAdapter; 65 private CheckBox mShowAllDisplaysCheckbox; 66 private ListView mListView; 67 private int mNextImageNumber; 68 69 // This state persist to restore the old presentation 70 private SparseArray<DemoPresentationContents> mSavedPresentationContents; 71 // List of all currently visible presentations indexed by display id. 72 private final SparseArray<DemoPresentation> mActivePresentations = 73 new SparseArray<DemoPresentation>(); 74 75 // Listens for displays to be added, changed or removed. 76 private DisplayManager.DisplayListener mDisplayListener; 77 // Listens for when presentations are dismissed. 78 private DialogInterface.OnDismissListener mOnDismissListener; 79 // Listens for Info button clicked. 80 private View.OnClickListener mOnClickListener; 81 // Listens for checked Display changed. 82 private CompoundButton.OnCheckedChangeListener mOnCheckedChangedListener; 83 84 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)85 public View onCreateView(LayoutInflater inflater, ViewGroup container, 86 Bundle savedInstanceState) { 87 View view = inflater.inflate(R.layout.presentation_fragment, container, false); 88 mSavedPresentationContents = new SparseArray<DemoPresentationContents>(); 89 // Get the display manager service. 90 mDisplayManager = (DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE); 91 mCurrentDisplay = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)) 92 .getDefaultDisplay(); 93 94 setupListeners(view); 95 96 mShowAllDisplaysCheckbox = view.findViewById(R.id.show_all_displays); 97 mShowAllDisplaysCheckbox.setOnCheckedChangeListener(mOnCheckedChangedListener); 98 99 mDisplayListAdapter = new DisplayListAdapter(getContext()); 100 101 mListView = view.findViewById(R.id.display_list); 102 mListView.setAdapter(mDisplayListAdapter); 103 104 return view; 105 } 106 setupListeners(View view)107 private void setupListeners(View view) { 108 mDisplayListener = new DisplayManager.DisplayListener() { 109 @Override 110 public void onDisplayAdded(int displayId) { 111 Log.d(TAG, "Display #" + displayId + " added."); 112 mDisplayListAdapter.updateShownDisplays(); 113 } 114 115 @Override 116 public void onDisplayChanged(int displayId) { 117 Log.d(TAG, "Display #" + displayId + " changed."); 118 mDisplayListAdapter.updateShownDisplays(); 119 } 120 121 @Override 122 public void onDisplayRemoved(int displayId) { 123 Log.d(TAG, "Display #" + displayId + " removed."); 124 mDisplayListAdapter.updateShownDisplays(); 125 } 126 }; 127 128 mOnDismissListener = new DialogInterface.OnDismissListener() { 129 @Override 130 public void onDismiss(DialogInterface dialog) { 131 DemoPresentation presentation = (DemoPresentation) dialog; 132 int displayId = presentation.getDisplay().getDisplayId(); 133 Log.d(TAG, "Presentation on display #" + displayId + " was dismissed."); 134 mActivePresentations.delete(displayId); 135 mDisplayListAdapter.notifyDataSetChanged(); 136 } 137 }; 138 139 mOnClickListener = new View.OnClickListener() { 140 @Override 141 public void onClick(View v) { 142 Context context = v.getContext(); 143 AlertDialog.Builder builder = new AlertDialog.Builder(context); 144 final Display display = (Display) v.getTag(); 145 Resources r = context.getResources(); 146 AlertDialog alert = builder 147 .setTitle(r.getString( 148 R.string.presentation_alert_info_text, display.getDisplayId())) 149 .setMessage(display.toString()) 150 .setNeutralButton(R.string.presentation_alert_dismiss_text, 151 (dialog, which) -> dialog.dismiss()) 152 .create(); 153 alert.show(); 154 } 155 }; 156 157 mOnCheckedChangedListener = new CompoundButton.OnCheckedChangeListener(){ 158 @Override 159 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 160 if (buttonView == mShowAllDisplaysCheckbox) { 161 // Show all displays checkbox was toggled. 162 mDisplayListAdapter.updateShownDisplays(); 163 } else { 164 // Display item checkbox was toggled. 165 final Display display = (Display) buttonView.getTag(); 166 if (isChecked) { 167 DemoPresentationContents contents = 168 new DemoPresentationContents(getNextPhoto()); 169 showPresentation(display, contents); 170 } else { 171 hidePresentation(display); 172 } 173 mDisplayListAdapter.updateShownDisplays(); 174 } 175 } 176 }; 177 } 178 179 @Override onResume()180 public void onResume() { 181 super.onResume(); 182 mDisplayListAdapter.updateShownDisplays(); 183 mSavedPresentationContents.clear(); 184 185 // Register to receive events from the display manager. 186 mDisplayManager.registerDisplayListener(mDisplayListener, null); 187 } 188 189 @Override onPause()190 public void onPause() { 191 super.onPause(); 192 mDisplayManager.unregisterDisplayListener(mDisplayListener); 193 Log.d(TAG, "Activity is being paused. Dismissing all active presentation."); 194 195 for (int i = 0; i < mActivePresentations.size(); i++) { 196 DemoPresentation presentation = mActivePresentations.valueAt(i); 197 presentation.dismiss(); 198 } 199 mActivePresentations.clear(); 200 } 201 202 /** 203 * Shows a Presentation on the specified display. 204 */ showPresentation(Display display, DemoPresentationContents contents)205 private void showPresentation(Display display, DemoPresentationContents contents) { 206 final int displayId = display.getDisplayId(); 207 if (mActivePresentations.get(displayId) != null) { 208 Log.w(TAG, "Active presentation exists on Display #" + displayId + "."); 209 return; 210 } 211 212 Log.d(TAG, "Showing presentation photo #" + contents.mPhoto 213 + " on display #" + displayId + "."); 214 215 DemoPresentation presentation = new DemoPresentation(getContext(), display, contents); 216 presentation.show(); 217 presentation.setOnDismissListener(mOnDismissListener); 218 mActivePresentations.put(displayId, presentation); 219 } 220 221 /** 222 * Hides a Presentation on the specified display. 223 */ hidePresentation(Display display)224 private void hidePresentation(Display display) { 225 final int displayId = display.getDisplayId(); 226 DemoPresentation presentation = mActivePresentations.get(displayId); 227 if (presentation == null) { 228 return; 229 } 230 231 Log.d(TAG, "Dismissing presentation on display #" + displayId + "."); 232 233 presentation.dismiss(); 234 mActivePresentations.delete(displayId); 235 } 236 getNextPhoto()237 private int getNextPhoto() { 238 final int photo = mNextImageNumber; 239 mNextImageNumber = (mNextImageNumber + 1) % PHOTOS.length; 240 return photo; 241 } 242 243 private final class DisplayListAdapter extends ArrayAdapter<Display> { 244 private final Activity mActivity; 245 private final Context mContext; 246 private Display[] mShownDisplays; 247 DisplayListAdapter(Context context)248 DisplayListAdapter(Context context) { 249 super(context, R.layout.presentation_list_item); 250 mActivity = (Activity) context; 251 mContext = context; 252 mShownDisplays = new Display[] {mCurrentDisplay}; 253 } 254 255 @Override getView(int position, View convertView, ViewGroup parent)256 public View getView(int position, View convertView, ViewGroup parent) { 257 final View view; 258 if (convertView == null) { 259 view = ((Activity) mContext).getLayoutInflater().inflate( 260 R.layout.presentation_list_item, null); 261 } else { 262 view = convertView; 263 } 264 final Display display = getItem(position); 265 final int displayId = display.getDisplayId(); 266 267 DemoPresentation presentation = mActivePresentations.get(displayId); 268 DemoPresentationContents contents = presentation != null 269 ? presentation.mContents : null; 270 271 CheckBox cb = view.findViewById(R.id.checkbox_presentation); 272 cb.setTag(display); 273 cb.setOnCheckedChangeListener(mOnCheckedChangedListener); 274 cb.setChecked(contents != null); 275 276 TextView tv = (TextView) view.findViewById(R.id.display_id); 277 tv.setText(view.getContext().getResources().getString( 278 R.string.presentation_display_id_text, displayId, display.getName())); 279 280 if (displayId == mCurrentDisplay.getDisplayId()) { 281 tv.append(" [Current display]"); 282 cb.setEnabled(false); 283 } else { 284 cb.setEnabled(true); 285 } 286 287 Button b = (Button) view.findViewById(R.id.info); 288 b.setTag(display); 289 b.setOnClickListener(mOnClickListener); 290 291 return view; 292 } 293 updateShownDisplays()294 public void updateShownDisplays() { 295 clear(); 296 297 String displayCategory = getDisplayCategory(); 298 Display[] displays = mDisplayManager.getDisplays(displayCategory); 299 addAll(displays); 300 301 Log.d(TAG, Arrays.toString(displays)); 302 } 303 getDisplayCategory()304 private String getDisplayCategory() { 305 return mShowAllDisplaysCheckbox.isChecked() ? null : 306 DisplayManager.DISPLAY_CATEGORY_PRESENTATION; 307 } 308 } 309 310 /** 311 * The presentation to show on the secondary display. 312 */ 313 private static final class DemoPresentation extends Presentation { 314 private DemoPresentationContents mContents; 315 DemoPresentation(Context context, Display display, DemoPresentationContents contents)316 DemoPresentation(Context context, Display display, DemoPresentationContents contents) { 317 super(context, display); 318 mContents = contents; 319 } 320 321 @Override onCreate(Bundle savedInstanceState)322 public void onCreate(Bundle savedInstanceState) { 323 super.onCreate(savedInstanceState); 324 setContentView(R.layout.presentation_content); 325 326 Resources r = getContext().getResources(); 327 ImageView image = (ImageView) findViewById(R.id.image); 328 TextView text = (TextView) findViewById(R.id.text); 329 330 Display display = getDisplay(); 331 int displayId = display.getDisplayId(); 332 int photo = mContents.mPhoto; 333 334 image.setImageDrawable(r.getDrawable(PHOTOS[photo])); 335 findViewById(android.R.id.content).setBackgroundColor(mContents.mColor); 336 text.setText(r.getString(R.string.presentation_photo_text, 337 photo, displayId, display.getName())); 338 } 339 } 340 341 /** 342 * Content to show in the presentation. 343 */ 344 private static final class DemoPresentationContents implements Parcelable { 345 private static final int[] COLORS = { 346 Color.RED, 347 Color.GREEN, 348 Color.BLUE, 349 Color.YELLOW, 350 Color.MAGENTA, 351 Color.BLACK, 352 Color.DKGRAY 353 }; 354 private int mPhoto; 355 private int mColor; 356 357 public static final Creator<DemoPresentationContents> CREATOR = 358 new Creator<DemoPresentationContents>() { 359 @Override 360 public DemoPresentationContents createFromParcel(Parcel in) { 361 return new DemoPresentationContents(in); 362 } 363 364 @Override 365 public DemoPresentationContents[] newArray(int size) { 366 return new DemoPresentationContents[size]; 367 } 368 }; 369 DemoPresentationContents(int photo)370 DemoPresentationContents(int photo) { 371 this.mPhoto = photo; 372 this.mColor = COLORS[(int) (Math.random() * COLORS.length)]; 373 } 374 DemoPresentationContents(Parcel in)375 private DemoPresentationContents(Parcel in) { 376 mPhoto = in.readInt(); 377 mColor = in.readInt(); 378 } 379 380 @Override describeContents()381 public int describeContents() { 382 return 0; 383 } 384 385 @Override writeToParcel(Parcel dest, int flags)386 public void writeToParcel(Parcel dest, int flags) { 387 dest.writeInt(mPhoto); 388 dest.writeInt(mColor); 389 } 390 } 391 } 392