1 /* 2 * Copyright (C) 2022 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.window; 18 19 import static java.util.Objects.requireNonNull; 20 21 import android.annotation.IntDef; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.SuppressLint; 25 import android.annotation.TestApi; 26 import android.content.Intent; 27 import android.content.res.Configuration; 28 import android.os.Binder; 29 import android.os.Bundle; 30 import android.os.IBinder; 31 import android.os.Parcel; 32 import android.os.Parcelable; 33 34 import java.lang.annotation.Retention; 35 import java.lang.annotation.RetentionPolicy; 36 import java.util.ArrayList; 37 import java.util.List; 38 39 /** 40 * Used to communicate information about what are changing on embedded TaskFragments belonging to 41 * the same TaskFragmentOrganizer. A transaction can contain multiple changes. 42 * @see TaskFragmentTransaction.Change 43 * @hide 44 */ 45 @TestApi 46 public final class TaskFragmentTransaction implements Parcelable { 47 48 /** Unique token to represent this transaction. */ 49 private final IBinder mTransactionToken; 50 51 /** Changes in this transaction. */ 52 private final ArrayList<Change> mChanges = new ArrayList<>(); 53 TaskFragmentTransaction()54 public TaskFragmentTransaction() { 55 mTransactionToken = new Binder(); 56 } 57 TaskFragmentTransaction(Parcel in)58 private TaskFragmentTransaction(Parcel in) { 59 mTransactionToken = in.readStrongBinder(); 60 in.readTypedList(mChanges, Change.CREATOR); 61 } 62 63 @Override writeToParcel(@onNull Parcel dest, int flags)64 public void writeToParcel(@NonNull Parcel dest, int flags) { 65 dest.writeStrongBinder(mTransactionToken); 66 dest.writeTypedList(mChanges); 67 } 68 69 @NonNull getTransactionToken()70 public IBinder getTransactionToken() { 71 return mTransactionToken; 72 } 73 74 /** Adds a {@link Change} to this transaction. */ addChange(@ullable Change change)75 public void addChange(@Nullable Change change) { 76 if (change != null) { 77 mChanges.add(change); 78 } 79 } 80 81 /** Whether this transaction contains any {@link Change}. */ isEmpty()82 public boolean isEmpty() { 83 return mChanges.isEmpty(); 84 } 85 86 @NonNull getChanges()87 public List<Change> getChanges() { 88 return mChanges; 89 } 90 91 @Override toString()92 public String toString() { 93 StringBuilder sb = new StringBuilder(); 94 sb.append("TaskFragmentTransaction{token="); 95 sb.append(mTransactionToken); 96 sb.append(" changes=["); 97 for (int i = 0; i < mChanges.size(); ++i) { 98 if (i > 0) { 99 sb.append(','); 100 } 101 sb.append(mChanges.get(i)); 102 } 103 sb.append("]}"); 104 return sb.toString(); 105 } 106 107 @Override describeContents()108 public int describeContents() { 109 return 0; 110 } 111 112 @NonNull 113 public static final Creator<TaskFragmentTransaction> CREATOR = new Creator<>() { 114 @Override 115 public TaskFragmentTransaction createFromParcel(Parcel in) { 116 return new TaskFragmentTransaction(in); 117 } 118 119 @Override 120 public TaskFragmentTransaction[] newArray(int size) { 121 return new TaskFragmentTransaction[size]; 122 } 123 }; 124 125 /** Change type: the TaskFragment is attached to the hierarchy. */ 126 public static final int TYPE_TASK_FRAGMENT_APPEARED = 1; 127 128 /** Change type: the status of the TaskFragment is changed. */ 129 public static final int TYPE_TASK_FRAGMENT_INFO_CHANGED = 2; 130 131 /** Change type: the TaskFragment is removed from the hierarchy. */ 132 public static final int TYPE_TASK_FRAGMENT_VANISHED = 3; 133 134 /** Change type: the status of the parent leaf Task is changed. */ 135 public static final int TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED = 4; 136 137 /** Change type: the TaskFragment related operation failed on the server side. */ 138 public static final int TYPE_TASK_FRAGMENT_ERROR = 5; 139 140 /** 141 * Change type: an Activity is reparented to the Task. For example, when an Activity enters and 142 * then exits Picture-in-picture, it will be reparented back to its original Task. In this case, 143 * we need to notify the organizer so that it can check if the Activity matches any split rule. 144 */ 145 public static final int TYPE_ACTIVITY_REPARENTED_TO_TASK = 6; 146 147 @IntDef(prefix = { "TYPE_" }, value = { 148 TYPE_TASK_FRAGMENT_APPEARED, 149 TYPE_TASK_FRAGMENT_INFO_CHANGED, 150 TYPE_TASK_FRAGMENT_VANISHED, 151 TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED, 152 TYPE_TASK_FRAGMENT_ERROR, 153 TYPE_ACTIVITY_REPARENTED_TO_TASK 154 }) 155 @Retention(RetentionPolicy.SOURCE) 156 @interface ChangeType {} 157 158 /** Represents the change an embedded TaskFragment undergoes. */ 159 public static final class Change implements Parcelable { 160 161 /** @see ChangeType */ 162 @ChangeType 163 private final int mType; 164 165 /** @see #setTaskFragmentToken(IBinder) */ 166 @Nullable 167 private IBinder mTaskFragmentToken; 168 169 /** @see #setTaskFragmentInfo(TaskFragmentInfo) */ 170 @Nullable 171 private TaskFragmentInfo mTaskFragmentInfo; 172 173 /** @see #setTaskId(int) */ 174 private int mTaskId; 175 176 /** @see #setErrorCallbackToken(IBinder) */ 177 @Nullable 178 private IBinder mErrorCallbackToken; 179 180 /** @see #setErrorBundle(Bundle) */ 181 @Nullable 182 private Bundle mErrorBundle; 183 184 /** @see #setActivityIntent(Intent) */ 185 @Nullable 186 private Intent mActivityIntent; 187 188 /** @see #setActivityToken(IBinder) */ 189 @Nullable 190 private IBinder mActivityToken; 191 192 @Nullable 193 private TaskFragmentParentInfo mTaskFragmentParentInfo; 194 Change(@hangeType int type)195 public Change(@ChangeType int type) { 196 mType = type; 197 } 198 Change(Parcel in)199 private Change(Parcel in) { 200 mType = in.readInt(); 201 mTaskFragmentToken = in.readStrongBinder(); 202 mTaskFragmentInfo = in.readTypedObject(TaskFragmentInfo.CREATOR); 203 mTaskId = in.readInt(); 204 mErrorCallbackToken = in.readStrongBinder(); 205 mErrorBundle = in.readBundle(TaskFragmentTransaction.class.getClassLoader()); 206 mActivityIntent = in.readTypedObject(Intent.CREATOR); 207 mActivityToken = in.readStrongBinder(); 208 mTaskFragmentParentInfo = in.readTypedObject(TaskFragmentParentInfo.CREATOR); 209 } 210 211 @Override writeToParcel(@onNull Parcel dest, int flags)212 public void writeToParcel(@NonNull Parcel dest, int flags) { 213 dest.writeInt(mType); 214 dest.writeStrongBinder(mTaskFragmentToken); 215 dest.writeTypedObject(mTaskFragmentInfo, flags); 216 dest.writeInt(mTaskId); 217 dest.writeStrongBinder(mErrorCallbackToken); 218 dest.writeBundle(mErrorBundle); 219 dest.writeTypedObject(mActivityIntent, flags); 220 dest.writeStrongBinder(mActivityToken); 221 dest.writeTypedObject(mTaskFragmentParentInfo, flags); 222 } 223 224 /** The change is related to the TaskFragment created with this unique token. */ 225 @NonNull setTaskFragmentToken(@onNull IBinder taskFragmentToken)226 public Change setTaskFragmentToken(@NonNull IBinder taskFragmentToken) { 227 mTaskFragmentToken = requireNonNull(taskFragmentToken); 228 return this; 229 } 230 231 /** Info of the embedded TaskFragment. */ 232 @NonNull setTaskFragmentInfo(@onNull TaskFragmentInfo info)233 public Change setTaskFragmentInfo(@NonNull TaskFragmentInfo info) { 234 mTaskFragmentInfo = requireNonNull(info); 235 return this; 236 } 237 238 /** Task id the parent Task. */ 239 @NonNull setTaskId(int taskId)240 public Change setTaskId(int taskId) { 241 mTaskId = taskId; 242 return this; 243 } 244 245 // TODO(b/241043377): Keep this API to prevent @TestApi changes. Remove in the next release. 246 /** Configuration of the parent Task. */ 247 @NonNull setTaskConfiguration(@onNull Configuration configuration)248 public Change setTaskConfiguration(@NonNull Configuration configuration) { 249 return this; 250 } 251 252 /** 253 * If the {@link #TYPE_TASK_FRAGMENT_ERROR} is from a {@link WindowContainerTransaction} 254 * from the {@link TaskFragmentOrganizer}, it may come with an error callback token to 255 * report back. 256 */ 257 @NonNull setErrorCallbackToken(@ullable IBinder errorCallbackToken)258 public Change setErrorCallbackToken(@Nullable IBinder errorCallbackToken) { 259 mErrorCallbackToken = errorCallbackToken; 260 return this; 261 } 262 263 /** 264 * Bundle with necessary info about the failure operation of 265 * {@link #TYPE_TASK_FRAGMENT_ERROR}. 266 */ 267 @NonNull setErrorBundle(@onNull Bundle errorBundle)268 public Change setErrorBundle(@NonNull Bundle errorBundle) { 269 mErrorBundle = requireNonNull(errorBundle); 270 return this; 271 } 272 273 /** 274 * Intent of the activity that is reparented to the Task for 275 * {@link #TYPE_ACTIVITY_REPARENTED_TO_TASK}. 276 */ 277 @NonNull setActivityIntent(@onNull Intent intent)278 public Change setActivityIntent(@NonNull Intent intent) { 279 mActivityIntent = requireNonNull(intent); 280 return this; 281 } 282 283 /** 284 * Token of the reparent activity for {@link #TYPE_ACTIVITY_REPARENTED_TO_TASK}. 285 * If the activity belongs to the same process as the organizer, this will be the actual 286 * activity token; if the activity belongs to a different process, the server will generate 287 * a temporary token that the organizer can use to reparent the activity through 288 * {@link WindowContainerTransaction} if needed. 289 */ 290 @NonNull setActivityToken(@onNull IBinder activityToken)291 public Change setActivityToken(@NonNull IBinder activityToken) { 292 mActivityToken = requireNonNull(activityToken); 293 return this; 294 } 295 296 // TODO(b/241043377): Hide this API to prevent @TestApi changes. Remove in the next release. 297 /** 298 * Sets info of the parent Task of the embedded TaskFragment. 299 * @see TaskFragmentParentInfo 300 * 301 * @hide pending unhide 302 */ 303 @NonNull setTaskFragmentParentInfo(@onNull TaskFragmentParentInfo info)304 public Change setTaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) { 305 mTaskFragmentParentInfo = requireNonNull(info); 306 return this; 307 } 308 309 @ChangeType getType()310 public int getType() { 311 return mType; 312 } 313 314 @Nullable getTaskFragmentToken()315 public IBinder getTaskFragmentToken() { 316 return mTaskFragmentToken; 317 } 318 319 @Nullable getTaskFragmentInfo()320 public TaskFragmentInfo getTaskFragmentInfo() { 321 return mTaskFragmentInfo; 322 } 323 getTaskId()324 public int getTaskId() { 325 return mTaskId; 326 } 327 328 // TODO(b/241043377): Keep this API to prevent @TestApi changes. Remove in the next release. 329 @Nullable getTaskConfiguration()330 public Configuration getTaskConfiguration() { 331 return mTaskFragmentParentInfo.getConfiguration(); 332 } 333 334 @Nullable getErrorCallbackToken()335 public IBinder getErrorCallbackToken() { 336 return mErrorCallbackToken; 337 } 338 339 @NonNull getErrorBundle()340 public Bundle getErrorBundle() { 341 return mErrorBundle != null ? mErrorBundle : Bundle.EMPTY; 342 } 343 344 @SuppressLint("IntentBuilderName") // This is not creating new Intent. 345 @Nullable getActivityIntent()346 public Intent getActivityIntent() { 347 return mActivityIntent; 348 } 349 350 @Nullable getActivityToken()351 public IBinder getActivityToken() { 352 return mActivityToken; 353 } 354 355 // TODO(b/241043377): Hide this API to prevent @TestApi changes. Remove in the next release. 356 /** @hide pending unhide */ 357 @Nullable getTaskFragmentParentInfo()358 public TaskFragmentParentInfo getTaskFragmentParentInfo() { 359 return mTaskFragmentParentInfo; 360 } 361 362 @Override toString()363 public String toString() { 364 return "Change{ type=" + mType + " }"; 365 } 366 367 @Override describeContents()368 public int describeContents() { 369 return 0; 370 } 371 372 @NonNull 373 public static final Creator<Change> CREATOR = new Creator<>() { 374 @Override 375 public Change createFromParcel(Parcel in) { 376 return new Change(in); 377 } 378 379 @Override 380 public Change[] newArray(int size) { 381 return new Change[size]; 382 } 383 }; 384 } 385 } 386