Pull clipping related classes to a separate package.

Change-Id: I125d1c34e475f9465e82014cb869f88c9c7cebc4
This commit is contained in:
Garfield, Tan
2016-07-12 11:02:09 -07:00
committed by Garfield Tan
parent 3b457c74d9
commit 415cd8b846
18 changed files with 168 additions and 128 deletions

View File

@@ -28,6 +28,9 @@ import android.net.Uri;
import android.os.RemoteException;
import android.text.format.DateUtils;
import com.android.documentsui.clipping.ClipStorage;
import com.android.documentsui.clipping.DocumentClipper;
public class DocumentsApplication extends Application {
private static final long PROVIDER_ANR_TIMEOUT = 20 * DateUtils.SECOND_IN_MILLIS;

View File

@@ -39,6 +39,7 @@ import android.view.MenuItem;
import com.android.documentsui.MenuManager.DirectoryDetails;
import com.android.documentsui.OperationDialogFragment.DialogType;
import com.android.documentsui.RecentsProvider.ResumeColumns;
import com.android.documentsui.clipping.DocumentClipper;
import com.android.documentsui.dirlist.AnimationView;
import com.android.documentsui.dirlist.DirectoryFragment;
import com.android.documentsui.dirlist.FragmentTuner;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.documentsui;
package com.android.documentsui.clipping;
import android.content.SharedPreferences;
import android.net.Uri;
@@ -24,15 +24,13 @@ import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;
import com.android.documentsui.Files;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileLock;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;
/**
@@ -45,7 +43,7 @@ public final class ClipStorage {
public static final int NO_SELECTION_TAG = -1;
static final String PREF_NAME = "ClipStoragePref";
public static final String PREF_NAME = "ClipStoragePref";
@VisibleForTesting
static final int NUM_OF_SLOTS = 20;
@@ -90,7 +88,7 @@ public final class ClipStorage {
* file may be overwritten.</li>
* </ul>
*/
public synchronized int claimStorageSlot() {
synchronized int claimStorageSlot() {
int curPos = mNextPos;
for (int i = 0; i < NUM_OF_SLOTS; ++i, curPos = (curPos + 1) % NUM_OF_SLOTS) {
createSlotFile(curPos);
@@ -167,9 +165,9 @@ public final class ClipStorage {
/**
* Returns a Reader. Callers must close the reader when finished.
*/
public Reader createReader(File file) throws IOException {
ClipStorageReader createReader(File file) throws IOException {
assert(file.getParentFile().getParentFile().equals(mOutDir));
return new Reader(file);
return new ClipStorageReader(file);
}
private File toSlotDataFile(int pos) {
@@ -186,7 +184,7 @@ public final class ClipStorage {
/**
* Provides initialization of the clip data storage directory.
*/
static File prepareStorage(File cacheDir) {
public static File prepareStorage(File cacheDir) {
File clipDir = getClipDir(cacheDir);
clipDir.mkdir();
@@ -198,96 +196,6 @@ public final class ClipStorage {
return new File(cacheDir, "clippings");
}
static final class Reader implements Iterable<Uri>, Closeable {
/**
* FileLock can't be held multiple times in a single JVM, but it's possible to have multiple
* readers reading the same clip file. Share the FileLock here so that it can be released
* when it's not needed.
*/
private static final Map<String, FileLockEntry> sLocks = new HashMap<>();
private final String mCanonicalPath;
private final Scanner mScanner;
private Reader(File file) throws IOException {
FileInputStream inStream = new FileInputStream(file);
mScanner = new Scanner(inStream);
mCanonicalPath = file.getCanonicalPath(); // Resolve symlink
synchronized (sLocks) {
if (sLocks.containsKey(mCanonicalPath)) {
// Read lock is already held by someone in this JVM, just increment the ref
// count.
sLocks.get(mCanonicalPath).mCount++;
} else {
// No map entry, need to lock the file so it won't pass this line until the
// corresponding writer is done writing.
FileLock lock = inStream.getChannel().lock(0L, Long.MAX_VALUE, true);
sLocks.put(mCanonicalPath, new FileLockEntry(1, lock, mScanner));
}
}
}
@Override
public Iterator iterator() {
return new Iterator(mScanner);
}
@Override
public void close() throws IOException {
synchronized (sLocks) {
FileLockEntry ref = sLocks.get(mCanonicalPath);
assert(ref.mCount > 0);
if (--ref.mCount == 0) {
// If ref count is 0 now, then there is no one who needs to hold the read lock.
// Release the lock, and remove the entry.
ref.mLock.release();
ref.mScanner.close();
sLocks.remove(mCanonicalPath);
}
if (mScanner != ref.mScanner) {
mScanner.close();
}
}
}
}
private static final class Iterator implements java.util.Iterator {
private final Scanner mScanner;
private Iterator(Scanner scanner) {
mScanner = scanner;
}
@Override
public boolean hasNext() {
return mScanner.hasNextLine();
}
@Override
public Uri next() {
String line = mScanner.nextLine();
return Uri.parse(line);
}
}
private static final class FileLockEntry {
private int mCount;
private FileLock mLock;
// We need to keep this scanner here because if the scanner is closed, the file lock is
// closed too.
private Scanner mScanner;
private FileLockEntry(int count, FileLock lock, Scanner scanner) {
mCount = count;
mLock = lock;
mScanner = scanner;
}
}
private static final class Writer implements Closeable {
private final FileOutputStream mOut;

View File

@@ -0,0 +1,125 @@
/*
* 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.clipping;
import android.net.Uri;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.channels.FileLock;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/**
* Reader class used to read uris from clip files stored in {@link ClipStorage}. It provides
* synchronization within a single process as an addition to {@link FileLock} which is for
* cross-process synchronization.
*/
class ClipStorageReader implements Iterable<Uri>, Closeable {
/**
* FileLock can't be held multiple times in a single JVM, but it's possible to have multiple
* readers reading the same clip file. Share the FileLock here so that it can be released
* when it's not needed.
*/
private static final Map<String, FileLockEntry> sLocks = new HashMap<>();
private final String mCanonicalPath;
private final Scanner mScanner;
ClipStorageReader(File file) throws IOException {
FileInputStream inStream = new FileInputStream(file);
mScanner = new Scanner(inStream);
mCanonicalPath = file.getCanonicalPath(); // Resolve symlink
synchronized (sLocks) {
if (sLocks.containsKey(mCanonicalPath)) {
// Read lock is already held by someone in this JVM, just increment the ref
// count.
sLocks.get(mCanonicalPath).mCount++;
} else {
// No map entry, need to lock the file so it won't pass this line until the
// corresponding writer is done writing.
FileLock lock = inStream.getChannel().lock(0L, Long.MAX_VALUE, true);
sLocks.put(mCanonicalPath, new FileLockEntry(1, lock, mScanner));
}
}
}
@Override
public Iterator iterator() {
return new Iterator(mScanner);
}
@Override
public void close() throws IOException {
FileLockEntry ref;
synchronized (sLocks) {
ref = sLocks.get(mCanonicalPath);
assert(ref.mCount > 0);
if (--ref.mCount == 0) {
// If ref count is 0 now, then there is no one who needs to hold the read lock.
// Release the lock, and remove the entry.
ref.mLock.release();
ref.mScanner.close();
sLocks.remove(mCanonicalPath);
}
}
if (mScanner != ref.mScanner) {
mScanner.close();
}
}
private static final class Iterator implements java.util.Iterator {
private final Scanner mScanner;
private Iterator(Scanner scanner) {
mScanner = scanner;
}
@Override
public boolean hasNext() {
return mScanner.hasNextLine();
}
@Override
public Uri next() {
String line = mScanner.nextLine();
return Uri.parse(line);
}
}
private static final class FileLockEntry {
private final FileLock mLock;
// We need to keep this scanner here because if the scanner is closed, the file lock is
// closed too.
private final Scanner mScanner;
private int mCount;
private FileLockEntry(int count, FileLock lock, Scanner scanner) {
mCount = count;
mLock = lock;
mScanner = scanner;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015 The Android Open Source Project
* 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.documentsui;
package com.android.documentsui.clipping;
import android.content.ClipData;
import android.content.ClipDescription;
@@ -27,6 +27,7 @@ import android.provider.DocumentsContract;
import android.support.annotation.Nullable;
import android.util.Log;
import com.android.documentsui.Shared;
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
@@ -59,7 +60,7 @@ public final class DocumentClipper {
private final ClipStorage mClipStorage;
private final ClipboardManager mClipboard;
DocumentClipper(Context context, ClipStorage storage) {
public DocumentClipper(Context context, ClipStorage storage) {
mContext = context;
mClipStorage = storage;
mClipboard = context.getSystemService(ClipboardManager.class);

View File

@@ -14,10 +14,10 @@
* limitations under the License.
*/
package com.android.documentsui;
package com.android.documentsui.clipping;
import static com.android.documentsui.DocumentClipper.OP_JUMBO_SELECTION_SIZE;
import static com.android.documentsui.DocumentClipper.OP_JUMBO_SELECTION_TAG;
import static com.android.documentsui.clipping.DocumentClipper.OP_JUMBO_SELECTION_SIZE;
import static com.android.documentsui.clipping.DocumentClipper.OP_JUMBO_SELECTION_TAG;
import android.content.ClipData;
import android.content.Context;
@@ -28,6 +28,8 @@ import android.os.PersistableBundle;
import android.support.annotation.VisibleForTesting;
import android.util.Log;
import com.android.documentsui.DocumentsApplication;
import com.android.documentsui.Shared;
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
import com.android.documentsui.services.FileOperation;
@@ -110,7 +112,7 @@ public abstract class UrisSupplier implements Parcelable {
private final File mFile;
private final int mSelectionSize;
private final transient AtomicReference<ClipStorage.Reader> mReader =
private final transient AtomicReference<ClipStorageReader> mReader =
new AtomicReference<>();
private JumboUrisSupplier(ClipData clipData, Context context) throws IOException {
@@ -143,7 +145,7 @@ public abstract class UrisSupplier implements Parcelable {
@Override
Iterable<Uri> getUris(ClipStorage storage) throws IOException {
ClipStorage.Reader reader = mReader.getAndSet(storage.createReader(mFile));
ClipStorageReader reader = mReader.getAndSet(storage.createReader(mFile));
if (reader != null) {
reader.close();
mReader.get().close();
@@ -156,7 +158,7 @@ public abstract class UrisSupplier implements Parcelable {
@Override
public void dispose() {
try {
ClipStorage.Reader reader = mReader.get();
ClipStorageReader reader = mReader.get();
if (reader != null) {
reader.close();
}

View File

@@ -73,7 +73,7 @@ import android.widget.Toolbar;
import com.android.documentsui.BaseActivity;
import com.android.documentsui.DirectoryLoader;
import com.android.documentsui.DirectoryResult;
import com.android.documentsui.DocumentClipper;
import com.android.documentsui.clipping.DocumentClipper;
import com.android.documentsui.DocumentsActivity;
import com.android.documentsui.DocumentsApplication;
import com.android.documentsui.Events.InputEvent;
@@ -92,7 +92,7 @@ import com.android.documentsui.Shared;
import com.android.documentsui.Snackbars;
import com.android.documentsui.State;
import com.android.documentsui.State.ViewMode;
import com.android.documentsui.UrisSupplier;
import com.android.documentsui.clipping.UrisSupplier;
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
import com.android.documentsui.dirlist.UserInputHandler.DocumentDetails;
import com.android.documentsui.model.DocumentInfo;

View File

@@ -55,7 +55,7 @@ import com.android.documentsui.DocumentsApplication;
import com.android.documentsui.Metrics;
import com.android.documentsui.R;
import com.android.documentsui.RootsCache;
import com.android.documentsui.UrisSupplier;
import com.android.documentsui.clipping.UrisSupplier;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.RootInfo;

View File

@@ -26,7 +26,7 @@ import android.content.Context;
import android.net.Uri;
import android.util.Log;
import com.android.documentsui.UrisSupplier;
import com.android.documentsui.clipping.UrisSupplier;
import com.android.documentsui.Metrics;
import com.android.documentsui.R;
import com.android.documentsui.model.DocumentInfo;

View File

@@ -27,7 +27,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.VisibleForTesting;
import com.android.documentsui.UrisSupplier;
import com.android.documentsui.clipping.UrisSupplier;
import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.services.FileOperationService.OpType;

View File

@@ -40,7 +40,7 @@ import android.os.RemoteException;
import android.provider.DocumentsContract;
import android.util.Log;
import com.android.documentsui.UrisSupplier;
import com.android.documentsui.clipping.UrisSupplier;
import com.android.documentsui.FilesActivity;
import com.android.documentsui.Metrics;
import com.android.documentsui.OperationDialogFragment;

View File

@@ -30,7 +30,7 @@ import android.provider.DocumentsContract.Document;
import android.util.Log;
import com.android.documentsui.R;
import com.android.documentsui.UrisSupplier;
import com.android.documentsui.clipping.UrisSupplier;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;

View File

@@ -14,9 +14,9 @@
* limitations under the License.
*/
package com.android.documentsui;
package com.android.documentsui.clipping;
import static com.android.documentsui.ClipStorage.NUM_OF_SLOTS;
import static com.android.documentsui.clipping.ClipStorage.NUM_OF_SLOTS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -29,7 +29,6 @@ import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import com.android.documentsui.ClipStorage.Reader;
import com.android.documentsui.testing.TestScheduledExecutorService;
import org.junit.AfterClass;
@@ -89,7 +88,7 @@ public class ClipStorageTest {
List<Uri> uris = new ArrayList<>();
File copy = mStorage.getFile(mTag);
try(Reader provider = mStorage.createReader(copy)) {
try(ClipStorageReader provider = mStorage.createReader(copy)) {
for (Uri uri : provider) {
uris.add(uri);
}
@@ -98,7 +97,7 @@ public class ClipStorageTest {
}
@Test
public void testGetTag_NoAvailableSlot() throws Exception {
public void testClaimStorageSlot_NoAvailableSlot() throws Exception {
int firstTag = mStorage.claimStorageSlot();
writeAll(firstTag, TEST_URIS);
mStorage.getFile(firstTag);
@@ -119,8 +118,8 @@ public class ClipStorageTest {
File copy = mStorage.getFile(mTag);
File copy2 = mStorage.getFile(mTag);
try(Reader reader = mStorage.createReader(copy)) {
try(Reader reader2 = mStorage.createReader(copy2)){
try(ClipStorageReader reader = mStorage.createReader(copy)) {
try(ClipStorageReader reader2 = mStorage.createReader(copy2)){
Iterator<Uri> iter = reader.iterator();
Iterator<Uri> iter2 = reader2.iterator();

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.documentsui;
package com.android.documentsui.clipping;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -27,6 +27,7 @@ import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
import com.android.documentsui.Shared;
import com.android.documentsui.testing.TestScheduledExecutorService;
import org.junit.AfterClass;

View File

@@ -27,7 +27,7 @@ import android.os.RemoteException;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
import com.android.documentsui.UrisSupplier;
import com.android.documentsui.clipping.UrisSupplier;
import com.android.documentsui.DocumentsProviderHelper;
import com.android.documentsui.StubProvider;
import com.android.documentsui.model.DocumentInfo;

View File

@@ -32,7 +32,7 @@ import android.os.Parcelable;
import android.test.ServiceTestCase;
import android.test.suitebuilder.annotation.MediumTest;
import com.android.documentsui.UrisSupplier;
import com.android.documentsui.clipping.UrisSupplier;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.testing.DocsProviders;

View File

@@ -23,7 +23,7 @@ import android.app.Notification;
import android.app.Notification.Builder;
import android.content.Context;
import com.android.documentsui.UrisSupplier;
import com.android.documentsui.clipping.UrisSupplier;
import com.android.documentsui.R;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;

View File

@@ -18,7 +18,7 @@ package com.android.documentsui.testing;
import android.net.Uri;
import com.android.documentsui.UrisSupplier;
import com.android.documentsui.clipping.UrisSupplier;
import java.util.List;