RuntimeDocumentClipper.java revision 85a479e941a733de22d4309bf7802e4cc6814786
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";
5530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    private static final String SRC_PARENT_KEY = "srcParent";
5630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    private static final String OP_TYPE_KEY = "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
10230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        return (selection.size() > Shared.MAX_DOCS_IN_INTENT)
10330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                ? createJumboClipData(uriBuilder, selection, opType)
10430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                : createStandardClipData(uriBuilder, selection, opType);
10530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
10630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
10730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    /**
10830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     * Returns ClipData representing the selection.
10930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     */
11030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    private ClipData createStandardClipData(
11130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            Function<String, Uri> uriBuilder, Selection selection, @OpType int opType) {
11230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
11330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        assert(!selection.isEmpty());
11430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        assert(selection.size() <= Shared.MAX_DOCS_IN_INTENT);
11530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
11630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        final ContentResolver resolver = mContext.getContentResolver();
11730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        final ArrayList<ClipData.Item> clipItems = new ArrayList<>();
11830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        final Set<String> clipTypes = new HashSet<>();
11930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
12030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        PersistableBundle bundle = new PersistableBundle();
12130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        bundle.putInt(OP_TYPE_KEY, opType);
12230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
12330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        for (String id : selection) {
12430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            assert(id != null);
12530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            Uri uri = uriBuilder.apply(id);
12630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            DocumentInfo.addMimeTypes(resolver, uri, clipTypes);
12730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            clipItems.add(new ClipData.Item(uri));
12830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        }
12930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
13030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        ClipDescription description = new ClipDescription(
13130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                "", // Currently "label" is not displayed anywhere in the UI.
13230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                clipTypes.toArray(new String[0]));
13330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        description.setExtras(bundle);
13430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
13530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        return createClipData(description, clipItems);
13630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
13730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
13830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    /**
13930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     * Returns ClipData representing the list of docs
14030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     */
14130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    private ClipData createJumboClipData(
14230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            Function<String, Uri> uriBuilder, Selection selection, @OpType int opType) {
14330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
14430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        assert(!selection.isEmpty());
14530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        assert(selection.size() > Shared.MAX_DOCS_IN_INTENT);
14630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
14730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        final List<Uri> uris = new ArrayList<>(selection.size());
14830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
14930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        final int capacity = Math.min(selection.size(), Shared.MAX_DOCS_IN_INTENT);
15030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        final ArrayList<ClipData.Item> clipItems = new ArrayList<>(capacity);
15130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
15230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        // Set up mime types for the first Shared.MAX_DOCS_IN_INTENT
15330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        final ContentResolver resolver = mContext.getContentResolver();
15430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        final Set<String> clipTypes = new HashSet<>();
15530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        int docCount = 0;
15630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        for (String id : selection) {
15730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            assert(id != null);
15830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            Uri uri = uriBuilder.apply(id);
15930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            if (docCount++ < Shared.MAX_DOCS_IN_INTENT) {
16030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                DocumentInfo.addMimeTypes(resolver, uri, clipTypes);
16130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                clipItems.add(new ClipData.Item(uri));
16230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            }
16330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
16430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            uris.add(uri);
16530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        }
16630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
16730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        // Prepare metadata
16830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        PersistableBundle bundle = new PersistableBundle();
16930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        bundle.putInt(OP_TYPE_KEY, opType);
17030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        bundle.putInt(OP_JUMBO_SELECTION_SIZE, selection.size());
17130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
17230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        // Persists clip items and gets the slot they were saved under.
17330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        int tag = mClipStore.persistUris(uris);
17430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        bundle.putInt(OP_JUMBO_SELECTION_TAG, tag);
17530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
17630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        ClipDescription description = new ClipDescription(
17730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                "", // Currently "label" is not displayed anywhere in the UI.
17830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                clipTypes.toArray(new String[0]));
17930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        description.setExtras(bundle);
18030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
18130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        return createClipData(description, clipItems);
18230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
18330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
18430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    @Override
18530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    public void clipDocumentsForCopy(Function<String, Uri> uriBuilder, Selection selection) {
18630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        ClipData data =
18730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                getClipDataForDocuments(uriBuilder, selection, FileOperationService.OPERATION_COPY);
18830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        assert(data != null);
18930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
19030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        mClipboard.setPrimaryClip(data);
19130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
19230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
19330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    @Override
19430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    public void clipDocumentsForCut(
19530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            Function<String, Uri> uriBuilder, Selection selection, DocumentInfo parent) {
19630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        assert(!selection.isEmpty());
19730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        assert(parent.derivedUri != null);
19830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
19930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        ClipData data = getClipDataForDocuments(uriBuilder, selection,
20030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                FileOperationService.OPERATION_MOVE);
20130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        assert(data != null);
20230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
20330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        PersistableBundle bundle = data.getDescription().getExtras();
20430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        bundle.putString(SRC_PARENT_KEY, parent.derivedUri.toString());
20530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
20630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        mClipboard.setPrimaryClip(data);
20730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
20830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
20930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
21030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    @Override
21130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    public void copyFromClipboard(
21230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            DocumentInfo destination,
21330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            DocumentStack docStack,
21430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            FileOperations.Callback callback) {
21530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
21630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        copyFromClipData(destination, docStack, mClipboard.getPrimaryClip(), callback);
21730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
21830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
21930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    @Override
22030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    public void copyFromClipboard(
22130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            DocumentStack docStack,
22230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            FileOperations.Callback callback) {
22330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
22430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        copyFromClipData(docStack, mClipboard.getPrimaryClip(), callback);
22530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
22630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
22730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    @Override
22830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    public void copyFromClipData(
22930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            final RootInfo root,
23085a479e941a733de22d4309bf7802e4cc6814786Garfield Tan            final DocumentInfo destination,
23130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            final @Nullable ClipData clipData,
23230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            final FileOperations.Callback callback) {
23330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        DocumentStack dstStack = new DocumentStack(root, destination);
23430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        copyFromClipData(dstStack, clipData, callback);
23530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
23630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
23730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    @Override
23830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    public void copyFromClipData(
23985a479e941a733de22d4309bf7802e4cc6814786Garfield Tan            final DocumentInfo destination,
24030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            final DocumentStack docStack,
24130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            final @Nullable ClipData clipData,
24230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            final FileOperations.Callback callback) {
24330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
24430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        DocumentStack dstStack = new DocumentStack(docStack, destination);
24530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        copyFromClipData(dstStack, clipData, callback);
24630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
24730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
24885a479e941a733de22d4309bf7802e4cc6814786Garfield Tan    @Override
24985a479e941a733de22d4309bf7802e4cc6814786Garfield Tan    public void copyFromClipData(
25030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            final DocumentStack dstStack,
25130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            final @Nullable ClipData clipData,
25230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            final FileOperations.Callback callback) {
25330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
25430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        if (clipData == null) {
25530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            Log.i(TAG, "Received null clipData. Ignoring.");
25630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            return;
25730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        }
25830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
25930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        PersistableBundle bundle = clipData.getDescription().getExtras();
26030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        @OpType int opType = getOpType(bundle);
26130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        try {
26230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            if (!canCopy(dstStack.peek())) {
26330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                callback.onOperationResult(
26430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                        FileOperations.Callback.STATUS_REJECTED, getOpType(clipData), 0);
26530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                return;
26630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            }
26730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
26830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            UrisSupplier uris = UrisSupplier.create(clipData, mClipStore);
26930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            if (uris.getItemCount() == 0) {
27030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                callback.onOperationResult(
27130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                        FileOperations.Callback.STATUS_ACCEPTED, opType, 0);
27230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                return;
27330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            }
27430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
27530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            String srcParentString = bundle.getString(SRC_PARENT_KEY);
27630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            Uri srcParent = srcParentString == null ? null : Uri.parse(srcParentString);
27730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
27830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            FileOperation operation = new FileOperation.Builder()
27930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                    .withOpType(opType)
28030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                    .withSrcParent(srcParent)
28130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                    .withDestination(dstStack)
28230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                    .withSrcs(uris)
28330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                    .build();
28430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
28530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            FileOperations.start(mContext, operation, callback);
28630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        } catch(IOException e) {
28730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            Log.e(TAG, "Cannot create uris supplier.", e);
28830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            callback.onOperationResult(FileOperations.Callback.STATUS_REJECTED, opType, 0);
28930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            return;
29030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        }
29130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
29230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
29330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    /**
29430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     * Returns true if the list of files can be copied to destination. Note that this
29530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     * is a policy check only. Currently the method does not attempt to verify
29630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     * available space or any other environmental aspects possibly resulting in
29730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     * failure to copy.
29830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     *
29930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     * @return true if the list of files can be copied to destination.
30030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin     */
30130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    private static boolean canCopy(@Nullable DocumentInfo dest) {
30230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        return dest != null && dest.isDirectory() && dest.isCreateSupported();
30330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
30430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
30530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    @Override
30630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    public @OpType int getOpType(ClipData data) {
30730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        PersistableBundle bundle = data.getDescription().getExtras();
30830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        return getOpType(bundle);
30930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
31030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
31130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    private @OpType int getOpType(PersistableBundle bundle) {
31230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        return bundle.getInt(OP_TYPE_KEY);
31330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
31430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
31530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    private static ClipData createClipData(
31630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            ClipDescription description, ArrayList<ClipData.Item> clipItems) {
31730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
31830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        // technically we want to check >= O, but we'd need to patch back the O version code :|
31930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        if (Features.OMC_RUNTIME) {
32030b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            return new ClipData(description, clipItems);
32130b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        }
32230b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin
32330b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        ClipData clip = new ClipData(description, clipItems.get(0));
32430b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        for (int i = 1; i < clipItems.size(); i++) {
32530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin            clip.addItem(clipItems.get(i));
32630b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        }
32730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        return clip;
32830b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin    }
32930b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin}
330