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

Commit c8889af6 authored by Steve McKay's avatar Steve McKay
Browse files

Move delete support into base ActionHandler.

Introduce an "Addons" interface that describes all of the
    extra stuff we expose in our concrete Activities allowing
    us to write test doubles for our Activities.
Add new "ui" package for UI specific stuff like:
Introduce DialogController, isolating dialog business in
    a separate class (that can be easily replaced with a double)
    Move delete confirmation and snackbar-error reporting
    into this class.
Introduce a Messages class for building messages, but isolated
    from implementation details.
Add test for delete logic in ActionHandler (now that the
    code is unit-testable.)
Introduce new ClipStore interface to improve testability
    of code that depends on clip storage...but in this
    CL we don't yet cover any of that.

Decouple all ActionHandler impls from their
Change-Id: Ic1449e501c855cdb72bf7666f4b67b9a9e9c1b49
parent e7e1b68b
Loading
Loading
Loading
Loading
+46 −21
Original line number Diff line number Diff line
@@ -22,30 +22,34 @@ import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.os.Parcelable;

import com.android.documentsui.AbstractActionHandler.CommonAddons;
import com.android.documentsui.base.BooleanConsumer;
import com.android.documentsui.base.ConfirmationCallback;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.DocumentStack;
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.base.Shared;
import com.android.documentsui.dirlist.DocumentDetails;
import com.android.documentsui.dirlist.Model;
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
import com.android.documentsui.manager.LauncherActivity;
import com.android.documentsui.sidebar.EjectRootTask;

import java.util.List;

/**
 * Provides support for specializing the actions (viewDocument etc.) to the host activity.
 */
public abstract class AbstractActionHandler<T extends Activity> implements ActionHandler {
public abstract class AbstractActionHandler<T extends Activity & CommonAddons>
        implements ActionHandler {

    protected final T mActivity;

    public AbstractActionHandler(T activity) {
        assert(activity != null);
        mActivity = activity;
    }

    @Override
    public void openSettings(RootInfo root) {
        throw new UnsupportedOperationException("Can't open settings.");
    }

    @Override
    public void ejectRoot(RootInfo root, BooleanConsumer listener) {
        new EjectRootTask(
@@ -55,21 +59,6 @@ public abstract class AbstractActionHandler<T extends Activity> implements Actio
                listener).executeOnExecutor(ProviderExecutor.forAuthority(root.authority));
    }

    @Override
    public void openRoot(ResolveInfo app) {
        throw new UnsupportedOperationException("Can't open an app.");
    }

    @Override
    public void showAppDetails(ResolveInfo info) {
        throw new UnsupportedOperationException("Can't show app details.");
    }

    @Override
    public boolean dropOn(ClipData data, RootInfo root) {
        throw new UnsupportedOperationException("Can't open an app.");
    }

    @Override
    public void openInNewWindow(DocumentStack path) {
        Metrics.logUserAction(mActivity, Metrics.USER_ACTION_NEW_WINDOW);
@@ -87,6 +76,26 @@ public abstract class AbstractActionHandler<T extends Activity> implements Actio
        mActivity.startActivity(intent);
    }

    @Override
    public void openSettings(RootInfo root) {
        throw new UnsupportedOperationException("Can't open settings.");
    }

    @Override
    public void openRoot(ResolveInfo app) {
        throw new UnsupportedOperationException("Can't open an app.");
    }

    @Override
    public void showAppDetails(ResolveInfo info) {
        throw new UnsupportedOperationException("Can't show app details.");
    }

    @Override
    public boolean dropOn(ClipData data, RootInfo root) {
        throw new UnsupportedOperationException("Can't open an app.");
    }

    @Override
    public void pasteIntoFolder(RootInfo root) {
        throw new UnsupportedOperationException("Can't paste into folder.");
@@ -101,4 +110,20 @@ public abstract class AbstractActionHandler<T extends Activity> implements Actio
    public boolean previewDocument(DocumentDetails doc) {
        throw new UnsupportedOperationException("Preview not supported!");
    }

    @Override
    public void deleteDocuments(Model model, Selection selection, ConfirmationCallback callback) {
        throw new UnsupportedOperationException("Delete not supported!");
    }

    /**
     * A class primarily for the support of isolating our tests
     * from our concrete activity implementations.
     */
    public interface CommonAddons {
       void onRootPicked(RootInfo root);
       void onDocumentPicked(DocumentInfo doc, Model model);
       // TODO: Move this to PickAddons.
       void onDocumentsPicked(List<DocumentInfo> docs);
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -20,9 +20,12 @@ import android.content.ClipData;
import android.content.pm.ResolveInfo;

import com.android.documentsui.base.BooleanConsumer;
import com.android.documentsui.base.ConfirmationCallback;
import com.android.documentsui.base.DocumentStack;
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.dirlist.DocumentDetails;
import com.android.documentsui.dirlist.Model;
import com.android.documentsui.dirlist.MultiSelectManager.Selection;

public interface ActionHandler {

@@ -54,4 +57,6 @@ public interface ActionHandler {
    boolean previewDocument(DocumentDetails doc);

    boolean openDocument(DocumentDetails doc);

    void deleteDocuments(Model model, Selection selection, ConfirmationCallback callback);
}
+17 −38
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

import com.android.documentsui.AbstractActionHandler.CommonAddons;
import com.android.documentsui.NavigationViewManager.Breadcrumb;
import com.android.documentsui.SearchViewManager.SearchManagerListener;
import com.android.documentsui.base.DocumentInfo;
@@ -70,11 +71,10 @@ import com.android.documentsui.dirlist.MultiSelectManager;
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
import com.android.documentsui.roots.LoadRootTask;
import com.android.documentsui.roots.RootsCache;
import com.android.documentsui.services.FileOperationService;
import com.android.documentsui.services.FileOperations;
import com.android.documentsui.sidebar.RootsFragment;
import com.android.documentsui.sorting.SortController;
import com.android.documentsui.sorting.SortModel;
import com.android.documentsui.ui.DialogController;

import java.util.ArrayList;
import java.util.Collection;
@@ -82,33 +82,8 @@ import java.util.Date;
import java.util.List;
import java.util.concurrent.Executor;

public abstract class BaseActivity extends Activity implements NavigationViewManager.Environment {

    public final FileOperations.Callback fileOpCallback = (status, opType, docCount) -> {
        if (status == FileOperations.Callback.STATUS_REJECTED) {
            Snackbars.showPasteFailed(this);
            return;
        }

        if (docCount == 0) {
            // Nothing has been pasted, so there is no need to show a snackbar.
            return;
        }

        switch (opType) {
            case FileOperationService.OPERATION_MOVE:
                Snackbars.showMove(this, docCount);
                break;
            case FileOperationService.OPERATION_COPY:
                Snackbars.showCopy(this, docCount);
                break;
            case FileOperationService.OPERATION_DELETE:
                // We don't show anything for deletion.
                break;
            default:
                throw new UnsupportedOperationException("Unsupported Operation: " + opType);
        }
    };
public abstract class BaseActivity
        extends Activity implements CommonAddons, NavigationViewManager.Environment {

    private static final String BENCHMARK_TESTING_PACKAGE = "com.android.documentsui.appperftests";

@@ -136,6 +111,12 @@ public abstract class BaseActivity extends Activity implements NavigationViewMan
    private boolean mNavDrawerHasFocus;
    private long mStartTime;

    protected abstract void onTaskFinished(Uri... uris);
    protected abstract void refreshDirectory(int anim);
    /** Allows sub-classes to include information in a newly created State instance. */
    protected abstract void includeState(State initialState);
    protected abstract void onDirectoryCreated(DocumentInfo doc);

    /**
     * Provides Activity a means of injection into and specialization of
     * DirectoryFragment.
@@ -154,6 +135,12 @@ public abstract class BaseActivity extends Activity implements NavigationViewMan
     */
    public abstract MenuManager getMenuManager();

    /**
     * Provides Activity a means of injection into and specialization of
     * DirectoryFragment.
     */
    public abstract DialogController getDialogController();

    /**
     * Provides Activity a means of injection into and specialization of
     * fragment actions.
@@ -163,15 +150,6 @@ public abstract class BaseActivity extends Activity implements NavigationViewMan
    public abstract ActionHandler getActionHandler(
            @Nullable Model model, @Nullable MultiSelectManager selectionMgr);

    public abstract void onDocumentPicked(DocumentInfo doc, Model model);
    public abstract void onDocumentsPicked(List<DocumentInfo> docs);

    protected abstract void onTaskFinished(Uri... uris);
    protected abstract void refreshDirectory(int anim);
    /** Allows sub-classes to include information in a newly created State instance. */
    protected abstract void includeState(State initialState);
    protected abstract void onDirectoryCreated(DocumentInfo doc);

    public BaseActivity(@LayoutRes int layoutId, String tag) {
        mLayoutId = layoutId;
        mTag = tag;
@@ -313,6 +291,7 @@ public abstract class BaseActivity extends Activity implements NavigationViewMan
        mNavigator.revealRootsDrawer(open);
    }

    @Override
    public void onRootPicked(RootInfo root) {
        // Clicking on the current root removes search
        mSearchManager.cancelSearch();
+1 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import android.widget.TextView.OnEditorActionListener;

import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.Shared;
import com.android.documentsui.ui.Snackbars;

/**
 * Dialog to create a new directory.
+7 −6
Original line number Diff line number Diff line
@@ -28,8 +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.ClipStore;
import com.android.documentsui.clipping.DocumentClipper;
import com.android.documentsui.clipping.ClipStorage;
import com.android.documentsui.roots.RootsCache;

public class DocumentsApplication extends Application {
@@ -38,7 +39,7 @@ public class DocumentsApplication extends Application {
    private RootsCache mRoots;

    private ThumbnailCache mThumbnailCache;
    private ClipStorage mClipStorage;
    private ClipStorage mClipStore;
    private DocumentClipper mClipper;

    public static RootsCache getRootsCache(Context context) {
@@ -65,8 +66,8 @@ public class DocumentsApplication extends Application {
        return ((DocumentsApplication) context.getApplicationContext()).mClipper;
    }

    public static ClipStorage getClipStorage(Context context) {
        return ((DocumentsApplication) context.getApplicationContext()).mClipStorage;
    public static ClipStore getClipStore(Context context) {
        return ((DocumentsApplication) context.getApplicationContext()).mClipStore;
    }

    @Override
@@ -81,10 +82,10 @@ public class DocumentsApplication extends Application {

        mThumbnailCache = new ThumbnailCache(memoryClassBytes / 4);

        mClipStorage = new ClipStorage(
        mClipStore = new ClipStorage(
                ClipStorage.prepareStorage(getCacheDir()),
                getSharedPreferences(ClipStorage.PREF_NAME, 0));
        mClipper = new DocumentClipper(this, mClipStorage);
        mClipper = new DocumentClipper(this, mClipStore);

        final IntentFilter packageFilter = new IntentFilter();
        packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
Loading