Merge "Add Present fragment to Multi-display test app" into qt-dev am: a378a3c9d0 am: b3cd8c276d
am: 3fe421e6cb
Change-Id: I484427b87c9bbea4c242618f5d399f7f440977e4
diff --git a/tests/MultiDisplayTest/res/drawable/animated_gif.gif b/tests/MultiDisplayTest/res/drawable/animated_gif.gif
new file mode 100644
index 0000000..51baf15
--- /dev/null
+++ b/tests/MultiDisplayTest/res/drawable/animated_gif.gif
Binary files differ
diff --git a/tests/MultiDisplayTest/res/drawable/photo_landscape.jpg b/tests/MultiDisplayTest/res/drawable/photo_landscape.jpg
new file mode 100644
index 0000000..6a90b92
--- /dev/null
+++ b/tests/MultiDisplayTest/res/drawable/photo_landscape.jpg
Binary files differ
diff --git a/tests/MultiDisplayTest/res/drawable/photo_portrait.jpg b/tests/MultiDisplayTest/res/drawable/photo_portrait.jpg
new file mode 100644
index 0000000..825682b
--- /dev/null
+++ b/tests/MultiDisplayTest/res/drawable/photo_portrait.jpg
Binary files differ
diff --git a/tests/MultiDisplayTest/res/layout/presentation_content.xml b/tests/MultiDisplayTest/res/layout/presentation_content.xml
new file mode 100644
index 0000000..e08b1a4
--- /dev/null
+++ b/tests/MultiDisplayTest/res/layout/presentation_content.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView android:id="@+id/image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <TextView android:id="@+id/text"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical|center_horizontal"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
+</FrameLayout>
diff --git a/tests/MultiDisplayTest/res/layout/presentation_fragment.xml b/tests/MultiDisplayTest/res/layout/presentation_fragment.xml
new file mode 100644
index 0000000..fd43c92
--- /dev/null
+++ b/tests/MultiDisplayTest/res/layout/presentation_fragment.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <!-- Message to show to use. -->
+ <TextView android:id="@+id/intro_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:gravity="center_vertical|center_horizontal"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/presentation_introduction"/>
+
+ <!-- A checkbox to toggle between showing all displays or only presentation displays. -->
+ <CheckBox android:id="@+id/show_all_displays"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:text="@string/presentation_show_all_displays" />
+
+ <!-- List that will show information about all connected displays. -->
+ <ListView android:id="@+id/display_list"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1" />
+</LinearLayout>
diff --git a/tests/MultiDisplayTest/res/layout/presentation_list_item.xml b/tests/MultiDisplayTest/res/layout/presentation_list_item.xml
new file mode 100644
index 0000000..6b83b29
--- /dev/null
+++ b/tests/MultiDisplayTest/res/layout/presentation_list_item.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<!-- The content that we show on secondary displays.
+ See corresponding Java code PresentationActivity.java. -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+ <CheckBox
+ android:id="@+id/checkbox_presentation"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_centerVertical="true"
+ android:layout_marginRight="16dip"/>
+
+ <TextView android:id="@+id/display_id"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/checkbox_presentation"
+ android:layout_centerVertical="true"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+ <Button android:id="@+id/info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/presentation_info_button"/>
+ </RelativeLayout>
+</LinearLayout>
diff --git a/tests/MultiDisplayTest/res/values/strings.xml b/tests/MultiDisplayTest/res/values/strings.xml
index 24fb4f9..5c5f064 100644
--- a/tests/MultiDisplayTest/res/values/strings.xml
+++ b/tests/MultiDisplayTest/res/values/strings.xml
@@ -22,4 +22,9 @@
Presentation and the DisplayManager to show content on other Displays.\n
Selecting a Display will open a Presentation on it</string>
<string name="presentation_show_all_displays" translatable="false">Show all displays</string>
+ <string name="presentation_photo_text">Showing photo #%1$d on display #%2$d: %3$s.</string>
+ <string name="presentation_info_button">Info</string>
+ <string name="presentation_display_id_text">Display #%1$d: %2$s</string>
+ <string name="presentation_alert_info_text">Display #%1$d Info</string>
+ <string name="presentation_alert_dismiss_text">OK</string>
</resources>
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/MDTest.java b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/MDTest.java
index 57e2834..a665772 100644
--- a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/MDTest.java
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/MDTest.java
@@ -32,6 +32,7 @@
import com.google.android.car.multidisplaytest.draw.DrawTestFragment;
import com.google.android.car.multidisplaytest.ime.InputTestFragment;
+import com.google.android.car.multidisplaytest.present.PresentTestFragment;
import com.google.android.car.multidisplaytest.touch.TouchTestFragment;
import java.util.Arrays;
@@ -108,7 +109,8 @@
private final List<MenuEntry> mMenuEntries = Arrays.asList(
new FragmentMenuEntry("Touch test", TouchTestFragment.class),
new FragmentMenuEntry("IME test", InputTestFragment.class),
- new FragmentMenuEntry("Draw test", DrawTestFragment.class)
+ new FragmentMenuEntry("Draw test", DrawTestFragment.class),
+ new FragmentMenuEntry("Present test", PresentTestFragment.class)
);
@Override
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/draw/photo_landscape.jpg b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/draw/photo_landscape.jpg
new file mode 100644
index 0000000..ae9794f
--- /dev/null
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/draw/photo_landscape.jpg
Binary files differ
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/draw/photo_portrait.jpg b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/draw/photo_portrait.jpg
new file mode 100644
index 0000000..825682b
--- /dev/null
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/draw/photo_portrait.jpg
Binary files differ
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/present/PresentTestFragment.java b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/present/PresentTestFragment.java
new file mode 100644
index 0000000..4a4d928
--- /dev/null
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/present/PresentTestFragment.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2019 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.google.android.car.multidisplaytest.present;
+
+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.Color;
+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.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import androidx.fragment.app.Fragment;
+
+import com.google.android.car.multidisplaytest.R;
+
+import java.util.Arrays;
+
+/**
+ * Modified from
+ * //development/samples/ApiDemos/src/com/example/android/apis/app/PresentationActivity.java
+ * Show content on selected displays with Presentation and DisplayManager APIs
+ */
+public class PresentTestFragment extends Fragment {
+ private static final String TAG = PresentTestFragment.class.getSimpleName();
+ private static final int[] PHOTOS = new int[] {
+ R.drawable.photo_landscape, R.drawable.photo_portrait
+ };
+ private Display mCurrentDisplay;
+ private DisplayManager mDisplayManager;
+ private DisplayListAdapter mDisplayListAdapter;
+ private CheckBox mShowAllDisplaysCheckbox;
+ private ListView mListView;
+ private int mNextImageNumber;
+
+ // This state persist to restore the old presentation
+ private SparseArray<DemoPresentationContents> mSavedPresentationContents;
+ // List of all currently visible presentations indexed by display id.
+ private final SparseArray<DemoPresentation> mActivePresentations =
+ new SparseArray<DemoPresentation>();
+
+ // Listens for displays to be added, changed or removed.
+ private DisplayManager.DisplayListener mDisplayListener;
+ // Listens for when presentations are dismissed.
+ private DialogInterface.OnDismissListener mOnDismissListener;
+ // Listens for Info button clicked.
+ private View.OnClickListener mOnClickListener;
+ // Listens for checked Display changed.
+ private CompoundButton.OnCheckedChangeListener mOnCheckedChangedListener;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.presentation_fragment, container, false);
+ mSavedPresentationContents = new SparseArray<DemoPresentationContents>();
+ // Get the display manager service.
+ mDisplayManager = (DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE);
+ mCurrentDisplay = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE))
+ .getDefaultDisplay();
+
+ setupListeners(view);
+
+ mShowAllDisplaysCheckbox = view.findViewById(R.id.show_all_displays);
+ mShowAllDisplaysCheckbox.setOnCheckedChangeListener(mOnCheckedChangedListener);
+
+ mDisplayListAdapter = new DisplayListAdapter(getContext());
+
+ mListView = view.findViewById(R.id.display_list);
+ mListView.setAdapter(mDisplayListAdapter);
+
+ return view;
+ }
+
+ private void setupListeners(View view) {
+ mDisplayListener = new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ Log.d(TAG, "Display #" + displayId + " added.");
+ mDisplayListAdapter.updateShownDisplays();
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ Log.d(TAG, "Display #" + displayId + " changed.");
+ mDisplayListAdapter.updateShownDisplays();
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ Log.d(TAG, "Display #" + displayId + " removed.");
+ mDisplayListAdapter.updateShownDisplays();
+ }
+ };
+
+ 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();
+ }
+ };
+
+ mOnClickListener = new View.OnClickListener() {
+ @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,
+ (dialog, which) -> dialog.dismiss())
+ .create();
+ alert.show();
+ }
+ };
+
+ mOnCheckedChangedListener = new CompoundButton.OnCheckedChangeListener(){
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (buttonView == mShowAllDisplaysCheckbox) {
+ // Show all displays checkbox was toggled.
+ mDisplayListAdapter.updateShownDisplays();
+ } 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.updateShownDisplays();
+ }
+ }
+ };
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mDisplayListAdapter.updateShownDisplays();
+ mSavedPresentationContents.clear();
+
+ // Register to receive events from the display manager.
+ mDisplayManager.registerDisplayListener(mDisplayListener, null);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ Log.d(TAG, "Activity is being paused. Dismissing all active presentation.");
+
+ for (int i = 0; i < mActivePresentations.size(); i++) {
+ DemoPresentation presentation = mActivePresentations.valueAt(i);
+ presentation.dismiss();
+ }
+ mActivePresentations.clear();
+ }
+
+ /**
+ * Shows a Presentation on the specified display.
+ */
+ private void showPresentation(Display display, DemoPresentationContents contents) {
+ final int displayId = display.getDisplayId();
+ if (mActivePresentations.get(displayId) != null) {
+ Log.w(TAG, "Active presentation exists on Display #" + displayId + ".");
+ return;
+ }
+
+ Log.d(TAG, "Showing presentation photo #" + contents.mPhoto
+ + " on display #" + displayId + ".");
+
+ DemoPresentation presentation = new DemoPresentation(getContext(), display, contents);
+ presentation.show();
+ presentation.setOnDismissListener(mOnDismissListener);
+ mActivePresentations.put(displayId, presentation);
+ }
+
+ /**
+ * Hides a 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);
+ }
+
+ private int getNextPhoto() {
+ final int photo = mNextImageNumber;
+ mNextImageNumber = (mNextImageNumber + 1) % PHOTOS.length;
+ return photo;
+ }
+
+ private final class DisplayListAdapter extends ArrayAdapter<Display> {
+ private final Activity mActivity;
+ private final Context mContext;
+ private Display[] mShownDisplays;
+
+ DisplayListAdapter(Context context) {
+ super(context, R.layout.presentation_list_item);
+ mActivity = (Activity) context;
+ mContext = context;
+ mShownDisplays = new Display[] {mCurrentDisplay};
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final View view;
+ if (convertView == null) {
+ view = ((Activity) mContext).getLayoutInflater().inflate(
+ R.layout.presentation_list_item, null);
+ } else {
+ view = convertView;
+ }
+ final Display display = getItem(position);
+ final int displayId = display.getDisplayId();
+
+ DemoPresentation presentation = mActivePresentations.get(displayId);
+ DemoPresentationContents contents = presentation != null
+ ? presentation.mContents : null;
+
+ CheckBox cb = view.findViewById(R.id.checkbox_presentation);
+ cb.setTag(display);
+ cb.setOnCheckedChangeListener(mOnCheckedChangedListener);
+ cb.setChecked(contents != null);
+
+ TextView tv = (TextView) view.findViewById(R.id.display_id);
+ tv.setText(view.getContext().getResources().getString(
+ R.string.presentation_display_id_text, displayId, display.getName()));
+
+ if (displayId == mCurrentDisplay.getDisplayId()) {
+ tv.append(" [Current display]");
+ cb.setEnabled(false);
+ } else {
+ cb.setEnabled(true);
+ }
+
+ Button b = (Button) view.findViewById(R.id.info);
+ b.setTag(display);
+ b.setOnClickListener(mOnClickListener);
+
+ return view;
+ }
+
+ public void updateShownDisplays() {
+ clear();
+
+ String displayCategory = getDisplayCategory();
+ Display[] displays = mDisplayManager.getDisplays(displayCategory);
+ addAll(displays);
+
+ Log.d(TAG, Arrays.toString(displays));
+ }
+
+ private String getDisplayCategory() {
+ return mShowAllDisplaysCheckbox.isChecked() ? null :
+ DisplayManager.DISPLAY_CATEGORY_PRESENTATION;
+ }
+ }
+
+ /**
+ * The presentation to show on the secondary display.
+ */
+ private static final class DemoPresentation extends Presentation {
+ private DemoPresentationContents mContents;
+
+ DemoPresentation(Context context, Display display, DemoPresentationContents contents) {
+ super(context, display);
+ mContents = contents;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.presentation_content);
+
+ Resources r = getContext().getResources();
+ ImageView image = (ImageView) findViewById(R.id.image);
+ TextView text = (TextView) findViewById(R.id.text);
+
+ Display display = getDisplay();
+ int displayId = display.getDisplayId();
+ int photo = mContents.mPhoto;
+
+ image.setImageDrawable(r.getDrawable(PHOTOS[photo]));
+ findViewById(android.R.id.content).setBackgroundColor(mContents.mColor);
+ text.setText(r.getString(R.string.presentation_photo_text,
+ photo, displayId, display.getName()));
+ }
+ }
+
+ /**
+ * Content to show in the presentation.
+ */
+ private static final class DemoPresentationContents implements Parcelable {
+ private static final int[] COLORS = {
+ Color.RED,
+ Color.GREEN,
+ Color.BLUE,
+ Color.YELLOW,
+ Color.MAGENTA,
+ Color.BLACK,
+ Color.DKGRAY
+ };
+ private int mPhoto;
+ private int mColor;
+
+ public static final Creator<DemoPresentationContents> CREATOR =
+ new Creator<DemoPresentationContents>() {
+ @Override
+ public DemoPresentationContents createFromParcel(Parcel in) {
+ return new DemoPresentationContents(in);
+ }
+
+ @Override
+ public DemoPresentationContents[] newArray(int size) {
+ return new DemoPresentationContents[size];
+ }
+ };
+
+ DemoPresentationContents(int photo) {
+ this.mPhoto = photo;
+ this.mColor = COLORS[(int) (Math.random() * COLORS.length)];
+ }
+
+ private DemoPresentationContents(Parcel in) {
+ mPhoto = in.readInt();
+ mColor = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mPhoto);
+ dest.writeInt(mColor);
+ }
+ }
+}