1 /* 2 * Copyright (C) 2014 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 android.app.usage.cts; 18 19 import android.app.Activity; 20 import android.app.SharedElementCallback; 21 import android.content.Intent; 22 import android.os.Bundle; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 import android.os.ResultReceiver; 26 import android.os.SystemClock; 27 import android.transition.ChangeBounds; 28 import android.transition.Explode; 29 import android.transition.Fade; 30 import android.transition.Transition; 31 import android.transition.Transition.TransitionListener; 32 import android.transition.TransitionListenerAdapter; 33 import android.view.View; 34 35 import java.util.List; 36 import java.util.concurrent.CountDownLatch; 37 38 /** 39 * A simple activity containing the start state for an Activity Transition 40 */ 41 public class ActivityTransitionActivity extends Activity { 42 private static final long DURATION = 50; 43 private static final long SHARED_ELEMENT_READY_DELAY = 50; 44 public static final String LAYOUT_ID = "layoutId"; 45 public static final String TEST = "test"; 46 public static final String RESULT_RECEIVER = "resultReceiver"; 47 public static final String PAUSE_ON_RESTART = "pauseOnRestart"; 48 public static final String QUICK_FINISH = "quickFinish"; 49 public static final String ALLOW_OVERLAP = "allowOverlap"; 50 public static final String NO_RETURN_TRANSITION = "noReturnTransition"; 51 52 public static final int NO_TEST = 0; 53 public static final int TEST_ARRIVE = 1; 54 55 public static final String ARRIVE_COUNT = "numArrived"; 56 public static final String ARRIVE_ENTER_START_VISIBILITY = "arriveEnterStartVisibility"; 57 public static final String ARRIVE_ENTER_DELAY_VISIBILITY = "arriveEnterDelayVisibility"; 58 public static final String ARRIVE_ENTER_TIME_READY = "arriveEnterTimeReady"; 59 public static final String ARRIVE_ENTER_TIME = "arriveEnterTime"; 60 public static final String ARRIVE_RETURN_TIME_READY = "arriveReturnTimeReady"; 61 public static final String ARRIVE_RETURN_TIME = "arriveReturnTime"; 62 63 public static final String ENTER_VISIBILITY = "enterVisibility"; 64 public static final String RETURN_VISIBILITY = "returnVisibility"; 65 66 private int mLayoutId; 67 private int mTest; 68 private ResultReceiver mResultReceiver; 69 private int mNumSharedElementsArrivedCalled = 0; 70 private boolean mEntering = true; 71 72 public int resultCode = 0; 73 public Bundle result = new Bundle(); 74 public final OptionalVisibility exitVisibility = new OptionalVisibility(); 75 public final OptionalVisibility reenterVisibility = new OptionalVisibility(); 76 77 public boolean mPauseOnRestart; 78 public boolean mQuickFinish; 79 public boolean mAllowOverlap; 80 public boolean mNoReturnTransition; 81 82 public CountDownLatch returnLatch = new CountDownLatch(1); 83 public CountDownLatch reenterLatch = new CountDownLatch(1); 84 85 @Override 86 public void onCreate(Bundle icicle) { 87 super.onCreate(icicle); 88 mLayoutId = 0; 89 if (icicle != null) { 90 mLayoutId = icicle.getInt(LAYOUT_ID); 91 mTest = icicle.getInt(TEST); 92 mResultReceiver = icicle.getParcelable(RESULT_RECEIVER); 93 mPauseOnRestart = icicle.getBoolean(PAUSE_ON_RESTART); 94 mQuickFinish = icicle.getBoolean(QUICK_FINISH); 95 mAllowOverlap = icicle.getBoolean(ALLOW_OVERLAP, true); 96 mNoReturnTransition = icicle.getBoolean(NO_RETURN_TRANSITION); 97 } 98 99 if (mLayoutId == 0) { 100 Intent intent = getIntent(); 101 mLayoutId = intent.getIntExtra(LAYOUT_ID, R.layout.start); 102 mTest = intent.getIntExtra(TEST, 0); 103 mResultReceiver = intent.getParcelableExtra(RESULT_RECEIVER); 104 mPauseOnRestart = intent.getBooleanExtra(PAUSE_ON_RESTART, false); 105 mQuickFinish = intent.getBooleanExtra(QUICK_FINISH, false); 106 mAllowOverlap = intent.getBooleanExtra(ALLOW_OVERLAP, true); 107 mNoReturnTransition = intent.getBooleanExtra(NO_RETURN_TRANSITION, false); 108 } 109 110 getWindow().setSharedElementEnterTransition(new ChangeBounds().setDuration(DURATION)); 111 getWindow().setSharedElementReturnTransition(new ChangeBounds().setDuration(DURATION)); 112 getWindow().setEnterTransition(createVisibilityTransition(true, ENTER_VISIBILITY)); 113 getWindow().setReturnTransition(createVisibilityTransition(true, RETURN_VISIBILITY)); 114 getWindow().setExitTransition(createVisibilityTransition(false, exitVisibility)); 115 getWindow().setReenterTransition(createVisibilityTransition(false, reenterVisibility)); 116 getWindow().getReenterTransition().addListener(new TransitionListenerAdapter() { 117 @Override 118 public void onTransitionEnd(Transition transition) { 119 reenterLatch.countDown(); 120 } 121 }); 122 123 setContentView(mLayoutId); 124 getWindow().setAllowReturnTransitionOverlap(mAllowOverlap); 125 if (mNoReturnTransition) { 126 getWindow().setReturnTransition(null); 127 getWindow().setSharedElementReturnTransition(null); 128 } 129 130 startTest(); 131 } 132 133 private Transition createVisibilityTransition(boolean isExplode, OptionalVisibility visibility) { 134 final Transition transition = isExplode ? new Explode() : new Fade(); 135 transition.setDuration(DURATION); 136 transition.addListener(new VisibilityCheck(R.id.redSquare, visibility)); 137 return transition; 138 } 139 140 private Transition createVisibilityTransition(boolean isExplode, String propertyName) { 141 final Transition transition = isExplode ? new Explode() : new Fade(); 142 transition.setDuration(DURATION); 143 transition.addListener(new VisibilityCheck(R.id.redSquare, propertyName)); 144 return transition; 145 } 146 147 @Override 148 protected void onSaveInstanceState(Bundle outState) { 149 outState.putInt(LAYOUT_ID, mLayoutId); 150 outState.putInt(TEST, mTest); 151 outState.putParcelable(RESULT_RECEIVER, mResultReceiver); 152 outState.putBoolean(PAUSE_ON_RESTART, mPauseOnRestart); 153 } 154 155 private void startTest() { 156 if (mTest == TEST_ARRIVE) { 157 setEnterSharedElementCallback(new SharedElementCallback() { 158 @Override 159 public void onSharedElementsArrived(List<String> sharedElementNames, 160 final List<View> sharedElements, 161 final OnSharedElementsReadyListener listener) { 162 mNumSharedElementsArrivedCalled++; 163 result.putInt(ARRIVE_COUNT, mNumSharedElementsArrivedCalled); 164 if (mEntering) { 165 result.putInt(ARRIVE_ENTER_START_VISIBILITY, sharedElements.get(0).getVisibility()); 166 result.putLong(ARRIVE_ENTER_TIME, SystemClock.uptimeMillis()); 167 } else { 168 result.putLong(ARRIVE_RETURN_TIME, SystemClock.uptimeMillis()); 169 } 170 171 getWindow().getDecorView().postDelayed(new Runnable() { 172 @Override 173 public void run() { 174 if (mEntering) { 175 result.putInt(ARRIVE_ENTER_DELAY_VISIBILITY, 176 sharedElements.get(0).getVisibility()); 177 result.putLong(ARRIVE_ENTER_TIME_READY, SystemClock.uptimeMillis()); 178 } else { 179 result.putLong(ARRIVE_RETURN_TIME_READY, 180 SystemClock.uptimeMillis()); 181 mResultReceiver.send(RESULT_OK, result); 182 } 183 listener.onSharedElementsReady(); 184 } 185 }, SHARED_ELEMENT_READY_DELAY); 186 } 187 }); 188 } 189 getWindow().getEnterTransition().addListener(new TransitionListener() { 190 @Override 191 public void onTransitionStart(Transition transition) { 192 } 193 194 @Override 195 public void onTransitionEnd(Transition transition) { 196 mEntering = false; 197 Intent intent = new Intent(); 198 intent.putExtra("Extra", new ExtraData("result")); 199 setResult(RESULT_OK, intent); 200 getWindow().getDecorView().post(new Runnable() { 201 @Override 202 public void run() { 203 finishAfterTransition(); 204 if (mQuickFinish) { 205 finish(); 206 } 207 } 208 }); 209 } 210 211 @Override 212 public void onTransitionCancel(Transition transition) { 213 } 214 215 @Override 216 public void onTransitionPause(Transition transition) { 217 } 218 219 @Override 220 public void onTransitionResume(Transition transition) { 221 } 222 }); 223 } 224 225 @Override 226 public void onActivityReenter(int resultCode, Intent data) { 227 if (resultCode == RESULT_OK) { 228 ExtraData extraData = data.getParcelableExtra("Extra"); 229 if (!"result".equals(extraData.data)) { 230 throw new RuntimeException("Incorrect returned intent data in onActivityReenter"); 231 } 232 } 233 super.onActivityReenter(resultCode, data); 234 } 235 236 @Override 237 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 238 super.onActivityResult(requestCode, resultCode, data); 239 this.resultCode = resultCode; 240 returnLatch.countDown(); 241 } 242 243 @Override 244 protected void onRestart() { 245 super.onRestart(); 246 if (mPauseOnRestart) { 247 postponeEnterTransition(); 248 getWindow().getDecorView().postDelayed(new Runnable() { 249 @Override 250 public void run() { 251 startPostponedEnterTransition(); 252 } 253 }, 500); 254 } 255 } 256 257 private class VisibilityCheck extends TransitionListenerAdapter { 258 private final int mViewId; 259 private final OptionalVisibility mVisibility; 260 private final String mPropertyName; 261 262 public VisibilityCheck(int viewId, OptionalVisibility visibility) { 263 mViewId = viewId; 264 mVisibility = visibility; 265 mPropertyName = null; 266 } 267 268 public VisibilityCheck(int viewId, String propertyName) { 269 mViewId = viewId; 270 mVisibility = null; 271 mPropertyName = propertyName; 272 } 273 274 @Override 275 public void onTransitionEnd(Transition transition) { 276 final View view = findViewById(mViewId); 277 if (mPropertyName != null) { 278 if (mResultReceiver != null) { 279 Bundle data = new Bundle(); 280 data.putInt(mPropertyName, view.getVisibility()); 281 mResultReceiver.send(RESULT_OK, data); 282 } 283 } else { 284 mVisibility.set(view.getVisibility()); 285 } 286 } 287 } 288 289 public static class OptionalVisibility { 290 private int mVisibility = -1; 291 292 public void set(int visibility) { 293 mVisibility = visibility; 294 } 295 296 public int get() { 297 return mVisibility; 298 } 299 300 public boolean isSet() { 301 return mVisibility != -1; 302 } 303 } 304 305 /** 306 * Used to test the class loader of the returned intent. 307 */ 308 public static class ExtraData implements Parcelable { 309 public final String data; 310 311 public ExtraData(String data) { 312 this.data = data; 313 } 314 315 protected ExtraData(Parcel in) { 316 this.data = in.readString(); 317 } 318 319 public static final Creator<ExtraData> CREATOR = new Creator<ExtraData>() { 320 @Override 321 public ExtraData createFromParcel(Parcel in) { 322 return new ExtraData(in); 323 } 324 325 @Override 326 public ExtraData[] newArray(int size) { 327 return new ExtraData[size]; 328 } 329 }; 330 331 @Override 332 public int describeContents() { 333 return 0; 334 } 335 336 @Override 337 public void writeToParcel(Parcel dest, int flags) { 338 dest.writeString(data); 339 } 340 } 341 } 342