Support of SmartPGP secure messaging
This commit is contained in:
@@ -298,6 +298,7 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
|
||||
// Just close
|
||||
finish();
|
||||
} else {
|
||||
mSecurityTokenHelper.clearSecureMessaging();
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
|
||||
@@ -58,12 +58,15 @@ import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
||||
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SettingsActivity extends AppCompatPreferenceActivity {
|
||||
|
||||
public static final int REQUEST_CODE_KEYSERVER_PREF = 0x00007005;
|
||||
public static final int REQUEST_CODE_SMARTPGP_AUTHORITIES_PREF = 0x00007006;
|
||||
private static final int REQUEST_PERMISSION_READ_CONTACTS = 13;
|
||||
|
||||
private static Preferences sPreferences;
|
||||
@@ -554,6 +557,8 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
||||
*/
|
||||
public static class ExperimentalPrefsFragment extends PresetPreferenceFragment {
|
||||
|
||||
private PreferenceScreen mSmartPGPAuthoritiesPreference = null;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -563,6 +568,51 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
||||
|
||||
initializeTheme((ListPreference) findPreference(Constants.Pref.THEME));
|
||||
|
||||
mSmartPGPAuthoritiesPreference = (PreferenceScreen) findPreference(Constants.Pref.EXPERIMENTAL_SMARTPGP_AUTHORITIES);
|
||||
|
||||
final KeyStore ks = SettingsSmartPGPAuthoritiesActivity.readKeystore(getActivity());
|
||||
int size = 0;
|
||||
try {
|
||||
if (ks != null) {
|
||||
size = ks.size();
|
||||
}
|
||||
} catch (KeyStoreException e) {}
|
||||
|
||||
mSmartPGPAuthoritiesPreference.setSummary(getActivity().getResources().getQuantityString(R.plurals.n_authorities, size, size));
|
||||
|
||||
mSmartPGPAuthoritiesPreference
|
||||
.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Intent intent = new Intent(getActivity(),
|
||||
SettingsSmartPGPAuthoritiesActivity.class);
|
||||
startActivityForResult(intent, REQUEST_CODE_SMARTPGP_AUTHORITIES_PREF);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
switch (requestCode) {
|
||||
case REQUEST_CODE_SMARTPGP_AUTHORITIES_PREF: {
|
||||
// update preference, in case it changed
|
||||
final KeyStore ks = SettingsSmartPGPAuthoritiesActivity.readKeystore(getActivity());
|
||||
int size = 0;
|
||||
try {
|
||||
if (ks != null) {
|
||||
size = ks.size();
|
||||
}
|
||||
} catch (KeyStoreException e) {}
|
||||
|
||||
mSmartPGPAuthoritiesPreference.setSummary(getActivity().getResources().getQuantityString(R.plurals.n_authorities, size, size));
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void initializeTheme(final ListPreference themePref) {
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
|
||||
*
|
||||
* 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.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
public class SettingsSmartPGPAuthoritiesActivity extends BaseActivity {
|
||||
|
||||
public static final String EXTRA_SMARTPGP_AUTHORITIES = "smartpgp_authorities";
|
||||
|
||||
private static final String KEYSTORE_FILE = "smartpgp_authorities.keystore";
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Intent intent = getIntent();
|
||||
String authorities[] = intent.getStringArrayExtra(EXTRA_SMARTPGP_AUTHORITIES);
|
||||
loadFragment(savedInstanceState, authorities);
|
||||
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initLayout() {
|
||||
setContentView(R.layout.smartpgp_authorities_preference);
|
||||
}
|
||||
|
||||
private void loadFragment(Bundle savedInstanceState, String[] authorities) {
|
||||
// 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.
|
||||
if (savedInstanceState != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
SettingsSmartPGPAuthorityFragment fragment = SettingsSmartPGPAuthorityFragment.newInstance(authorities);
|
||||
|
||||
// Add the fragment to the 'fragment_container' FrameLayout
|
||||
// NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.smartpgp_authorities_settings_fragment_container, fragment)
|
||||
.commitAllowingStateLoss();
|
||||
// do it immediately!
|
||||
getSupportFragmentManager().executePendingTransactions();
|
||||
}
|
||||
|
||||
public static final KeyStore readKeystore(final Context ctx) {
|
||||
try {
|
||||
final File kf = new File(ctx.getFilesDir(), KEYSTORE_FILE);
|
||||
final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
|
||||
ks.load(null, null);
|
||||
|
||||
if (kf.exists()) {
|
||||
final FileInputStream fis = new FileInputStream(kf);
|
||||
ks.load(fis, null);
|
||||
fis.close();
|
||||
}
|
||||
|
||||
return ks;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static final void writeKeystore(final Context ctx, final KeyStore ks) {
|
||||
try {
|
||||
final File kf = new File(ctx.getFilesDir(), KEYSTORE_FILE);
|
||||
|
||||
if (kf.exists()) {
|
||||
kf.delete();
|
||||
}
|
||||
|
||||
final FileOutputStream fos = new FileOutputStream(kf);
|
||||
ks.store(fos, null);
|
||||
fos.flush();
|
||||
fos.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* Copyright (C) 2012-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.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
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.R;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.AddEditSmartPGPAuthorityDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperAdapter;
|
||||
import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperDragCallback;
|
||||
import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperViewHolder;
|
||||
import org.sufficientlysecure.keychain.ui.util.recyclerview.RecyclerItemClickListener;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class SettingsSmartPGPAuthorityFragment extends Fragment implements RecyclerItemClickListener.OnItemClickListener {
|
||||
|
||||
private ItemTouchHelper mItemTouchHelper;
|
||||
|
||||
private ArrayList<String> mAuthorities;
|
||||
private AuthorityListAdapter mAdapter;
|
||||
|
||||
public static SettingsSmartPGPAuthorityFragment newInstance(String[] authorities) {
|
||||
return new SettingsSmartPGPAuthorityFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
|
||||
savedInstanceState) {
|
||||
|
||||
return inflater.inflate(R.layout.settings_smartpgp_authority_fragment, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
List<String> authorities = new LinkedList();
|
||||
|
||||
try {
|
||||
final KeyStore ks = SettingsSmartPGPAuthoritiesActivity.readKeystore(getActivity());
|
||||
final Enumeration<String> it = ks.aliases();
|
||||
|
||||
while (it.hasMoreElements()) {
|
||||
authorities.add(it.nextElement());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
mAuthorities = new ArrayList<>(authorities);
|
||||
mAdapter = new AuthorityListAdapter(mAuthorities);
|
||||
|
||||
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.smartpgp_authority_recycler_view);
|
||||
recyclerView.setAdapter(mAdapter);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
|
||||
ItemTouchHelper.Callback callback = new ItemTouchHelperDragCallback(mAdapter);
|
||||
mItemTouchHelper = new ItemTouchHelper(callback);
|
||||
mItemTouchHelper.attachToRecyclerView(recyclerView);
|
||||
|
||||
// for clicks
|
||||
recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(getActivity(), this));
|
||||
|
||||
// can't use item decoration because it doesn't move with drag and drop
|
||||
// recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), null));
|
||||
|
||||
// We have a menu item to show in action bar.
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.smartpgp_authority_pref_menu, menu);
|
||||
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
|
||||
case R.id.menu_add_smartpgp_authority:
|
||||
startAddAuthorityDialog();
|
||||
return true;
|
||||
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void startAddAuthorityDialog() {
|
||||
startEditAuthorityDialog(AddEditSmartPGPAuthorityDialogFragment.Action.ADD, null, null, -1);
|
||||
}
|
||||
|
||||
private void startEditAuthorityDialog(AddEditSmartPGPAuthorityDialogFragment.Action action,
|
||||
final String old_alias, final Uri uri, final int position) {
|
||||
Handler returnHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
Bundle data = message.getData();
|
||||
final String new_alias = data.getString(AddEditSmartPGPAuthorityDialogFragment.OUT_ALIAS);
|
||||
final int position = data.getInt(AddEditSmartPGPAuthorityDialogFragment.OUT_POSITION);
|
||||
final String uri = data.getString(AddEditSmartPGPAuthorityDialogFragment.OUT_URI);
|
||||
|
||||
final AddEditSmartPGPAuthorityDialogFragment.Action action =
|
||||
(AddEditSmartPGPAuthorityDialogFragment.Action)
|
||||
data.getSerializable(AddEditSmartPGPAuthorityDialogFragment.OUT_ACTION);
|
||||
|
||||
switch(action) {
|
||||
case ADD:
|
||||
if (editAuthority(old_alias, new_alias, position, uri)) {
|
||||
Notify.create(getActivity(), "Authority " + new_alias + " added",
|
||||
Notify.LENGTH_SHORT, Notify.Style.OK).show();
|
||||
}
|
||||
break;
|
||||
|
||||
case EDIT:
|
||||
if (editAuthority(old_alias, new_alias, position, uri)){
|
||||
Notify.create(getActivity(), "Authority " + old_alias + " modified",
|
||||
Notify.LENGTH_SHORT, Notify.Style.OK).show();
|
||||
}
|
||||
break;
|
||||
|
||||
case DELETE:
|
||||
if (deleteAuthority(position)) {
|
||||
Notify.create(getActivity(), "Authority " + old_alias + " deleted",
|
||||
Notify.LENGTH_SHORT, Notify.Style.OK).show();
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(returnHandler);
|
||||
AddEditSmartPGPAuthorityDialogFragment dialogFragment = AddEditSmartPGPAuthorityDialogFragment
|
||||
.newInstance(messenger, action, old_alias, uri, position);
|
||||
dialogFragment.show(getFragmentManager(), "addSmartPGPAuthorityDialog");
|
||||
}
|
||||
|
||||
|
||||
private boolean editAuthority(final String old_alias, final String new_alias, final int position, final String uri) {
|
||||
try {
|
||||
final KeyStore ks = SettingsSmartPGPAuthoritiesActivity.readKeystore(getContext());
|
||||
|
||||
if (ks == null) {
|
||||
throw new KeyStoreException("no keystore found");
|
||||
}
|
||||
|
||||
Certificate old_cert = null;
|
||||
if (old_alias != null) {
|
||||
old_cert = ks.getCertificate(old_alias);
|
||||
ks.deleteEntry(old_alias);
|
||||
mAuthorities.remove(old_alias);
|
||||
mAdapter.notifyItemRemoved(position);
|
||||
}
|
||||
|
||||
Certificate new_cert = null;
|
||||
if (uri == null) {
|
||||
new_cert = old_cert;
|
||||
} else {
|
||||
final InputStream fis = getContext().getContentResolver().openInputStream(Uri.parse(uri));
|
||||
|
||||
final CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
new_cert = cf.generateCertificate(fis);
|
||||
if (!(new_cert instanceof X509Certificate)) {
|
||||
Notify.create(getActivity(), "Invalid certificate", Notify.LENGTH_LONG, Notify.Style.ERROR).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
fis.close();
|
||||
}
|
||||
|
||||
if (new_alias == null || new_cert == null) {
|
||||
Notify.create(getActivity(), "Missing alias or certificate", Notify.LENGTH_LONG, Notify.Style.ERROR).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
final X509Certificate x509cert = (X509Certificate)new_cert;
|
||||
|
||||
x509cert.checkValidity();
|
||||
|
||||
ks.setCertificateEntry(new_alias, x509cert);
|
||||
|
||||
SettingsSmartPGPAuthoritiesActivity.writeKeystore(getContext(), ks);
|
||||
|
||||
mAuthorities.add(new_alias);
|
||||
mAdapter.notifyItemInserted(mAuthorities.size() - 1);
|
||||
|
||||
return true;
|
||||
|
||||
} catch (IOException e) {
|
||||
Notify.create(getActivity(), "failed to open certificate (" + e.getMessage() + ")", Notify.LENGTH_LONG, Notify.Style.ERROR).show();
|
||||
} catch (CertificateException e) {
|
||||
Notify.create(getActivity(), "invalid certificate (" + e.getMessage() + ")", Notify.LENGTH_LONG, Notify.Style.ERROR).show();
|
||||
} catch (KeyStoreException e) {
|
||||
Notify.create(getActivity(), "invalid keystore (" + e.getMessage() + ")", Notify.LENGTH_LONG, Notify.Style.ERROR).show();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean deleteAuthority(final int position) {
|
||||
try {
|
||||
final KeyStore ks = SettingsSmartPGPAuthoritiesActivity.readKeystore(getContext());
|
||||
|
||||
if (ks == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ks.deleteEntry(mAuthorities.get(position));
|
||||
|
||||
SettingsSmartPGPAuthoritiesActivity.writeKeystore(getContext(), ks);
|
||||
|
||||
mAuthorities.remove(mAuthorities.get(position));
|
||||
mAdapter.notifyItemRemoved(position);
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(View view, int position) {
|
||||
startEditAuthorityDialog(AddEditSmartPGPAuthorityDialogFragment.Action.EDIT,
|
||||
mAuthorities.get(position), null, position);
|
||||
}
|
||||
|
||||
|
||||
public class AuthorityListAdapter extends RecyclerView.Adapter<AuthorityListAdapter.ViewHolder>
|
||||
implements ItemTouchHelperAdapter {
|
||||
|
||||
private final List<String> mAuthorities;
|
||||
|
||||
public AuthorityListAdapter(List<String> authorities) {
|
||||
mAuthorities = authorities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.settings_smartpgp_authority_item, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(final ViewHolder holder, int position) {
|
||||
holder.authorityName.setText(mAuthorities.get(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemMove(RecyclerView.ViewHolder source, RecyclerView.ViewHolder target,
|
||||
int fromPosition, int toPosition) {
|
||||
Collections.swap(mAuthorities, fromPosition, toPosition);
|
||||
notifyItemMoved(fromPosition, toPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mAuthorities.size();
|
||||
}
|
||||
|
||||
|
||||
public class ViewHolder extends RecyclerView.ViewHolder implements
|
||||
ItemTouchHelperViewHolder {
|
||||
|
||||
public final ViewGroup outerLayout;
|
||||
public final TextView authorityName;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
outerLayout = (ViewGroup) itemView.findViewById(R.id.outer_layout);
|
||||
authorityName = (TextView) itemView.findViewById(R.id.smartpgp_authority_tv);
|
||||
itemView.setClickable(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemSelected() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClear() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,7 +168,7 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
|
||||
@Override
|
||||
protected IOException doInBackground(Void... params) {
|
||||
try {
|
||||
handleSecurityToken(transport);
|
||||
handleSecurityToken(transport, BaseSecurityTokenActivity.this);
|
||||
} catch (IOException e) {
|
||||
return e;
|
||||
}
|
||||
@@ -428,13 +428,13 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleSecurityToken(Transport transport) throws IOException {
|
||||
protected void handleSecurityToken(Transport transport, Context ctx) throws IOException {
|
||||
// Don't reconnect if device was already connected
|
||||
if (!(mSecurityTokenHelper.isPersistentConnectionAllowed()
|
||||
&& mSecurityTokenHelper.isConnected()
|
||||
&& mSecurityTokenHelper.getTransport().equals(transport))) {
|
||||
mSecurityTokenHelper.setTransport(transport);
|
||||
mSecurityTokenHelper.connectToDevice();
|
||||
mSecurityTokenHelper.connectToDevice(ctx);
|
||||
}
|
||||
doSecurityTokenInBackground();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* 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.Dialog;
|
||||
import android.content.Context;
|
||||
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.support.annotation.NonNull;
|
||||
import android.support.design.widget.TextInputLayout;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.TextView.OnEditorActionListener;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.EncryptFilesFragment;
|
||||
import org.sufficientlysecure.keychain.util.FileHelper;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
|
||||
public class AddEditSmartPGPAuthorityDialogFragment extends DialogFragment implements OnEditorActionListener {
|
||||
private static final String IN_MESSENGER = "in_messenger";
|
||||
private static final String IN_ACTION = "in_dialog_action";
|
||||
private static final String IN_POSITION = "in_position";
|
||||
private static final String IN_ALIAS = "in_authority";
|
||||
private static final String IN_URI = "in_uri";
|
||||
|
||||
public static final String OUT_ACTION = "out_action";
|
||||
public static final String OUT_ALIAS = "out_alias";
|
||||
public static final String OUT_POSITION = "out_position";
|
||||
public static final String OUT_URI = "out_uri";
|
||||
|
||||
private Messenger mMessenger;
|
||||
private Action mAction;
|
||||
private int mPosition;
|
||||
private Uri mURI;
|
||||
|
||||
private EditText mAuthorityAliasText;
|
||||
private TextInputLayout mAuthorityAliasTextLayout;
|
||||
private Button mAuthorityAdd;
|
||||
|
||||
public enum Action {
|
||||
ADD,
|
||||
EDIT,
|
||||
DELETE
|
||||
}
|
||||
|
||||
public static AddEditSmartPGPAuthorityDialogFragment newInstance(Messenger messenger,
|
||||
Action action,
|
||||
String alias,
|
||||
Uri uri,
|
||||
int position) {
|
||||
AddEditSmartPGPAuthorityDialogFragment frag = new AddEditSmartPGPAuthorityDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putParcelable(IN_MESSENGER, messenger);
|
||||
args.putSerializable(IN_ACTION, action);
|
||||
args.putString(IN_ALIAS, alias);
|
||||
args.putInt(IN_POSITION, position);
|
||||
if (uri != null) {
|
||||
args.putString(IN_URI, uri.toString());
|
||||
}
|
||||
|
||||
frag.setArguments(args);
|
||||
|
||||
return frag;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final Activity activity = getActivity();
|
||||
|
||||
mMessenger = getArguments().getParcelable(IN_MESSENGER);
|
||||
mAction = (Action) getArguments().getSerializable(IN_ACTION);
|
||||
mPosition = getArguments().getInt(IN_POSITION);
|
||||
if (getArguments().getString(IN_URI) == null)
|
||||
mURI = null;
|
||||
else
|
||||
mURI = Uri.parse(getArguments().getString(IN_URI));
|
||||
|
||||
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
|
||||
|
||||
LayoutInflater inflater = activity.getLayoutInflater();
|
||||
View view = inflater.inflate(R.layout.add_smartpgp_authority_dialog, null);
|
||||
alert.setView(view);
|
||||
|
||||
mAuthorityAliasText = (EditText) view.findViewById(R.id.smartpgp_authority_alias_edit_text);
|
||||
mAuthorityAliasTextLayout = (TextInputLayout) view.findViewById(R.id.smartpgp_authority_alias_edit_text_layout);
|
||||
mAuthorityAdd = (Button) view.findViewById(R.id.smartpgp_authority_filename);
|
||||
|
||||
mAuthorityAdd.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
FileHelper.openDocument(AddEditSmartPGPAuthorityDialogFragment.this, null, "*/*", false,
|
||||
EncryptFilesFragment.REQUEST_CODE_INPUT);
|
||||
}
|
||||
});
|
||||
|
||||
mAuthorityAliasText.setText(getArguments().getString(IN_ALIAS));
|
||||
|
||||
switch (mAction) {
|
||||
case ADD:
|
||||
alert.setTitle(R.string.add_smartpgp_authority_dialog_title);
|
||||
break;
|
||||
case EDIT:
|
||||
case DELETE:
|
||||
alert.setTitle(R.string.show_smartpgp_authority_dialog_title);
|
||||
break;
|
||||
}
|
||||
|
||||
// we don't want dialog to be dismissed on click for keyserver addition or edit,
|
||||
// thereby requiring the hack seen below and in onStart
|
||||
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
// we need to have an empty listener to prevent errors on some devices as mentioned
|
||||
// at http://stackoverflow.com/q/13746412/3000919
|
||||
// actual listener set in onStart for adding keyservers or editing them
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
switch (mAction) {
|
||||
case EDIT:
|
||||
case DELETE:
|
||||
alert.setNeutralButton(R.string.label_smartpgp_authority_dialog_delete,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
deleteAuthority();
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
// Hack to open keyboard.
|
||||
// This is the only method that I found to work across all Android versions
|
||||
// http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/
|
||||
// Notes: * onCreateView can't be used because we want to add buttons to the dialog
|
||||
// * opening in onActivityCreated does not work on Android 4.4
|
||||
mAuthorityAliasText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
mAuthorityAliasText.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
InputMethodManager imm = (InputMethodManager) getActivity()
|
||||
.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.showSoftInput(mAuthorityAliasText, InputMethodManager.SHOW_IMPLICIT);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
mAuthorityAliasText.requestFocus();
|
||||
|
||||
mAuthorityAliasText.setImeActionLabel(getString(android.R.string.ok),
|
||||
EditorInfo.IME_ACTION_DONE);
|
||||
mAuthorityAliasText.setOnEditorActionListener(this);
|
||||
|
||||
return alert.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
switch (requestCode) {
|
||||
case EncryptFilesFragment.REQUEST_CODE_INPUT:
|
||||
if (data != null) {
|
||||
mURI = data.getData();
|
||||
} else {
|
||||
mURI = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
AlertDialog addKeyserverDialog = (AlertDialog) getDialog();
|
||||
if (addKeyserverDialog != null) {
|
||||
Button positiveButton = addKeyserverDialog.getButton(Dialog.BUTTON_POSITIVE);
|
||||
positiveButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mAuthorityAliasTextLayout.setErrorEnabled(false);
|
||||
|
||||
dismiss();
|
||||
// return unverified keyserver back to activity
|
||||
authorityEdited();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void authorityEdited() {
|
||||
dismiss();
|
||||
Bundle data = new Bundle();
|
||||
data.putSerializable(OUT_ACTION, mAction);
|
||||
data.putString(OUT_ALIAS, mAuthorityAliasText.getText().toString());
|
||||
data.putInt(OUT_POSITION, mPosition);
|
||||
if (mURI != null) {
|
||||
data.putString(OUT_URI, mURI.toString());
|
||||
}
|
||||
|
||||
sendMessageToHandler(data);
|
||||
}
|
||||
|
||||
public void deleteAuthority() {
|
||||
dismiss();
|
||||
Bundle data = new Bundle();
|
||||
data.putSerializable(OUT_ACTION, Action.DELETE);
|
||||
data.putString(OUT_ALIAS, mAuthorityAliasText.getText().toString());
|
||||
data.putInt(OUT_POSITION, mPosition);
|
||||
|
||||
sendMessageToHandler(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
// hide keyboard on dismiss
|
||||
hideKeyboard();
|
||||
|
||||
super.onDismiss(dialog);
|
||||
}
|
||||
|
||||
private void hideKeyboard() {
|
||||
if (getActivity() == null) {
|
||||
return;
|
||||
}
|
||||
InputMethodManager inputManager = (InputMethodManager) getActivity()
|
||||
.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
|
||||
//check if no view has focus:
|
||||
View v = getActivity().getCurrentFocus();
|
||||
if (v == null)
|
||||
return;
|
||||
|
||||
inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate the "done" button on the soft keyboard with the okay button in the view
|
||||
*/
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if (EditorInfo.IME_ACTION_DONE == actionId) {
|
||||
AlertDialog dialog = ((AlertDialog) getDialog());
|
||||
Button bt = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
|
||||
|
||||
bt.performClick();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send message back to handler which is initialized in a activity
|
||||
*
|
||||
*/
|
||||
private void sendMessageToHandler(Bundle data) {
|
||||
Message msg = Message.obtain();
|
||||
if (data != null) {
|
||||
msg.setData(data);
|
||||
}
|
||||
|
||||
try {
|
||||
mMessenger.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user