ParcelableWorkContinuationImpl.java

/*
 * Copyright 2020 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 androidx.work.multiprocess.parcelable;

import static androidx.work.multiprocess.parcelable.ParcelUtils.readBooleanValue;
import static androidx.work.multiprocess.parcelable.ParcelUtils.writeBooleanValue;

import android.annotation.SuppressLint;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.work.ExistingWorkPolicy;
import androidx.work.WorkRequest;
import androidx.work.impl.WorkContinuationImpl;
import androidx.work.impl.WorkManagerImpl;
import androidx.work.impl.WorkRequestHolder;

import java.util.ArrayList;
import java.util.List;

/**
 * {@link androidx.work.impl.WorkContinuationImpl}, but parcelable.
 *
 * @hide
 */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@SuppressLint("BanParcelableUsage")
public class ParcelableWorkContinuationImpl implements Parcelable {

    private static final ExistingWorkPolicy[] sValues = ExistingWorkPolicy.values();

    private WorkContinuationImplInfo mInfo;

    public ParcelableWorkContinuationImpl(@NonNull WorkContinuationImpl continuation) {
        mInfo = new WorkContinuationImplInfo(continuation);
    }

    public ParcelableWorkContinuationImpl(@NonNull WorkContinuationImplInfo info) {
        mInfo = info;
    }

    @NonNull
    public WorkContinuationImplInfo getInfo() {
        return mInfo;
    }

    @SuppressWarnings("unchecked")
    protected ParcelableWorkContinuationImpl(@NonNull Parcel parcel) {
        // name
        String name = null;
        boolean hasName = readBooleanValue(parcel);
        if (hasName) {
            name = parcel.readString();
        }
        // workPolicy
        int ordinal = parcel.readInt();
        ExistingWorkPolicy workPolicy = sValues[ordinal];
        // workRequests
        int requestSize = parcel.readInt();
        List<WorkRequestHolder> requests = new ArrayList<>(requestSize);
        ClassLoader loader = getClass().getClassLoader();
        for (int i = 0; i < requestSize; i++) {
            ParcelableWorkRequest parcelledRequest = parcel.readParcelable(loader);
            // We always serialize to a WorkRequestHolder
            WorkRequestHolder requestHolder = (WorkRequestHolder) parcelledRequest.getWorkRequest();
            requests.add(requestHolder);
        }
        // parents
        List<WorkContinuationImplInfo> parents = null;
        boolean hasParents = readBooleanValue(parcel);
        if (hasParents) {
            int parentsSize = parcel.readInt();
            parents = new ArrayList<>(parentsSize);
            for (int i = 0; i < parentsSize; i++) {
                ParcelableWorkContinuationImpl parcelledContinuation =
                        parcel.readParcelable(loader);
                parents.add(parcelledContinuation.getInfo());
            }
        }
        mInfo = new WorkContinuationImplInfo(name, workPolicy, requests, parents);
    }

    public static final Creator<ParcelableWorkContinuationImpl> CREATOR =
            new Creator<ParcelableWorkContinuationImpl>() {
                @Override
                public ParcelableWorkContinuationImpl createFromParcel(@NonNull Parcel in) {
                    return new ParcelableWorkContinuationImpl(in);
                }

                @Override
                public ParcelableWorkContinuationImpl[] newArray(int size) {
                    return new ParcelableWorkContinuationImpl[size];
                }
            };

    @Override
    public int describeContents() {
        // No file descriptors being returned.
        return 0;
    }

    @Override
    public void writeToParcel(@NonNull Parcel parcel, int flags) {
        // name
        String name = mInfo.getName();
        boolean hasName = !TextUtils.isEmpty(name);
        writeBooleanValue(parcel, hasName);
        if (hasName) {
            parcel.writeString(name);
        }
        // workPolicy
        ExistingWorkPolicy policy = mInfo.getExistingWorkPolicy();
        parcel.writeInt(policy.ordinal());
        // workRequests
        List<? extends WorkRequest> requests = mInfo.getWork();
        parcel.writeInt(requests.size());
        if (!requests.isEmpty()) {
            for (int i = 0; i < requests.size(); i++) {
                WorkRequest request = requests.get(i);
                ParcelableWorkRequest parcelledRequest = new ParcelableWorkRequest(request);
                parcel.writeParcelable(parcelledRequest, flags);
            }
        }
        // parents
        List<WorkContinuationImplInfo> parents = mInfo.getParentInfos();
        boolean hasParents = parents != null && !parents.isEmpty();
        writeBooleanValue(parcel, hasParents);
        if (hasParents) {
            parcel.writeInt(parents.size());
            for (int i = 0; i < parents.size(); i++) {
                ParcelableWorkContinuationImpl parcelledContinuationImpl =
                        new ParcelableWorkContinuationImpl(parents.get(i));
                parcel.writeParcelable(parcelledContinuationImpl, flags);
            }
        }
    }

    /**
     * Converts an instance of {@link ParcelableWorkContinuationImpl} to a
     * {@link WorkContinuationImpl}.
     *
     * @param workManager The {@link  WorkManagerImpl} instance.
     * @return The {@link WorkContinuationImpl} instance
     */
    @NonNull
    public WorkContinuationImpl toWorkContinuationImpl(@NonNull WorkManagerImpl workManager) {
        return mInfo.toWorkContinuationImpl(workManager);
    }

    /**
     * Holds all the information that needs to be tracked inside a
     * {@link androidx.work.WorkContinuation}.
     */
    public static class WorkContinuationImplInfo {
        private final String mName;
        private final ExistingWorkPolicy mWorkPolicy;
        private final List<? extends WorkRequest> mRequests;
        private List<WorkContinuationImplInfo> mParents;

        public WorkContinuationImplInfo(@NonNull WorkContinuationImpl continuation) {
            mName = continuation.getName();
            mWorkPolicy = continuation.getExistingWorkPolicy();
            mRequests = continuation.getWork();
            List<WorkContinuationImpl> continuations = continuation.getParents();
            mParents = null;
            if (continuations != null) {
                mParents = new ArrayList<>(continuations.size());
                for (WorkContinuationImpl workContinuation : continuations) {
                    mParents.add(new WorkContinuationImplInfo(workContinuation));
                }
            }
        }

        public WorkContinuationImplInfo(
                @Nullable String name,
                @NonNull ExistingWorkPolicy workPolicy,
                @NonNull List<? extends WorkRequest> requests,
                @Nullable List<WorkContinuationImplInfo> parents) {
            mName = name;
            mWorkPolicy = workPolicy;
            mRequests = requests;
            mParents = parents;
        }

        @Nullable
        public String getName() {
            return mName;
        }

        @NonNull
        public ExistingWorkPolicy getExistingWorkPolicy() {
            return mWorkPolicy;
        }

        @NonNull
        public List<? extends WorkRequest> getWork() {
            return mRequests;
        }

        @Nullable
        public List<WorkContinuationImplInfo> getParentInfos() {
            return mParents;
        }

        /**
         * Converts an instance of {@link WorkContinuationImplInfo} to a
         * {@link androidx.work.WorkContinuation}.
         *
         * @param workManager The {@link  WorkManagerImpl} instance.
         * @return The {@link WorkContinuationImpl} instance
         */
        @NonNull
        public WorkContinuationImpl toWorkContinuationImpl(@NonNull WorkManagerImpl workManager) {
            return new WorkContinuationImpl(
                    workManager,
                    getName(),
                    getExistingWorkPolicy(),
                    getWork(),
                    parents(workManager, getParentInfos())
            );
        }

        @Nullable
        private static List<WorkContinuationImpl> parents(
                @NonNull WorkManagerImpl workManager,
                @Nullable List<WorkContinuationImplInfo> parentInfos) {

            if (parentInfos == null) {
                return null;
            }

            List<WorkContinuationImpl> continuations = new ArrayList<>(parentInfos.size());
            for (WorkContinuationImplInfo info : parentInfos) {
                continuations.add(
                        new WorkContinuationImpl(
                                workManager,
                                info.getName(),
                                info.getExistingWorkPolicy(),
                                info.getWork(),
                                parents(workManager, info.getParentInfos()))
                );
            }
            return continuations;
        }
    }
}