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