From 5e5febaee38836d546d3cc5b9ce0003f206d4aa5 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 18 Jun 2015 16:25:18 +0200 Subject: rename activities to match new purpose --- .../keychain/ui/DecryptActivity.java | 153 +++++ .../keychain/ui/DecryptFilesActivity.java | 150 ----- .../keychain/ui/DecryptFilesInputFragment.java | 2 +- .../keychain/ui/DecryptFilesListFragment.java | 633 --------------------- .../keychain/ui/DecryptListFragment.java | 633 +++++++++++++++++++++ .../keychain/ui/DecryptTextActivity.java | 228 -------- .../keychain/ui/DecryptTextFragment.java | 189 ------ .../keychain/ui/DisplayTextActivity.java | 230 ++++++++ .../keychain/ui/DisplayTextFragment.java | 189 ++++++ .../ui/EncryptDecryptOverviewFragment.java | 8 +- 10 files changed, 1210 insertions(+), 1205 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesListFragment.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextActivity.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextFragment.java (limited to 'OpenKeychain/src/main/java') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java new file mode 100644 index 000000000..aba680807 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sufficientlysecure.keychain.ui; + +import java.util.ArrayList; + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; +import android.view.View; +import android.widget.Toast; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.intents.OpenKeychainIntents; +import org.sufficientlysecure.keychain.ui.base.BaseActivity; + + +public class DecryptActivity extends BaseActivity { + + /* Intents */ + public static final String ACTION_DECRYPT_DATA = OpenKeychainIntents.DECRYPT_DATA; + // TODO handle this intent + public static final String ACTION_DECRYPT_TEXT = OpenKeychainIntents.DECRYPT_TEXT; + + // intern + public static final String ACTION_DECRYPT_DATA_OPEN = Constants.INTENT_PREFIX + "DECRYPT_DATA_OPEN"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setFullScreenDialogClose(new View.OnClickListener() { + @Override + public void onClick(View v) { + setResult(Activity.RESULT_CANCELED); + finish(); + } + }, false); + + // Handle intent actions + handleActions(savedInstanceState, getIntent()); + } + + @Override + protected void initLayout() { + setContentView(R.layout.decrypt_files_activity); + } + + /** + * Handles all actions with this intent + */ + private void handleActions(Bundle savedInstanceState, Intent intent) { + + // No need to initialize fragments if we are just being restored + if (savedInstanceState != null) { + return; + } + + ArrayList uris = new ArrayList<>(); + + String action = intent.getAction(); + + // TODO handle ACTION_DECRYPT_FROM_CLIPBOARD + switch (action) { + case Intent.ACTION_SEND: { + // When sending to Keychain Decrypt via share menu + // Binary via content provider (could also be files) + // override uri to get stream from send + action = ACTION_DECRYPT_DATA; + uris.add(intent.getParcelableExtra(Intent.EXTRA_STREAM)); + break; + } + + case Intent.ACTION_SEND_MULTIPLE: { + action = ACTION_DECRYPT_DATA; + uris.addAll(intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM)); + break; + } + + case Intent.ACTION_VIEW: + // Android's Action when opening file associated to Keychain (see AndroidManifest.xml) + action = ACTION_DECRYPT_DATA; + + // fallthrough + default: + uris.add(intent.getData()); + + } + + if (ACTION_DECRYPT_DATA.equals(action)) { + // Definitely need a data uri with the decrypt_data intent + if (uris.isEmpty()) { + Toast.makeText(this, "No data to decrypt!", Toast.LENGTH_LONG).show(); + setResult(Activity.RESULT_CANCELED); + finish(); + } + displayListFragment(uris); + return; + } + + boolean showOpenDialog = ACTION_DECRYPT_DATA_OPEN.equals(action); + displayInputFragment(showOpenDialog); + + } + + public void displayInputFragment(boolean showOpenDialog) { + DecryptFilesInputFragment frag = DecryptFilesInputFragment.newInstance(showOpenDialog); + + // Add the fragment to the 'fragment_container' FrameLayout + // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! + getSupportFragmentManager().beginTransaction() + .replace(R.id.decrypt_files_fragment_container, frag) + .commit(); + } + + public void displayListFragment(ArrayList inputUris) { + + DecryptListFragment frag = DecryptListFragment.newInstance(inputUris); + + FragmentManager fragMan = getSupportFragmentManager(); + + FragmentTransaction trans = fragMan.beginTransaction(); + trans.replace(R.id.decrypt_files_fragment_container, frag); + + // if there already is a fragment, allow going back to that. otherwise, we're top level! + if (fragMan.getFragments() != null && !fragMan.getFragments().isEmpty()) { + trans.addToBackStack("list"); + } + + trans.commit(); + + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java deleted file mode 100644 index 672015aaa..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui; - -import java.util.ArrayList; - -import android.app.Activity; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; -import android.view.View; -import android.widget.Toast; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.intents.OpenKeychainIntents; -import org.sufficientlysecure.keychain.ui.base.BaseActivity; - - -public class DecryptFilesActivity extends BaseActivity { - - /* Intents */ - public static final String ACTION_DECRYPT_DATA = OpenKeychainIntents.DECRYPT_DATA; - - // intern - public static final String ACTION_DECRYPT_DATA_OPEN = Constants.INTENT_PREFIX + "DECRYPT_DATA_OPEN"; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setFullScreenDialogClose(new View.OnClickListener() { - @Override - public void onClick(View v) { - setResult(Activity.RESULT_CANCELED); - finish(); - } - }, false); - - // Handle intent actions - handleActions(savedInstanceState, getIntent()); - } - - @Override - protected void initLayout() { - setContentView(R.layout.decrypt_files_activity); - } - - /** - * Handles all actions with this intent - */ - private void handleActions(Bundle savedInstanceState, Intent intent) { - - // No need to initialize fragments if we are just being restored - if (savedInstanceState != null) { - return; - } - - ArrayList uris = new ArrayList<>(); - - String action = intent.getAction(); - - switch (action) { - case Intent.ACTION_SEND: { - // When sending to Keychain Decrypt via share menu - // Binary via content provider (could also be files) - // override uri to get stream from send - action = ACTION_DECRYPT_DATA; - uris.add(intent.getParcelableExtra(Intent.EXTRA_STREAM)); - break; - } - - case Intent.ACTION_SEND_MULTIPLE: { - action = ACTION_DECRYPT_DATA; - uris.addAll(intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM)); - break; - } - - case Intent.ACTION_VIEW: - // Android's Action when opening file associated to Keychain (see AndroidManifest.xml) - action = ACTION_DECRYPT_DATA; - - // fallthrough - default: - uris.add(intent.getData()); - - } - - if (ACTION_DECRYPT_DATA.equals(action)) { - // Definitely need a data uri with the decrypt_data intent - if (uris.isEmpty()) { - Toast.makeText(this, "No data to decrypt!", Toast.LENGTH_LONG).show(); - setResult(Activity.RESULT_CANCELED); - finish(); - } - displayListFragment(uris); - return; - } - - boolean showOpenDialog = ACTION_DECRYPT_DATA_OPEN.equals(action); - displayInputFragment(showOpenDialog); - - } - - public void displayInputFragment(boolean showOpenDialog) { - DecryptFilesInputFragment frag = DecryptFilesInputFragment.newInstance(showOpenDialog); - - // Add the fragment to the 'fragment_container' FrameLayout - // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! - getSupportFragmentManager().beginTransaction() - .replace(R.id.decrypt_files_fragment_container, frag) - .commit(); - } - - public void displayListFragment(ArrayList inputUris) { - - DecryptFilesListFragment frag = DecryptFilesListFragment.newInstance(inputUris); - - FragmentManager fragMan = getSupportFragmentManager(); - - FragmentTransaction trans = fragMan.beginTransaction(); - trans.replace(R.id.decrypt_files_fragment_container, frag); - - // if there already is a fragment, allow going back to that. otherwise, we're top level! - if (fragMan.getFragments() != null && !fragMan.getFragments().isEmpty()) { - trans.addToBackStack("list"); - } - - trans.commit(); - - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesInputFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesInputFragment.java index 2b9219f49..9a0d95bb4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesInputFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesInputFragment.java @@ -127,7 +127,7 @@ public class DecryptFilesInputFragment extends Fragment { return; } - DecryptFilesActivity activity = (DecryptFilesActivity) getActivity(); + DecryptActivity activity = (DecryptActivity) getActivity(); ArrayList uris = new ArrayList<>(); uris.add(mInputUri); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesListFragment.java deleted file mode 100644 index 35f534c90..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesListFragment.java +++ /dev/null @@ -1,633 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui; - - -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ResolveInfo; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.support.v7.widget.DefaultItemAnimator; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.PopupMenu; -import android.widget.PopupMenu.OnDismissListener; -import android.widget.PopupMenu.OnMenuItemClickListener; -import android.widget.ProgressBar; -import android.widget.TextView; -import android.widget.ViewAnimator; - -import org.openintents.openpgp.OpenPgpMetadata; -import org.openintents.openpgp.OpenPgpSignatureResult; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; -import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider; -// this import NEEDS to be above the ViewModel one, or it won't compile! (as of 06/06/15) -import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.StatusHolder; -import org.sufficientlysecure.keychain.ui.DecryptFilesListFragment.DecryptFilesAdapter.ViewModel; -import org.sufficientlysecure.keychain.ui.adapter.SpacesItemDecoration; -import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; -import org.sufficientlysecure.keychain.ui.util.FormattingUtils; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.ui.util.Notify; -import org.sufficientlysecure.keychain.ui.util.Notify.Style; -import org.sufficientlysecure.keychain.util.FileHelper; -import org.sufficientlysecure.keychain.util.Log; - -public class DecryptFilesListFragment - extends CryptoOperationFragment - implements OnMenuItemClickListener { - public static final String ARG_URIS = "uris"; - - private static final int REQUEST_CODE_OUTPUT = 0x00007007; - - private ArrayList mInputUris; - private HashMap mOutputUris; - private ArrayList mPendingInputUris; - - private Uri mCurrentInputUri; - - private DecryptFilesAdapter mAdapter; - - /** - * Creates new instance of this fragment - */ - public static DecryptFilesListFragment newInstance(ArrayList uris) { - DecryptFilesListFragment frag = new DecryptFilesListFragment(); - - Bundle args = new Bundle(); - args.putParcelableArrayList(ARG_URIS, uris); - frag.setArguments(args); - - return frag; - } - - /** - * Inflate the layout for this fragment - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.decrypt_files_list_fragment, container, false); - - RecyclerView vFilesList = (RecyclerView) view.findViewById(R.id.decrypted_files_list); - - vFilesList.addItemDecoration(new SpacesItemDecoration( - FormattingUtils.dpToPx(getActivity(), 4))); - vFilesList.setHasFixedSize(true); - vFilesList.setLayoutManager(new LinearLayoutManager(getActivity())); - vFilesList.setItemAnimator(new DefaultItemAnimator()); - - mAdapter = new DecryptFilesAdapter(getActivity(), this); - vFilesList.setAdapter(mAdapter); - - return view; - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - - outState.putParcelableArrayList(ARG_URIS, mInputUris); - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - displayInputUris(getArguments().getParcelableArrayList(ARG_URIS)); - } - - private String removeEncryptedAppend(String name) { - if (name.endsWith(Constants.FILE_EXTENSION_ASC) - || name.endsWith(Constants.FILE_EXTENSION_PGP_MAIN) - || name.endsWith(Constants.FILE_EXTENSION_PGP_ALTERNATE)) { - return name.substring(0, name.length() - 4); - } - return name; - } - - private void askForOutputFilename(Uri inputUri, String originalFilename, String mimeType) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - File file = new File(inputUri.getPath()); - File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR; - File targetFile = new File(parentDir, originalFilename); - 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, mimeType, originalFilename, REQUEST_CODE_OUTPUT); - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case REQUEST_CODE_OUTPUT: { - // This happens after output file was selected, so start our operation - if (resultCode == Activity.RESULT_OK && data != null) { - Uri saveUri = data.getData(); - Uri outputUri = mOutputUris.get(mCurrentInputUri); - // TODO save from outputUri to saveUri - - mCurrentInputUri = null; - } - return; - } - - default: { - super.onActivityResult(requestCode, resultCode, data); - } - } - } - - private void displayInputUris(ArrayList uris) { - mInputUris = uris; - mOutputUris = new HashMap<>(uris.size()); - for (Uri uri : uris) { - mAdapter.add(uri); - mOutputUris.put(uri, TemporaryStorageProvider.createFile(getActivity())); - } - - mPendingInputUris = uris; - - cryptoOperation(); - } - - @Override - protected void cryptoOperation(CryptoInputParcel cryptoInput) { - super.cryptoOperation(cryptoInput, false); - } - - @Override - protected boolean onCryptoSetProgress(String msg, int progress, int max) { - mAdapter.setProgress(mCurrentInputUri, progress, max, msg); - return true; - } - - @Override - protected void dismissProgress() { - // progress shown inline, so never mind - } - - @Override - protected void onCryptoOperationError(DecryptVerifyResult result) { - final Uri uri = mCurrentInputUri; - mCurrentInputUri = null; - - mAdapter.addResult(uri, result, null, null, null); - - cryptoOperation(); - } - - @Override - protected void onCryptoOperationSuccess(DecryptVerifyResult result) { - final Uri uri = mCurrentInputUri; - mCurrentInputUri = null; - - Drawable icon = null; - OnClickListener onFileClick = null, onKeyClick = null; - - if (result.getDecryptMetadata() != null && result.getDecryptMetadata().getMimeType() != null) { - icon = loadIcon(result.getDecryptMetadata().getMimeType()); - } - - OpenPgpSignatureResult sigResult = result.getSignatureResult(); - if (sigResult != null) { - final long keyId = sigResult.getKeyId(); - if (sigResult.getStatus() != OpenPgpSignatureResult.SIGNATURE_KEY_MISSING) { - onKeyClick = new OnClickListener() { - @Override - public void onClick(View view) { - Activity activity = getActivity(); - if (activity == null) { - return; - } - Intent intent = new Intent(activity, ViewKeyActivity.class); - intent.setData(KeyRings.buildUnifiedKeyRingUri(keyId)); - activity.startActivity(intent); - } - }; - } - } - - if (result.success() && result.getDecryptMetadata() != null) { - final OpenPgpMetadata metadata = result.getDecryptMetadata(); - onFileClick = new OnClickListener() { - @Override - public void onClick(View view) { - Activity activity = getActivity(); - if (activity == null || mCurrentInputUri != null) { - return; - } - - Uri outputUri = mOutputUris.get(uri); - Intent intent = new Intent(); - intent.setDataAndType(outputUri, metadata.getMimeType()); - activity.startActivity(intent); - } - }; - } - - mAdapter.addResult(uri, result, icon, onFileClick, onKeyClick); - - cryptoOperation(); - - } - - @Override - protected PgpDecryptVerifyInputParcel createOperationInput() { - - if (mCurrentInputUri == null) { - if (mPendingInputUris.isEmpty()) { - // nothing left to do - return null; - } - - mCurrentInputUri = mPendingInputUris.remove(0); - } - - Uri currentOutputUri = mOutputUris.get(mCurrentInputUri); - Log.d(Constants.TAG, "mInputUri=" + mCurrentInputUri + ", mOutputUri=" + currentOutputUri); - - return new PgpDecryptVerifyInputParcel(mCurrentInputUri, currentOutputUri) - .setAllowSymmetricDecryption(true); - - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - } - - @Override - public boolean onMenuItemClick(MenuItem menuItem) { - if (mAdapter.mMenuClickedModel == null || !mAdapter.mMenuClickedModel.hasResult()) { - return false; - } - Activity activity = getActivity(); - if (activity == null) { - return false; - } - - ViewModel model = mAdapter.mMenuClickedModel; - DecryptVerifyResult result = model.mResult; - switch (menuItem.getItemId()) { - case R.id.view_log: - Intent intent = new Intent(activity, LogDisplayActivity.class); - intent.putExtra(LogDisplayFragment.EXTRA_RESULT, result); - activity.startActivity(intent); - return true; - case R.id.decrypt_save: - OpenPgpMetadata metadata = result.getDecryptMetadata(); - if (metadata == null) { - return true; - } - mCurrentInputUri = model.mInputUri; - askForOutputFilename(model.mInputUri, metadata.getFilename(), metadata.getMimeType()); - return true; - case R.id.decrypt_delete: - Notify.create(activity, "decrypt/delete not yet implemented", Style.ERROR).show(this); - return true; - } - return false; - } - - public static class DecryptFilesAdapter extends RecyclerView.Adapter { - private Context mContext; - private ArrayList mDataset; - private OnMenuItemClickListener mMenuItemClickListener; - private ViewModel mMenuClickedModel; - - public class ViewModel { - Context mContext; - Uri mInputUri; - DecryptVerifyResult mResult; - Drawable mIcon; - - OnClickListener mOnFileClickListener; - OnClickListener mOnKeyClickListener; - - int mProgress, mMax; - String mProgressMsg; - - ViewModel(Context context, Uri uri) { - mContext = context; - mInputUri = uri; - mProgress = 0; - mMax = 100; - } - - void addResult(DecryptVerifyResult result) { - mResult = result; - } - - void addIcon(Drawable icon) { - mIcon = icon; - } - - void setOnClickListeners(OnClickListener onFileClick, OnClickListener onKeyClick) { - mOnFileClickListener = onFileClick; - mOnKeyClickListener = onKeyClick; - } - - boolean hasResult() { - return mResult != null; - } - - void setProgress(int progress, int max, String msg) { - if (msg != null) { - mProgressMsg = msg; - } - mProgress = progress; - mMax = max; - } - - // Depends on inputUri only - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ViewModel viewModel = (ViewModel) o; - return !(mResult != null ? !mResult.equals(viewModel.mResult) - : viewModel.mResult != null); - } - - // Depends on inputUri only - @Override - public int hashCode() { - return mResult != null ? mResult.hashCode() : 0; - } - - @Override - public String toString() { - return mResult.toString(); - } - } - - // Provide a suitable constructor (depends on the kind of dataset) - public DecryptFilesAdapter(Context context, OnMenuItemClickListener menuItemClickListener) { - mContext = context; - mMenuItemClickListener = menuItemClickListener; - mDataset = new ArrayList<>(); - } - - // Create new views (invoked by the layout manager) - @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - //inflate your layout and pass it to view holder - View v = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.decrypt_list_entry, parent, false); - return new ViewHolder(v); - } - - // Replace the contents of a view (invoked by the layout manager) - @Override - public void onBindViewHolder(ViewHolder holder, final int position) { - // - get element from your dataset at this position - // - replace the contents of the view with that element - final ViewModel model = mDataset.get(position); - - if (model.hasResult()) { - if (holder.vAnimator.getDisplayedChild() != 1) { - holder.vAnimator.setDisplayedChild(1); - } - - KeyFormattingUtils.setStatus(mContext, holder, model.mResult); - - OpenPgpMetadata metadata = model.mResult.getDecryptMetadata(); - holder.vFilename.setText(metadata.getFilename()); - - long size = metadata.getOriginalSize(); - if (size == -1 || size == 0) { - holder.vFilesize.setText(""); - } else { - holder.vFilesize.setText(FileHelper.readableFileSize(size)); - } - - // TODO thumbnail from OpenPgpMetadata - if (model.mIcon != null) { - holder.vThumbnail.setImageDrawable(model.mIcon); - } else { - holder.vThumbnail.setImageResource(R.drawable.ic_doc_generic_am); - } - - holder.vFile.setOnClickListener(model.mOnFileClickListener); - holder.vSignatureLayout.setOnClickListener(model.mOnKeyClickListener); - - holder.vContextMenu.setTag(model); - holder.vContextMenu.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - mMenuClickedModel = model; - PopupMenu menu = new PopupMenu(mContext, view); - menu.inflate(R.menu.decrypt_item_context_menu); - menu.setOnMenuItemClickListener(mMenuItemClickListener); - menu.setOnDismissListener(new OnDismissListener() { - @Override - public void onDismiss(PopupMenu popupMenu) { - mMenuClickedModel = null; - } - }); - menu.show(); - } - }); - - } else { - if (holder.vAnimator.getDisplayedChild() != 0) { - holder.vAnimator.setDisplayedChild(0); - } - - holder.vProgress.setProgress(model.mProgress); - holder.vProgress.setMax(model.mMax); - holder.vProgressMsg.setText(model.mProgressMsg); - } - - } - - // Return the size of your dataset (invoked by the layout manager) - @Override - public int getItemCount() { - return mDataset.size(); - } - - public void add(Uri uri) { - ViewModel newModel = new ViewModel(mContext, uri); - mDataset.add(newModel); - notifyItemInserted(mDataset.size()); - } - - public void setProgress(Uri uri, int progress, int max, String msg) { - ViewModel newModel = new ViewModel(mContext, uri); - int pos = mDataset.indexOf(newModel); - mDataset.get(pos).setProgress(progress, max, msg); - notifyItemChanged(pos); - } - - public void addResult(Uri uri, DecryptVerifyResult result, Drawable icon, - OnClickListener onFileClick, OnClickListener onKeyClick) { - - ViewModel model = new ViewModel(mContext, uri); - int pos = mDataset.indexOf(model); - model = mDataset.get(pos); - - model.addResult(result); - if (icon != null) { - model.addIcon(icon); - } - model.setOnClickListeners(onFileClick, onKeyClick); - - notifyItemChanged(pos); - } - - } - - - // Provide a reference to the views for each data item - // Complex data items may need more than one view per item, and - // you provide access to all the views for a data item in a view holder - public static class ViewHolder extends RecyclerView.ViewHolder implements StatusHolder { - public ViewAnimator vAnimator; - - public ProgressBar vProgress; - public TextView vProgressMsg; - - public View vFile; - public TextView vFilename; - public TextView vFilesize; - public ImageView vThumbnail; - - public ImageView vEncStatusIcon; - public TextView vEncStatusText; - - public ImageView vSigStatusIcon; - public TextView vSigStatusText; - public View vSignatureLayout; - public TextView vSignatureName; - public TextView vSignatureMail; - public TextView vSignatureAction; - - public View vContextMenu; - - public ViewHolder(View itemView) { - super(itemView); - - vAnimator = (ViewAnimator) itemView.findViewById(R.id.view_animator); - - vProgress = (ProgressBar) itemView.findViewById(R.id.progress); - vProgressMsg = (TextView) itemView.findViewById(R.id.progress_msg); - - vFile = itemView.findViewById(R.id.file); - vFilename = (TextView) itemView.findViewById(R.id.filename); - vFilesize = (TextView) itemView.findViewById(R.id.filesize); - vThumbnail = (ImageView) itemView.findViewById(R.id.thumbnail); - - vEncStatusIcon = (ImageView) itemView.findViewById(R.id.result_encryption_icon); - vEncStatusText = (TextView) itemView.findViewById(R.id.result_encryption_text); - - vSigStatusIcon = (ImageView) itemView.findViewById(R.id.result_signature_icon); - vSigStatusText = (TextView) itemView.findViewById(R.id.result_signature_text); - vSignatureLayout = itemView.findViewById(R.id.result_signature_layout); - vSignatureName = (TextView) itemView.findViewById(R.id.result_signature_name); - vSignatureMail= (TextView) itemView.findViewById(R.id.result_signature_email); - vSignatureAction = (TextView) itemView.findViewById(R.id.result_signature_action); - - vContextMenu = itemView.findViewById(R.id.context_menu); - - } - - @Override - public ImageView getEncryptionStatusIcon() { - return vEncStatusIcon; - } - - @Override - public TextView getEncryptionStatusText() { - return vEncStatusText; - } - - @Override - public ImageView getSignatureStatusIcon() { - return vSigStatusIcon; - } - - @Override - public TextView getSignatureStatusText() { - return vSigStatusText; - } - - @Override - public View getSignatureLayout() { - return vSignatureLayout; - } - - @Override - public TextView getSignatureAction() { - return vSignatureAction; - } - - @Override - public TextView getSignatureUserName() { - return vSignatureName; - } - - @Override - public TextView getSignatureUserEmail() { - return vSignatureMail; - } - - @Override - public boolean hasEncrypt() { - return true; - } - } - - private Drawable loadIcon(String mimeType) { - final Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setType(mimeType); - - final List matches = getActivity() - .getPackageManager().queryIntentActivities(intent, 0); - //noinspection LoopStatementThatDoesntLoop - for (ResolveInfo match : matches) { - return match.loadIcon(getActivity().getPackageManager()); - } - return null; - - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java new file mode 100644 index 000000000..65d0fc8cf --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java @@ -0,0 +1,633 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sufficientlysecure.keychain.ui; + + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.support.v7.widget.DefaultItemAnimator; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.PopupMenu; +import android.widget.PopupMenu.OnDismissListener; +import android.widget.PopupMenu.OnMenuItemClickListener; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.ViewAnimator; + +import org.openintents.openpgp.OpenPgpMetadata; +import org.openintents.openpgp.OpenPgpSignatureResult; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider; +// this import NEEDS to be above the ViewModel one, or it won't compile! (as of 06/06/15) +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.StatusHolder; +import org.sufficientlysecure.keychain.ui.DecryptListFragment.DecryptFilesAdapter.ViewModel; +import org.sufficientlysecure.keychain.ui.adapter.SpacesItemDecoration; +import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; +import org.sufficientlysecure.keychain.ui.util.FormattingUtils; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.ui.util.Notify; +import org.sufficientlysecure.keychain.ui.util.Notify.Style; +import org.sufficientlysecure.keychain.util.FileHelper; +import org.sufficientlysecure.keychain.util.Log; + +public class DecryptListFragment + extends CryptoOperationFragment + implements OnMenuItemClickListener { + public static final String ARG_URIS = "uris"; + + private static final int REQUEST_CODE_OUTPUT = 0x00007007; + + private ArrayList mInputUris; + private HashMap mOutputUris; + private ArrayList mPendingInputUris; + + private Uri mCurrentInputUri; + + private DecryptFilesAdapter mAdapter; + + /** + * Creates new instance of this fragment + */ + public static DecryptListFragment newInstance(ArrayList uris) { + DecryptListFragment frag = new DecryptListFragment(); + + Bundle args = new Bundle(); + args.putParcelableArrayList(ARG_URIS, uris); + frag.setArguments(args); + + return frag; + } + + /** + * Inflate the layout for this fragment + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.decrypt_files_list_fragment, container, false); + + RecyclerView vFilesList = (RecyclerView) view.findViewById(R.id.decrypted_files_list); + + vFilesList.addItemDecoration(new SpacesItemDecoration( + FormattingUtils.dpToPx(getActivity(), 4))); + vFilesList.setHasFixedSize(true); + vFilesList.setLayoutManager(new LinearLayoutManager(getActivity())); + vFilesList.setItemAnimator(new DefaultItemAnimator()); + + mAdapter = new DecryptFilesAdapter(getActivity(), this); + vFilesList.setAdapter(mAdapter); + + return view; + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + outState.putParcelableArrayList(ARG_URIS, mInputUris); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + displayInputUris(getArguments().getParcelableArrayList(ARG_URIS)); + } + + private String removeEncryptedAppend(String name) { + if (name.endsWith(Constants.FILE_EXTENSION_ASC) + || name.endsWith(Constants.FILE_EXTENSION_PGP_MAIN) + || name.endsWith(Constants.FILE_EXTENSION_PGP_ALTERNATE)) { + return name.substring(0, name.length() - 4); + } + return name; + } + + private void askForOutputFilename(Uri inputUri, String originalFilename, String mimeType) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + File file = new File(inputUri.getPath()); + File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR; + File targetFile = new File(parentDir, originalFilename); + 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, mimeType, originalFilename, REQUEST_CODE_OUTPUT); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case REQUEST_CODE_OUTPUT: { + // This happens after output file was selected, so start our operation + if (resultCode == Activity.RESULT_OK && data != null) { + Uri saveUri = data.getData(); + Uri outputUri = mOutputUris.get(mCurrentInputUri); + // TODO save from outputUri to saveUri + + mCurrentInputUri = null; + } + return; + } + + default: { + super.onActivityResult(requestCode, resultCode, data); + } + } + } + + private void displayInputUris(ArrayList uris) { + mInputUris = uris; + mOutputUris = new HashMap<>(uris.size()); + for (Uri uri : uris) { + mAdapter.add(uri); + mOutputUris.put(uri, TemporaryStorageProvider.createFile(getActivity())); + } + + mPendingInputUris = uris; + + cryptoOperation(); + } + + @Override + protected void cryptoOperation(CryptoInputParcel cryptoInput) { + super.cryptoOperation(cryptoInput, false); + } + + @Override + protected boolean onCryptoSetProgress(String msg, int progress, int max) { + mAdapter.setProgress(mCurrentInputUri, progress, max, msg); + return true; + } + + @Override + protected void dismissProgress() { + // progress shown inline, so never mind + } + + @Override + protected void onCryptoOperationError(DecryptVerifyResult result) { + final Uri uri = mCurrentInputUri; + mCurrentInputUri = null; + + mAdapter.addResult(uri, result, null, null, null); + + cryptoOperation(); + } + + @Override + protected void onCryptoOperationSuccess(DecryptVerifyResult result) { + final Uri uri = mCurrentInputUri; + mCurrentInputUri = null; + + Drawable icon = null; + OnClickListener onFileClick = null, onKeyClick = null; + + if (result.getDecryptMetadata() != null && result.getDecryptMetadata().getMimeType() != null) { + icon = loadIcon(result.getDecryptMetadata().getMimeType()); + } + + OpenPgpSignatureResult sigResult = result.getSignatureResult(); + if (sigResult != null) { + final long keyId = sigResult.getKeyId(); + if (sigResult.getStatus() != OpenPgpSignatureResult.SIGNATURE_KEY_MISSING) { + onKeyClick = new OnClickListener() { + @Override + public void onClick(View view) { + Activity activity = getActivity(); + if (activity == null) { + return; + } + Intent intent = new Intent(activity, ViewKeyActivity.class); + intent.setData(KeyRings.buildUnifiedKeyRingUri(keyId)); + activity.startActivity(intent); + } + }; + } + } + + if (result.success() && result.getDecryptMetadata() != null) { + final OpenPgpMetadata metadata = result.getDecryptMetadata(); + onFileClick = new OnClickListener() { + @Override + public void onClick(View view) { + Activity activity = getActivity(); + if (activity == null || mCurrentInputUri != null) { + return; + } + + Uri outputUri = mOutputUris.get(uri); + Intent intent = new Intent(); + intent.setDataAndType(outputUri, metadata.getMimeType()); + activity.startActivity(intent); + } + }; + } + + mAdapter.addResult(uri, result, icon, onFileClick, onKeyClick); + + cryptoOperation(); + + } + + @Override + protected PgpDecryptVerifyInputParcel createOperationInput() { + + if (mCurrentInputUri == null) { + if (mPendingInputUris.isEmpty()) { + // nothing left to do + return null; + } + + mCurrentInputUri = mPendingInputUris.remove(0); + } + + Uri currentOutputUri = mOutputUris.get(mCurrentInputUri); + Log.d(Constants.TAG, "mInputUri=" + mCurrentInputUri + ", mOutputUri=" + currentOutputUri); + + return new PgpDecryptVerifyInputParcel(mCurrentInputUri, currentOutputUri) + .setAllowSymmetricDecryption(true); + + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + } + + @Override + public boolean onMenuItemClick(MenuItem menuItem) { + if (mAdapter.mMenuClickedModel == null || !mAdapter.mMenuClickedModel.hasResult()) { + return false; + } + Activity activity = getActivity(); + if (activity == null) { + return false; + } + + ViewModel model = mAdapter.mMenuClickedModel; + DecryptVerifyResult result = model.mResult; + switch (menuItem.getItemId()) { + case R.id.view_log: + Intent intent = new Intent(activity, LogDisplayActivity.class); + intent.putExtra(LogDisplayFragment.EXTRA_RESULT, result); + activity.startActivity(intent); + return true; + case R.id.decrypt_save: + OpenPgpMetadata metadata = result.getDecryptMetadata(); + if (metadata == null) { + return true; + } + mCurrentInputUri = model.mInputUri; + askForOutputFilename(model.mInputUri, metadata.getFilename(), metadata.getMimeType()); + return true; + case R.id.decrypt_delete: + Notify.create(activity, "decrypt/delete not yet implemented", Style.ERROR).show(this); + return true; + } + return false; + } + + public static class DecryptFilesAdapter extends RecyclerView.Adapter { + private Context mContext; + private ArrayList mDataset; + private OnMenuItemClickListener mMenuItemClickListener; + private ViewModel mMenuClickedModel; + + public class ViewModel { + Context mContext; + Uri mInputUri; + DecryptVerifyResult mResult; + Drawable mIcon; + + OnClickListener mOnFileClickListener; + OnClickListener mOnKeyClickListener; + + int mProgress, mMax; + String mProgressMsg; + + ViewModel(Context context, Uri uri) { + mContext = context; + mInputUri = uri; + mProgress = 0; + mMax = 100; + } + + void addResult(DecryptVerifyResult result) { + mResult = result; + } + + void addIcon(Drawable icon) { + mIcon = icon; + } + + void setOnClickListeners(OnClickListener onFileClick, OnClickListener onKeyClick) { + mOnFileClickListener = onFileClick; + mOnKeyClickListener = onKeyClick; + } + + boolean hasResult() { + return mResult != null; + } + + void setProgress(int progress, int max, String msg) { + if (msg != null) { + mProgressMsg = msg; + } + mProgress = progress; + mMax = max; + } + + // Depends on inputUri only + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ViewModel viewModel = (ViewModel) o; + return !(mResult != null ? !mResult.equals(viewModel.mResult) + : viewModel.mResult != null); + } + + // Depends on inputUri only + @Override + public int hashCode() { + return mResult != null ? mResult.hashCode() : 0; + } + + @Override + public String toString() { + return mResult.toString(); + } + } + + // Provide a suitable constructor (depends on the kind of dataset) + public DecryptFilesAdapter(Context context, OnMenuItemClickListener menuItemClickListener) { + mContext = context; + mMenuItemClickListener = menuItemClickListener; + mDataset = new ArrayList<>(); + } + + // Create new views (invoked by the layout manager) + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + //inflate your layout and pass it to view holder + View v = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.decrypt_list_entry, parent, false); + return new ViewHolder(v); + } + + // Replace the contents of a view (invoked by the layout manager) + @Override + public void onBindViewHolder(ViewHolder holder, final int position) { + // - get element from your dataset at this position + // - replace the contents of the view with that element + final ViewModel model = mDataset.get(position); + + if (model.hasResult()) { + if (holder.vAnimator.getDisplayedChild() != 1) { + holder.vAnimator.setDisplayedChild(1); + } + + KeyFormattingUtils.setStatus(mContext, holder, model.mResult); + + OpenPgpMetadata metadata = model.mResult.getDecryptMetadata(); + holder.vFilename.setText(metadata.getFilename()); + + long size = metadata.getOriginalSize(); + if (size == -1 || size == 0) { + holder.vFilesize.setText(""); + } else { + holder.vFilesize.setText(FileHelper.readableFileSize(size)); + } + + // TODO thumbnail from OpenPgpMetadata + if (model.mIcon != null) { + holder.vThumbnail.setImageDrawable(model.mIcon); + } else { + holder.vThumbnail.setImageResource(R.drawable.ic_doc_generic_am); + } + + holder.vFile.setOnClickListener(model.mOnFileClickListener); + holder.vSignatureLayout.setOnClickListener(model.mOnKeyClickListener); + + holder.vContextMenu.setTag(model); + holder.vContextMenu.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + mMenuClickedModel = model; + PopupMenu menu = new PopupMenu(mContext, view); + menu.inflate(R.menu.decrypt_item_context_menu); + menu.setOnMenuItemClickListener(mMenuItemClickListener); + menu.setOnDismissListener(new OnDismissListener() { + @Override + public void onDismiss(PopupMenu popupMenu) { + mMenuClickedModel = null; + } + }); + menu.show(); + } + }); + + } else { + if (holder.vAnimator.getDisplayedChild() != 0) { + holder.vAnimator.setDisplayedChild(0); + } + + holder.vProgress.setProgress(model.mProgress); + holder.vProgress.setMax(model.mMax); + holder.vProgressMsg.setText(model.mProgressMsg); + } + + } + + // Return the size of your dataset (invoked by the layout manager) + @Override + public int getItemCount() { + return mDataset.size(); + } + + public void add(Uri uri) { + ViewModel newModel = new ViewModel(mContext, uri); + mDataset.add(newModel); + notifyItemInserted(mDataset.size()); + } + + public void setProgress(Uri uri, int progress, int max, String msg) { + ViewModel newModel = new ViewModel(mContext, uri); + int pos = mDataset.indexOf(newModel); + mDataset.get(pos).setProgress(progress, max, msg); + notifyItemChanged(pos); + } + + public void addResult(Uri uri, DecryptVerifyResult result, Drawable icon, + OnClickListener onFileClick, OnClickListener onKeyClick) { + + ViewModel model = new ViewModel(mContext, uri); + int pos = mDataset.indexOf(model); + model = mDataset.get(pos); + + model.addResult(result); + if (icon != null) { + model.addIcon(icon); + } + model.setOnClickListeners(onFileClick, onKeyClick); + + notifyItemChanged(pos); + } + + } + + + // Provide a reference to the views for each data item + // Complex data items may need more than one view per item, and + // you provide access to all the views for a data item in a view holder + public static class ViewHolder extends RecyclerView.ViewHolder implements StatusHolder { + public ViewAnimator vAnimator; + + public ProgressBar vProgress; + public TextView vProgressMsg; + + public View vFile; + public TextView vFilename; + public TextView vFilesize; + public ImageView vThumbnail; + + public ImageView vEncStatusIcon; + public TextView vEncStatusText; + + public ImageView vSigStatusIcon; + public TextView vSigStatusText; + public View vSignatureLayout; + public TextView vSignatureName; + public TextView vSignatureMail; + public TextView vSignatureAction; + + public View vContextMenu; + + public ViewHolder(View itemView) { + super(itemView); + + vAnimator = (ViewAnimator) itemView.findViewById(R.id.view_animator); + + vProgress = (ProgressBar) itemView.findViewById(R.id.progress); + vProgressMsg = (TextView) itemView.findViewById(R.id.progress_msg); + + vFile = itemView.findViewById(R.id.file); + vFilename = (TextView) itemView.findViewById(R.id.filename); + vFilesize = (TextView) itemView.findViewById(R.id.filesize); + vThumbnail = (ImageView) itemView.findViewById(R.id.thumbnail); + + vEncStatusIcon = (ImageView) itemView.findViewById(R.id.result_encryption_icon); + vEncStatusText = (TextView) itemView.findViewById(R.id.result_encryption_text); + + vSigStatusIcon = (ImageView) itemView.findViewById(R.id.result_signature_icon); + vSigStatusText = (TextView) itemView.findViewById(R.id.result_signature_text); + vSignatureLayout = itemView.findViewById(R.id.result_signature_layout); + vSignatureName = (TextView) itemView.findViewById(R.id.result_signature_name); + vSignatureMail= (TextView) itemView.findViewById(R.id.result_signature_email); + vSignatureAction = (TextView) itemView.findViewById(R.id.result_signature_action); + + vContextMenu = itemView.findViewById(R.id.context_menu); + + } + + @Override + public ImageView getEncryptionStatusIcon() { + return vEncStatusIcon; + } + + @Override + public TextView getEncryptionStatusText() { + return vEncStatusText; + } + + @Override + public ImageView getSignatureStatusIcon() { + return vSigStatusIcon; + } + + @Override + public TextView getSignatureStatusText() { + return vSigStatusText; + } + + @Override + public View getSignatureLayout() { + return vSignatureLayout; + } + + @Override + public TextView getSignatureAction() { + return vSignatureAction; + } + + @Override + public TextView getSignatureUserName() { + return vSignatureName; + } + + @Override + public TextView getSignatureUserEmail() { + return vSignatureMail; + } + + @Override + public boolean hasEncrypt() { + return true; + } + } + + private Drawable loadIcon(String mimeType) { + final Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setType(mimeType); + + final List matches = getActivity() + .getPackageManager().queryIntentActivities(intent, 0); + //noinspection LoopStatementThatDoesntLoop + for (ResolveInfo match : matches) { + return match.loadIcon(getActivity().getPackageManager()); + } + return null; + + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java deleted file mode 100644 index 0c463c2cd..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (C) 2012-2015 Dominik Schürmann - * Copyright (C) 2010-2014 Thialfihar - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui; - -import android.app.Activity; -import android.content.Intent; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.text.TextUtils; -import android.view.View; -import android.widget.Toast; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; -import org.sufficientlysecure.keychain.intents.OpenKeychainIntents; -import org.sufficientlysecure.keychain.operations.results.OperationResult; -import org.sufficientlysecure.keychain.operations.results.SingletonResult; -import org.sufficientlysecure.keychain.pgp.PgpHelper; -import org.sufficientlysecure.keychain.ui.base.BaseActivity; -import org.sufficientlysecure.keychain.util.Log; - -import java.util.regex.Matcher; - -public class DecryptTextActivity extends BaseActivity { - - /* Intents */ - public static final String ACTION_DECRYPT_TEXT = OpenKeychainIntents.DECRYPT_TEXT; - public static final String EXTRA_TEXT = OpenKeychainIntents.DECRYPT_EXTRA_TEXT; - - // intern - public static final String ACTION_DECRYPT_FROM_CLIPBOARD = Constants.INTENT_PREFIX + "DECRYPT_TEXT_FROM_CLIPBOARD"; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setFullScreenDialogClose(new View.OnClickListener() { - @Override - public void onClick(View v) { - setResult(Activity.RESULT_CANCELED); - finish(); - } - }, false); - - // Handle intent actions - handleActions(savedInstanceState, getIntent()); - } - - @Override - protected void initLayout() { - setContentView(R.layout.decrypt_text_activity); - } - - /** - * Fixing broken PGP MESSAGE Strings coming from GMail/AOSP Mail - */ - private String fixPgpMessage(String message) { - // windows newline -> unix newline - message = message.replaceAll("\r\n", "\n"); - // Mac OS before X newline -> unix newline - message = message.replaceAll("\r", "\n"); - - // remove whitespaces before newline - message = message.replaceAll(" +\n", "\n"); - // only two consecutive newlines are allowed - message = message.replaceAll("\n\n+", "\n\n"); - - // replace non breakable spaces - message = message.replaceAll("\\xa0", " "); - - return message; - } - - /** - * Fixing broken PGP SIGNED MESSAGE Strings coming from GMail/AOSP Mail - */ - private String fixPgpCleartextSignature(CharSequence input) { - if (!TextUtils.isEmpty(input)) { - String text = input.toString(); - - // windows newline -> unix newline - text = text.replaceAll("\r\n", "\n"); - // Mac OS before X newline -> unix newline - text = text.replaceAll("\r", "\n"); - - return text; - } else { - return null; - } - } - - private String getPgpContent(CharSequence input) { - // only decrypt if clipboard content is available and a pgp message or cleartext signature - if (!TextUtils.isEmpty(input)) { - Log.dEscaped(Constants.TAG, "input: " + input); - - Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(input); - if (matcher.matches()) { - String text = matcher.group(1); - text = fixPgpMessage(text); - - Log.dEscaped(Constants.TAG, "input fixed: " + text); - return text; - } else { - matcher = PgpHelper.PGP_CLEARTEXT_SIGNATURE.matcher(input); - if (matcher.matches()) { - String text = matcher.group(1); - text = fixPgpCleartextSignature(text); - - Log.dEscaped(Constants.TAG, "input fixed: " + text); - return text; - } else { - return null; - } - } - } else { - return null; - } - } - - /** - * Handles all actions with this intent - */ - private void handleActions(Bundle savedInstanceState, Intent intent) { - String action = intent.getAction(); - Bundle extras = intent.getExtras(); - String type = intent.getType(); - - if (extras == null) { - extras = new Bundle(); - } - - if (savedInstanceState != null) { - return; - } - - if (Intent.ACTION_SEND.equals(action) && type != null) { - Log.d(Constants.TAG, "ACTION_SEND"); - Log.logDebugBundle(extras, "SEND extras"); - - // When sending to Keychain Decrypt via share menu - if ("text/plain".equals(type)) { - String sharedText = extras.getString(Intent.EXTRA_TEXT); - sharedText = getPgpContent(sharedText); - - if (sharedText != null) { - loadFragment(sharedText); - } else { - Log.e(Constants.TAG, "EXTRA_TEXT does not contain PGP content!"); - Toast.makeText(this, R.string.error_invalid_data, Toast.LENGTH_LONG).show(); - finish(); - } - } else { - Log.e(Constants.TAG, "ACTION_SEND received non-plaintext, this should not happen in this activity!"); - Toast.makeText(this, R.string.error_invalid_data, Toast.LENGTH_LONG).show(); - finish(); - } - } else if (ACTION_DECRYPT_TEXT.equals(action)) { - Log.d(Constants.TAG, "ACTION_DECRYPT_TEXT"); - - String extraText = extras.getString(EXTRA_TEXT); - extraText = getPgpContent(extraText); - - if (extraText != null) { - loadFragment(extraText); - } else { - Log.e(Constants.TAG, "EXTRA_TEXT does not contain PGP content!"); - Toast.makeText(this, R.string.error_invalid_data, Toast.LENGTH_LONG).show(); - finish(); - } - } else if (ACTION_DECRYPT_FROM_CLIPBOARD.equals(action)) { - Log.d(Constants.TAG, "ACTION_DECRYPT_FROM_CLIPBOARD"); - - CharSequence clipboardText = ClipboardReflection.getClipboardText(this); - String text = getPgpContent(clipboardText); - - if (text != null) { - loadFragment(text); - } else { - returnInvalidResult(); - } - } else if (ACTION_DECRYPT_TEXT.equals(action)) { - Log.e(Constants.TAG, "Include the extra 'text' in your Intent!"); - Toast.makeText(this, R.string.error_invalid_data, Toast.LENGTH_LONG).show(); - finish(); - } - } - - private void returnInvalidResult() { - SingletonResult result = new SingletonResult( - SingletonResult.RESULT_ERROR, OperationResult.LogType.MSG_NO_VALID_ENC); - Intent intent = new Intent(); - intent.putExtra(SingletonResult.EXTRA_RESULT, result); - setResult(RESULT_OK, intent); - finish(); - } - - private void loadFragment(String ciphertext) { - // Create an instance of the fragment - Fragment frag = DecryptTextFragment.newInstance(ciphertext); - - // Add the fragment to the 'fragment_container' FrameLayout - // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! - getSupportFragmentManager().beginTransaction() - .replace(R.id.decrypt_text_fragment_container, frag) - .commitAllowingStateLoss(); - // do it immediately! - getSupportFragmentManager().executePendingTransactions(); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java deleted file mode 100644 index 051da5d6b..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui; - -import android.content.Intent; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; -import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; -import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; -import org.sufficientlysecure.keychain.ui.util.Notify; -import org.sufficientlysecure.keychain.util.ShareHelper; - -import java.io.UnsupportedEncodingException; - -public class DecryptTextFragment extends DecryptFragment { - public static final String ARG_CIPHERTEXT = "ciphertext"; - public static final String ARG_SHOW_MENU = "show_menu"; - - // view - private TextView mText; - - // model - private String mCiphertext; - private boolean mShowMenuOptions; - - public static DecryptTextFragment newInstance(String ciphertext) { - DecryptTextFragment frag = new DecryptTextFragment(); - - Bundle args = new Bundle(); - args.putString(ARG_CIPHERTEXT, ciphertext); - - frag.setArguments(args); - - return frag; - } - - /** - * Inflate the layout for this fragment - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.decrypt_text_fragment, container, false); - mText = (TextView) view.findViewById(R.id.decrypt_text_plaintext); - - return view; - } - - /** - * Create Intent Chooser but exclude decrypt activites - */ - private Intent sendWithChooserExcludingDecrypt(String text) { - Intent prototype = createSendIntent(text); - String title = getString(R.string.title_share_message); - - // we don't want to decrypt the decrypted, no inception ;) - String[] blacklist = new String[]{ - Constants.PACKAGE_NAME + ".ui.DecryptTextActivity", - "org.thialfihar.android.apg.ui.DecryptActivity" - }; - - return new ShareHelper(getActivity()).createChooserExcluding(prototype, title, blacklist); - } - - private Intent createSendIntent(String text) { - Intent sendIntent = new Intent(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_TEXT, text); - sendIntent.setType("text/plain"); - return sendIntent; - } - - private void copyToClipboard(String text) { - ClipboardReflection.copyToClipboard(getActivity(), text); - Notify.create(getActivity(), R.string.text_copied_to_clipboard, Notify.Style.OK).show(); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setHasOptionsMenu(true); - - Bundle args = savedInstanceState == null ? getArguments() : savedInstanceState; - mCiphertext = args.getString(ARG_CIPHERTEXT); - mShowMenuOptions = args.getBoolean(ARG_SHOW_MENU, false); - - if (savedInstanceState == null) { - cryptoOperation(); - } - - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - - outState.putString(ARG_CIPHERTEXT, mCiphertext); - outState.putBoolean(ARG_SHOW_MENU, mShowMenuOptions); - // no need to save the decrypted text, it's in the textview - - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - if (mShowMenuOptions) { - inflater.inflate(R.menu.decrypt_menu, menu); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.decrypt_share: { - startActivity(sendWithChooserExcludingDecrypt(mText.getText().toString())); - break; - } - case R.id.decrypt_copy: { - copyToClipboard(mText.getText().toString()); - break; - } - default: { - return super.onOptionsItemSelected(item); - } - } - - return true; - } - - @Override - protected PgpDecryptVerifyInputParcel createOperationInput() { - PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(mCiphertext.getBytes()); - input.setAllowSymmetricDecryption(true); - return input; - } - - @Override - protected void onVerifyLoaded(boolean hideErrorOverlay) { - mShowMenuOptions = hideErrorOverlay; - getActivity().supportInvalidateOptionsMenu(); - } - - @Override - protected void onCryptoOperationSuccess(DecryptVerifyResult result) { - - byte[] decryptedMessage = result.getOutputBytes(); - String displayMessage; - if (result.getCharset() != null) { - try { - displayMessage = new String(decryptedMessage, result.getCharset()); - } catch (UnsupportedEncodingException e) { - // if we can't decode properly, just fall back to utf-8 - displayMessage = new String(decryptedMessage); - } - } else { - displayMessage = new String(decryptedMessage); - } - mText.setText(displayMessage); - - // display signature result in activity - loadVerifyResult(result); - - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextActivity.java new file mode 100644 index 000000000..5c44e345e --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextActivity.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2012-2015 Dominik Schürmann + * Copyright (C) 2010-2014 Thialfihar + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sufficientlysecure.keychain.ui; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.TextUtils; +import android.view.View; +import android.widget.Toast; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; +import org.sufficientlysecure.keychain.intents.OpenKeychainIntents; +import org.sufficientlysecure.keychain.operations.results.OperationResult; +import org.sufficientlysecure.keychain.operations.results.SingletonResult; +import org.sufficientlysecure.keychain.pgp.PgpHelper; +import org.sufficientlysecure.keychain.ui.base.BaseActivity; +import org.sufficientlysecure.keychain.util.Log; + +import java.util.regex.Matcher; + +public class DisplayTextActivity extends BaseActivity { + + // TODO make this only display text (maybe we need only the fragment?) + + /* Intents */ + public static final String ACTION_DECRYPT_TEXT = OpenKeychainIntents.DECRYPT_TEXT; + public static final String EXTRA_TEXT = OpenKeychainIntents.DECRYPT_EXTRA_TEXT; + + // intern + public static final String ACTION_DECRYPT_FROM_CLIPBOARD = Constants.INTENT_PREFIX + "DECRYPT_TEXT_FROM_CLIPBOARD"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setFullScreenDialogClose(new View.OnClickListener() { + @Override + public void onClick(View v) { + setResult(Activity.RESULT_CANCELED); + finish(); + } + }, false); + + // Handle intent actions + handleActions(savedInstanceState, getIntent()); + } + + @Override + protected void initLayout() { + setContentView(R.layout.decrypt_text_activity); + } + + /** + * Fixing broken PGP MESSAGE Strings coming from GMail/AOSP Mail + */ + private String fixPgpMessage(String message) { + // windows newline -> unix newline + message = message.replaceAll("\r\n", "\n"); + // Mac OS before X newline -> unix newline + message = message.replaceAll("\r", "\n"); + + // remove whitespaces before newline + message = message.replaceAll(" +\n", "\n"); + // only two consecutive newlines are allowed + message = message.replaceAll("\n\n+", "\n\n"); + + // replace non breakable spaces + message = message.replaceAll("\\xa0", " "); + + return message; + } + + /** + * Fixing broken PGP SIGNED MESSAGE Strings coming from GMail/AOSP Mail + */ + private String fixPgpCleartextSignature(CharSequence input) { + if (!TextUtils.isEmpty(input)) { + String text = input.toString(); + + // windows newline -> unix newline + text = text.replaceAll("\r\n", "\n"); + // Mac OS before X newline -> unix newline + text = text.replaceAll("\r", "\n"); + + return text; + } else { + return null; + } + } + + private String getPgpContent(CharSequence input) { + // only decrypt if clipboard content is available and a pgp message or cleartext signature + if (!TextUtils.isEmpty(input)) { + Log.dEscaped(Constants.TAG, "input: " + input); + + Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(input); + if (matcher.matches()) { + String text = matcher.group(1); + text = fixPgpMessage(text); + + Log.dEscaped(Constants.TAG, "input fixed: " + text); + return text; + } else { + matcher = PgpHelper.PGP_CLEARTEXT_SIGNATURE.matcher(input); + if (matcher.matches()) { + String text = matcher.group(1); + text = fixPgpCleartextSignature(text); + + Log.dEscaped(Constants.TAG, "input fixed: " + text); + return text; + } else { + return null; + } + } + } else { + return null; + } + } + + /** + * Handles all actions with this intent + */ + private void handleActions(Bundle savedInstanceState, Intent intent) { + String action = intent.getAction(); + Bundle extras = intent.getExtras(); + String type = intent.getType(); + + if (extras == null) { + extras = new Bundle(); + } + + if (savedInstanceState != null) { + return; + } + + if (Intent.ACTION_SEND.equals(action) && type != null) { + Log.d(Constants.TAG, "ACTION_SEND"); + Log.logDebugBundle(extras, "SEND extras"); + + // When sending to Keychain Decrypt via share menu + if ("text/plain".equals(type)) { + String sharedText = extras.getString(Intent.EXTRA_TEXT); + sharedText = getPgpContent(sharedText); + + if (sharedText != null) { + loadFragment(sharedText); + } else { + Log.e(Constants.TAG, "EXTRA_TEXT does not contain PGP content!"); + Toast.makeText(this, R.string.error_invalid_data, Toast.LENGTH_LONG).show(); + finish(); + } + } else { + Log.e(Constants.TAG, "ACTION_SEND received non-plaintext, this should not happen in this activity!"); + Toast.makeText(this, R.string.error_invalid_data, Toast.LENGTH_LONG).show(); + finish(); + } + } else if (ACTION_DECRYPT_TEXT.equals(action)) { + Log.d(Constants.TAG, "ACTION_DECRYPT_TEXT"); + + String extraText = extras.getString(EXTRA_TEXT); + extraText = getPgpContent(extraText); + + if (extraText != null) { + loadFragment(extraText); + } else { + Log.e(Constants.TAG, "EXTRA_TEXT does not contain PGP content!"); + Toast.makeText(this, R.string.error_invalid_data, Toast.LENGTH_LONG).show(); + finish(); + } + } else if (ACTION_DECRYPT_FROM_CLIPBOARD.equals(action)) { + Log.d(Constants.TAG, "ACTION_DECRYPT_FROM_CLIPBOARD"); + + CharSequence clipboardText = ClipboardReflection.getClipboardText(this); + String text = getPgpContent(clipboardText); + + if (text != null) { + loadFragment(text); + } else { + returnInvalidResult(); + } + } else if (ACTION_DECRYPT_TEXT.equals(action)) { + Log.e(Constants.TAG, "Include the extra 'text' in your Intent!"); + Toast.makeText(this, R.string.error_invalid_data, Toast.LENGTH_LONG).show(); + finish(); + } + } + + private void returnInvalidResult() { + SingletonResult result = new SingletonResult( + SingletonResult.RESULT_ERROR, OperationResult.LogType.MSG_NO_VALID_ENC); + Intent intent = new Intent(); + intent.putExtra(SingletonResult.EXTRA_RESULT, result); + setResult(RESULT_OK, intent); + finish(); + } + + private void loadFragment(String ciphertext) { + // Create an instance of the fragment + Fragment frag = DisplayTextFragment.newInstance(ciphertext); + + // Add the fragment to the 'fragment_container' FrameLayout + // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! + getSupportFragmentManager().beginTransaction() + .replace(R.id.decrypt_text_fragment_container, frag) + .commitAllowingStateLoss(); + // do it immediately! + getSupportFragmentManager().executePendingTransactions(); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextFragment.java new file mode 100644 index 000000000..cd75e2bc3 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextFragment.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sufficientlysecure.keychain.ui; + +import android.content.Intent; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; +import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; +import org.sufficientlysecure.keychain.ui.util.Notify; +import org.sufficientlysecure.keychain.util.ShareHelper; + +import java.io.UnsupportedEncodingException; + +public class DisplayTextFragment extends DecryptFragment { + public static final String ARG_CIPHERTEXT = "ciphertext"; + public static final String ARG_SHOW_MENU = "show_menu"; + + // view + private TextView mText; + + // model + private String mCiphertext; + private boolean mShowMenuOptions; + + public static DisplayTextFragment newInstance(String ciphertext) { + DisplayTextFragment frag = new DisplayTextFragment(); + + Bundle args = new Bundle(); + args.putString(ARG_CIPHERTEXT, ciphertext); + + frag.setArguments(args); + + return frag; + } + + /** + * Inflate the layout for this fragment + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.decrypt_text_fragment, container, false); + mText = (TextView) view.findViewById(R.id.decrypt_text_plaintext); + + return view; + } + + /** + * Create Intent Chooser but exclude decrypt activites + */ + private Intent sendWithChooserExcludingDecrypt(String text) { + Intent prototype = createSendIntent(text); + String title = getString(R.string.title_share_message); + + // we don't want to decrypt the decrypted, no inception ;) + String[] blacklist = new String[]{ + Constants.PACKAGE_NAME + ".ui.DecryptTextActivity", + "org.thialfihar.android.apg.ui.DecryptActivity" + }; + + return new ShareHelper(getActivity()).createChooserExcluding(prototype, title, blacklist); + } + + private Intent createSendIntent(String text) { + Intent sendIntent = new Intent(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, text); + sendIntent.setType("text/plain"); + return sendIntent; + } + + private void copyToClipboard(String text) { + ClipboardReflection.copyToClipboard(getActivity(), text); + Notify.create(getActivity(), R.string.text_copied_to_clipboard, Notify.Style.OK).show(); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setHasOptionsMenu(true); + + Bundle args = savedInstanceState == null ? getArguments() : savedInstanceState; + mCiphertext = args.getString(ARG_CIPHERTEXT); + mShowMenuOptions = args.getBoolean(ARG_SHOW_MENU, false); + + if (savedInstanceState == null) { + cryptoOperation(); + } + + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + outState.putString(ARG_CIPHERTEXT, mCiphertext); + outState.putBoolean(ARG_SHOW_MENU, mShowMenuOptions); + // no need to save the decrypted text, it's in the textview + + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + if (mShowMenuOptions) { + inflater.inflate(R.menu.decrypt_menu, menu); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.decrypt_share: { + startActivity(sendWithChooserExcludingDecrypt(mText.getText().toString())); + break; + } + case R.id.decrypt_copy: { + copyToClipboard(mText.getText().toString()); + break; + } + default: { + return super.onOptionsItemSelected(item); + } + } + + return true; + } + + @Override + protected PgpDecryptVerifyInputParcel createOperationInput() { + PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(mCiphertext.getBytes()); + input.setAllowSymmetricDecryption(true); + return input; + } + + @Override + protected void onVerifyLoaded(boolean hideErrorOverlay) { + mShowMenuOptions = hideErrorOverlay; + getActivity().supportInvalidateOptionsMenu(); + } + + @Override + protected void onCryptoOperationSuccess(DecryptVerifyResult result) { + + byte[] decryptedMessage = result.getOutputBytes(); + String displayMessage; + if (result.getCharset() != null) { + try { + displayMessage = new String(decryptedMessage, result.getCharset()); + } catch (UnsupportedEncodingException e) { + // if we can't decode properly, just fall back to utf-8 + displayMessage = new String(decryptedMessage); + } + } else { + displayMessage = new String(decryptedMessage); + } + mText.setText(displayMessage); + + // display signature result in activity + loadVerifyResult(result); + + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java index a6fad8881..c4007b214 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java @@ -74,8 +74,8 @@ public class EncryptDecryptOverviewFragment extends Fragment { mDecryptFile.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - Intent filesDecrypt = new Intent(getActivity(), DecryptFilesActivity.class); - filesDecrypt.setAction(DecryptFilesActivity.ACTION_DECRYPT_DATA_OPEN); + Intent filesDecrypt = new Intent(getActivity(), DecryptActivity.class); + filesDecrypt.setAction(DecryptActivity.ACTION_DECRYPT_DATA_OPEN); startActivity(filesDecrypt); } }); @@ -83,8 +83,8 @@ public class EncryptDecryptOverviewFragment extends Fragment { mDecryptFromClipboard.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - Intent clipboardDecrypt = new Intent(getActivity(), DecryptTextActivity.class); - clipboardDecrypt.setAction(DecryptTextActivity.ACTION_DECRYPT_FROM_CLIPBOARD); + Intent clipboardDecrypt = new Intent(getActivity(), DisplayTextActivity.class); + clipboardDecrypt.setAction(DisplayTextActivity.ACTION_DECRYPT_FROM_CLIPBOARD); startActivityForResult(clipboardDecrypt, 0); } }); -- cgit v1.2.3