/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with context work for additional information * regarding copyright ownership. The ASF licenses context file * to you under the Apache License, Version 2.0 (the * "License"); you may not use context file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package com.doplgangr.secrecy.fragments; import android.app.Fragment; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Parcelable; import android.support.v7.app.ActionBarActivity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.webkit.MimeTypeMap; import android.widget.Toast; import com.doplgangr.secrecy.Config; import com.doplgangr.secrecy.CustomApp; import com.doplgangr.secrecy.R; import com.doplgangr.secrecy.utils.Util; import com.doplgangr.secrecy.activities.FilesActivity; import com.doplgangr.secrecy.filesystem.CryptStateListener; import com.doplgangr.secrecy.filesystem.OurFileProvider; import com.doplgangr.secrecy.filesystem.Storage; import com.doplgangr.secrecy.filesystem.encryption.Vault; import com.doplgangr.secrecy.filesystem.files.EncryptedFile; import com.doplgangr.secrecy.filesystem.files.SecrecyFile; import com.doplgangr.secrecy.jobs.AddFileJob; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; public abstract class FileViewer extends Fragment { ActionBarActivity context; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.activity_file_viewer, container, false); } void addFileInBackground(final Vault secret, final Uri data) { new Thread(new Runnable() { @Override public void run() { CustomApp.jobManager.addJobInBackground(new AddFileJob(context, secret, data)); } }).start(); } void decrypt(final EncryptedFile encryptedFile, final Runnable onFinish) { new AsyncTask<EncryptedFile, Void, File>() { @Override protected File doInBackground(EncryptedFile... encryptedFiles) { return getFile(encryptedFile, onFinish); } @Override protected void onPostExecute(File tempFile){ if (tempFile != null) { if (tempFile.getParentFile().equals(Storage.getTempFolder())) { tempFile = new File(Storage.getTempFolder(), tempFile.getName()); } Uri uri = OurFileProvider.getUriForFile(context, OurFileProvider.FILE_PROVIDER_AUTHORITY, tempFile); MimeTypeMap myMime = MimeTypeMap.getSingleton(); Intent newIntent = new Intent(android.content.Intent.ACTION_VIEW); String mimeType = myMime.getMimeTypeFromExtension(encryptedFile.getType()); newIntent.setDataAndType(uri, mimeType); newIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //altIntent: resort to using file provider when content provider does not work. Intent altIntent = new Intent(android.content.Intent.ACTION_VIEW); Uri rawuri = Uri.fromFile(tempFile); altIntent.setDataAndType(rawuri, mimeType); afterDecrypt(newIntent, altIntent); } } }.execute(encryptedFile); } void sendMultiple(final ArrayList<FilesListFragment.DecryptArgHolder> args) { new Thread(new Runnable() { @Override public void run() { ArrayList<Uri> uris = new ArrayList<Uri>(); Set<String> mimes = new HashSet<String>(); MimeTypeMap myMime = MimeTypeMap.getSingleton(); for (FilesListFragment.DecryptArgHolder arg : args) { File tempFile = getFile(arg.encryptedFile, arg.onFinish); //File specified is not invalid if (tempFile != null) { if (tempFile.getParentFile().equals(Storage.getTempFolder())) tempFile = new java.io.File(Storage.getTempFolder(), tempFile.getName()); uris.add(OurFileProvider.getUriForFile(context, OurFileProvider.FILE_PROVIDER_AUTHORITY, tempFile)); mimes.add(myMime.getMimeTypeFromExtension(arg.encryptedFile.getType())); } } if (uris.size() == 0 || mimes.size() == 0) return; Intent newIntent; if (uris.size() == 1) { newIntent = new Intent(Intent.ACTION_SEND); newIntent.putExtra(Intent.EXTRA_STREAM, uris.get(0)); } else { newIntent = new Intent(Intent.ACTION_SEND_MULTIPLE); newIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris); } if (mimes.size() > 1) newIntent.setType("text/plain"); //Mixed filetypes else newIntent.setType(new ArrayList<String>(mimes).get(0)); newIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Intent chooserIntent = generateCustomChooserIntent(newIntent, uris); try { startActivity(Intent.createChooser(chooserIntent, CustomApp.context.getString(R.string.Dialog__send_file))); FilesActivity.onPauseDecision.startActivity(); } catch (android.content.ActivityNotFoundException e) { Util.toast(context, CustomApp.context.getString(R.string.Error__no_activity_view), Toast.LENGTH_LONG); FilesActivity.onPauseDecision.finishActivity(); } } }).start(); } File getFile(final EncryptedFile encryptedFile, final Runnable onfinish) { CryptStateListener listener = new CryptStateListener() { @Override public void updateProgress(int progress) { updatePBar(encryptedFile, progress); } @Override public void setMax(int max) { maxPBar(encryptedFile, max); } @Override public void onFailed(int statCode) { String message; switch (statCode) { case Config.wrong_password: message = getString(R.string.Error__wrong_password); break; case Config.file_not_found: message = getString(R.string.Error__file_not_found); break; default: message = getString(R.string.Error__unknown); } alert(message); } @Override public void Finished() { onfinish.run(); } }; return encryptedFile.readFile(listener); } void afterDecrypt(Intent newIntent, Intent altIntent) { try { startActivity(newIntent); FilesActivity.onPauseDecision.startActivity(); } catch (android.content.ActivityNotFoundException e) { try { startActivity(altIntent); FilesActivity.onPauseDecision.startActivity(); } catch (android.content.ActivityNotFoundException e2) { Util.toast(context, getString(R.string.Error__no_activity_view), Toast.LENGTH_LONG); FilesActivity.onPauseDecision.finishActivity(); } } catch (IllegalStateException e) { //duh why you leave so early FilesActivity.onPauseDecision.finishActivity(); } } void alert(String message) { DialogInterface.OnClickListener click = new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { finish(); } }; Util.alert(context, getString(R.string.Error__decrypt_file), message, click, null); } void updatePBar(SecrecyFile file, int progress) { if (file.getProgressBar() != null) file.getProgressBar().setProgress(progress); } void maxPBar(EncryptedFile file, int max) { if (file.getProgressBar() != null) file.getProgressBar().setMax(max); } private Intent generateCustomChooserIntent(Intent prototype, ArrayList<Uri> uris) { List<Intent> targetedShareIntents = new ArrayList<Intent>(); List<HashMap<String, String>> intentMetaInfo = new ArrayList<HashMap<String, String>>(); Intent chooserIntent; Intent dummy = new Intent(prototype.getAction()); dummy.setType(prototype.getType()); List<ResolveInfo> resInfo = context.getPackageManager().queryIntentActivities(dummy, 0); if (!resInfo.isEmpty()) { for (ResolveInfo resolveInfo : resInfo) { if (resolveInfo.activityInfo == null || resolveInfo.activityInfo.packageName.equalsIgnoreCase("com.doplgangr.secrecy")) continue; HashMap<String, String> info = new HashMap<String, String>(); info.put("packageName", resolveInfo.activityInfo.packageName); info.put("className", resolveInfo.activityInfo.name); info.put("simpleName", String.valueOf(resolveInfo.activityInfo.loadLabel(context.getPackageManager()))); intentMetaInfo.add(info); for (Uri uri : uris) context.grantUriPermission(resolveInfo.activityInfo.packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); } if (!intentMetaInfo.isEmpty()) { // sorting for nice readability Collections.sort(intentMetaInfo, new Comparator<HashMap<String, String>>() { @Override public int compare(HashMap<String, String> map, HashMap<String, String> map2) { return map.get("simpleName").compareTo(map2.get("simpleName")); } }); // create the custom intent list for (HashMap<String, String> metaInfo : intentMetaInfo) { Intent targetedShareIntent = (Intent) prototype.clone(); targetedShareIntent.setPackage(metaInfo.get("packageName")); targetedShareIntent.setClassName(metaInfo.get("packageName"), metaInfo.get("className")); targetedShareIntents.add(targetedShareIntent); } chooserIntent = Intent.createChooser(targetedShareIntents.remove(targetedShareIntents.size() - 1), CustomApp.context.getString(R.string.Dialog__send_file)); chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray(new Parcelable[targetedShareIntents.size()])); return chooserIntent; } } return new Intent(Intent.ACTION_SEND); //Unable to do anything. Duh. } void finish() { getActivity().finish(); } }