blob: 4bfaeb234d0bdd6fd4b57c25c1075135dee6f8aa [file] [log] [blame]
/*
* 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.content.Intent;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
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.transition.TransitionListenerAdapter;
import android.view.View;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
* 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 String PAUSE_ON_RESTART = "pauseOnRestart";
public static final String QUICK_FINISH = "quickFinish";
public static final String ALLOW_OVERLAP = "allowOverlap";
public static final String NO_RETURN_TRANSITION = "noReturnTransition";
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";
public static final String ENTER_VISIBILITY = "enterVisibility";
public static final String RETURN_VISIBILITY = "returnVisibility";
public static final String ACTIVITY_FINISHED = "activityFinished";
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();
public final OptionalVisibility exitVisibility = new OptionalVisibility();
public final OptionalVisibility reenterVisibility = new OptionalVisibility();
public boolean mPauseOnRestart;
public boolean mQuickFinish;
public boolean mAllowOverlap;
public boolean mNoReturnTransition;
public boolean mIsResumed;
public CountDownLatch returnLatch = new CountDownLatch(1);
public CountDownLatch reenterLatch = new CountDownLatch(1);
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mLayoutId = 0;
if (icicle != null) {
mLayoutId = icicle.getInt(LAYOUT_ID);
mTest = icicle.getInt(TEST);
mResultReceiver = icicle.getParcelable(RESULT_RECEIVER);
mPauseOnRestart = icicle.getBoolean(PAUSE_ON_RESTART);
mQuickFinish = icicle.getBoolean(QUICK_FINISH);
mAllowOverlap = icicle.getBoolean(ALLOW_OVERLAP, true);
mNoReturnTransition = icicle.getBoolean(NO_RETURN_TRANSITION);
}
if (mLayoutId == 0) {
Intent intent = getIntent();
mLayoutId = intent.getIntExtra(LAYOUT_ID, R.layout.start);
mTest = intent.getIntExtra(TEST, 0);
mResultReceiver = intent.getParcelableExtra(RESULT_RECEIVER);
mPauseOnRestart = intent.getBooleanExtra(PAUSE_ON_RESTART, false);
mQuickFinish = intent.getBooleanExtra(QUICK_FINISH, false);
mAllowOverlap = intent.getBooleanExtra(ALLOW_OVERLAP, true);
mNoReturnTransition = intent.getBooleanExtra(NO_RETURN_TRANSITION, false);
}
getWindow().setSharedElementEnterTransition(new ChangeBounds().setDuration(DURATION));
getWindow().setSharedElementReturnTransition(new ChangeBounds().setDuration(DURATION));
getWindow().setEnterTransition(createVisibilityTransition(true, ENTER_VISIBILITY));
getWindow().setReturnTransition(createVisibilityTransition(true, RETURN_VISIBILITY));
getWindow().setExitTransition(createVisibilityTransition(false, exitVisibility));
getWindow().setReenterTransition(createVisibilityTransition(false, reenterVisibility));
getWindow().getReenterTransition().addListener(new TransitionListenerAdapter() {
@Override
public void onTransitionEnd(Transition transition) {
reenterLatch.countDown();
}
});
setContentView(mLayoutId);
getWindow().setAllowReturnTransitionOverlap(mAllowOverlap);
if (mNoReturnTransition) {
getWindow().setReturnTransition(null);
getWindow().setSharedElementReturnTransition(null);
}
startTest();
}
private Transition createVisibilityTransition(boolean isExplode, OptionalVisibility visibility) {
final Transition transition = isExplode ? new Explode() : new Fade();
transition.setDuration(DURATION);
transition.addListener(new VisibilityCheck(R.id.redSquare, visibility));
return transition;
}
private Transition createVisibilityTransition(boolean isExplode, String propertyName) {
final Transition transition = isExplode ? new Explode() : new Fade();
transition.setDuration(DURATION);
transition.addListener(new VisibilityCheck(R.id.redSquare, propertyName));
return transition;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt(LAYOUT_ID, mLayoutId);
outState.putInt(TEST, mTest);
outState.putParcelable(RESULT_RECEIVER, mResultReceiver);
outState.putBoolean(PAUSE_ON_RESTART, mPauseOnRestart);
}
private void sendResult() {
if (mResultReceiver != null) {
mResultReceiver.send(RESULT_OK, result);
}
}
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());
}
sendResult();
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());
}
sendResult();
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;
Intent intent = new Intent();
intent.putExtra("Extra", new ExtraData("result"));
setResult(RESULT_OK, intent);
getWindow().getDecorView().post(new Runnable() {
@Override
public void run() {
finishAfterTransition();
if (mQuickFinish) {
finish();
}
}
});
}
@Override
public void onTransitionCancel(Transition transition) {
}
@Override
public void onTransitionPause(Transition transition) {
}
@Override
public void onTransitionResume(Transition transition) {
}
});
}
@Override
public void onActivityReenter(int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
ExtraData extraData = data.getParcelableExtra("Extra");
if (!"result".equals(extraData.data)) {
throw new RuntimeException("Incorrect returned intent data in onActivityReenter");
}
}
super.onActivityReenter(resultCode, data);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
this.resultCode = resultCode;
returnLatch.countDown();
}
@Override
protected void onRestart() {
super.onRestart();
if (mPauseOnRestart) {
postponeEnterTransition();
getWindow().getDecorView().postDelayed(new Runnable() {
@Override
public void run() {
startPostponedEnterTransition();
}
}, 500);
}
}
@Override
protected synchronized void onPause() {
super.onPause();
mIsResumed = false;
}
@Override
protected synchronized void onResume() {
super.onResume();
mIsResumed = true;
notifyAll();
}
@Override
public void finish() {
if (mResultReceiver != null) {
Bundle result = new Bundle();
result.putBoolean(ACTIVITY_FINISHED, true);
mResultReceiver.send(RESULT_OK, result);
}
super.finish();
}
private class VisibilityCheck extends TransitionListenerAdapter {
private final int mViewId;
private final OptionalVisibility mVisibility;
private final String mPropertyName;
public VisibilityCheck(int viewId, OptionalVisibility visibility) {
mViewId = viewId;
mVisibility = visibility;
mPropertyName = null;
}
public VisibilityCheck(int viewId, String propertyName) {
mViewId = viewId;
mVisibility = null;
mPropertyName = propertyName;
}
@Override
public void onTransitionEnd(Transition transition) {
final View view = findViewById(mViewId);
if (mPropertyName != null) {
if (mResultReceiver != null) {
Bundle data = new Bundle();
data.putInt(mPropertyName, view.getVisibility());
mResultReceiver.send(RESULT_OK, data);
}
} else {
mVisibility.set(view.getVisibility());
}
}
}
public static class OptionalVisibility {
private int mVisibility = -1;
public void set(int visibility) {
mVisibility = visibility;
}
public int get() {
return mVisibility;
}
public boolean isSet() {
return mVisibility != -1;
}
}
/**
* Used to test the class loader of the returned intent.
*/
public static class ExtraData implements Parcelable {
public final String data;
public ExtraData(String data) {
this.data = data;
}
protected ExtraData(Parcel in) {
this.data = in.readString();
}
public static final Creator<ExtraData> CREATOR = new Creator<ExtraData>() {
@Override
public ExtraData createFromParcel(Parcel in) {
return new ExtraData(in);
}
@Override
public ExtraData[] newArray(int size) {
return new ExtraData[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(data);
}
}
}