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

Commit 748ea8cc authored by Tomasz Mikolajewski's avatar Tomasz Mikolajewski
Browse files

Show a warning notification and a dialog in case of conversion when copying.

Change-Id: I451b0944069deb5f0b49b54c514e7761be449e3d
parent d5ee4703
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -181,6 +181,8 @@
    <string name="copy_failure_alert_content">These files weren\'t copied: <xliff:g id="list">%1$s</xliff:g></string>
    <!-- Contents of the moving failure alert dialog. [CHAR LIMIT=48] -->
    <string name="move_failure_alert_content">These files weren\'t moved: <xliff:g id="list">%1$s</xliff:g></string>
    <!-- Contents of the copying warning dialog due to converted files. [CHAR LIMIT=64] -->
    <string name="copy_converted_warning_content">These files were converted to another format: <xliff:g id="list" example="Document.pdf, Photo.jpg, Song.ogg">%1$s</xliff:g></string>
    <!-- Toast shown when a user copies files to clipboard. -->
    <plurals name="clipboard_files_clipped">
        <item quantity="one">Copied <xliff:g id="count" example="1">%1$d</xliff:g> file to clipboard.</item>
@@ -192,6 +194,9 @@
    <string name="menu_rename">Rename</string>
    <!-- Toast shown when renaming document failed with an error [CHAR LIMIT=48] -->
    <string name="rename_error">Failed to rename document</string>
    <!-- First line for notifications saying that some files were converted to a different format
         during a copy. [CHAR LIMIT=48] -->
    <string name="notification_copy_files_converted_title">Some files were converted</string>

    <!--  DO NOT TRANSLATE - final phrase has not been decided yet (b/26750152) -->
    <string name="open_external_dialog_request">Grant <xliff:g id="appName" example="System Settings"><b>^1</b></xliff:g>
+12 −10
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import static com.android.documentsui.Shared.DEBUG;
import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkState;
import static com.android.documentsui.OperationDialogFragment.DialogType;
import static com.android.documentsui.OperationDialogFragment.DIALOG_TYPE_UNKNOWN;

import android.app.Activity;
import android.app.FragmentManager;
@@ -121,20 +123,20 @@ public class FilesActivity extends BaseActivity {
                    ProviderExecutor.forAuthority(homeUri.getAuthority()));
        }

        final int failure = intent.getIntExtra(FileOperationService.EXTRA_FAILURE, 0);
        final @DialogType int dialogType = intent.getIntExtra(
                FileOperationService.EXTRA_DIALOG_TYPE, DIALOG_TYPE_UNKNOWN);
        // DialogFragment takes care of restoring the dialog on configuration change.
        // Only show it manually for the first time (icicle is null).
        if (icicle == null && dialogType != DIALOG_TYPE_UNKNOWN) {
            final int opType = intent.getIntExtra(
                    FileOperationService.EXTRA_OPERATION,
                    FileOperationService.OPERATION_COPY);

        // DialogFragment takes care of restoring the dialog on configuration change.
        // Only show it manually for the first time (icicle is null).
        if (icicle == null && failure != 0) {
            final ArrayList<DocumentInfo> failedSrcList =
            final ArrayList<DocumentInfo> srcList =
                    intent.getParcelableArrayListExtra(FileOperationService.EXTRA_SRC_LIST);
            FailureDialogFragment.show(
            OperationDialogFragment.show(
                    getFragmentManager(),
                    failure,
                    failedSrcList,
                    dialogType,
                    srcList,
                    mState.stack,
                    opType);
        }
+132 −0
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.
@@ -16,8 +16,10 @@

package com.android.documentsui;

import static com.android.documentsui.services.FileOperationService.OpType;
import static com.android.internal.util.Preconditions.checkArgument;

import android.annotation.IntDef;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
@@ -31,30 +33,43 @@ import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.services.FileOperationService;
import com.android.documentsui.services.FileOperations;
import com.android.documentsui.services.Job;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;

/**
 * Alert dialog for failed operations.
 * Alert dialog for operation dialogs.
 */
public class FailureDialogFragment extends DialogFragment {
    private static final String TAG = "FailureDialogFragment";

    public static void show(FragmentManager fm, int failure,
            ArrayList<DocumentInfo> failedSrcList, DocumentStack dstStack, int operationType) {
        // TODO: Add support for other failures than copy.
        if (failure != FileOperationService.FAILURE_COPY) {
            return;
        }
public class OperationDialogFragment extends DialogFragment {

    public static final int DIALOG_TYPE_UNKNOWN = 0;
    public static final int DIALOG_TYPE_FAILURE = 1;
    public static final int DIALOG_TYPE_CONVERTED = 2;

    @IntDef(flag = true, value = {
        DIALOG_TYPE_UNKNOWN,
        DIALOG_TYPE_FAILURE,
        DIALOG_TYPE_CONVERTED
    })

    @Retention(RetentionPolicy.SOURCE)
    public @interface DialogType {}

    private static final String TAG = "OperationDialogFragment";

    public static void show(FragmentManager fm, @DialogType int dialogType,
            ArrayList<DocumentInfo> failedSrcList, DocumentStack dstStack,
            @OpType int operationType) {
        final Bundle args = new Bundle();
        args.putInt(FileOperationService.EXTRA_FAILURE, failure);
        args.putInt(FileOperationService.EXTRA_DIALOG_TYPE, dialogType);
        args.putInt(FileOperationService.EXTRA_OPERATION, operationType);
        args.putParcelableArrayList(FileOperationService.EXTRA_SRC_LIST, failedSrcList);

        final FragmentTransaction ft = fm.beginTransaction();
        final FailureDialogFragment fragment = new FailureDialogFragment();
        final OperationDialogFragment fragment = new OperationDialogFragment();
        fragment.setArguments(args);

        ft.add(fragment, TAG);
@@ -65,38 +80,53 @@ public class FailureDialogFragment extends DialogFragment {
    public Dialog onCreateDialog(Bundle inState) {
        super.onCreate(inState);

        final int operationType = getArguments().getInt(FileOperationService.EXTRA_OPERATION);
        final List<DocumentInfo> failedSrcList = getArguments().getParcelableArrayList(
        final @DialogType int dialogType =
              getArguments().getInt(FileOperationService.EXTRA_DIALOG_TYPE);
        final @OpType int operationType =
              getArguments().getInt(FileOperationService.EXTRA_OPERATION);
        final ArrayList<DocumentInfo> srcList = getArguments().getParcelableArrayList(
                FileOperationService.EXTRA_SRC_LIST);

        final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        String messageFormat;

        switch (dialogType) {
            case DIALOG_TYPE_CONVERTED:
                messageFormat = getString(R.string.copy_converted_warning_content);
                break;

            case DIALOG_TYPE_FAILURE:
                switch (operationType) {
                    case FileOperationService.OPERATION_COPY:
                        messageFormat = getString(R.string.copy_failure_alert_content);
                        break;
                    case FileOperationService.OPERATION_MOVE:
                        messageFormat = getString(R.string.move_failure_alert_content);
                        break;
                    default:
                        throw new UnsupportedOperationException();
                }
                break;

            default:
                throw new UnsupportedOperationException();
        }

        final StringBuilder list = new StringBuilder("<p>");
        for (DocumentInfo documentInfo : failedSrcList) {
        for (DocumentInfo documentInfo : srcList) {
            list.append(String.format("&#8226; %s<br>", documentInfo.displayName));
        }
        list.append("</p>");

        // TODO: Add support for other file operations.
        checkArgument(
                operationType == FileOperationService.OPERATION_COPY
                || operationType == FileOperationService.OPERATION_MOVE);

        int messageId = operationType == FileOperationService.OPERATION_COPY
                ? R.string.copy_failure_alert_content
                : R.string.move_failure_alert_content;

        final String messageFormat = getString(
                messageId);

        final String message = String.format(messageFormat, list.toString());

        return new AlertDialog.Builder(getActivity())
                .setMessage(Html.fromHtml(message))
                .setPositiveButton(R.string.close, new DialogInterface.OnClickListener() {
        builder.setMessage(Html.fromHtml(String.format(messageFormat, list.toString())));
        builder.setPositiveButton(
                R.string.close,
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int id) {
                        dialog.dismiss();
                    }
                })
                .create();
                });

        return builder.create();
    }
}
+40 −0
Original line number Diff line number Diff line
@@ -21,17 +21,23 @@ import static android.provider.DocumentsContract.buildChildDocumentsUri;
import static android.provider.DocumentsContract.buildDocumentUri;
import static android.provider.DocumentsContract.getDocumentId;
import static android.provider.DocumentsContract.isChildDocument;
import static com.android.documentsui.OperationDialogFragment.DIALOG_TYPE_CONVERTED;
import static com.android.documentsui.Shared.DEBUG;
import static com.android.documentsui.model.DocumentInfo.getCursorLong;
import static com.android.documentsui.model.DocumentInfo.getCursorString;
import static com.android.documentsui.services.FileOperationService.EXTRA_DIALOG_TYPE;
import static com.android.documentsui.services.FileOperationService.EXTRA_OPERATION;
import static com.android.documentsui.services.FileOperationService.EXTRA_SRC_LIST;
import static com.android.documentsui.services.FileOperationService.OPERATION_COPY;
import static com.google.common.base.Preconditions.checkArgument;

import android.annotation.StringRes;
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.PendingIntent;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
@@ -56,12 +62,14 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;

class CopyJob extends Job {
    private static final String TAG = "CopyJob";
    private static final int PROGRESS_INTERVAL_MILLIS = 1000;
    final List<DocumentInfo> mSrcs;
    final ArrayList<DocumentInfo> convertedFiles = new ArrayList<>();

    private long mStartTime = -1;
    private long mBatchSize;
@@ -175,6 +183,29 @@ class CopyJob extends Job {
                R.plurals.copy_error_notification_title, R.drawable.ic_menu_copy);
    }

    @Override
    Notification getWarningNotification() {
        final Intent navigateIntent = buildNavigateIntent();
        navigateIntent.putExtra(EXTRA_DIALOG_TYPE, DIALOG_TYPE_CONVERTED);
        navigateIntent.putExtra(EXTRA_OPERATION, operationType);

        navigateIntent.putParcelableArrayListExtra(EXTRA_SRC_LIST, convertedFiles);

        // TODO: Consider adding a dialog on tapping the notification with a list of
        // converted files.
        final Notification.Builder warningBuilder = new Notification.Builder(service)
                .setContentTitle(service.getResources().getString(
                        R.string.notification_copy_files_converted_title))
                .setContentText(service.getString(
                        R.string.notification_touch_for_details))
                .setContentIntent(PendingIntent.getActivity(appContext, 0, navigateIntent,
                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT))
                .setCategory(Notification.CATEGORY_ERROR)
                .setSmallIcon(R.drawable.ic_menu_copy)
                .setAutoCancel(true);
        return warningBuilder.build();
    }

    @Override
    void start() throws RemoteException {
        mStartTime = elapsedRealtime();
@@ -203,6 +234,11 @@ class CopyJob extends Job {
        }
    }

    @Override
    boolean hasWarnings() {
        return !convertedFiles.isEmpty();
    }

    /**
     * Logs progress on the current copy operation. Displays/Updates the progress notification.
     *
@@ -425,6 +461,10 @@ class CopyJob extends Job {
            }
        }

        if (src.isVirtualDocument() && success) {
           convertedFiles.add(src);
        }

        return success;
    }

+5 −0
Original line number Diff line number Diff line
@@ -74,6 +74,11 @@ final class DeleteJob extends Job {
                R.plurals.delete_error_notification_title, R.drawable.ic_menu_delete);
    }

    @Override
    Notification getWarningNotification() {
        throw new UnsupportedOperationException();
    }

    @Override
    void start() throws RemoteException {
        for (DocumentInfo doc : mSrcs) {
Loading