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