Merge branch 'master' of github.com:open-keychain/open-keychain

This commit is contained in:
Dominik Schürmann
2015-07-06 11:27:57 +02:00
75 changed files with 2270 additions and 430 deletions

View File

@@ -54,6 +54,7 @@ import org.sufficientlysecure.keychain.ui.base.CachingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
import java.util.ArrayList;
@@ -311,6 +312,11 @@ public class CertifyKeyFragment
CertifyActionsParcel actionsParcel = new CertifyActionsParcel(selectedKeyId);
actionsParcel.mCertifyActions.addAll(certifyActions);
if (mUploadKeyCheckbox.isChecked()) {
actionsParcel.keyServerUri = Preferences.getPreferences(getActivity())
.getPreferredKeyserver();
}
// cached for next cryptoOperation loop
cacheActionsParcel(actionsParcel);

View File

@@ -23,6 +23,7 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -385,14 +386,18 @@ public class CreateKeyFinalFragment extends Fragment {
}
public void handleResult(ExportResult result) {
// TODO: ExportOperation UPLOAD_KEYSERVER needs logs!
// TODO: then merge logs here!
//saveKeyResult.getLog().add(result, 0);
Activity activity = getActivity();
if (activity == null) {
return;
}
saveKeyResult.getLog().add(result, 0);
Intent data = new Intent();
data.putExtra(OperationResult.EXTRA_RESULT, saveKeyResult);
getActivity().setResult(Activity.RESULT_OK, data);
getActivity().finish();
activity.setResult(Activity.RESULT_OK, data);
activity.finish();
}
@Override

View File

@@ -41,7 +41,9 @@ import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.NfcListenerFragment;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
public class CreateYubiKeyImportFragment
@@ -131,7 +133,18 @@ public class CreateYubiKeyImportFragment
view.findViewById(R.id.button_search).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
refreshSearch();
final Preferences.ProxyPrefs proxyPrefs = Preferences.getPreferences(getActivity()).getProxyPrefs();
Runnable ignoreTor = new Runnable() {
@Override
public void run() {
refreshSearch(new ParcelableProxy(null, -1, null));
}
};
if (OrbotHelper.putOrbotInRequiredState(R.string.orbot_ignore_tor, ignoreTor, proxyPrefs,
getActivity())) {
refreshSearch(proxyPrefs.parcelableProxy);
}
}
});
@@ -170,9 +183,10 @@ public class CreateYubiKeyImportFragment
}
}
public void refreshSearch() {
public void refreshSearch(ParcelableProxy parcelableProxy) {
// TODO: PHILIP verify proxy implementation in YubiKey parts
mListFragment.loadNew(new ImportKeysListFragment.CloudLoaderState("0x" + mNfcFingerprint,
Preferences.getPreferences(getActivity()).getCloudSearchPrefs()));
Preferences.getPreferences(getActivity()).getCloudSearchPrefs()), parcelableProxy);
}
public void importKey() {
@@ -210,7 +224,20 @@ public class CreateYubiKeyImportFragment
public void onNfcPostExecute() throws IOException {
setData();
refreshSearch();
Preferences.ProxyPrefs proxyPrefs = Preferences.getPreferences(getActivity()).getProxyPrefs();
Runnable ignoreTor = new Runnable() {
@Override
public void run() {
refreshSearch(new ParcelableProxy(null, -1, null));
}
};
if (OrbotHelper.putOrbotInRequiredState(R.string.orbot_ignore_tor, ignoreTor, proxyPrefs,
getActivity())) {
refreshSearch(proxyPrefs.parcelableProxy);
}
}
@Override

View File

@@ -19,15 +19,11 @@ package org.sufficientlysecure.keychain.ui;
import java.util.ArrayList;
import android.app.Activity;
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.support.v4.app.Fragment;
import android.os.Parcelable;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
@@ -52,13 +48,14 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.CachingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
public abstract class DecryptFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {

View File

@@ -304,7 +304,7 @@ public class DecryptListFragment
}
final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setType(type);
intent.setDataAndType(outputUri, type);
final List<ResolveInfo> matches =
context.getPackageManager().queryIntentActivities(intent, 0);

View File

@@ -17,6 +17,10 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
@@ -29,9 +33,9 @@ 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.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.util.ShareHelper;
public class DisplayTextFragment extends DecryptFragment {
@@ -80,8 +84,19 @@ public class DisplayTextFragment extends DecryptFragment {
}
private void copyToClipboard(String text) {
ClipboardReflection.copyToClipboard(getActivity(), text);
Notify.create(getActivity(), R.string.text_copied_to_clipboard, Notify.Style.OK).show();
Activity activity = getActivity();
if (activity == null) {
return;
}
ClipboardManager clipMan = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
if (clipMan == null) {
Notify.create(activity, R.string.error_clipboard_copy, Style.ERROR).show();
return;
}
clipMan.setPrimaryClip(ClipData.newPlainText(Constants.CLIPBOARD_LABEL, text));
Notify.create(activity, R.string.text_copied_to_clipboard, Style.OK).show();
}
@Override

View File

@@ -124,8 +124,7 @@ public class EncryptDecryptOverviewFragment extends Fragment {
super.onResume();
// get text from clipboard
final CharSequence clipboardText =
ClipboardReflection.getClipboardText(getActivity());
final CharSequence clipboardText = ClipboardReflection.getClipboardText(getActivity());
// if it's null, nothing to do here /o/
if (clipboardText == null) {

View File

@@ -35,6 +35,7 @@ import android.graphics.Point;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
@@ -388,6 +389,12 @@ public class EncryptFilesFragment
@Override
public void onCryptoOperationSuccess(final SignEncryptResult result) {
FragmentActivity activity = getActivity();
if (activity == null) {
// it's gone, there's nothing we can do here
return;
}
if (mDeleteAfterEncrypt) {
// TODO make behavior coherent here
DeleteFileDialogFragment deleteFileDialog =
@@ -400,13 +407,18 @@ public class EncryptFilesFragment
// Share encrypted message/file
startActivity(sendWithChooserExcludingEncrypt());
} else {
Activity activity = getActivity();
if (activity == null) {
// it's gone, there's nothing we can do here
return;
}
// Save encrypted file
result.createNotify(getActivity()).show();
result.createNotify(activity).show();
}
}
});
deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog");
deleteFileDialog.show(activity.getSupportFragmentManager(), "deleteDialog");
} else {
switch (mAfterEncryptAction) {
@@ -417,25 +429,24 @@ public class EncryptFilesFragment
break;
case COPY:
Activity activity = getActivity();
if (activity == null) {
// it's gone, there's nothing we can do here
return;
}
ClipboardManager clipMan = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
if (clipMan == null) {
Notify.create(activity, R.string.error_clipboard_copy, Style.ERROR).show();
break;
}
ClipData clip = new ClipData(getString(R.string.label_clip_title),
// make available as application/pgp-encrypted
new String[] { "text/plain" },
new ClipData.Item(mOutputUris.get(0))
);
clipMan.setPrimaryClip(clip);
result.createNotify(getActivity()).show();
result.createNotify(activity).show();
break;
case SAVE:
// Encrypted file was saved already, just show notification
result.createNotify(getActivity()).show();
result.createNotify(activity).show();
break;
}
}

View File

@@ -18,6 +18,9 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.text.Editable;
@@ -33,7 +36,6 @@ import android.widget.TextView;
import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.PgpConstants;
@@ -265,8 +267,21 @@ public class EncryptTextFragment
return data;
}
private void copyToClipboard(byte[] resultBytes) {
ClipboardReflection.copyToClipboard(getActivity(), new String(resultBytes));
private void copyToClipboard(SignEncryptResult result) {
Activity activity = getActivity();
if (activity == null) {
return;
}
ClipboardManager clipMan = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
if (clipMan == null) {
Notify.create(activity, R.string.error_clipboard_copy, Style.ERROR).show();
return;
}
ClipData clip = ClipData.newPlainText(Constants.CLIPBOARD_LABEL, new String(result.getResultBytes()));
clipMan.setPrimaryClip(clip);
result.createNotify(activity).show();
}
/**
@@ -323,11 +338,7 @@ public class EncryptTextFragment
startActivity(sendWithChooserExcludingEncrypt(result.getResultBytes()));
} else {
// Copy to clipboard
copyToClipboard(result.getResultBytes());
result.createNotify(getActivity()).show();
// Notify.create(EncryptTextActivity.this,
// R.string.encrypt_sign_clipboard_successful, Notify.Style.OK)
// .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment));
copyToClipboard(result);
}
}

View File

@@ -42,6 +42,8 @@ import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
import java.io.IOException;
import java.util.ArrayList;
@@ -87,11 +89,14 @@ public class ImportKeysActivity extends BaseNfcActivity
private ArrayList<ParcelableKeyRing> mKeyList;
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mOperationHelper;
private Preferences.ProxyPrefs mProxyPrefs;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mProxyPrefs = Preferences.getPreferences(this).getProxyPrefs();
mImportButton = findViewById(R.id.import_import);
mImportButton.setOnClickListener(new OnClickListener() {
@Override
@@ -224,7 +229,7 @@ public class ImportKeysActivity extends BaseNfcActivity
Notify.Style.WARN).show(mTopFragment);
// we just set the keyserver
startCloudFragment(savedInstanceState, null, false, keyserver);
// it's not necessary to set the keyserver for ImportKeysListFragment since
// we don't set the keyserver for ImportKeysListFragment since
// it'll be taken care of by ImportKeysCloudFragment when the user clicks
// the search button
startListFragment(savedInstanceState, null, null, null, null);
@@ -316,7 +321,8 @@ public class ImportKeysActivity extends BaseNfcActivity
* specified in user preferences
*/
private void startCloudFragment(Bundle savedInstanceState, String query, boolean disableQueryEdit, String keyserver) {
private void startCloudFragment(Bundle savedInstanceState, String query, boolean disableQueryEdit, String
keyserver) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
@@ -346,8 +352,24 @@ public class ImportKeysActivity extends BaseNfcActivity
}
}
public void loadCallback(ImportKeysListFragment.LoaderState loaderState) {
mListFragment.loadNew(loaderState);
public void loadCallback(final ImportKeysListFragment.LoaderState loaderState) {
if (loaderState instanceof ImportKeysListFragment.CloudLoaderState) {
// do the tor check
// this handle will set tor to be ignored whenever a message is received
Runnable ignoreTor = new Runnable() {
@Override
public void run() {
// disables Tor until Activity is recreated
mProxyPrefs = new Preferences.ProxyPrefs(false, false, null, -1, null);
mListFragment.loadNew(loaderState, mProxyPrefs.parcelableProxy);
}
};
if (OrbotHelper.putOrbotInRequiredState(R.string.orbot_ignore_tor, ignoreTor, mProxyPrefs, this)) {
mListFragment.loadNew(loaderState, mProxyPrefs.parcelableProxy);
}
} else if (loaderState instanceof ImportKeysListFragment.BytesLoaderState) { // must always be true
mListFragment.loadNew(loaderState, mProxyPrefs.parcelableProxy);
}
}
private void handleMessage(Message message) {

View File

@@ -41,6 +41,7 @@ import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListLoader;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Preferences;
import java.io.ByteArrayInputStream;
@@ -64,6 +65,7 @@ public class ImportKeysListFragment extends ListFragment implements
private ImportKeysAdapter mAdapter;
private LoaderState mLoaderState;
private ParcelableProxy mProxy;
private static final int LOADER_ID_BYTES = 0;
private static final int LOADER_ID_CLOUD = 1;
@@ -126,6 +128,7 @@ public class ImportKeysListFragment extends ListFragment implements
/**
* Creates an interactive ImportKeyListFragment which reads keyrings from bytes, or file specified
* by dataUri, or searches a keyserver for serverQuery, if parameter is not null, in that order
* Will immediately load data if non-null bytes/dataUri/serverQuery
*
* @param bytes byte data containing list of keyrings to be imported
* @param dataUri file from which keyrings are to be imported
@@ -141,7 +144,7 @@ public class ImportKeysListFragment extends ListFragment implements
/**
* Visually consists of a list of keyrings with checkboxes to specify which are to be imported
* Can immediately load keyrings specified by any of its parameters
* Will immediately load data if non-null bytes/dataUri/serverQuery is supplied
*
* @param bytes byte data containing list of keyrings to be imported
* @param dataUri file from which keyrings are to be imported
@@ -258,7 +261,9 @@ public class ImportKeysListFragment extends ListFragment implements
mAdapter.notifyDataSetChanged();
}
public void loadNew(LoaderState loaderState) {
public void loadNew(LoaderState loaderState, ParcelableProxy proxy) {
mProxy = proxy;
mLoaderState = loaderState;
restartLoaders();
@@ -301,7 +306,7 @@ public class ImportKeysListFragment extends ListFragment implements
}
case LOADER_ID_CLOUD: {
CloudLoaderState ls = (CloudLoaderState) mLoaderState;
return new ImportKeysListCloudLoader(getActivity(), ls.mServerQuery, ls.mCloudPrefs);
return new ImportKeysListCloudLoader(getActivity(), ls.mServerQuery, ls.mCloudPrefs, mProxy);
}
default:

View File

@@ -44,7 +44,9 @@ import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
import java.util.ArrayList;
import java.util.Locale;
@@ -157,8 +159,7 @@ public class ImportKeysProxyActivity extends FragmentActivity
returnResult(intent);
return;
}
String fingerprint = uri.getEncodedSchemeSpecificPart().toLowerCase(Locale.ENGLISH);
final String fingerprint = uri.getEncodedSchemeSpecificPart().toLowerCase(Locale.ENGLISH);
if (!fingerprint.matches("[a-fA-F0-9]{40}")) {
SingletonResult result = new SingletonResult(
SingletonResult.RESULT_ERROR, LogType.MSG_WRONG_QR_CODE_FP);
@@ -273,7 +274,8 @@ public class ImportKeysProxyActivity extends FragmentActivity
// only one message sent during the beam
NdefMessage msg = (NdefMessage) rawMsgs[0];
// record 0 contains the MIME type, record 1 is the AAR, if present
byte[] receivedKeyringBytes = msg.getRecords()[0].getPayload();
final byte[] receivedKeyringBytes = msg.getRecords()[0].getPayload();
importKeys(receivedKeyringBytes);
}

View File

@@ -52,6 +52,9 @@ import android.widget.TextView;
import com.getbase.floatingactionbutton.FloatingActionButton;
import com.getbase.floatingactionbutton.FloatingActionsMenu;
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
@@ -71,19 +74,16 @@ import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.util.ExportHelper;
import org.sufficientlysecure.keychain.util.FabContainer;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
/**
* Public key list with sticky list headers. It does _not_ extend ListFragment because it uses
* StickyListHeaders library which does not extend upon ListView.
@@ -478,10 +478,6 @@ public class KeyListFragment extends LoaderFragment
updateAllKeys();
return true;
case R.id.menu_key_list_debug_cons:
consolidate();
return true;
case R.id.menu_key_list_debug_read:
try {
KeychainDatabase.debugBackup(getActivity(), true);

View File

@@ -128,6 +128,11 @@ public class MainActivity extends BaseNfcActivity implements FabContainer, OnBac
getSupportFragmentManager().addOnBackStackChangedListener(this);
// all further initialization steps are saved as instance state
if (savedInstanceState != null) {
return;
}
Intent data = getIntent();
// If we got an EXTRA_RESULT in the intent, show the notification
if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
@@ -135,20 +140,18 @@ public class MainActivity extends BaseNfcActivity implements FabContainer, OnBac
result.createNotify(this).show();
}
if (savedInstanceState == null) {
// always initialize keys fragment to the bottom of the backstack
onKeysSelected();
// always initialize keys fragment to the bottom of the backstack
onKeysSelected();
if (data != null && data.hasExtra(EXTRA_INIT_FRAG)) {
// initialize FragmentLayout with KeyListFragment at first
switch (data.getIntExtra(EXTRA_INIT_FRAG, -1)) {
case ID_ENCRYPT_DECRYPT:
onEnDecryptSelected();
break;
case ID_APPS:
onAppsSelected();
break;
}
if (data != null && data.hasExtra(EXTRA_INIT_FRAG)) {
// initialize FragmentLayout with KeyListFragment at first
switch (data.getIntExtra(EXTRA_INIT_FRAG, -1)) {
case ID_ENCRYPT_DECRYPT:
onEnDecryptSelected();
break;
case ID_APPS:
onAppsSelected();
break;
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
/**
* Simply encapsulates a dialog. If orbot is not installed, it shows an install dialog, else a
* dialog to enable orbot.
*/
public class OrbotRequiredDialogActivity extends FragmentActivity {
// to provide any previous crypto input into which proxy preference is merged
public static final String EXTRA_CRYPTO_INPUT = "extra_crypto_input";
public static final String RESULT_CRYPTO_INPUT = "result_crypto_input";
private CryptoInputParcel mCryptoInputParcel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCryptoInputParcel = getIntent().getParcelableExtra(EXTRA_CRYPTO_INPUT);
if (mCryptoInputParcel == null) {
mCryptoInputParcel = new CryptoInputParcel();
}
showDialog();
}
/**
* Displays an install or start orbot dialog depending on orbot's presence and state
*/
public void showDialog() {
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
public void run() {
Runnable ignoreTor = new Runnable() {
@Override
public void run() {
Intent intent = new Intent();
mCryptoInputParcel.addParcelableProxy(ParcelableProxy.getForNoProxy());
intent.putExtra(RESULT_CRYPTO_INPUT, mCryptoInputParcel);
setResult(RESULT_OK, intent);
finish();
}
};
Runnable dialogDismissed = new Runnable() {
@Override
public void run() {
finish();
}
};
if (OrbotHelper.putOrbotInRequiredState(R.string.orbot_ignore_tor, ignoreTor, dialogDismissed,
Preferences.getPreferences(OrbotRequiredDialogActivity.this)
.getProxyPrefs(),
OrbotRequiredDialogActivity.this)) {
// no action required after all
Intent intent = new Intent();
intent.putExtra(RESULT_CRYPTO_INPUT, mCryptoInputParcel);
setResult(RESULT_OK, intent);
}
}
});
}
}

View File

@@ -43,7 +43,9 @@ import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
@@ -207,7 +209,7 @@ public class PassphraseWizardActivity extends FragmentActivity {
// transaction.replace(R.id.fragmentContainer, lpf).addToBackStack(null).commit();
}
} catch (IOException | FormatException e) {
e.printStackTrace();
Log.e(Constants.TAG, "Failed to write on NFC tag", e);
}
} else if (readNFC && AUTHENTICATION.equals(selectedAction)) {
@@ -232,7 +234,7 @@ public class PassphraseWizardActivity extends FragmentActivity {
}
}
} catch (IOException | FormatException e) {
e.printStackTrace();
Log.e(Constants.TAG, "Failed to read NFC tag", e);
}
}
}
@@ -263,7 +265,7 @@ public class PassphraseWizardActivity extends FragmentActivity {
try {
password = readText(ndefRecord);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
Log.e(Constants.TAG, "Failed to read password from tag", e);
}
}
}

View File

@@ -18,14 +18,21 @@
package org.sufficientlysecure.keychain.ui;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
@@ -33,9 +40,11 @@ import android.widget.LinearLayout;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.AppCompatPreferenceActivity;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.widget.IntegerListPreference;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
import java.util.List;
@@ -43,6 +52,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
public static final String ACTION_PREFS_CLOUD = "org.sufficientlysecure.keychain.ui.PREFS_CLOUD";
public static final String ACTION_PREFS_ADV = "org.sufficientlysecure.keychain.ui.PREFS_ADV";
public static final String ACTION_PREFS_PROXY = "org.sufficientlysecure.keychain.ui.PREFS_PROXY";
public static final int REQUEST_CODE_KEYSERVER_PREF = 0x00007005;
@@ -209,9 +219,205 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
}
}
public static class ProxyPrefsFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new Initializer(this).initialize();
}
public static class Initializer {
private CheckBoxPreference mUseTor;
private CheckBoxPreference mUseNormalProxy;
private EditTextPreference mProxyHost;
private EditTextPreference mProxyPort;
private ListPreference mProxyType;
private PreferenceActivity mActivity;
private PreferenceFragment mFragment;
public Initializer(PreferenceFragment fragment) {
mFragment = fragment;
}
public Initializer(PreferenceActivity activity) {
mActivity = activity;
}
public Preference automaticallyFindPreference(String key) {
if (mFragment != null) {
return mFragment.findPreference(key);
} else {
return mActivity.findPreference(key);
}
}
public void initialize() {
// makes android's preference framework write to our file instead of default
// This allows us to use the "persistent" attribute to simplify code
if (mFragment != null) {
Preferences.setPreferenceManagerFileAndMode(mFragment.getPreferenceManager());
// Load the preferences from an XML resource
mFragment.addPreferencesFromResource(R.xml.proxy_prefs);
} else {
Preferences.setPreferenceManagerFileAndMode(mActivity.getPreferenceManager());
// Load the preferences from an XML resource
mActivity.addPreferencesFromResource(R.xml.proxy_prefs);
}
mUseTor = (CheckBoxPreference) automaticallyFindPreference(Constants.Pref.USE_TOR_PROXY);
mUseNormalProxy = (CheckBoxPreference) automaticallyFindPreference(Constants.Pref.USE_NORMAL_PROXY);
mProxyHost = (EditTextPreference) automaticallyFindPreference(Constants.Pref.PROXY_HOST);
mProxyPort = (EditTextPreference) automaticallyFindPreference(Constants.Pref.PROXY_PORT);
mProxyType = (ListPreference) automaticallyFindPreference(Constants.Pref.PROXY_TYPE);
initializeUseTorPref();
initializeUseNormalProxyPref();
initializeEditTextPreferences();
initializeProxyTypePreference();
if (mUseTor.isChecked()) disableNormalProxyPrefs();
else if (mUseNormalProxy.isChecked()) disableUseTorPrefs();
}
private void initializeUseTorPref() {
mUseTor.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
Activity activity = mFragment != null ? mFragment.getActivity() : mActivity;
if ((Boolean) newValue) {
boolean installed = OrbotHelper.isOrbotInstalled(activity);
if (!installed) {
Log.d(Constants.TAG, "Prompting to install Tor");
OrbotHelper.getPreferenceInstallDialogFragment().show(activity.getFragmentManager(),
"installDialog");
// don't let the user check the box until he's installed orbot
return false;
} else {
disableNormalProxyPrefs();
// let the enable tor box be checked
return true;
}
} else {
// we're unchecking Tor, so enable other proxy
enableNormalProxyPrefs();
return true;
}
}
});
}
private void initializeUseNormalProxyPref() {
mUseNormalProxy.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if ((Boolean) newValue) {
disableUseTorPrefs();
} else {
enableUseTorPrefs();
}
return true;
}
});
}
private void initializeEditTextPreferences() {
mProxyHost.setSummary(mProxyHost.getText());
mProxyPort.setSummary(mProxyPort.getText());
mProxyHost.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
Activity activity = mFragment != null ? mFragment.getActivity() : mActivity;
if (TextUtils.isEmpty((String) newValue)) {
Notify.create(
activity,
R.string.pref_proxy_host_err_invalid,
Notify.Style.ERROR
).show();
return false;
} else {
mProxyHost.setSummary((CharSequence) newValue);
return true;
}
}
});
mProxyPort.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
Activity activity = mFragment != null ? mFragment.getActivity() : mActivity;
try {
int port = Integer.parseInt((String) newValue);
if (port < 0 || port > 65535) {
Notify.create(
activity,
R.string.pref_proxy_port_err_invalid,
Notify.Style.ERROR
).show();
return false;
}
// no issues, save port
mProxyPort.setSummary("" + port);
return true;
} catch (NumberFormatException e) {
Notify.create(
activity,
R.string.pref_proxy_port_err_invalid,
Notify.Style.ERROR
).show();
return false;
}
}
});
}
private void initializeProxyTypePreference() {
mProxyType.setSummary(mProxyType.getEntry());
mProxyType.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
CharSequence entry = mProxyType.getEntries()[mProxyType.findIndexOfValue((String) newValue)];
mProxyType.setSummary(entry);
return true;
}
});
}
private void disableNormalProxyPrefs() {
mUseNormalProxy.setChecked(false);
mUseNormalProxy.setEnabled(false);
mProxyHost.setEnabled(false);
mProxyPort.setEnabled(false);
mProxyType.setEnabled(false);
}
private void enableNormalProxyPrefs() {
mUseNormalProxy.setEnabled(true);
mProxyHost.setEnabled(true);
mProxyPort.setEnabled(true);
mProxyType.setEnabled(true);
}
private void disableUseTorPrefs() {
mUseTor.setChecked(false);
mUseTor.setEnabled(false);
}
private void enableUseTorPrefs() {
mUseTor.setEnabled(true);
}
}
}
@TargetApi(Build.VERSION_CODES.KITKAT)
protected boolean isValidFragment(String fragmentName) {
return AdvancedPrefsFragment.class.getName().equals(fragmentName)
|| CloudSearchPrefsFragment.class.getName().equals(fragmentName)
|| ProxyPrefsFragment.class.getName().equals(fragmentName)
|| super.isValidFragment(fragmentName);
}
@@ -270,7 +476,8 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
String[] servers = sPreferences.getKeyServers();
String serverSummary = context.getResources().getQuantityString(
R.plurals.n_keyservers, servers.length, servers.length);
return serverSummary + "; " + context.getString(R.string.label_preferred) + ": " + sPreferences.getPreferredKeyserver();
return serverSummary + "; " + context.getString(R.string.label_preferred) + ": " + sPreferences
.getPreferredKeyserver();
}
private static void initializeUseDefaultYubiKeyPin(final CheckBoxPreference mUseDefaultYubiKeyPin) {

View File

@@ -26,7 +26,6 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
@@ -38,6 +37,7 @@ import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
/**
* Sends the selected public key to a keyserver
@@ -132,8 +132,7 @@ public class UploadKeyActivity extends BaseActivity
@Override
public void onCryptoOperationSuccess(ExportResult result) {
Toast.makeText(UploadKeyActivity.this, R.string.msg_crt_upload_success,
Toast.LENGTH_SHORT).show();
result.createNotify(this).show();
}
@Override
@@ -143,7 +142,7 @@ public class UploadKeyActivity extends BaseActivity
@Override
public void onCryptoOperationError(ExportResult result) {
// TODO: Implement proper log for key upload then show error
result.createNotify(this).show();
}
@Override

View File

@@ -84,7 +84,9 @@ import org.sufficientlysecure.keychain.util.ContactHelper;
import org.sufficientlysecure.keychain.util.ExportHelper;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.NfcHelper;
import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
import java.io.IOException;
import java.util.ArrayList;
@@ -464,11 +466,11 @@ public class ViewKeyActivity extends BaseNfcActivity implements
HashMap<String, Object> data = providerHelper.getGenericData(
baseUri,
new String[] {KeychainContract.Keys.MASTER_KEY_ID, KeychainContract.KeyRings.HAS_SECRET},
new int[] {ProviderHelper.FIELD_TYPE_INTEGER, ProviderHelper.FIELD_TYPE_INTEGER});
new String[]{KeychainContract.Keys.MASTER_KEY_ID, KeychainContract.KeyRings.HAS_SECRET},
new int[]{ProviderHelper.FIELD_TYPE_INTEGER, ProviderHelper.FIELD_TYPE_INTEGER});
exportHelper.showExportKeysDialog(
new long[] {(Long) data.get(KeychainContract.KeyRings.MASTER_KEY_ID)},
new long[]{(Long) data.get(KeychainContract.KeyRings.MASTER_KEY_ID)},
Constants.Path.APP_DIR_FILE, ((Long) data.get(KeychainContract.KeyRings.HAS_SECRET) != 0)
);
} catch (ProviderHelper.NotFoundException e) {
@@ -492,7 +494,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(returnHandler);
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
new long[] {mMasterKeyId});
new long[]{mMasterKeyId});
deleteKeyDialog.show(getSupportFragmentManager(), "deleteKeyDialog");
}
@@ -636,7 +638,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements
long keyId = new ProviderHelper(this)
.getCachedPublicKeyRing(dataUri)
.extractOrGetMasterKeyId();
long[] encryptionKeyIds = new long[] {keyId};
long[] encryptionKeyIds = new long[]{keyId};
Intent intent;
if (text) {
intent = new Intent(this, EncryptTextActivity.class);
@@ -742,7 +744,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements
// These are the rows that we will retrieve.
static final String[] PROJECTION = new String[] {
static final String[] PROJECTION = new String[]{
KeychainContract.KeyRings._ID,
KeychainContract.KeyRings.MASTER_KEY_ID,
KeychainContract.KeyRings.USER_ID,
@@ -830,7 +832,8 @@ public class ViewKeyActivity extends BaseNfcActivity implements
AsyncTask<Long, Void, Bitmap> photoTask =
new AsyncTask<Long, Void, Bitmap>() {
protected Bitmap doInBackground(Long... mMasterKeyId) {
return ContactHelper.loadPhotoByMasterKeyId(getContentResolver(), mMasterKeyId[0], true);
return ContactHelper.loadPhotoByMasterKeyId(getContentResolver(),
mMasterKeyId[0], true);
}
protected void onPostExecute(Bitmap photo) {

View File

@@ -25,6 +25,9 @@ import java.io.OutputStreamWriter;
import android.app.Activity;
import android.app.ActivityOptions;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
@@ -49,7 +52,6 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
@@ -58,6 +60,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
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.ui.util.QrCodeUtils;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.NfcHelper;
@@ -197,7 +200,15 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
}
if (toClipboard) {
ClipboardReflection.copyToClipboard(activity, content);
ClipboardManager clipMan = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
if (clipMan == null) {
Notify.create(activity, R.string.error_clipboard_copy, Style.ERROR);
return;
}
ClipData clip = ClipData.newPlainText(Constants.CLIPBOARD_LABEL, content);
clipMan.setPrimaryClip(clip);
Notify.create(activity, fingerprintOnly ? R.string.fingerprint_copied_to_clipboard
: R.string.key_copied_to_clipboard, Notify.Style.OK).show();
return;

View File

@@ -51,6 +51,9 @@ import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
import java.util.ArrayList;
import java.util.Hashtable;
@@ -197,8 +200,22 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
mStartSearch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mStartSearch.setEnabled(false);
new DescribeKey().execute(fingerprint);
final Preferences.ProxyPrefs proxyPrefs = Preferences.getPreferences(getActivity())
.getProxyPrefs();
Runnable ignoreTor = new Runnable() {
@Override
public void run() {
mStartSearch.setEnabled(false);
new DescribeKey(proxyPrefs.parcelableProxy).execute(fingerprint);
}
};
if (OrbotHelper.putOrbotInRequiredState(R.string.orbot_ignore_tor, ignoreTor, proxyPrefs,
getActivity())) {
mStartSearch.setEnabled(false);
new DescribeKey(proxyPrefs.parcelableProxy).execute(fingerprint);
}
}
});
}
@@ -229,6 +246,11 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
// look for evidence from keybase in the background, make tabular version of result
//
private class DescribeKey extends AsyncTask<String, Void, ResultPage> {
ParcelableProxy mParcelableProxy;
public DescribeKey(ParcelableProxy parcelableProxy) {
mParcelableProxy = parcelableProxy;
}
@Override
protected ResultPage doInBackground(String... args) {
@@ -237,7 +259,7 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
final ArrayList<CharSequence> proofList = new ArrayList<CharSequence>();
final Hashtable<Integer, ArrayList<Proof>> proofs = new Hashtable<Integer, ArrayList<Proof>>();
try {
User keybaseUser = User.findByFingerprint(fingerprint);
User keybaseUser = User.findByFingerprint(fingerprint, mParcelableProxy.getProxy());
for (Proof proof : keybaseUser.getProofs()) {
Integer proofType = proof.getType();
appendIfOK(proofs, proofType, proof);

View File

@@ -210,7 +210,7 @@ public class ViewKeyYubiKeyFragment
}
@Override
protected void onCryptoOperationResult(PromoteKeyResult result) {
public void onCryptoOperationSuccess(PromoteKeyResult result) {
result.createNotify(getActivity()).show();
}
}

View File

@@ -27,6 +27,7 @@ import org.sufficientlysecure.keychain.keyimport.Keyserver;
import org.sufficientlysecure.keychain.operations.results.GetKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Preferences;
import java.util.ArrayList;
@@ -38,15 +39,18 @@ public class ImportKeysListCloudLoader
Preferences.CloudSearchPrefs mCloudPrefs;
String mServerQuery;
private ParcelableProxy mParcelableProxy;
private ArrayList<ImportKeysListEntry> mEntryList = new ArrayList<>();
private AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper;
public ImportKeysListCloudLoader(Context context, String serverQuery, Preferences.CloudSearchPrefs cloudPrefs) {
public ImportKeysListCloudLoader(Context context, String serverQuery, Preferences.CloudSearchPrefs cloudPrefs,
ParcelableProxy proxy) {
super(context);
mContext = context;
mServerQuery = serverQuery;
mCloudPrefs = cloudPrefs;
mParcelableProxy = proxy;
}
@Override
@@ -96,8 +100,11 @@ public class ImportKeysListCloudLoader
*/
private void queryServer(boolean enforceFingerprint) {
try {
ArrayList<ImportKeysListEntry> searchResult
= CloudSearch.search(mServerQuery, mCloudPrefs);
ArrayList<ImportKeysListEntry> searchResult = CloudSearch.search(
mServerQuery,
mCloudPrefs,
mParcelableProxy.getProxy()
);
mEntryList.clear();
// add result to data

View File

@@ -31,8 +31,12 @@ public abstract class CachingCryptoOperationFragment <T extends Parcelable, S ex
}
@Override
protected void onCryptoOperationResult(S result) {
super.onCryptoOperationResult(result);
public void onCryptoOperationSuccess(S result) {
mCachedActionsParcel = null;
}
@Override
public void onCryptoOperationError(S result) {
mCachedActionsParcel = null;
}

View File

@@ -20,56 +20,88 @@ package org.sufficientlysecure.keychain.ui.base;
import android.content.Intent;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
/**
* All fragments executing crypto operations need to extend this class.
/** This is a base class for fragments which implement a cryptoOperation.
*
* Subclasses of this class can call the cryptoOperation method to execute an
* operation in KeychainService which takes a parcelable of type T as its input
* and returns an OperationResult of type S as a result.
*
* The input (of type T) is not given directly to the cryptoOperation method,
* but must be provided by the overriden createOperationInput method to be
* available upon request during execution of the cryptoOperation.
*
* After running cryptoOperation, one of the onCryptoOperation*() methods will
* be called, depending on the success status of the operation. The subclass
* must override at least onCryptoOperationSuccess to proceed after a
* successful operation.
*
* @see KeychainService
*
*/
public abstract class CryptoOperationFragment<T extends Parcelable, S extends OperationResult>
extends Fragment implements CryptoOperationHelper.Callback<T, S> {
private CryptoOperationHelper<T, S> mOperationHelper;
final private CryptoOperationHelper<T, S> mOperationHelper;
public CryptoOperationFragment() {
mOperationHelper = new CryptoOperationHelper<>(this, this);
}
public void setProgressMessageResource(int id) {
mOperationHelper.setProgressMessageResource(id);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
mOperationHelper.handleActivityResult(requestCode, resultCode, data);
}
@Override
public abstract T createOperationInput();
/** Starts execution of the cryptographic operation.
*
* During this process, the createOperationInput() method will be called,
* this input will be handed to KeychainService, where it is executed in
* the appropriate *Operation class. If the result is a PendingInputResult,
* it is handled accordingly. Otherwise, it is returned in one of the
* onCryptoOperation* callbacks.
*/
protected void cryptoOperation() {
cryptoOperation(new CryptoInputParcel());
mOperationHelper.cryptoOperation();
}
protected void cryptoOperation(CryptoInputParcel cryptoInput) {
cryptoOperation(cryptoInput, true);
mOperationHelper.cryptoOperation(cryptoInput);
}
protected void cryptoOperation(CryptoInputParcel cryptoInput, boolean showProgress) {
mOperationHelper.cryptoOperation(cryptoInput, showProgress);
}
@Override @Nullable
/** Creates input for the crypto operation. Called internally after the
* crypto operation is started by a call to cryptoOperation(). Silently
* cancels operation if this method returns null. */
public abstract T createOperationInput();
/** Returns false, indicating that we did not handle progress ourselves. */
public boolean onCryptoSetProgress(String msg, int progress, int max) {
return false;
}
public void setProgressMessageResource(int id) {
mOperationHelper.setProgressMessageResource(id);
}
@Override
/** Called when the cryptoOperation() was successful. No default behavior
* here, this should always be implemented by a subclass! */
abstract public void onCryptoOperationSuccess(S result);
@Override
public void onCryptoOperationError(S result) {
onCryptoOperationResult(result);
result.createNotify(getActivity()).show();
}
@@ -77,19 +109,4 @@ public abstract class CryptoOperationFragment<T extends Parcelable, S extends Op
public void onCryptoOperationCancelled() {
}
@Override
public void onCryptoOperationSuccess(S result) {
onCryptoOperationResult(result);
}
/**
*
* To be overriden by subclasses, if desired. Provides a way to access the method by the
* same name in CryptoOperationHelper, if super.onCryptoOperationSuccess and
* super.onCryptoOperationError are called at the start of the respective functions in the
* subclass overriding them
* @param result
*/
protected void onCryptoOperationResult(S result) {
}
}

View File

@@ -28,8 +28,8 @@ import android.os.Messenger;
import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
@@ -39,6 +39,7 @@ import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.NfcOperationActivity;
import org.sufficientlysecure.keychain.ui.OrbotRequiredDialogActivity;
import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
@@ -52,16 +53,21 @@ import org.sufficientlysecure.keychain.util.Log;
*/
public class CryptoOperationHelper<T extends Parcelable, S extends OperationResult> {
public interface Callback <T extends Parcelable, S extends OperationResult> {
public interface Callback<T extends Parcelable, S extends OperationResult> {
T createOperationInput();
void onCryptoOperationSuccess(S result);
void onCryptoOperationCancelled();
void onCryptoOperationError(S result);
boolean onCryptoSetProgress(String msg, int progress, int max);
}
public static final int REQUEST_CODE_PASSPHRASE = 0x00008001;
public static final int REQUEST_CODE_NFC = 0x00008002;
public static final int REQUEST_CODE_ENABLE_ORBOT = 0x00008004;
// keeps track of request code used to start an activity from this CryptoOperationHelper.
// this is necessary when multiple CryptoOperationHelpers are used in the same fragment/activity
@@ -78,8 +84,6 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
/**
* If OperationHelper is being integrated into an activity
*
* @param activity
*/
public CryptoOperationHelper(FragmentActivity activity, Callback<T, S> callback, int progressMessageString) {
mActivity = activity;
@@ -90,8 +94,6 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
/**
* if OperationHelper is being integrated into a fragment
*
* @param fragment
*/
public CryptoOperationHelper(Fragment fragment, Callback<T, S> callback, int progressMessageString) {
mFragment = fragment;
@@ -102,8 +104,6 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
/**
* if OperationHelper is being integrated into a fragment with default message for the progress dialog
*
* @param fragment
*/
public CryptoOperationHelper(Fragment fragment, Callback<T, S> callback) {
mFragment = fragment;
@@ -116,11 +116,14 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
mProgressMessageResource = id;
}
private void initiateInputActivity(RequiredInputParcel requiredInput) {
private void initiateInputActivity(RequiredInputParcel requiredInput,
CryptoInputParcel cryptoInputParcel) {
Activity activity = mUseFragment ? mFragment.getActivity() : mActivity;
switch (requiredInput.mType) {
// TODO: Verify that all started activities add to cryptoInputParcel if necessary (like OrbotRequiredDialogActivity)
// don't forget to set mRequestedCode!
case NFC_MOVE_KEY_TO_CARD:
case NFC_DECRYPT:
case NFC_SIGN: {
@@ -130,7 +133,7 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
if (mUseFragment) {
mFragment.startActivityForResult(intent, mRequestedCode);
} else {
mActivity.startActivityForResult(intent, mRequestedCode);
activity.startActivityForResult(intent, mRequestedCode);
}
return;
}
@@ -143,22 +146,32 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
if (mUseFragment) {
mFragment.startActivityForResult(intent, mRequestedCode);
} else {
mActivity.startActivityForResult(intent, mRequestedCode);
activity.startActivityForResult(intent, mRequestedCode);
}
return;
}
}
throw new RuntimeException("Unhandled pending result!");
case ENABLE_ORBOT: {
Intent intent = new Intent(activity, OrbotRequiredDialogActivity.class);
intent.putExtra(OrbotRequiredDialogActivity.EXTRA_CRYPTO_INPUT, cryptoInputParcel);
mRequestedCode = REQUEST_CODE_ENABLE_ORBOT;
if (mUseFragment) {
mFragment.startActivityForResult(intent, mRequestedCode);
} else {
activity.startActivityForResult(intent, mRequestedCode);
}
return;
}
default: {
throw new RuntimeException("Unhandled pending result!");
}
}
}
/**
* Attempts the result of an activity started by this helper. Returns true if requestCode is recognized,
* false otherwise.
*
* @param requestCode
* @param resultCode
* @param data
* Attempts the result of an activity started by this helper. Returns true if requestCode is
* recognized, false otherwise.
* @return true if requestCode was recognized, false otherwise
*/
public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
@@ -196,6 +209,16 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
break;
}
case REQUEST_CODE_ENABLE_ORBOT: {
if (resultCode == Activity.RESULT_OK && data != null) {
CryptoInputParcel cryptoInput =
data.getParcelableExtra(
OrbotRequiredDialogActivity.RESULT_CRYPTO_INPUT);
cryptoOperation(cryptoInput);
return true;
}
}
default: {
return false;
}
@@ -225,7 +248,7 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
}
public void cryptoOperation(CryptoInputParcel cryptoInput, boolean showProgress) {
public void cryptoOperation(final CryptoInputParcel cryptoInput, boolean showProgress) {
FragmentActivity activity = mUseFragment ? mFragment.getActivity() : mActivity;
@@ -257,14 +280,14 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
final OperationResult result =
returnData.getParcelable(OperationResult.EXTRA_RESULT);
onHandleResult(result);
onHandleResult(result, cryptoInput);
}
}
@Override
protected void onSetProgress(String msg, int progress, int max) {
// allow handling of progress in fragment, or delegate upwards
if ( ! mCallback.onCryptoSetProgress(msg, progress, max)) {
if (!mCallback.onCryptoSetProgress(msg, progress, max)) {
super.onSetProgress(msg, progress, max);
}
}
@@ -291,15 +314,7 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
cryptoOperation(new CryptoInputParcel());
}
protected void onCryptoOperationResult(S result) {
if (result.success()) {
mCallback.onCryptoOperationSuccess(result);
} else {
mCallback.onCryptoOperationError(result);
}
}
public void onHandleResult(OperationResult result) {
public void onHandleResult(OperationResult result, CryptoInputParcel oldCryptoInput) {
Log.d(Constants.TAG, "Handling result in OperationHelper success: " + result.success());
if (result instanceof InputPendingResult) {
@@ -307,7 +322,7 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
if (pendingResult.isPending()) {
RequiredInputParcel requiredInput = pendingResult.getRequiredInputParcel();
initiateInputActivity(requiredInput);
initiateInputActivity(requiredInput, oldCryptoInput);
return;
}
}
@@ -315,8 +330,13 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
dismissProgress();
try {
// noinspection unchecked, because type erasure :(
onCryptoOperationResult((S) result);
if (result.success()) {
// noinspection unchecked, because type erasure :(
mCallback.onCryptoOperationSuccess((S) result);
} else {
// noinspection unchecked, because type erasure :(
mCallback.onCryptoOperationError((S) result);
}
} catch (ClassCastException e) {
throw new AssertionError("bad return class ("
+ result.getClass().getSimpleName() + "), this is a programming error!");

View File

@@ -48,10 +48,17 @@ import android.widget.EditText;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.TlsHelper;
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
import java.net.Proxy;
public class AddEditKeyserverDialogFragment extends DialogFragment implements OnEditorActionListener {
private static final String ARG_MESSENGER = "arg_messenger";
@@ -205,9 +212,21 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
@Override
public void onClick(View v) {
// behaviour same for edit and add
String keyserverUrl = mKeyserverEditText.getText().toString();
final String keyserverUrl = mKeyserverEditText.getText().toString();
if (mVerifyKeyserverCheckBox.isChecked()) {
verifyConnection(keyserverUrl);
final Preferences.ProxyPrefs proxyPrefs = Preferences.getPreferences(getActivity())
.getProxyPrefs();
Runnable ignoreTor = new Runnable() {
@Override
public void run() {
verifyConnection(keyserverUrl, null);
}
};
if (OrbotHelper.putOrbotInRequiredState(R.string.orbot_ignore_tor, ignoreTor, proxyPrefs,
getActivity())) {
verifyConnection(keyserverUrl, proxyPrefs.parcelableProxy.getProxy());
}
} else {
dismiss();
// return unverified keyserver back to activity
@@ -249,7 +268,7 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
sendMessageToHandler(MESSAGE_VERIFICATION_FAILED, data);
}
public void verifyConnection(String keyserver) {
public void verifyConnection(String keyserver, final Proxy proxy) {
new AsyncTask<String, Void, FailureReason>() {
ProgressDialog mProgressDialog;
@@ -283,10 +302,11 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
}
URI newKeyserver = new URI(scheme, schemeSpecificPart, fragment);
Log.d(Constants.TAG, "Converted URL" + newKeyserver);
Log.d("Converted URL", newKeyserver.toString());
// just see if we can get a connection, then immediately close
TlsHelper.openConnection(newKeyserver.toURL()).getInputStream().close();
OkHttpClient client = HkpKeyserver.getClient(newKeyserver.toURL(), proxy);
TlsHelper.pinCertificateIfNecessary(client, newKeyserver.toURL());
client.newCall(new Request.Builder().url(newKeyserver.toURL()).build()).execute();
} catch (TlsHelper.TlsHelperException e) {
reason = FailureReason.CONNECTION_FAILED;
} catch (MalformedURLException | URISyntaxException e) {

View File

@@ -0,0 +1,129 @@
/*
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui.dialog;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.app.DialogFragment;
import android.view.ContextThemeWrapper;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
/**
* displays a dialog asking the user to enable Tor
*/
public class OrbotStartDialogFragment extends DialogFragment {
private static final String ARG_MESSENGER = "messenger";
private static final String ARG_TITLE = "title";
private static final String ARG_MESSAGE = "message";
private static final String ARG_MIDDLE_BUTTON = "middleButton";
public static final int MESSAGE_MIDDLE_BUTTON = 1;
public static final int MESSAGE_DIALOG_DISMISSED = 2; // for either cancel or enable pressed
public static OrbotStartDialogFragment newInstance(Messenger messenger, int title, int message, int middleButton) {
Bundle args = new Bundle();
args.putParcelable(ARG_MESSENGER, messenger);
args.putInt(ARG_TITLE, title);
args.putInt(ARG_MESSAGE, message);
args.putInt(ARG_MIDDLE_BUTTON, middleButton);
OrbotStartDialogFragment fragment = new OrbotStartDialogFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Messenger messenger = getArguments().getParcelable(ARG_MESSENGER);
int title = getArguments().getInt(ARG_TITLE);
final int message = getArguments().getInt(ARG_MESSAGE);
int middleButton = getArguments().getInt(ARG_MIDDLE_BUTTON);
final Activity activity = getActivity();
// if the dialog is displayed from the application class, design is missing.
// hack to get holo design (which is not automatically applied due to activity's
// Theme.NoDisplay)
ContextThemeWrapper theme = new ContextThemeWrapper(activity,
R.style.Theme_AppCompat_Light_Dialog);
CustomAlertDialogBuilder builder = new CustomAlertDialogBuilder(theme);
builder.setTitle(title).setMessage(message);
builder.setNegativeButton(R.string.orbot_start_dialog_cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Message msg = Message.obtain();
msg.what = MESSAGE_DIALOG_DISMISSED;
try {
messenger.send(msg);
} catch (RemoteException e) {
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
} catch (NullPointerException e) {
Log.w(Constants.TAG, "Messenger is null!", e);
}
}
});
builder.setPositiveButton(R.string.orbot_start_dialog_start, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
getActivity().startActivityForResult(OrbotHelper.getOrbotStartIntent(), 1);
Message msg = Message.obtain();
msg.what = MESSAGE_DIALOG_DISMISSED;
try {
messenger.send(msg);
} catch (RemoteException e) {
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
} catch (NullPointerException e) {
Log.w(Constants.TAG, "Messenger is null!", e);
}
}
});
builder.setNeutralButton(middleButton, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Message msg = new Message();
msg.what = MESSAGE_MIDDLE_BUTTON;
try {
messenger.send(msg);
} catch (RemoteException e) {
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
} catch (NullPointerException e) {
Log.w(Constants.TAG, "Messenger is null!", e);
}
}
});
return builder.show();
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui.dialog;
import android.app.Dialog;
import android.os.Bundle;
import android.os.Messenger;
import android.app.DialogFragment;
import org.sufficientlysecure.keychain.ui.util.InstallDialogFragmentHelper;
public class PreferenceInstallDialogFragment extends DialogFragment {
public static final int MESSAGE_MIDDLE_CLICKED = 1;
public static final int MESSAGE_DIALOG_DISMISSED = 2;
/**
* Creates a dialog which prompts the user to install an application. Consists of two default buttons ("Install"
* and "Cancel") and an optional third button. Callbacks are provided only for the middle button, if set.
*
* @param messenger required only for callback from middle button if it has been set
* @param title
* @param message content of dialog
* @param packageToInstall package name of application to install
* @param middleButton if not null, adds a third button to the app with a call back
* @return The dialog to display
*/
public static PreferenceInstallDialogFragment newInstance(Messenger messenger, int title, int message,
String packageToInstall, int middleButton, boolean
useMiddleButton) {
PreferenceInstallDialogFragment frag = new PreferenceInstallDialogFragment();
Bundle args = new Bundle();
InstallDialogFragmentHelper.wrapIntoArgs(messenger, title, message, packageToInstall, middleButton,
useMiddleButton, args);
frag.setArguments(args);
return frag;
}
/**
* To create a DialogFragment with only two buttons
*
* @param title
* @param message
* @param packageToInstall
* @return
*/
public static PreferenceInstallDialogFragment newInstance(int title, int message,
String packageToInstall) {
return newInstance(null, title, message, packageToInstall, -1, false);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return InstallDialogFragmentHelper.getInstallDialogFromArgs(getArguments(), getActivity(),
MESSAGE_MIDDLE_CLICKED, MESSAGE_DIALOG_DISMISSED);
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui.dialog;
import android.app.Dialog;
import android.os.Bundle;
import android.os.Messenger;
import android.support.v4.app.DialogFragment;
import org.sufficientlysecure.keychain.ui.util.InstallDialogFragmentHelper;
public class SupportInstallDialogFragment extends DialogFragment {
public static final int MESSAGE_MIDDLE_CLICKED = 1;
public static final int MESSAGE_DIALOG_DISMISSED = 2;
/**
* Creates a dialog which prompts the user to install an application. Consists of two default buttons ("Install"
* and "Cancel") and an optional third button. Callbacks are provided only for the middle button, if set.
*
* @param messenger required only for callback from middle button if it has been set
* @param title
* @param message content of dialog
* @param packageToInstall package name of application to install
* @param middleButton if not null, adds a third button to the app with a call back
* @return The dialog to display
*/
public static SupportInstallDialogFragment newInstance(Messenger messenger, int title, int message,
String packageToInstall, int middleButton, boolean
useMiddleButton) {
SupportInstallDialogFragment frag = new SupportInstallDialogFragment();
Bundle args = new Bundle();
InstallDialogFragmentHelper.wrapIntoArgs(messenger, title, message, packageToInstall, middleButton,
useMiddleButton, args);
frag.setArguments(args);
return frag;
}
/**
* To create a DialogFragment with only two buttons
*
* @param title
* @param message
* @param packageToInstall
* @return
*/
public static SupportInstallDialogFragment newInstance(int title, int message,
String packageToInstall) {
return newInstance(null, title, message, packageToInstall, -1, false);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return InstallDialogFragmentHelper.getInstallDialogFromArgs(getArguments(), getActivity(),
MESSAGE_MIDDLE_CLICKED, MESSAGE_DIALOG_DISMISSED);
}
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui.util;
import android.app.Activity;
import android.app.AlertDialog;
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.os.RemoteException;
import android.view.ContextThemeWrapper;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
import org.sufficientlysecure.keychain.util.Log;
public class InstallDialogFragmentHelper {
private static final String ARG_MESSENGER = "messenger";
private static final String ARG_TITLE = "title";
private static final String ARG_MESSAGE = "message";
private static final String ARG_MIDDLE_BUTTON = "middleButton";
private static final String ARG_INSTALL_PATH = "installPath";
private static final String ARG_USE_MIDDLE_BUTTON = "useMiddleButton";
private static final String PLAY_STORE_PATH = "market://search?q=pname:";
public static void wrapIntoArgs(Messenger messenger, int title, int message, String packageToInstall,
int middleButton, boolean useMiddleButton, Bundle args) {
args.putParcelable(ARG_MESSENGER, messenger);
args.putInt(ARG_TITLE, title);
args.putInt(ARG_MESSAGE, message);
args.putInt(ARG_MIDDLE_BUTTON, middleButton);
args.putString(ARG_INSTALL_PATH, PLAY_STORE_PATH + packageToInstall);
args.putBoolean(ARG_USE_MIDDLE_BUTTON, useMiddleButton);
}
public static AlertDialog getInstallDialogFromArgs(Bundle args, final Activity activity,
final int messengerMiddleButtonClicked,
final int messengerDialogDimissed) {
final Messenger messenger = args.getParcelable(ARG_MESSENGER);
final int title = args.getInt(ARG_TITLE);
final int message = args.getInt(ARG_MESSAGE);
final int middleButton = args.getInt(ARG_MIDDLE_BUTTON);
final String installPath = args.getString(ARG_INSTALL_PATH);
final boolean useMiddleButton = args.getBoolean(ARG_USE_MIDDLE_BUTTON);
// if the dialog is displayed from the application class, design is missing.
// hack to get holo design (which is not automatically applied due to activity's
// Theme.NoDisplay)
ContextThemeWrapper theme = new ContextThemeWrapper(activity,
R.style.Theme_AppCompat_Light_Dialog);
CustomAlertDialogBuilder builder = new CustomAlertDialogBuilder(theme);
builder.setTitle(title).setMessage(message);
builder.setNegativeButton(R.string.orbot_install_dialog_cancel,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Message msg = Message.obtain();
msg.what = messengerDialogDimissed;
try {
messenger.send(msg);
} catch (RemoteException e) {
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
} catch (NullPointerException e) {
Log.w(Constants.TAG, "Messenger is null!", e);
}
}
});
builder.setPositiveButton(R.string.orbot_install_dialog_install,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Uri uri = Uri.parse(installPath);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
activity.startActivity(intent);
Message msg = Message.obtain();
msg.what = messengerDialogDimissed;
try {
messenger.send(msg);
} catch (RemoteException e) {
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
} catch (NullPointerException e) {
Log.w(Constants.TAG, "Messenger is null!", e);
}
}
}
);
if (useMiddleButton) {
builder.setNeutralButton(middleButton,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Message msg = Message.obtain();
msg.what = messengerMiddleButtonClicked;
try {
messenger.send(msg);
} catch (RemoteException e) {
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
} catch (NullPointerException e) {
Log.w(Constants.TAG, "Messenger is null!", e);
}
}
}
);
}
return builder.show();
}
}

View File

@@ -31,7 +31,9 @@ import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.Log;
/**
* Created by Matt Allen