130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin/*
230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin * Copyright (C) 2016 The Android Open Source Project
330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin *
430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin * Licensed under the Apache License, Version 2.0 (the "License");
530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin * you may not use this file except in compliance with the License.
630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin * You may obtain a copy of the License at
730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin *
830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin *      http://www.apache.org/licenses/LICENSE-2.0
930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin *
1030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin * Unless required by applicable law or agreed to in writing, software
1130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin * distributed under the License is distributed on an "AS IS" BASIS,
1230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin * See the License for the specific language governing permissions and
1430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin * limitations under the License.
1530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin */
1630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
1730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linpackage com.android.documentsui.clipping;
1830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
1930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport android.content.ClipData;
2030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport android.content.ClipDescription;
2130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport android.content.ClipboardManager;
2230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport android.content.ContentResolver;
2330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport android.content.Context;
2430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport android.net.Uri;
2530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport android.os.PersistableBundle;
2630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport android.provider.DocumentsContract;
2730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport android.support.annotation.Nullable;
2830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport android.util.Log;
2930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
3030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport com.android.documentsui.base.DocumentInfo;
3130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport com.android.documentsui.base.DocumentStack;
3230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport com.android.documentsui.base.Features;
3330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport com.android.documentsui.base.RootInfo;
3430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport com.android.documentsui.base.Shared;
3530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport com.android.documentsui.selection.Selection;
3630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport com.android.documentsui.services.FileOperation;
3730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport com.android.documentsui.services.FileOperationService;
3830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport com.android.documentsui.services.FileOperationService.OpType;
3930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport com.android.documentsui.services.FileOperations;
4030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
4130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport java.io.IOException;
4230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport java.util.ArrayList;
4330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport java.util.HashSet;
4430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport java.util.List;
4530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport java.util.Set;
4630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linimport java.util.function.Function;
4730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
4830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin/**
4930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin * ClipboardManager wrapper class providing higher level logical
5030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin * support for dealing with Documents.
5130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin */
5230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Linfinal class RuntimeDocumentClipper implements DocumentClipper {
5330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
5430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    private static final String TAG = "DocumentClipper";
55da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan    private static final String SRC_PARENT_KEY = "clipper:srcParent";
56da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan    private static final String OP_TYPE_KEY = "clipper:opType";
5730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
5830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    private final Context mContext;
5930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    private final ClipStore mClipStore;
6030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    private final ClipboardManager mClipboard;
6130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
6230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    RuntimeDocumentClipper(Context context, ClipStore clipStore) {
6330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        mContext = context;
6430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        mClipStore = clipStore;
6530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        mClipboard = context.getSystemService(ClipboardManager.class);
6630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
6730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
6830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    @Override
6930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    public boolean hasItemsToPaste() {
7030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        if (mClipboard.hasPrimaryClip()) {
7130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            ClipData clipData = mClipboard.getPrimaryClip();
7230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
7330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            int count = clipData.getItemCount();
7430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            if (count > 0) {
7530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                for (int i = 0; i < count; ++i) {
7630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                    ClipData.Item item = clipData.getItemAt(i);
7730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                    Uri uri = item.getUri();
7830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                    if (isDocumentUri(uri)) {
7930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                        return true;
8030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                    }
8130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                }
8230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            }
8330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        }
8430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        return false;
8530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
8630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
8730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    private boolean isDocumentUri(@Nullable Uri uri) {
8830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        return uri != null && DocumentsContract.isDocumentUri(mContext, uri);
8930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
9030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
9130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    @Override
9230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    public ClipData getClipDataForDocuments(
9330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        Function<String, Uri> uriBuilder, Selection selection, @OpType int opType) {
9430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
9530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        assert(selection != null);
9630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
9730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        if (selection.isEmpty()) {
9830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            Log.w(TAG, "Attempting to clip empty selection. Ignoring.");
9930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            return null;
10030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        }
10130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
102da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan        final List<Uri> uris = new ArrayList<>(selection.size());
103da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan        for (String id : selection) {
104da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan            uris.add(uriBuilder.apply(id));
105da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan        }
106da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan        return getClipDataForDocuments(uris, opType);
107da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan    }
108da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan
109da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan    @Override
110da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan    public ClipData getClipDataForDocuments(
111da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan            List<Uri> uris, @OpType int opType, DocumentInfo parent) {
112da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan        ClipData clipData = getClipDataForDocuments(uris, opType);
113da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan        clipData.getDescription().getExtras().putString(
114da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan                SRC_PARENT_KEY, parent.derivedUri.toString());
115da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan        return clipData;
116da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan    }
117da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan
1182e81db6f2030ec6c8dd758c3020ce5db8363bf25Garfield Tan    @Override
1192e81db6f2030ec6c8dd758c3020ce5db8363bf25Garfield Tan    public ClipData getClipDataForDocuments(List<Uri> uris, @OpType int opType) {
120da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan        return (uris.size() > Shared.MAX_DOCS_IN_INTENT)
121da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan                ? createJumboClipData(uris, opType)
122da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan                : createStandardClipData(uris, opType);
12330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
12430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
12530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    /**
12630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     * Returns ClipData representing the selection.
12730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     */
128da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan    private ClipData createStandardClipData(List<Uri> uris, @OpType int opType) {
12930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
130da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan        assert(!uris.isEmpty());
131da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan        assert(uris.size() <= Shared.MAX_DOCS_IN_INTENT);
13230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
13330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        final ContentResolver resolver = mContext.getContentResolver();
13430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        final ArrayList<ClipData.Item> clipItems = new ArrayList<>();
13530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        final Set<String> clipTypes = new HashSet<>();
13630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
13730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        PersistableBundle bundle = new PersistableBundle();
13830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        bundle.putInt(OP_TYPE_KEY, opType);
13930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
140da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan        for (Uri uri : uris) {
14130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            DocumentInfo.addMimeTypes(resolver, uri, clipTypes);
14230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            clipItems.add(new ClipData.Item(uri));
14330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        }
14430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
14530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        ClipDescription description = new ClipDescription(
14630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                "", // Currently "label" is not displayed anywhere in the UI.
14730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                clipTypes.toArray(new String[0]));
14830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        description.setExtras(bundle);
14930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
15030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        return createClipData(description, clipItems);
15130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
15230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
15330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    /**
15430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     * Returns ClipData representing the list of docs
15530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     */
156da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan    private ClipData createJumboClipData(List<Uri> uris, @OpType int opType) {
15730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
158da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan        assert(!uris.isEmpty());
159da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan        assert(uris.size() > Shared.MAX_DOCS_IN_INTENT);
16030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
161da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan        final int capacity = Math.min(uris.size(), Shared.MAX_DOCS_IN_INTENT);
16230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        final ArrayList<ClipData.Item> clipItems = new ArrayList<>(capacity);
16330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
16430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        // Set up mime types for the first Shared.MAX_DOCS_IN_INTENT
16530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        final ContentResolver resolver = mContext.getContentResolver();
16630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        final Set<String> clipTypes = new HashSet<>();
16730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        int docCount = 0;
168da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan        for (Uri uri : uris) {
16930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            if (docCount++ < Shared.MAX_DOCS_IN_INTENT) {
17030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                DocumentInfo.addMimeTypes(resolver, uri, clipTypes);
17130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                clipItems.add(new ClipData.Item(uri));
17230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            }
17330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        }
17430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
17530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        // Prepare metadata
17630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        PersistableBundle bundle = new PersistableBundle();
17730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        bundle.putInt(OP_TYPE_KEY, opType);
178da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan        bundle.putInt(OP_JUMBO_SELECTION_SIZE, uris.size());
17930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
18030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        // Persists clip items and gets the slot they were saved under.
18130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        int tag = mClipStore.persistUris(uris);
18230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        bundle.putInt(OP_JUMBO_SELECTION_TAG, tag);
18330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
18430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        ClipDescription description = new ClipDescription(
18530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                "", // Currently "label" is not displayed anywhere in the UI.
18630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                clipTypes.toArray(new String[0]));
18730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        description.setExtras(bundle);
18830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
18930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        return createClipData(description, clipItems);
19030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
19130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
19230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    @Override
19330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    public void clipDocumentsForCopy(Function<String, Uri> uriBuilder, Selection selection) {
19430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        ClipData data =
19530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                getClipDataForDocuments(uriBuilder, selection, FileOperationService.OPERATION_COPY);
19630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        assert(data != null);
19730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
19830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        mClipboard.setPrimaryClip(data);
19930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
20030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
20130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    @Override
20230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    public void clipDocumentsForCut(
20330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            Function<String, Uri> uriBuilder, Selection selection, DocumentInfo parent) {
20430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        assert(!selection.isEmpty());
20530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        assert(parent.derivedUri != null);
20630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
20730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        ClipData data = getClipDataForDocuments(uriBuilder, selection,
20830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                FileOperationService.OPERATION_MOVE);
20930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        assert(data != null);
21030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
21130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        PersistableBundle bundle = data.getDescription().getExtras();
21230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        bundle.putString(SRC_PARENT_KEY, parent.derivedUri.toString());
21330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
21430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        mClipboard.setPrimaryClip(data);
21530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
21630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
21730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
21830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    @Override
21930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    public void copyFromClipboard(
22030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            DocumentInfo destination,
22130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            DocumentStack docStack,
22230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            FileOperations.Callback callback) {
22330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
22430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        copyFromClipData(destination, docStack, mClipboard.getPrimaryClip(), callback);
22530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
22630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
22730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    @Override
22830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    public void copyFromClipboard(
22930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            DocumentStack docStack,
23030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            FileOperations.Callback callback) {
23130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
23230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        copyFromClipData(docStack, mClipboard.getPrimaryClip(), callback);
23330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
23430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
23530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    @Override
23630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    public void copyFromClipData(
237da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan            DocumentInfo destination,
238da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan            DocumentStack docStack,
239da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan            @Nullable ClipData clipData,
240da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan            FileOperations.Callback callback) {
241da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan
242da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan        DocumentStack dstStack = new DocumentStack(docStack, destination);
24330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        copyFromClipData(dstStack, clipData, callback);
24430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
24530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
24630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    @Override
24730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    public void copyFromClipData(
248da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan            DocumentStack dstStack,
249da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan            ClipData clipData,
250da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan            @OpType int opType,
251da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan            FileOperations.Callback callback) {
25230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
253da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan        clipData.getDescription().getExtras().putInt(OP_TYPE_KEY, opType);
25430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        copyFromClipData(dstStack, clipData, callback);
25530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
25630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
25785a479e941a733de22d4309bf7802e4cc6814786Garfield Tan    @Override
25885a479e941a733de22d4309bf7802e4cc6814786Garfield Tan    public void copyFromClipData(
259da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan            DocumentStack dstStack,
260da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan            @Nullable ClipData clipData,
261da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan            FileOperations.Callback callback) {
26230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
26330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        if (clipData == null) {
26430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            Log.i(TAG, "Received null clipData. Ignoring.");
26530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            return;
26630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        }
26730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
26830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        PersistableBundle bundle = clipData.getDescription().getExtras();
26930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        @OpType int opType = getOpType(bundle);
27030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        try {
27130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            if (!canCopy(dstStack.peek())) {
27230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                callback.onOperationResult(
27330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                        FileOperations.Callback.STATUS_REJECTED, getOpType(clipData), 0);
27430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                return;
27530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            }
27630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
27730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            UrisSupplier uris = UrisSupplier.create(clipData, mClipStore);
27830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            if (uris.getItemCount() == 0) {
27930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                callback.onOperationResult(
28030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                        FileOperations.Callback.STATUS_ACCEPTED, opType, 0);
28130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                return;
28230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            }
28330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
28430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            String srcParentString = bundle.getString(SRC_PARENT_KEY);
28530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            Uri srcParent = srcParentString == null ? null : Uri.parse(srcParentString);
28630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
28730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            FileOperation operation = new FileOperation.Builder()
28830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                    .withOpType(opType)
28930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                    .withSrcParent(srcParent)
29030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                    .withDestination(dstStack)
29130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                    .withSrcs(uris)
29230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                    .build();
29330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
2940d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski            FileOperations.start(mContext, operation, callback, FileOperations.createJobId());
2950d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski        } catch (IOException e) {
29630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            Log.e(TAG, "Cannot create uris supplier.", e);
29730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            callback.onOperationResult(FileOperations.Callback.STATUS_REJECTED, opType, 0);
29830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            return;
29930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        }
30030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
30130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
30230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    /**
30330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     * Returns true if the list of files can be copied to destination. Note that this
30430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     * is a policy check only. Currently the method does not attempt to verify
30530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     * available space or any other environmental aspects possibly resulting in
30630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     * failure to copy.
30730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     *
30830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     * @return true if the list of files can be copied to destination.
30930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     */
31030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    private static boolean canCopy(@Nullable DocumentInfo dest) {
31130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        return dest != null && dest.isDirectory() && dest.isCreateSupported();
31230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
31330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
314da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan    private @OpType int getOpType(ClipData data) {
31530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        PersistableBundle bundle = data.getDescription().getExtras();
31630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        return getOpType(bundle);
31730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
31830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
31930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    private @OpType int getOpType(PersistableBundle bundle) {
32030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        return bundle.getInt(OP_TYPE_KEY);
32130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
32230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
32330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    private static ClipData createClipData(
32430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            ClipDescription description, ArrayList<ClipData.Item> clipItems) {
32530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
32630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        // technically we want to check >= O, but we'd need to patch back the O version code :|
32730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        if (Features.OMC_RUNTIME) {
32830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            return new ClipData(description, clipItems);
32930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        }
33030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
33130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        ClipData clip = new ClipData(description, clipItems.get(0));
33230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        for (int i = 1; i < clipItems.size(); i++) {
33330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            clip.addItem(clipItems.get(i));
33430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        }
33530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        return clip;
33630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
33730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin}
338