Merge "CTS: Activity Transition: onSharedElementsArrived tests." into mnc-dev
diff --git a/tests/tests/app.usage/AndroidManifest.xml b/tests/tests/app.usage/AndroidManifest.xml
index 3bd795a..54fde71 100644
--- a/tests/tests/app.usage/AndroidManifest.xml
+++ b/tests/tests/app.usage/AndroidManifest.xml
@@ -32,6 +32,7 @@
         <activity android:name=".Activities$ActivityTwo" />
         <activity android:name=".Activities$ActivityThree" />
         <activity android:name=".Activities$ActivityFour" />
+        <activity android:name=".ActivityTransitionActivity" />
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/app.usage/res/layout/end.xml b/tests/tests/app.usage/res/layout/end.xml
new file mode 100644
index 0000000..2847249
--- /dev/null
+++ b/tests/tests/app.usage/res/layout/end.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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">
+    <View android:layout_width="10dp"
+          android:layout_height="10dp"
+          android:background="#F00"
+          android:id="@+id/redSquare"/>
+    <TextView android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:transitionName="target"
+              android:text="@string/hello"/>
+    <View android:layout_width="10dp"
+          android:layout_height="10dp"
+          android:background="#0F0"
+          android:id="@+id/greenSquare"/>
+</LinearLayout>
diff --git a/tests/tests/app.usage/res/layout/start.xml b/tests/tests/app.usage/res/layout/start.xml
new file mode 100644
index 0000000..793e9b5
--- /dev/null
+++ b/tests/tests/app.usage/res/layout/start.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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">
+    <View android:layout_width="10dp"
+          android:layout_height="10dp"
+          android:background="#0F0"
+          android:id="@+id/greenSquare"/>
+    <View android:layout_width="10dp"
+          android:layout_height="10dp"
+          android:background="#F00"
+          android:id="@+id/redSquare"/>
+    <TextView android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:transitionName="source"
+              android:id="@+id/hello"
+              android:text="@string/hello"/>
+</LinearLayout>
diff --git a/tests/tests/app.usage/res/values/strings.xml b/tests/tests/app.usage/res/values/strings.xml
new file mode 100644
index 0000000..8ca6f73
--- /dev/null
+++ b/tests/tests/app.usage/res/values/strings.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+<resources>
+    <string name="hello">Hello</string>
+</resources>
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/ActivityTransitionActivity.java b/tests/tests/app.usage/src/android/app/usage/cts/ActivityTransitionActivity.java
new file mode 100644
index 0000000..10ac4ea
--- /dev/null
+++ b/tests/tests/app.usage/src/android/app/usage/cts/ActivityTransitionActivity.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2014 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 android.app.usage.cts;
+
+import android.app.Activity;
+import android.app.SharedElementCallback;
+import android.app.usage.cts.R;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.os.SystemClock;
+import android.transition.ChangeBounds;
+import android.transition.Explode;
+import android.transition.Fade;
+import android.transition.Transition;
+import android.transition.Transition.TransitionListener;
+import android.view.View;
+
+import java.util.List;
+
+/**
+ * A simple activity containing the start state for an Activity Transition
+ */
+public class ActivityTransitionActivity extends Activity {
+    private static final long DURATION = 50;
+    private static final long SHARED_ELEMENT_READY_DELAY = 50;
+    public static final String LAYOUT_ID = "layoutId";
+    public static final String TEST = "test";
+    public static final String RESULT_RECEIVER = "resultReceiver";
+
+    public static final int NO_TEST = 0;
+    public static final int TEST_ARRIVE = 1;
+
+    public static final String ARRIVE_COUNT = "numArrived";
+    public static final String ARRIVE_ENTER_START_VISIBILITY = "arriveEnterStartVisibility";
+    public static final String ARRIVE_ENTER_DELAY_VISIBILITY = "arriveEnterDelayVisibility";
+    public static final String ARRIVE_ENTER_TIME_READY = "arriveEnterTimeReady";
+    public static final String ARRIVE_ENTER_TIME = "arriveEnterTime";
+    public static final String ARRIVE_RETURN_TIME_READY = "arriveReturnTimeReady";
+    public static final String ARRIVE_RETURN_TIME = "arriveReturnTime";
+
+    private int mLayoutId;
+    private int mTest;
+    private ResultReceiver mResultReceiver;
+    private int mNumSharedElementsArrivedCalled = 0;
+    private boolean mEntering = true;
+
+    public int resultCode = 0;
+    public Bundle result = new Bundle();
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        getWindow().setSharedElementEnterTransition(new ChangeBounds().setDuration(DURATION));
+        getWindow().setSharedElementReturnTransition(new ChangeBounds().setDuration(DURATION));
+        getWindow().setEnterTransition(new Explode().setDuration(DURATION));
+        getWindow().setReturnTransition(new Explode().setDuration(DURATION));
+        getWindow().setExitTransition(new Fade().setDuration(DURATION));
+        mLayoutId = 0;
+        if (icicle != null) {
+            mLayoutId =  icicle.getInt(LAYOUT_ID);
+            mTest = icicle.getInt(TEST);
+            mResultReceiver = icicle.getParcelable(RESULT_RECEIVER);
+        }
+
+        if (mLayoutId == 0) {
+            Intent intent = getIntent();
+            mLayoutId = intent.getIntExtra(LAYOUT_ID, R.layout.start);
+            mTest = intent.getIntExtra(TEST, 0);
+            mResultReceiver = intent.getParcelableExtra(RESULT_RECEIVER);
+        }
+
+        setContentView(mLayoutId);
+
+        startTest();
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        outState.putInt(LAYOUT_ID, mLayoutId);
+        outState.putInt(TEST, mTest);
+        outState.putParcelable(RESULT_RECEIVER, mResultReceiver);
+    }
+
+    private void startTest() {
+        if (mTest == TEST_ARRIVE) {
+            setEnterSharedElementCallback(new SharedElementCallback() {
+                @Override
+                public void onSharedElementsArrived(List<String> sharedElementNames,
+                        final List<View> sharedElements,
+                        final OnSharedElementsReadyListener listener) {
+                    mNumSharedElementsArrivedCalled++;
+                    result.putInt(ARRIVE_COUNT, mNumSharedElementsArrivedCalled);
+                    if (mEntering) {
+                        result.putInt(ARRIVE_ENTER_START_VISIBILITY, sharedElements.get(0).getVisibility());
+                        result.putLong(ARRIVE_ENTER_TIME, SystemClock.uptimeMillis());
+                    } else {
+                        result.putLong(ARRIVE_RETURN_TIME, SystemClock.uptimeMillis());
+                    }
+
+                    getWindow().getDecorView().postDelayed(new Runnable() {
+                        @Override
+                        public void run() {
+                            if (mEntering) {
+                                result.putInt(ARRIVE_ENTER_DELAY_VISIBILITY,
+                                        sharedElements.get(0).getVisibility());
+                                result.putLong(ARRIVE_ENTER_TIME_READY, SystemClock.uptimeMillis());
+                            } else {
+                                result.putLong(ARRIVE_RETURN_TIME_READY,
+                                        SystemClock.uptimeMillis());
+                                mResultReceiver.send(RESULT_OK, result);
+                            }
+                            listener.onSharedElementsReady();
+                        }
+                    }, SHARED_ELEMENT_READY_DELAY);
+                }
+            });
+            getWindow().getEnterTransition().addListener(new TransitionListener() {
+                @Override
+                public void onTransitionStart(Transition transition) {
+                }
+
+                @Override
+                public void onTransitionEnd(Transition transition) {
+                    mEntering = false;
+                    setResult(RESULT_OK);
+                    getWindow().getDecorView().post(new Runnable() {
+                        @Override
+                        public void run() {
+                            finishAfterTransition();
+                        }
+                    });
+                }
+
+                @Override
+                public void onTransitionCancel(Transition transition) {
+                }
+
+                @Override
+                public void onTransitionPause(Transition transition) {
+                }
+
+                @Override
+                public void onTransitionResume(Transition transition) {
+                }
+            });
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        synchronized (this) {
+            super.onActivityResult(requestCode, resultCode, data);
+            this.resultCode = resultCode;
+            this.notifyAll();
+        }
+    }
+}
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/ActivityTransitionTest.java b/tests/tests/app.usage/src/android/app/usage/cts/ActivityTransitionTest.java
new file mode 100644
index 0000000..ef126fe
--- /dev/null
+++ b/tests/tests/app.usage/src/android/app/usage/cts/ActivityTransitionTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2015 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 android.app.usage.cts;
+
+import android.app.ActivityOptions;
+import android.app.SharedElementCallback;
+import android.app.usage.cts.R;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ResultReceiver;
+import android.os.SystemClock;
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.View;
+
+import java.util.List;
+
+public class ActivityTransitionTest extends
+        ActivityInstrumentationTestCase2<ActivityTransitionActivity> {
+    protected ActivityTransitionActivity mActivity;
+
+    private int mNumArrivedCalls;
+    private PassInfo mReceiver;
+    private long mExitTime;
+    private long mExitTimeReady;
+    private long mReenterTime;
+    private long mReenterTimeReady;
+
+    public ActivityTransitionTest() {
+        super(ActivityTransitionActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        setActivityInitialTouchMode(false);
+        mActivity = getActivity();
+        mNumArrivedCalls = 0;
+    }
+
+    public void testOnSharedElementsArrived() throws Throwable {
+        getInstrumentation().waitForIdleSync();
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mReceiver = new PassInfo(new Handler());
+                mActivity.setExitSharedElementCallback(new SharedElementCallback() {
+                    @Override
+                    public void onSharedElementsArrived(List<String> sharedElementNames,
+                            List<View> sharedElements,
+                            final OnSharedElementsReadyListener listener) {
+                        mNumArrivedCalls++;
+                        final boolean isExiting = mExitTimeReady == 0;
+                        if (isExiting) {
+                            mExitTime = SystemClock.uptimeMillis();
+                        } else {
+                            mReenterTime = SystemClock.uptimeMillis();
+                        }
+                        mActivity.getWindow().getDecorView().postDelayed(new Runnable() {
+                            @Override
+                            public void run() {
+                                if (isExiting) {
+                                    mExitTimeReady = SystemClock.uptimeMillis();
+                                } else {
+                                    mReenterTimeReady = SystemClock.uptimeMillis();
+                                }
+                                listener.onSharedElementsReady();
+                            }
+                        }, 60);
+                    }
+                });
+
+                Bundle options = ActivityOptions.makeSceneTransitionAnimation(mActivity,
+                        mActivity.findViewById(R.id.hello), "target").toBundle();
+                Intent intent = new Intent(mActivity, ActivityTransitionActivity.class);
+                intent.putExtra(ActivityTransitionActivity.TEST,
+                        ActivityTransitionActivity.TEST_ARRIVE);
+                intent.putExtra(ActivityTransitionActivity.LAYOUT_ID, R.layout.end);
+                intent.putExtra(ActivityTransitionActivity.RESULT_RECEIVER, mReceiver);
+                mActivity.startActivityForResult(intent, 0, options);
+            }
+        });
+
+        long endTime = SystemClock.uptimeMillis() + 1500;
+        synchronized (mActivity) {
+            while (mActivity.resultCode == 0) {
+                long waitTime = endTime - SystemClock.uptimeMillis();
+                if (waitTime <= 0) {
+                    fail("Activity didn't finish!");
+                }
+                mActivity.wait(waitTime);
+            }
+        }
+        assertNotNull(mReceiver.resultData);
+        assertEquals(2, mReceiver.resultData.getInt(
+                ActivityTransitionActivity.ARRIVE_COUNT, -1));
+        assertEquals(2, mNumArrivedCalls);
+        assertNotSame(View.VISIBLE, mReceiver.resultData.getInt(
+                ActivityTransitionActivity.ARRIVE_ENTER_START_VISIBILITY));
+        assertNotSame(View.VISIBLE, mReceiver.resultData.getInt(
+                ActivityTransitionActivity.ARRIVE_ENTER_DELAY_VISIBILITY));
+        long enterTimeReady = mReceiver.resultData.getLong(
+                ActivityTransitionActivity.ARRIVE_ENTER_TIME_READY);
+        long returnTimeReady = mReceiver.resultData.getLong(
+                ActivityTransitionActivity.ARRIVE_RETURN_TIME_READY);
+        long enterTime = mReceiver.resultData.getLong(
+                ActivityTransitionActivity.ARRIVE_ENTER_TIME);
+        long returnTime = mReceiver.resultData.getLong(
+                ActivityTransitionActivity.ARRIVE_RETURN_TIME);
+
+        assertTrue(mExitTime < mExitTimeReady);
+        assertTrue(mExitTimeReady <= enterTime);
+        assertTrue(enterTime < enterTimeReady);
+        assertTrue(enterTimeReady <= returnTime);
+        assertTrue(returnTime < returnTimeReady);
+        assertTrue(returnTimeReady <= mReenterTime);
+        assertTrue(mReenterTime < mReenterTimeReady);
+    }
+
+    public static class PassInfo extends ResultReceiver {
+        public int resultCode;
+        public Bundle resultData;
+
+        public PassInfo(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        protected void onReceiveResult(int resultCode, Bundle resultData) {
+            this.resultCode = resultCode;
+            this.resultData = resultData;
+        }
+    }
+}