/* * Copyright (C) 2016 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 com.android.documentsui.services; import static android.os.SystemClock.elapsedRealtime; import static com.android.documentsui.base.SharedMinimal.DEBUG; import static com.android.documentsui.services.FileOperationService.EXTRA_CANCEL; import static com.android.documentsui.services.FileOperationService.EXTRA_JOB_ID; import static com.android.documentsui.services.FileOperationService.EXTRA_OPERATION; import androidx.annotation.IntDef; import android.app.Activity; import android.content.Context; import android.content.Intent; import androidx.annotation.VisibleForTesting; import android.util.Log; import com.android.documentsui.services.FileOperationService.OpType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import javax.annotation.Nullable; /** * Helper functions for starting various file operations. */ public final class FileOperations { private static final String TAG = "FileOperations"; private static final IdBuilder idBuilder = new IdBuilder(); private FileOperations() {} public static String createJobId() { return idBuilder.getNext(); } /** * Tries to start the activity. Returns the job id. * @param jobId Optional job id. If null, then it will be auto-generated. */ public static String start(Context context, FileOperation operation, Callback callback, @Nullable String jobId) { if (DEBUG) { Log.d(TAG, "Handling generic 'start' call."); } String newJobId = jobId != null ? jobId : createJobId(); Intent intent = createBaseIntent(context, newJobId, operation); if (callback != null) { callback.onOperationResult(Callback.STATUS_ACCEPTED, operation.getOpType(), operation.getSrc().getItemCount()); } context.startService(intent); return newJobId; } @VisibleForTesting public static void cancel(Activity activity, String jobId) { if (DEBUG) { Log.d(TAG, "Attempting to canceling operation: " + jobId); } Intent intent = new Intent(activity, FileOperationService.class); intent.putExtra(EXTRA_CANCEL, true); intent.putExtra(EXTRA_JOB_ID, jobId); activity.startService(intent); } /** * Starts the service for an operation. * * @param jobId A unique jobid for this job. * Use {@link #createJobId} if you don't have one handy. * @return Id of the job. */ public static Intent createBaseIntent( Context context, String jobId, FileOperation operation) { Intent intent = new Intent(context, FileOperationService.class); intent.putExtra(EXTRA_JOB_ID, jobId); intent.putExtra(EXTRA_OPERATION, operation); return intent; } private static final class IdBuilder { // Remember last job time so we can guard against collisions. private long mLastJobTime; // If we detect a collision, use subId to make distinct. private int mSubId; public synchronized String getNext() { long time = elapsedRealtime(); if (time == mLastJobTime) { mSubId++; } else { mSubId = 0; } mLastJobTime = time; return String.valueOf(mLastJobTime) + "-" + String.valueOf(mSubId); } } /** * A functional callback called when the file operation starts or fails to start. */ @FunctionalInterface public interface Callback { @Retention(RetentionPolicy.SOURCE) @IntDef({STATUS_ACCEPTED, STATUS_REJECTED}) @interface Status {} static final int STATUS_ACCEPTED = 0; static final int STATUS_REJECTED = 1; static final int STATUS_FAILED = 2; /** * Performs operation when the file operation starts or fails to start. * * @param status {@link Status} of this operation. * @param opType file operation type {@link OpType}. * @param docCount number of documents operated. */ void onOperationResult(@Status int status, @OpType int opType, int docCount); } }