diff options
15 files changed, 291 insertions, 497 deletions
| diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index e1bf1afa4..eeb9fa389 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -27,6 +27,8 @@ import org.sufficientlysecure.keychain.ui.DecryptActivity;  import org.sufficientlysecure.keychain.ui.EncryptActivity;  import org.sufficientlysecure.keychain.ui.KeyListActivity; +import java.io.File; +  public final class Constants {      public static final boolean DEBUG = BuildConfig.DEBUG; @@ -52,9 +54,8 @@ public final class Constants {      public static boolean KITKAT = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;      public static final class Path { -        public static final String APP_DIR = Environment.getExternalStorageDirectory() -                + "/OpenKeychain"; -        public static final String APP_DIR_FILE = APP_DIR + "/export.asc"; +        public static final File APP_DIR = new File(Environment.getExternalStorageDirectory(), "OpenKeychain"); +        public static final File APP_DIR_FILE = new File(APP_DIR, "export.asc");      }      public static final class Pref { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java index dfd39b345..be9c1e405 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java @@ -31,7 +31,6 @@ import org.sufficientlysecure.keychain.helper.TlsHelper;  import org.sufficientlysecure.keychain.util.Log;  import org.sufficientlysecure.keychain.util.PRNGFixes; -import java.io.File;  import java.security.Provider;  import java.security.Security; @@ -71,8 +70,7 @@ public class KeychainApplication extends Application {          // Create APG directory on sdcard if not existing          if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { -            File dir = new File(Constants.Path.APP_DIR); -            if (!dir.exists() && !dir.mkdirs()) { +            if (!Constants.Path.APP_DIR.exists() && !Constants.Path.APP_DIR.mkdirs()) {                  // ignore this for now, it's not crucial                  // that the directory doesn't exist at this point              } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java index 16ef28311..ae9438148 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java @@ -30,7 +30,6 @@ import android.widget.Toast;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;  import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;  import org.sufficientlysecure.keychain.provider.ProviderHelper;  import org.sufficientlysecure.keychain.service.KeychainIntentService; @@ -39,9 +38,10 @@ import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;  import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;  import org.sufficientlysecure.keychain.util.Log; +import java.io.File; +  public class ExportHelper { -    protected FileDialogFragment mFileDialog; -    protected String mExportFilename; +    protected File mExportFile;      ActionBarActivity mActivity; @@ -68,47 +68,30 @@ public class ExportHelper {      /**       * Show dialog where to export keys       */ -    public void showExportKeysDialog(final long[] masterKeyIds, final String exportFilename, +    public void showExportKeysDialog(final long[] masterKeyIds, final File exportFile,                                       final boolean showSecretCheckbox) { -        mExportFilename = exportFilename; - -        // Message is received after file is selected -        Handler returnHandler = new Handler() { -            @Override -            public void handleMessage(Message message) { -                if (message.what == FileDialogFragment.MESSAGE_OKAY) { -                    Bundle data = message.getData(); -                    mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); - -                    exportKeys(masterKeyIds, data.getBoolean(FileDialogFragment.MESSAGE_DATA_CHECKED)); -                } -            } -        }; +        mExportFile = exportFile; -        // Create a new Messenger for the communication back -        final Messenger messenger = new Messenger(returnHandler); - -        DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { -            public void run() { -                String title = null; -                if (masterKeyIds == null) { -                    // export all keys -                    title = mActivity.getString(R.string.title_export_keys); -                } else { -                    // export only key specified at data uri -                    title = mActivity.getString(R.string.title_export_key); -                } - -                String message = mActivity.getString(R.string.specify_file_to_export_to); -                String checkMsg = showSecretCheckbox ? -                        mActivity.getString(R.string.also_export_secret_keys) : null; +        String title = null; +        if (masterKeyIds == null) { +            // export all keys +            title = mActivity.getString(R.string.title_export_keys); +        } else { +            // export only key specified at data uri +            title = mActivity.getString(R.string.title_export_key); +        } -                mFileDialog = FileDialogFragment.newInstance(messenger, title, message, -                        exportFilename, checkMsg); +        String message = mActivity.getString(R.string.specify_file_to_export_to); +        String checkMsg = showSecretCheckbox ? +                mActivity.getString(R.string.also_export_secret_keys) : null; -                mFileDialog.show(mActivity.getSupportFragmentManager(), "fileDialog"); +        FileHelper.saveFile(new FileHelper.FileDialogCallback() { +            @Override +            public void onFileSelected(File file, boolean checked) { +                mExportFile = file; +                exportKeys(masterKeyIds, checked);              } -        }); +        }, mActivity.getSupportFragmentManager() ,title, message, exportFile, checkMsg);      }      /** @@ -125,7 +108,7 @@ public class ExportHelper {          // fill values for this action          Bundle data = new Bundle(); -        data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFilename); +        data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFile.getAbsolutePath());          data.putBoolean(KeychainIntentService.EXPORT_SECRET, exportSecret);          if (masterKeyIds == null) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java index e0c94b947..2898c7030 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java @@ -26,12 +26,19 @@ import android.database.Cursor;  import android.net.Uri;  import android.os.Build;  import android.os.Environment; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import android.provider.OpenableColumns;  import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager;  import android.widget.Toast; -import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; +import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment; + +import java.io.File;  public class FileHelper { @@ -55,25 +62,18 @@ public class FileHelper {       * Opens the preferred installed file manager on Android and shows a toast if no manager is       * installed.       * -     * @param activity -     * @param filename    default selected file, not supported by all file managers +     * @param fragment +     * @param last        default selected Uri, not supported by all file managers       * @param mimeType    can be text/plain for example       * @param requestCode requestCode used to identify the result coming back from file manager to       *                    onActivityResult() in your activity       */ -    public static void openFile(Activity activity, String filename, String mimeType, int requestCode) { -        Intent intent = buildFileIntent(filename, mimeType); - -        try { -            activity.startActivityForResult(intent, requestCode); -        } catch (ActivityNotFoundException e) { -            // No compatible file manager was found. -            Toast.makeText(activity, R.string.no_filemanager_installed, Toast.LENGTH_SHORT).show(); -        } -    } +    public static void openFile(Fragment fragment, Uri last, String mimeType, int requestCode) { +        Intent intent = new Intent(Intent.ACTION_GET_CONTENT); +        intent.addCategory(Intent.CATEGORY_OPENABLE); -    public static void openFile(Fragment fragment, String filename, String mimeType, int requestCode) { -        Intent intent = buildFileIntent(filename, mimeType); +        intent.setData(last); +        intent.setType(mimeType);          try {              fragment.startActivityForResult(intent, requestCode); @@ -84,19 +84,62 @@ public class FileHelper {          }      } +    public static void saveFile(final FileDialogCallback callback, final FragmentManager fragmentManager, +                                final String title, final String message, final File defaultFile, +                                final String checkMsg) { +        // Message is received after file is selected +        Handler returnHandler = new Handler() { +            @Override +            public void handleMessage(Message message) { +                if (message.what == FileDialogFragment.MESSAGE_OKAY) { +                    callback.onFileSelected( +                            new File(message.getData().getString(FileDialogFragment.MESSAGE_DATA_FILE)), +                            message.getData().getBoolean(FileDialogFragment.MESSAGE_DATA_CHECKED)); +                } +            } +        }; + +        // Create a new Messenger for the communication back +        final Messenger messenger = new Messenger(returnHandler); + +        DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { +            @Override +            public void run() { +                FileDialogFragment fileDialog = FileDialogFragment.newInstance(messenger, title, message, +                        defaultFile, checkMsg); + +                fileDialog.show(fragmentManager, "fileDialog"); +            } +        }); +    } + +    public static void saveFile(Fragment fragment, String title, String message, File defaultFile, int requestCode) { +        saveFile(fragment, title, message, defaultFile, requestCode, null); +    } + +    public static void saveFile(final Fragment fragment, String title, String message, File defaultFile, +                                final int requestCode, String checkMsg) { +        saveFile(new FileDialogCallback() { +            @Override +            public void onFileSelected(File file, boolean checked) { +                Intent intent = new Intent(); +                intent.setData(Uri.fromFile(file)); +                fragment.onActivityResult(requestCode, Activity.RESULT_OK, intent); +            } +        }, fragment.getActivity().getSupportFragmentManager(), title, message, defaultFile, checkMsg); +    } +      /**       * Opens the storage browser on Android 4.4 or later for opening a file       * @param fragment -     * @param last default selected file       * @param mimeType can be text/plain for example       * @param requestCode used to identify the result coming back from storage browser onActivityResult() in your       */      @TargetApi(Build.VERSION_CODES.KITKAT) -    public static void openDocument(Fragment fragment, Uri last, String mimeType, int requestCode) { +    public static void openDocument(Fragment fragment, String mimeType, int requestCode) {          Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);          intent.addCategory(Intent.CATEGORY_OPENABLE); -        intent.setData(last);          intent.setType(mimeType);          fragment.startActivityForResult(intent, requestCode);      } @@ -104,66 +147,42 @@ public class FileHelper {      /**       * Opens the storage browser on Android 4.4 or later for saving a file       * @param fragment -     * @param last default selected file       * @param mimeType can be text/plain for example +     * @param suggestedName a filename desirable for the file to be saved       * @param requestCode used to identify the result coming back from storage browser onActivityResult() in your       */      @TargetApi(Build.VERSION_CODES.KITKAT) -    public static void saveDocument(Fragment fragment, Uri last, String mimeType, int requestCode) { +    public static void saveDocument(Fragment fragment, String mimeType, String suggestedName, int requestCode) {          Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);          intent.addCategory(Intent.CATEGORY_OPENABLE); -        intent.setData(last);          intent.setType(mimeType); +        intent.putExtra("android.content.extra.SHOW_ADVANCED", true); // Note: This is not documented, but works +        intent.putExtra(Intent.EXTRA_TITLE, suggestedName);          fragment.startActivityForResult(intent, requestCode);      } -    private static Intent buildFileIntent(String filename, String mimeType) { -        Intent intent = new Intent(Intent.ACTION_GET_CONTENT); -        intent.addCategory(Intent.CATEGORY_OPENABLE); - -        intent.setData(Uri.parse("file://" + filename)); -        intent.setType(mimeType); - -        return intent; -    } +    public static String getFilename(Context context, Uri uri) { +        String filename = null; +        try { +            Cursor cursor = context.getContentResolver().query(uri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null); -    /** -     * Get a file path from a Uri. -     * <p/> -     * from https://github.com/iPaulPro/aFileChooser/blob/master/aFileChooser/src/com/ipaulpro/ -     * afilechooser/utils/FileUtils.java -     * -     * @param context -     * @param uri -     * @return -     * @author paulburke -     */ -    public static String getPath(Context context, Uri uri) { -        Log.d(Constants.TAG + " File -", -                "Authority: " + uri.getAuthority() + ", Fragment: " + uri.getFragment() -                        + ", Port: " + uri.getPort() + ", Query: " + uri.getQuery() + ", Scheme: " -                        + uri.getScheme() + ", Host: " + uri.getHost() + ", Segments: " -                        + uri.getPathSegments().toString()); - -        if ("content".equalsIgnoreCase(uri.getScheme())) { -            String[] projection = {"_data"}; -            Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null); -            try { -                if (cursor != null && cursor.moveToFirst()) { -                    int columnIndex = cursor.getColumnIndexOrThrow("_data"); -                    return cursor.getString(columnIndex); -                } -            } catch (Exception e) { -                // Eat it -            } finally { -                if (cursor != null) { -                    cursor.close(); +            if (cursor != null) { +                if (cursor.moveToNext()) { +                    filename = cursor.getString(0);                  } +                cursor.close();              } -        } else if ("file".equalsIgnoreCase(uri.getScheme())) { -            return uri.getPath(); +        } catch (Exception ignored) { +            // This happens in rare cases (eg: document deleted since selection) and should not cause a failure          } +        if (filename == null) { +            String[] split = uri.toString().split("/"); +            filename = split[split.length - 1]; +        } +        return filename; +    } -        return null; +    public static interface FileDialogCallback { +        public void onFileSelected(File file, boolean checked);      }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index e1514b16f..a2676b1a4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -139,6 +139,7 @@ public class KeychainIntentService extends IntentService      // export key      public static final String EXPORT_OUTPUT_STREAM = "export_output_stream";      public static final String EXPORT_FILENAME = "export_filename"; +    public static final String EXPORT_URI = "export_uri";      public static final String EXPORT_SECRET = "export_secret";      public static final String EXPORT_ALL = "export_all";      public static final String EXPORT_KEY_RING_MASTER_KEY_ID = "export_key_ring_id"; @@ -393,13 +394,16 @@ public class KeychainIntentService extends IntentService                  boolean exportSecret = data.getBoolean(EXPORT_SECRET, false);                  long[] masterKeyIds = data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID);                  String outputFile = data.getString(EXPORT_FILENAME); +                Uri outputUri = data.getParcelable(EXPORT_URI);                  // If not exporting all keys get the masterKeyIds of the keys to export from the intent                  boolean exportAll = data.getBoolean(EXPORT_ALL); -                // check if storage is ready -                if (!FileHelper.isStorageMounted(outputFile)) { -                    throw new PgpGeneralException(getString(R.string.error_external_storage_not_ready)); +                if (outputFile != null) { +                    // check if storage is ready +                    if (!FileHelper.isStorageMounted(outputFile)) { +                        throw new PgpGeneralException(getString(R.string.error_external_storage_not_ready)); +                    }                  }                  ArrayList<Long> publicMasterKeyIds = new ArrayList<Long>(); @@ -431,12 +435,19 @@ public class KeychainIntentService extends IntentService                      }                  } +                OutputStream outStream; +                if (outputFile != null) { +                    outStream = new FileOutputStream(outputFile); +                } else { +                    outStream = getContentResolver().openOutputStream(outputUri); +                } +                  PgpImportExport pgpImportExport = new PgpImportExport(this, this, this);                  Bundle resultData = pgpImportExport                          .exportKeyRings(publicMasterKeyIds, secretMasterKeyIds, -                                new FileOutputStream(outputFile)); +                                outStream); -                if (mIsCanceled) { +                if (mIsCanceled && outputFile != null) {                      boolean isDeleted = new File(outputFile).delete();                  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java index 5b21be6e4..33659f3e5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java @@ -23,11 +23,9 @@ import android.net.Uri;  import android.os.Bundle;  import android.support.v4.view.PagerTabStrip;  import android.support.v4.view.ViewPager; -import android.widget.Toast;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.FileHelper;  import org.sufficientlysecure.keychain.pgp.PgpHelper;  import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;  import org.sufficientlysecure.keychain.util.Log; @@ -114,7 +112,7 @@ public class DecryptActivity extends DrawerActivity {              } else {                  // Binary via content provider (could also be files)                  // override uri to get stream from send -                uri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM); +                uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);                  action = ACTION_DECRYPT;              }          } else if (Intent.ACTION_VIEW.equals(action)) { @@ -155,21 +153,8 @@ public class DecryptActivity extends DrawerActivity {                  }              }          } else if (ACTION_DECRYPT.equals(action) && uri != null) { -            // get file path from uri -            String path = FileHelper.getPath(this, uri); - -            if (path != null) { -                mFileFragmentBundle.putString(DecryptFileFragment.ARG_FILENAME, path); -                mSwitchToTab = PAGER_TAB_FILE; -            } else { -                Log.e(Constants.TAG, -                        "Direct binary data without actual file in filesystem is not supported. " + -                        "Please use the Remote Service API!"); -                Toast.makeText(this, R.string.error_only_files_are_supported, Toast.LENGTH_LONG) -                        .show(); -                // end activity -                finish(); -            } +            mFileFragmentBundle.putParcelable(DecryptFileFragment.ARG_URI, uri); +            mSwitchToTab = PAGER_TAB_FILE;          } else {              Log.e(Constants.TAG,                      "Include the extra 'text' or an Uri with setData() in your Intent!"); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java index c12b5b7be..430f85b6f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java @@ -20,22 +20,17 @@ package org.sufficientlysecure.keychain.ui;  import android.app.Activity;  import android.app.ProgressDialog;  import android.content.Intent; -import android.database.Cursor;  import android.net.Uri;  import android.os.Bundle; -import android.os.Handler;  import android.os.Message;  import android.os.Messenger; -import android.provider.OpenableColumns;  import android.view.LayoutInflater;  import android.view.View;  import android.view.ViewGroup;  import android.widget.CheckBox; -import android.widget.EditText; +import android.widget.TextView;  import android.widget.ImageButton; -import com.devspark.appmsg.AppMsg; -  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.helper.FileHelper; @@ -44,29 +39,26 @@ import org.sufficientlysecure.keychain.service.KeychainIntentService;  import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;  import org.sufficientlysecure.keychain.util.Notify;  import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; -import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;  import org.sufficientlysecure.keychain.util.Log;  import java.io.File;  public class DecryptFileFragment extends DecryptFragment { -    public static final String ARG_FILENAME = "filename"; +    public static final String ARG_URI = "uri"; -    private static final int RESULT_CODE_FILE = 0x00007003; +    private static final int REQUEST_CODE_INPUT = 0x00007003; +    private static final int REQUEST_CODE_OUTPUT = 0x00007007;      // view -    private EditText mFilename; +    private TextView mFilename;      private CheckBox mDeleteAfter;      private ImageButton mBrowse;      private View mDecryptButton; -    private String mInputFilename = null; +    // model      private Uri mInputUri = null; -    private String mOutputFilename = null;      private Uri mOutputUri = null; -    private FileDialogFragment mFileDialog; -      /**       * Inflate the layout for this fragment       */ @@ -74,17 +66,17 @@ public class DecryptFileFragment extends DecryptFragment {      public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {          View view = inflater.inflate(R.layout.decrypt_file_fragment, container, false); -        mFilename = (EditText) view.findViewById(R.id.decrypt_file_filename); +        mFilename = (TextView) view.findViewById(R.id.decrypt_file_filename);          mBrowse = (ImageButton) view.findViewById(R.id.decrypt_file_browse);          mDeleteAfter = (CheckBox) view.findViewById(R.id.decrypt_file_delete_after_decryption);          mDecryptButton = view.findViewById(R.id.decrypt_file_action_decrypt);          mBrowse.setOnClickListener(new View.OnClickListener() {              public void onClick(View v) {                  if (Constants.KITKAT) { -                    FileHelper.openDocument(DecryptFileFragment.this, mInputUri, "*/*", RESULT_CODE_FILE); +                    FileHelper.openDocument(DecryptFileFragment.this, "*/*", REQUEST_CODE_INPUT);                  } else { -                    FileHelper.openFile(DecryptFileFragment.this, mFilename.getText().toString(), "*/*", -                            RESULT_CODE_FILE); +                    FileHelper.openFile(DecryptFileFragment.this, mInputUri, "*/*", +                            REQUEST_CODE_INPUT);                  }              }          }); @@ -102,78 +94,48 @@ public class DecryptFileFragment extends DecryptFragment {      public void onActivityCreated(Bundle savedInstanceState) {          super.onActivityCreated(savedInstanceState); -        String filename = getArguments().getString(ARG_FILENAME); -        if (filename != null) { -            mFilename.setText(filename); -        } +        setInputUri(getArguments().<Uri>getParcelable(ARG_URI));      } -    private String guessOutputFilename() { -        File file = new File(mInputFilename); -        String filename = file.getName(); -        if (filename.endsWith(".asc") || filename.endsWith(".gpg") || filename.endsWith(".pgp")) { -            filename = filename.substring(0, filename.length() - 4); +    private void setInputUri(Uri inputUri) { +        if (inputUri == null) { +            mInputUri = null; +            mFilename.setText(""); +            return;          } -        return Constants.Path.APP_DIR + "/" + filename; + +        mInputUri = inputUri; +        mFilename.setText(FileHelper.getFilename(getActivity(), mInputUri));      }      private void decryptAction() { -        String currentFilename = mFilename.getText().toString(); -        if (mInputFilename == null || !mInputFilename.equals(currentFilename)) { -            mInputUri = null; -            mInputFilename = mFilename.getText().toString(); -        } -          if (mInputUri == null) { -            mOutputFilename = guessOutputFilename(); -        } - -        if (mInputFilename.equals("")) {              //AppMsg.makeText(getActivity(), R.string.no_file_selected, AppMsg.STYLE_ALERT).show();              Notify.showNotify(getActivity(), R.string.no_file_selected, Notify.Style.ERROR);              return;          } -        if (mInputUri == null && mInputFilename.startsWith("file")) { -            File file = new File(mInputFilename); -            if (!file.exists() || !file.isFile()) { -                AppMsg.makeText( -                        getActivity(), -                        getString(R.string.error_message, -                                getString(R.string.error_file_not_found)), AppMsg.STYLE_ALERT) -                        .show(); -                return; -            } -        } -          askForOutputFilename();      } -    private void askForOutputFilename() { -        // Message is received after passphrase is cached -        Handler returnHandler = new Handler() { -            @Override -            public void handleMessage(Message message) { -                if (message.what == FileDialogFragment.MESSAGE_OKAY) { -                    Bundle data = message.getData(); -                    if (data.containsKey(FileDialogFragment.MESSAGE_DATA_URI)) { -                        mOutputUri = data.getParcelable(FileDialogFragment.MESSAGE_DATA_URI); -                    } else { -                        mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); -                    } -                    decryptStart(null); -                } -            } -        }; - -        // Create a new Messenger for the communication back -        Messenger messenger = new Messenger(returnHandler); - -        mFileDialog = FileDialogFragment.newInstance(messenger, -                getString(R.string.title_decrypt_to_file), -                getString(R.string.specify_file_to_decrypt_to), mOutputFilename, null); +    private String removeEncryptedAppend(String name) { +        if (name.endsWith(".asc") || name.endsWith(".gpg") || name.endsWith(".pgp")) { +            return name.substring(0, name.length() - 4); +        } +        return name; +    } -        mFileDialog.show(getActivity().getSupportFragmentManager(), "fileDialog"); +    private void askForOutputFilename() { +        String targetName = removeEncryptedAppend(FileHelper.getFilename(getActivity(), mInputUri)); +        if (!Constants.KITKAT) { +            File file = new File(mInputUri.getPath()); +            File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR; +            File targetFile = new File(parentDir, targetName); +            FileHelper.saveFile(this, getString(R.string.title_decrypt_to_file), +                    getString(R.string.specify_file_to_decrypt_to), targetFile, REQUEST_CODE_OUTPUT); +        } else { +            FileHelper.saveDocument(this, "*/*", targetName, REQUEST_CODE_OUTPUT); +        }      }      @Override @@ -189,25 +151,13 @@ public class DecryptFileFragment extends DecryptFragment {          intent.setAction(KeychainIntentService.ACTION_DECRYPT_VERIFY);          // data -        Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename=" -                + mOutputFilename + ",mInputUri=" + mInputUri + ", mOutputUri=" -                + mOutputUri); +        Log.d(Constants.TAG, "mInputUri=" + mInputUri + ", mOutputUri=" + mOutputUri); -        if (mInputUri != null) { -            data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URI); -            data.putParcelable(KeychainIntentService.ENCRYPT_INPUT_URI, mInputUri); -        } else { -            data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_FILE); -            data.putString(KeychainIntentService.ENCRYPT_INPUT_FILE, mInputFilename); -        } +        data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URI); +        data.putParcelable(KeychainIntentService.ENCRYPT_INPUT_URI, mInputUri); -        if (mOutputUri != null) { -            data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URI); -            data.putParcelable(KeychainIntentService.ENCRYPT_OUTPUT_URI, mOutputUri); -        } else { -            data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_FILE); -            data.putString(KeychainIntentService.ENCRYPT_OUTPUT_FILE, mOutputFilename); -        } +        data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URI); +        data.putParcelable(KeychainIntentService.ENCRYPT_OUTPUT_URI, mOutputUri);          data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, passphrase); @@ -238,14 +188,9 @@ public class DecryptFileFragment extends DecryptFragment {                          if (mDeleteAfter.isChecked()) {                              // Create and show dialog to delete original file -                            DeleteFileDialogFragment deleteFileDialog; -                            if (mInputUri != null) { -                                deleteFileDialog = DeleteFileDialogFragment.newInstance(mInputUri); -                            } else { -                                deleteFileDialog = DeleteFileDialogFragment -                                        .newInstance(mInputFilename); -                            } +                            DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment.newInstance(mInputUri);                              deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog"); +                            setInputUri(null);                          }                      }                  } @@ -266,28 +211,17 @@ public class DecryptFileFragment extends DecryptFragment {      @Override      public void onActivityResult(int requestCode, int resultCode, Intent data) {          switch (requestCode) { -            case RESULT_CODE_FILE: { +            case REQUEST_CODE_INPUT: {                  if (resultCode == Activity.RESULT_OK && data != null) { -                    if (Constants.KITKAT) { -                        mInputUri = data.getData(); -                        Cursor cursor = getActivity().getContentResolver().query(mInputUri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null); -                        if (cursor != null) { -                            if (cursor.moveToNext()) { -                                mInputFilename = cursor.getString(0); -                                mFilename.setText(mInputFilename); -                            } -                            cursor.close(); -                        } -                    } else { -                        try { -                            String path = FileHelper.getPath(getActivity(), data.getData()); -                            Log.d(Constants.TAG, "path=" + path); - -                            mFilename.setText(path); -                        } catch (NullPointerException e) { -                            Log.e(Constants.TAG, "Nullpointer while retrieving path!"); -                        } -                    } +                    setInputUri(data.getData()); +                } +                return; +            } +            case REQUEST_CODE_OUTPUT: { +                // This happens after output file was selected, so start our operation +                if (resultCode == Activity.RESULT_OK && data != null) { +                    mOutputUri = data.getData(); +                    decryptStart(null);                  }                  return;              } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java index 39d4a09bc..e93a63cc8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -23,11 +23,9 @@ import android.net.Uri;  import android.os.Bundle;  import android.support.v4.view.PagerTabStrip;  import android.support.v4.view.ViewPager; -import android.widget.Toast;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.FileHelper;  import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;  import org.sufficientlysecure.keychain.util.Log; @@ -98,11 +96,7 @@ public class EncryptActivity extends DrawerActivity implements      @Override      public boolean isModeSymmetric() { -        if (PAGER_MODE_SYMMETRIC == mViewPagerMode.getCurrentItem()) { -            return true; -        } else { -            return false; -        } +        return PAGER_MODE_SYMMETRIC == mViewPagerMode.getCurrentItem();      }      @Override @@ -201,7 +195,7 @@ public class EncryptActivity extends DrawerActivity implements                  }              } else {                  // Files via content provider, override uri and action -                uri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM); +                uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);                  action = ACTION_ENCRYPT;              }          } @@ -232,23 +226,8 @@ public class EncryptActivity extends DrawerActivity implements              mSwitchToContent = PAGER_CONTENT_MESSAGE;          } else if (ACTION_ENCRYPT.equals(action) && uri != null) {              // encrypt file based on Uri - -            // get file path from uri -            String path = FileHelper.getPath(this, uri); - -            if (path != null) { -                mFileFragmentBundle.putString(EncryptFileFragment.ARG_FILENAME, path); -                mSwitchToContent = PAGER_CONTENT_FILE; -            } else { -                Log.e(Constants.TAG, -                        "Direct binary data without actual file in filesystem is not supported " + -                                "by Intents. Please use the Remote Service API!" -                ); -                Toast.makeText(this, R.string.error_only_files_are_supported, -                        Toast.LENGTH_LONG).show(); -                // end activity -                finish(); -            } +            mFileFragmentBundle.putParcelable(EncryptFileFragment.ARG_URI, uri); +            mSwitchToContent = PAGER_CONTENT_FILE;          } else {              Log.e(Constants.TAG,                      "Include the extra 'text' or an Uri with setData() in your Intent!"); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java index f5d89d186..2fabeb82c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java @@ -20,22 +20,20 @@ package org.sufficientlysecure.keychain.ui;  import android.app.Activity;  import android.app.ProgressDialog;  import android.content.Intent; -import android.database.Cursor;  import android.net.Uri;  import android.os.Bundle;  import android.os.Handler;  import android.os.Message;  import android.os.Messenger; -import android.provider.OpenableColumns;  import android.support.v4.app.Fragment;  import android.view.LayoutInflater;  import android.view.View;  import android.view.ViewGroup;  import android.widget.ArrayAdapter;  import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.ImageButton;  import android.widget.Spinner; +import android.widget.TextView; +import android.widget.ImageButton;  import com.devspark.appmsg.AppMsg; @@ -47,7 +45,6 @@ import org.sufficientlysecure.keychain.service.KeychainIntentService;  import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;  import org.sufficientlysecure.keychain.service.PassphraseCacheService;  import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; -import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;  import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;  import org.sufficientlysecure.keychain.util.Choice;  import org.sufficientlysecure.keychain.util.Log; @@ -55,28 +52,25 @@ import org.sufficientlysecure.keychain.util.Log;  import java.io.File;  public class EncryptFileFragment extends Fragment { -    public static final String ARG_FILENAME = "filename"; +    public static final String ARG_URI = "uri";      public static final String ARG_ASCII_ARMOR = "ascii_armor"; -    private static final int RESULT_CODE_FILE = 0x00007003; +    private static final int REQUEST_CODE_INPUT = 0x00007003; +    private static final int REQUEST_CODE_OUTPUT = 0x00007007;      private EncryptActivityInterface mEncryptInterface;      // view      private CheckBox mAsciiArmor = null;      private Spinner mFileCompression = null; -    private EditText mFilename = null; +    private TextView mFilename = null;      private CheckBox mDeleteAfter = null;      private CheckBox mShareAfter = null;      private ImageButton mBrowse = null;      private View mEncryptFile; -    private FileDialogFragment mFileDialog; -      // model -    private String mInputFilename = null;      private Uri mInputUri = null; -    private String mOutputFilename = null;      private Uri mOutputUri = null;      @Override @@ -104,15 +98,15 @@ public class EncryptFileFragment extends Fragment {              }          }); -        mFilename = (EditText) view.findViewById(R.id.filename); +        mFilename = (TextView) view.findViewById(R.id.filename);          mBrowse = (ImageButton) view.findViewById(R.id.btn_browse);          mBrowse.setOnClickListener(new View.OnClickListener() {              public void onClick(View v) {                  if (Constants.KITKAT) { -                    FileHelper.openDocument(EncryptFileFragment.this, mInputUri, "*/*", RESULT_CODE_FILE); +                    FileHelper.openDocument(EncryptFileFragment.this, "*/*", REQUEST_CODE_INPUT);                  } else { -                    FileHelper.openFile(EncryptFileFragment.this, mFilename.getText().toString(), "*/*", -                            RESULT_CODE_FILE); +                    FileHelper.openFile(EncryptFileFragment.this, mInputUri, "*/*", +                            REQUEST_CODE_INPUT);                  }              }          }); @@ -154,86 +148,45 @@ public class EncryptFileFragment extends Fragment {      public void onActivityCreated(Bundle savedInstanceState) {          super.onActivityCreated(savedInstanceState); -        String filename = getArguments().getString(ARG_FILENAME); -        if (filename != null) { -            mFilename.setText(filename); -        } +        setInputUri(getArguments().<Uri>getParcelable(ARG_URI));          boolean asciiArmor = getArguments().getBoolean(ARG_ASCII_ARMOR);          if (asciiArmor) { -            mAsciiArmor.setChecked(asciiArmor); +            mAsciiArmor.setChecked(true);          }      } -    /** -     * Guess output filename based on input path -     * -     * @param path -     * @return Suggestion for output filename -     */ -    private String guessOutputFilename(String path) { -        // output in the same directory but with additional ending -        File file = new File(path); -        String ending = (mAsciiArmor.isChecked() ? ".asc" : ".gpg"); -        String outputFilename = file.getParent() + File.separator + file.getName() + ending; +    private void setInputUri(Uri inputUri) { +        if (inputUri == null) { +            mInputUri = null; +            mFilename.setText(""); +            return; +        } -        return outputFilename; +        mInputUri = inputUri; +        mFilename.setText(FileHelper.getFilename(getActivity(), mInputUri));      }      private void showOutputFileDialog() { -        // Message is received after file is selected -        Handler returnHandler = new Handler() { -            @Override -            public void handleMessage(Message message) { -                if (message.what == FileDialogFragment.MESSAGE_OKAY) { -                    Bundle data = message.getData(); -                    if (data.containsKey(FileDialogFragment.MESSAGE_DATA_URI)) { -                        mOutputUri = data.getParcelable(FileDialogFragment.MESSAGE_DATA_URI); -                    } else { -                        mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); -                    } -                    encryptStart(); -                } -            } -        }; - -        // Create a new Messenger for the communication back -        Messenger messenger = new Messenger(returnHandler); - -        mFileDialog = FileDialogFragment.newInstance(messenger, -                getString(R.string.title_encrypt_to_file), -                getString(R.string.specify_file_to_encrypt_to), mOutputFilename, null); - -        mFileDialog.show(getActivity().getSupportFragmentManager(), "fileDialog"); +        if (!Constants.KITKAT) { +            File file = new File(mInputUri.getPath()); +            File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR; +            String targetName = FileHelper.getFilename( +                    getActivity(), mInputUri) + (mAsciiArmor.isChecked() ? ".asc" : ".gpg"); +            File targetFile = new File(parentDir, targetName); +            FileHelper.saveFile(this, getString(R.string.title_encrypt_to_file), +                    getString(R.string.specify_file_to_encrypt_to), targetFile, REQUEST_CODE_OUTPUT); +        } else { +            FileHelper.saveDocument(this, "*/*", FileHelper.getFilename(getActivity(), mInputUri) + +                    (mAsciiArmor.isChecked() ? ".asc" : ".gpg"), REQUEST_CODE_OUTPUT); +        }      }      private void encryptClicked() { -        String currentFilename = mFilename.getText().toString(); -        if (mInputFilename == null || !mInputFilename.equals(currentFilename)) { -            mInputUri = null; -            mInputFilename = mFilename.getText().toString(); -        } -          if (mInputUri == null) { -            mOutputFilename = guessOutputFilename(mInputFilename); -        } - -        if (mInputFilename.equals("")) {              AppMsg.makeText(getActivity(), R.string.no_file_selected, AppMsg.STYLE_ALERT).show();              return;          } -        if (mInputUri == null && !mInputFilename.startsWith("content")) { -            File file = new File(mInputFilename); -            if (!file.exists() || !file.isFile()) { -                AppMsg.makeText( -                        getActivity(), -                        getString(R.string.error_message, -                                getString(R.string.error_file_not_found)), AppMsg.STYLE_ALERT) -                        .show(); -                return; -            } -        } -          if (mEncryptInterface.isModeSymmetric()) {              // symmetric encryption @@ -287,6 +240,10 @@ public class EncryptFileFragment extends Fragment {      }      private void encryptStart() { +        if (mInputUri == null || mOutputUri == null) { +            throw new IllegalStateException("Something went terribly wrong if this happens!"); +        } +          // Send all information needed to service to edit key in other thread          Intent intent = new Intent(getActivity(), KeychainIntentService.class); @@ -295,25 +252,13 @@ public class EncryptFileFragment extends Fragment {          // fill values for this action          Bundle data = new Bundle(); -        Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename=" -                + mOutputFilename + ",mInputUri=" + mInputUri + ", mOutputUri=" -                + mOutputUri); +        Log.d(Constants.TAG, "mInputUri=" + mInputUri + ", mOutputUri=" + mOutputUri); -        if (mInputUri != null) { -            data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URI); -            data.putParcelable(KeychainIntentService.ENCRYPT_INPUT_URI, mInputUri); -        } else { -            data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_FILE); -            data.putString(KeychainIntentService.ENCRYPT_INPUT_FILE, mInputFilename); -        } +        data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URI); +        data.putParcelable(KeychainIntentService.ENCRYPT_INPUT_URI, mInputUri); -        if (mOutputUri != null) { -            data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URI); -            data.putParcelable(KeychainIntentService.ENCRYPT_OUTPUT_URI, mOutputUri); -        } else { -            data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_FILE); -            data.putString(KeychainIntentService.ENCRYPT_OUTPUT_FILE, mOutputFilename); -        } +        data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URI); +        data.putParcelable(KeychainIntentService.ENCRYPT_OUTPUT_URI, mOutputUri);          if (mEncryptInterface.isModeSymmetric()) {              Log.d(Constants.TAG, "Symmetric encryption enabled!"); @@ -350,25 +295,16 @@ public class EncryptFileFragment extends Fragment {                      if (mDeleteAfter.isChecked()) {                          // Create and show dialog to delete original file -                        DeleteFileDialogFragment deleteFileDialog; -                        if (mInputUri != null) { -                            deleteFileDialog = DeleteFileDialogFragment.newInstance(mInputUri); -                        } else { -                            deleteFileDialog = DeleteFileDialogFragment -                                    .newInstance(mInputFilename); -                        } +                        DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment.newInstance(mInputUri);                          deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog"); +                        setInputUri(null);                      }                      if (mShareAfter.isChecked()) {                          // Share encrypted file                          Intent sendFileIntent = new Intent(Intent.ACTION_SEND);                          sendFileIntent.setType("*/*"); -                        if (mOutputUri != null) { -                            sendFileIntent.putExtra(Intent.EXTRA_STREAM, mOutputUri); -                        } else { -                            sendFileIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(mOutputFilename)); -                        } +                        sendFileIntent.putExtra(Intent.EXTRA_STREAM, mOutputUri);                          startActivity(Intent.createChooser(sendFileIntent,                                  getString(R.string.title_share_file)));                      } @@ -390,28 +326,17 @@ public class EncryptFileFragment extends Fragment {      @Override      public void onActivityResult(int requestCode, int resultCode, Intent data) {          switch (requestCode) { -            case RESULT_CODE_FILE: { +            case REQUEST_CODE_INPUT: {                  if (resultCode == Activity.RESULT_OK && data != null) { -                    if (Constants.KITKAT) { -                        mInputUri = data.getData(); -                        Cursor cursor = getActivity().getContentResolver().query(mInputUri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null); -                        if (cursor != null) { -                            if (cursor.moveToNext()) { -                                mInputFilename = cursor.getString(0); -                                mFilename.setText(mInputFilename); -                            } -                            cursor.close(); -                        } -                    } else { -                        try { -                            String path = FileHelper.getPath(getActivity(), data.getData()); -                            Log.d(Constants.TAG, "path=" + path); - -                            mFilename.setText(path); -                        } catch (NullPointerException e) { -                            Log.e(Constants.TAG, "Nullpointer while retrieving path!"); -                        } -                    } +                    setInputUri(data.getData()); +                } +                return; +            } +            case REQUEST_CODE_OUTPUT: { +                // This happens after output file was selected, so start our operation +                if (resultCode == Activity.RESULT_OK && data != null) { +                    mOutputUri = data.getData(); +                    encryptStart();                  }                  return;              } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java index ce885c419..cb53647f6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java @@ -66,7 +66,7 @@ public class ImportKeysFileFragment extends Fragment {                  // open .asc or .gpg files                  // setting it to text/plain prevents Cyanogenmod's file manager from selecting asc                  // or gpg types! -                FileHelper.openFile(ImportKeysFileFragment.this, Constants.Path.APP_DIR + "/", +                FileHelper.openFile(ImportKeysFileFragment.this, Uri.fromFile(Constants.Path.APP_DIR),                          "*/*", REQUEST_CODE_FILE);              }          }); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index d2cb5283d..0940d5632 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -181,8 +181,8 @@ public class KeyListFragment extends LoaderFragment                          case R.id.menu_key_list_multi_export: {                              ids = mAdapter.getCurrentSelectedMasterKeyIds();                              ExportHelper mExportHelper = new ExportHelper((ActionBarActivity) getActivity()); -                            mExportHelper.showExportKeysDialog( -                                    ids, Constants.Path.APP_DIR_FILE, mAdapter.isAnySecretSelected()); +                            mExportHelper.showExportKeysDialog(ids, Constants.Path.APP_DIR_FILE, +                                    mAdapter.isAnySecretSelected());                              break;                          }                          case R.id.menu_key_list_multi_select_all: { @@ -205,7 +205,7 @@ public class KeyListFragment extends LoaderFragment                  public void onItemCheckedStateChanged(ActionMode mode, int position, long id,                                                        boolean checked) {                      if (checked) { -                        mAdapter.setNewSelection(position, checked); +                        mAdapter.setNewSelection(position, true);                      } else {                          mAdapter.removeSelection(position);                      } @@ -452,7 +452,7 @@ public class KeyListFragment extends LoaderFragment              ItemViewHolder holder = new ItemViewHolder();              holder.mMainUserId = (TextView) view.findViewById(R.id.mainUserId);              holder.mMainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); -            holder.mStatusDivider = (View) view.findViewById(R.id.status_divider); +            holder.mStatusDivider = view.findViewById(R.id.status_divider);              holder.mStatusLayout = (FrameLayout) view.findViewById(R.id.status_layout);              holder.mButton = (ImageButton) view.findViewById(R.id.edit);              holder.mRevoked = (TextView) view.findViewById(R.id.revoked); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index a9cd0976b..22a23e6a5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -312,8 +312,7 @@ public class ViewKeyActivity extends ActionBarActivity implements          exportHelper.showExportKeysDialog(                  new long[]{(Long) data.get(KeychainContract.KeyRings.MASTER_KEY_ID)}, -                Constants.Path.APP_DIR_FILE, -                ((Long) data.get(KeychainContract.KeyRings.HAS_SECRET) == 1) +                Constants.Path.APP_DIR_FILE, ((Long) data.get(KeychainContract.KeyRings.HAS_SECRET) == 1)          );      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java index cae6cf043..f9111d885 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java @@ -18,43 +18,23 @@  package org.sufficientlysecure.keychain.ui.dialog;  import android.app.Dialog; -import android.app.ProgressDialog;  import android.content.DialogInterface; -import android.content.Intent;  import android.net.Uri;  import android.os.Bundle; -import android.os.Message; -import android.os.Messenger;  import android.provider.DocumentsContract;  import android.support.v4.app.DialogFragment;  import android.support.v4.app.FragmentActivity; -import android.widget.Toast; +import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.helper.FileHelper;  public class DeleteFileDialogFragment extends DialogFragment { -    private static final String ARG_DELETE_FILE = "delete_file";      private static final String ARG_DELETE_URI = "delete_uri";      /**       * Creates new instance of this delete file dialog fragment       */ -    public static DeleteFileDialogFragment newInstance(String deleteFile) { -        DeleteFileDialogFragment frag = new DeleteFileDialogFragment(); -        Bundle args = new Bundle(); - -        args.putString(ARG_DELETE_FILE, deleteFile); - -        frag.setArguments(args); - -        return frag; -    } - -    /** -     * Creates new instance of this delete file dialog fragment -     */      public static DeleteFileDialogFragment newInstance(Uri deleteUri) {          DeleteFileDialogFragment frag = new DeleteFileDialogFragment();          Bundle args = new Bundle(); @@ -74,14 +54,14 @@ public class DeleteFileDialogFragment extends DialogFragment {          final FragmentActivity activity = getActivity();          final Uri deleteUri = getArguments().containsKey(ARG_DELETE_URI) ? getArguments().<Uri>getParcelable(ARG_DELETE_URI) : null; -        final String deleteFile = getArguments().getString(ARG_DELETE_FILE); +        String deleteFilename = FileHelper.getFilename(getActivity(), deleteUri);          CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);          alert.setIcon(R.drawable.ic_dialog_alert_holo_light);          alert.setTitle(R.string.warning); -        alert.setMessage(this.getString(R.string.file_delete_confirmation, deleteFile)); +        alert.setMessage(this.getString(R.string.file_delete_confirmation, deleteFilename));          alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @@ -89,12 +69,14 @@ public class DeleteFileDialogFragment extends DialogFragment {              public void onClick(DialogInterface dialog, int id) {                  dismiss(); -                if (deleteUri != null) { +                if (Constants.KITKAT) {                      // We can not securely delete Documents, so just use usual delete on them -                    DocumentsContract.deleteDocument(getActivity().getContentResolver(), deleteUri); -                    return; +                    if (DocumentsContract.deleteDocument(getActivity().getContentResolver(), deleteUri)) return;                  } +                // TODO!!! We can't delete files from Uri without trying to find it's real path + +                /*                  // Send all information needed to service to edit key in other thread                  Intent intent = new Intent(activity, KeychainIntentService.class); @@ -102,7 +84,6 @@ public class DeleteFileDialogFragment extends DialogFragment {                  Bundle data = new Bundle();                  intent.setAction(KeychainIntentService.ACTION_DELETE_FILE_SECURELY); -                data.putString(KeychainIntentService.DELETE_FILE, deleteFile);                  intent.putExtra(KeychainIntentService.EXTRA_DATA, data);                  ProgressDialogFragment deletingDialog = ProgressDialogFragment.newInstance( @@ -134,6 +115,7 @@ public class DeleteFileDialogFragment extends DialogFragment {                  // start service with intent                  activity.startService(intent); +                */              }          });          alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java index 448787ee2..15d50d7ed 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java @@ -23,13 +23,11 @@ import android.app.Dialog;  import android.content.Context;  import android.content.DialogInterface;  import android.content.Intent; -import android.database.Cursor;  import android.net.Uri;  import android.os.Bundle;  import android.os.Message;  import android.os.Messenger;  import android.os.RemoteException; -import android.provider.OpenableColumns;  import android.support.v4.app.DialogFragment;  import android.view.LayoutInflater;  import android.view.View; @@ -37,12 +35,16 @@ import android.widget.CheckBox;  import android.widget.EditText;  import android.widget.ImageButton;  import android.widget.TextView; -  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.helper.FileHelper;  import org.sufficientlysecure.keychain.util.Log; +import java.io.File; + +/** + * This is a file chooser dialog no longer used with KitKat + */  public class FileDialogFragment extends DialogFragment {      private static final String ARG_MESSENGER = "messenger";      private static final String ARG_TITLE = "title"; @@ -52,8 +54,7 @@ public class FileDialogFragment extends DialogFragment {      public static final int MESSAGE_OKAY = 1; -    public static final String MESSAGE_DATA_URI = "uri"; -    public static final String MESSAGE_DATA_FILENAME = "filename"; +    public static final String MESSAGE_DATA_FILE = "file";      public static final String MESSAGE_DATA_CHECKED = "checked";      private Messenger mMessenger; @@ -63,8 +64,7 @@ public class FileDialogFragment extends DialogFragment {      private CheckBox mCheckBox;      private TextView mMessageTextView; -    private String mOutputFilename; -    private Uri mOutputUri; +    private File mFile;      private static final int REQUEST_CODE = 0x00007004; @@ -72,14 +72,14 @@ public class FileDialogFragment extends DialogFragment {       * Creates new instance of this file dialog fragment       */      public static FileDialogFragment newInstance(Messenger messenger, String title, String message, -                                                 String defaultFile, String checkboxText) { +                                                 File defaultFile, String checkboxText) {          FileDialogFragment frag = new FileDialogFragment();          Bundle args = new Bundle();          args.putParcelable(ARG_MESSENGER, messenger);          args.putString(ARG_TITLE, title);          args.putString(ARG_MESSAGE, message); -        args.putString(ARG_DEFAULT_FILE, defaultFile); +        args.putString(ARG_DEFAULT_FILE, defaultFile.getAbsolutePath());          args.putString(ARG_CHECKBOX_TEXT, checkboxText);          frag.setArguments(args); @@ -98,7 +98,11 @@ public class FileDialogFragment extends DialogFragment {          String title = getArguments().getString(ARG_TITLE);          String message = getArguments().getString(ARG_MESSAGE); -        mOutputFilename = getArguments().getString(ARG_DEFAULT_FILE); +        mFile = new File(getArguments().getString(ARG_DEFAULT_FILE)); +        if (!mFile.isAbsolute()) { +            // We use OK dir by default +            mFile = new File(Constants.Path.APP_DIR.getAbsolutePath(), mFile.getName()); +        }          String checkboxText = getArguments().getString(ARG_CHECKBOX_TEXT);          LayoutInflater inflater = (LayoutInflater) activity @@ -112,18 +116,14 @@ public class FileDialogFragment extends DialogFragment {          mMessageTextView.setText(message);          mFilename = (EditText) view.findViewById(R.id.input); -        mFilename.setText(mOutputFilename); +        mFilename.setText(mFile.getName());          mBrowse = (ImageButton) view.findViewById(R.id.btn_browse);          mBrowse.setOnClickListener(new View.OnClickListener() {              public void onClick(View v) {                  // only .asc or .gpg files                  // setting it to text/plain prevents Cynaogenmod's file manager from selecting asc                  // or gpg types! -                if (Constants.KITKAT) { -                    FileHelper.saveDocument(FileDialogFragment.this, mOutputUri, "*/*", REQUEST_CODE); -                } else { -                    FileHelper.openFile(FileDialogFragment.this, mOutputFilename, "*/*", REQUEST_CODE); -                } +                FileHelper.openFile(FileDialogFragment.this, Uri.fromFile(mFile), "*/*", REQUEST_CODE);              }          }); @@ -146,19 +146,23 @@ public class FileDialogFragment extends DialogFragment {                  dismiss();                  String currentFilename = mFilename.getText().toString(); -                if (mOutputFilename == null || !mOutputFilename.equals(currentFilename)) { -                    mOutputUri = null; -                    mOutputFilename = mFilename.getText().toString(); +                if (currentFilename == null || currentFilename.isEmpty()) { +                    // No file is like pressing cancel, UI: maybe disable positive button in this case? +                    return; +                } + +                if (mFile == null || currentFilename.startsWith("/")) { +                    mFile = new File(currentFilename); +                } else if (!mFile.getName().equals(currentFilename)) { +                    // We update our File object if user changed name! +                    mFile = new File(mFile.getParentFile(), currentFilename);                  }                  boolean checked = mCheckBox.isEnabled() && mCheckBox.isChecked();                  // return resulting data back to activity                  Bundle data = new Bundle(); -                if (mOutputUri != null) { -                    data.putParcelable(MESSAGE_DATA_URI, mOutputUri); -                } -                data.putString(MESSAGE_DATA_FILENAME, mFilename.getText().toString()); +                data.putString(MESSAGE_DATA_FILE, mFile.getAbsolutePath());                  data.putBoolean(MESSAGE_DATA_CHECKED, checked);                  sendMessageToHandler(MESSAGE_OKAY, data); @@ -175,44 +179,17 @@ public class FileDialogFragment extends DialogFragment {          return alert.show();      } -    /** -     * Updates filename in dialog, normally called in onActivityResult in activity using the -     * FileDialog -     */ -    private void setFilename(String filename) { -        AlertDialog dialog = (AlertDialog) getDialog(); -        EditText filenameEditText = (EditText) dialog.findViewById(R.id.input); - -        if (filenameEditText != null) { -            filenameEditText.setText(filename); -        } -    } -      @Override      public void onActivityResult(int requestCode, int resultCode, Intent data) {          switch (requestCode & 0xFFFF) {              case REQUEST_CODE: {                  if (resultCode == Activity.RESULT_OK && data != null) { -                    if (Constants.KITKAT) { -                        mOutputUri = data.getData(); -                        Cursor cursor = getActivity().getContentResolver().query(mOutputUri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null); -                        if (cursor != null) { -                            if (cursor.moveToNext()) { -                                mOutputFilename = cursor.getString(0); -                                mFilename.setText(mOutputFilename); -                            } -                            cursor.close(); -                        } +                    File file = new File(data.getData().getPath()); +                    if (file.getParentFile().exists()) { +                        mFile = file; +                        mFilename.setText(mFile.getName());                      } else { -                        try { -                            String path = data.getData().getPath(); -                            Log.d(Constants.TAG, "path=" + path); - -                            // set filename used in export/import dialogs -                            setFilename(path); -                        } catch (NullPointerException e) { -                            Log.e(Constants.TAG, "Nullpointer while retrieving path!", e); -                        } +                        AppMsg.makeText(getActivity(), R.string.no_file_selected, AppMsg.STYLE_ALERT).show();                      }                  } diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 39b105664..25cdfb541 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -196,6 +196,7 @@      <string name="wrong_passphrase">Wrong passphrase.</string>      <string name="set_a_passphrase">Set a passphrase first.</string>      <string name="no_filemanager_installed">No compatible file manager installed.</string> +    <string name="filemanager_no_write">The file manager does not support saving.</string>      <string name="passphrases_do_not_match">The passphrases didn\'t match.</string>      <string name="passphrase_must_not_be_empty">Please enter a passphrase.</string>      <string name="passphrase_for_symmetric_encryption">Symmetric encryption.</string> | 
