Merge "Add Present fragment to Multi-display test app" into qt-dev
am: a378a3c9d0

Change-Id: Ie9652c7695e8f6a7c1951be281332353c5bc83c6
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);
+        }
+    }
+}