Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9666ce69 authored by Garfield, Tan's avatar Garfield, Tan
Browse files

Pull clipping related classes to a separate package.

Change-Id: I125d1c34e475f9465e82014cb869f88c9c7cebc4
parent 74956af5
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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;

+1 −0
Original line number Diff line number Diff line
@@ -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;
+8 −100
Original line number Diff line number Diff line
@@ -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;
+125 −0
Original line number Diff line number Diff line
/*
 * 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;
        }
    }
}
+4 −3
Original line number Diff line number Diff line
/*
 * 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);
Loading