diff options
author | Vincent Breitmoser <valodim@mugenguild.com> | 2015-03-02 14:39:28 +0100 |
---|---|---|
committer | Vincent Breitmoser <valodim@mugenguild.com> | 2015-03-02 14:39:28 +0100 |
commit | 2f83291920e024b0f8038fe0caa747051b41cf1c (patch) | |
tree | 780fc0975e56b3dbd8a1eb4cb2988ac3e2e23ea1 /OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui | |
parent | aa22a0defcf94d5f734d3a00288f5c8d916d2e57 (diff) | |
parent | 4e29d027af05fc574dc5398d2fb3afcdf3defc70 (diff) | |
download | open-keychain-2f83291920e024b0f8038fe0caa747051b41cf1c.tar.gz open-keychain-2f83291920e024b0f8038fe0caa747051b41cf1c.tar.bz2 open-keychain-2f83291920e024b0f8038fe0caa747051b41cf1c.zip |
NON-WORKING Merge branch 'development' into linked-identities
Conflicts:
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java
OpenKeychain/src/main/res/layout/view_key_main_fragment.xml
OpenKeychain/src/main/res/values/strings.xml
extern/spongycastle
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui')
90 files changed, 3995 insertions, 3255 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BaseActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BaseActivity.java new file mode 100644 index 000000000..e6c2542a2 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BaseActivity.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2015 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; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v7.app.ActionBar; +import android.support.v7.app.ActionBarActivity; +import android.support.v7.widget.Toolbar; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.R; + +/** + * Setups Toolbar + */ +public abstract class BaseActivity extends ActionBarActivity { + protected Toolbar mToolbar; + protected View mStatusBar; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + initLayout(); + initToolbar(); + } + + protected abstract void initLayout(); + + protected void initToolbar() { + mToolbar = (Toolbar) findViewById(R.id.toolbar); + if (mToolbar != null) { + setSupportActionBar(mToolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } + mStatusBar = findViewById(R.id.status_bar); + } + + protected void setActionBarIcon(int iconRes) { + mToolbar.setNavigationIcon(iconRes); + } + + /** + * Inflate custom design to look like a full screen dialog, as specified in Material Design Guidelines + * see http://www.google.com/design/spec/components/dialogs.html#dialogs-full-screen-dialogs + */ + protected void setFullScreenDialogDoneClose(int doneText, View.OnClickListener doneOnClickListener, + View.OnClickListener cancelOnClickListener) { + setActionBarIcon(R.drawable.ic_close_white_24dp); + + // Inflate the custom action bar view + final LayoutInflater inflater = (LayoutInflater) getSupportActionBar().getThemedContext() + .getSystemService(Activity.LAYOUT_INFLATER_SERVICE); + final View customActionBarView = inflater.inflate(R.layout.full_screen_dialog, null); + + TextView firstTextView = ((TextView) customActionBarView.findViewById(R.id.full_screen_dialog_done_text)); + firstTextView.setText(doneText); + customActionBarView.findViewById(R.id.full_screen_dialog_done).setOnClickListener( + doneOnClickListener); + + getSupportActionBar().setDisplayShowCustomEnabled(true); + getSupportActionBar().setDisplayShowTitleEnabled(true); + getSupportActionBar().setCustomView(customActionBarView, new ActionBar.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT, + Gravity.END)); + mToolbar.setNavigationOnClickListener(cancelOnClickListener); + } + + /** + * Close button only + */ + protected void setFullScreenDialogClose(View.OnClickListener cancelOnClickListener) { + setActionBarIcon(R.drawable.ic_close_white_24dp); + getSupportActionBar().setDisplayShowTitleEnabled(true); + mToolbar.setNavigationOnClickListener(cancelOnClickListener); + } + + /** + * Inflate custom design with two buttons using drawables. + * This does not conform to the Material Design Guidelines, but we deviate here as this is used + * to indicate "Allow access"/"Disallow access" to the API, which must be clearly indicated + */ + protected void setFullScreenDialogTwoButtons(int firstText, int firstDrawableId, View.OnClickListener firstOnClickListener, + int secondText, int secondDrawableId, View.OnClickListener secondOnClickListener) { + + // Inflate the custom action bar view + final LayoutInflater inflater = (LayoutInflater) getSupportActionBar().getThemedContext() + .getSystemService(Activity.LAYOUT_INFLATER_SERVICE); + final View customActionBarView = inflater.inflate( + R.layout.full_screen_dialog_2, null); + + TextView firstTextView = ((TextView) customActionBarView.findViewById(R.id.actionbar_done_text)); + firstTextView.setText(firstText); + firstTextView.setCompoundDrawablesWithIntrinsicBounds(firstDrawableId, 0, 0, 0); + customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener( + firstOnClickListener); + TextView secondTextView = ((TextView) customActionBarView.findViewById(R.id.actionbar_cancel_text)); + secondTextView.setText(secondText); + secondTextView.setCompoundDrawablesWithIntrinsicBounds(secondDrawableId, 0, 0, 0); + customActionBarView.findViewById(R.id.actionbar_cancel).setOnClickListener( + secondOnClickListener); + + // Show the custom action bar view and hide the normal Home icon and title. + getSupportActionBar().setDisplayShowTitleEnabled(false); + getSupportActionBar().setDisplayShowHomeEnabled(false); + getSupportActionBar().setDisplayHomeAsUpEnabled(false); + getSupportActionBar().setDisplayShowCustomEnabled(true); + getSupportActionBar().setCustomView(customActionBarView, new ActionBar.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvancedActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintActivity.java index f49f8e7cf..b7c80c1ed 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvancedActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintActivity.java @@ -1,6 +1,5 @@ /* - * Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de> - * Copyright (C) 2013 Bahtiar 'kalkin' Gadimov + * Copyright (C) 2015 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 @@ -20,55 +19,43 @@ package org.sufficientlysecure.keychain.ui; import android.net.Uri; import android.os.Bundle; -import android.support.v7.app.ActionBar; -import android.support.v7.app.ActionBarActivity; import android.view.View; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.ui.util.ActionBarHelper; -import org.sufficientlysecure.keychain.util.ExportHelper; import org.sufficientlysecure.keychain.util.Log; -public class ViewKeyAdvancedActivity extends ActionBarActivity { +public class CertifyFingerprintActivity extends BaseActivity { - ExportHelper mExportHelper; - ProviderHelper mProviderHelper; + protected Uri mDataUri; @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mExportHelper = new ExportHelper(this); - mProviderHelper = new ProviderHelper(this); - - // Inflate a "Done" custom action bar - ActionBarHelper.setOneButtonView(getSupportActionBar(), - R.string.btn_okay, R.drawable.ic_action_done, - new View.OnClickListener() { - @Override - public void onClick(View v) { - // "Done" - finish(); - } - } - ); - - setContentView(R.layout.view_key_advanced_activity); - - Uri dataUri = getIntent().getData(); - if (dataUri == null) { + mDataUri = getIntent().getData(); + if (mDataUri == null) { Log.e(Constants.TAG, "Data missing. Should be uri of key!"); finish(); return; } - Log.i(Constants.TAG, "mDataUri: " + dataUri.toString()); + setFullScreenDialogClose(new View.OnClickListener() { + @Override + public void onClick(View v) { + finish(); + } + }); - startFragment(savedInstanceState, dataUri); + Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString()); + + startFragment(savedInstanceState, mDataUri); } + @Override + protected void initLayout() { + setContentView(R.layout.certify_fingerprint_activity); + } private void startFragment(Bundle savedInstanceState, Uri dataUri) { // However, if we're being restored from a previous state, @@ -79,12 +66,12 @@ public class ViewKeyAdvancedActivity extends ActionBarActivity { } // Create an instance of the fragment - ViewKeyAdvancedFragment frag = ViewKeyAdvancedFragment.newInstance(dataUri); + CertifyFingerprintFragment frag = CertifyFingerprintFragment.newInstance(dataUri); // Add the fragment to the 'fragment_container' FrameLayout // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! getSupportFragmentManager().beginTransaction() - .replace(R.id.view_key_advanced_fragment, frag) + .replace(R.id.certify_fingerprint_fragment, frag) .commitAllowingStateLoss(); // do it immediately! getSupportFragmentManager().executePendingTransactions(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java new file mode 100644 index 000000000..a6b8a0e85 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2015 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; + +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.Log; + + +public class CertifyFingerprintFragment extends LoaderFragment implements + LoaderManager.LoaderCallbacks<Cursor> { + + static final int REQUEST_CERTIFY = 1; + + public static final String ARG_DATA_URI = "uri"; + + private TextView mFingerprint; + + private static final int LOADER_ID_UNIFIED = 0; + + private Uri mDataUri; + + private View mActionNo; + private View mActionYes; + + /** + * Creates new instance of this fragment + */ + public static CertifyFingerprintFragment newInstance(Uri dataUri) { + CertifyFingerprintFragment frag = new CertifyFingerprintFragment(); + Bundle args = new Bundle(); + args.putParcelable(ARG_DATA_URI, dataUri); + + frag.setArguments(args); + + return frag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { + View root = super.onCreateView(inflater, superContainer, savedInstanceState); + View view = inflater.inflate(R.layout.certify_fingerprint_fragment, getContainer()); + + mActionNo = view.findViewById(R.id.certify_fingerprint_button_no); + mActionYes = view.findViewById(R.id.certify_fingerprint_button_yes); + + mFingerprint = (TextView) view.findViewById(R.id.certify_fingerprint_fingerprint); + + mActionNo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + getActivity().finish(); + } + }); + mActionYes.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + certify(mDataUri); + } + }); + + return root; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); + if (dataUri == null) { + Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); + getActivity().finish(); + return; + } + + loadData(dataUri); + } + + private void loadData(Uri dataUri) { + mDataUri = dataUri; + + Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString()); + + // Prepare the loaders. Either re-connect with an existing ones, + // or start new ones. + getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); + } + + static final String[] UNIFIED_PROJECTION = new String[]{ + KeyRings._ID, KeyRings.FINGERPRINT, + + }; + static final int INDEX_UNIFIED_FINGERPRINT = 1; + + public Loader<Cursor> onCreateLoader(int id, Bundle args) { + setContentShown(false); + switch (id) { + case LOADER_ID_UNIFIED: { + Uri baseUri = KeyRings.buildUnifiedKeyRingUri(mDataUri); + return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null); + } + + default: + return null; + } + } + + public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + /* TODO better error handling? May cause problems when a key is deleted, + * because the notification triggers faster than the activity closes. + */ + // Avoid NullPointerExceptions... + if (data.getCount() == 0) { + return; + } + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + switch (loader.getId()) { + case LOADER_ID_UNIFIED: { + if (data.moveToFirst()) { + + byte[] fingerprintBlob = data.getBlob(INDEX_UNIFIED_FINGERPRINT); + String fingerprint = KeyFormattingUtils.convertFingerprintToHex(fingerprintBlob); + mFingerprint.setText(KeyFormattingUtils.colorizeFingerprint(fingerprint)); + + break; + } + } + + } + setContentShown(true); + } + + /** + * This is called when the last Cursor provided to onLoadFinished() above is about to be closed. + * We need to make sure we are no longer using it. + */ + public void onLoaderReset(Loader<Cursor> loader) { + } + + private void certify(Uri dataUri) { + long keyId = 0; + try { + keyId = new ProviderHelper(getActivity()) + .getCachedPublicKeyRing(dataUri) + .extractOrGetMasterKeyId(); + } catch (PgpKeyNotFoundException e) { + Log.e(Constants.TAG, "key not found!", e); + } + Intent certifyIntent = new Intent(getActivity(), CertifyKeyActivity.class); + certifyIntent.putExtras(getActivity().getIntent()); + certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[]{keyId}); + startActivityForResult(certifyIntent, REQUEST_CERTIFY); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + // always just pass this one through + if (requestCode == REQUEST_CERTIFY) { + getActivity().setResult(resultCode, data); + getActivity().finish(); + return; + } + super.onActivityResult(requestCode, resultCode, data); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java index a97e73934..1fb88b182 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java @@ -18,24 +18,19 @@ package org.sufficientlysecure.keychain.ui; -import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; - import org.sufficientlysecure.keychain.R; /** * Signs the specified public key with the specified secret master key */ -public class CertifyKeyActivity extends ActionBarActivity { +public class CertifyKeyActivity extends BaseActivity { public static final String EXTRA_RESULT = "operation_result"; public static final String EXTRA_KEY_IDS = "extra_key_ids"; public static final String EXTRA_CERTIFY_KEY_ID = "certify_key_id"; @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - + protected void initLayout() { setContentView(R.layout.certify_key_activity); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java index 841485c7c..e1467ec61 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java @@ -31,6 +31,7 @@ import android.os.Bundle; import android.os.Message; import android.os.Messenger; import android.os.Parcel; +import android.os.Parcelable; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; @@ -94,6 +95,7 @@ public class CertifyKeyFragment extends LoaderFragment private static final int INDEX_IS_REVOKED = 4; private MultiUserIdsAdapter mUserIdsAdapter; + private Messenger mPassthroughMessenger; @Override public void onActivityCreated(Bundle savedInstanceState) { @@ -109,6 +111,9 @@ public class CertifyKeyFragment extends LoaderFragment return; } + mPassthroughMessenger = getActivity().getIntent().getParcelableExtra( + KeychainIntentService.EXTRA_MESSENGER); + // preselect certify key id if given long certifyKeyId = getActivity().getIntent().getLongExtra(CertifyKeyActivity.EXTRA_CERTIFY_KEY_ID, Constants.key.none); if (certifyKeyId != Constants.key.none) { @@ -234,7 +239,7 @@ public class CertifyKeyFragment extends LoaderFragment long lastMasterKeyId = 0; String lastName = ""; - ArrayList<String> uids = new ArrayList<String>(); + ArrayList<String> uids = new ArrayList<>(); boolean header = true; @@ -332,7 +337,6 @@ public class CertifyKeyFragment extends LoaderFragment } } - @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { @@ -362,51 +366,63 @@ public class CertifyKeyFragment extends LoaderFragment return; } + Bundle data = new Bundle(); + { + // fill values for this action + CertifyActionsParcel parcel = new CertifyActionsParcel(mSignMasterKeyId); + parcel.mCertifyActions.addAll(certifyActions); + + data.putParcelable(KeychainIntentService.CERTIFY_PARCEL, parcel); + if (mUploadKeyCheckbox.isChecked()) { + String keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver(); + data.putString(KeychainIntentService.UPLOAD_KEY_SERVER, keyserver); + } + } + // Send all information needed to service to sign key in other thread Intent intent = new Intent(getActivity(), KeychainIntentService.class); - intent.setAction(KeychainIntentService.ACTION_CERTIFY_KEYRING); - - // fill values for this action - CertifyActionsParcel parcel = new CertifyActionsParcel(mSignMasterKeyId); - parcel.mCertifyActions.addAll(certifyActions); - - Bundle data = new Bundle(); - data.putParcelable(KeychainIntentService.CERTIFY_PARCEL, parcel); - if (mUploadKeyCheckbox.isChecked()) { - String keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver(); - data.putString(KeychainIntentService.UPLOAD_KEY_SERVER, keyserver); - } intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - // Message is received after signing is done in KeychainIntentService - KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(), - getString(R.string.progress_certifying), ProgressDialog.STYLE_SPINNER, true) { - public void handleMessage(Message message) { - // handle messages by standard KeychainIntentServiceHandler first - super.handleMessage(message); - - if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { - Bundle data = message.getData(); - CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT); - - Intent intent = new Intent(); - intent.putExtra(CertifyResult.EXTRA_RESULT, result); - getActivity().setResult(Activity.RESULT_OK, intent); - getActivity().finish(); + if (mPassthroughMessenger != null) { + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, mPassthroughMessenger); + } else { + + // Message is received after signing is done in KeychainIntentService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(), + getString(R.string.progress_certifying), ProgressDialog.STYLE_SPINNER, true) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + Bundle data = message.getData(); + CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT); + + Intent intent = new Intent(); + intent.putExtra(CertifyResult.EXTRA_RESULT, result); + getActivity().setResult(Activity.RESULT_OK, intent); + getActivity().finish(); + } } - } - }; + }; - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); - // show progress dialog - saveHandler.showProgressDialog(getActivity()); + // show progress dialog + saveHandler.showProgressDialog(getActivity()); + } // start service with intent getActivity().startService(intent); + + if (mPassthroughMessenger != null) { + getActivity().setResult(Activity.RESULT_OK); + getActivity().finish(); + } + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java index 0d8905c5b..f0ef8b9ef 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java @@ -61,7 +61,7 @@ public class ConsolidateDialogActivity extends FragmentActivity { /* don't care about the results (for now?) // get returned data bundle - Bundle returnData = message.getData(); + Bundle returnData = message.getInputData(); if (returnData == null) { return; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java index 534ac5811..62c38d136 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java @@ -20,11 +20,10 @@ package org.sufficientlysecure.keychain.ui; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; -import android.support.v7.app.ActionBarActivity; import org.sufficientlysecure.keychain.R; -public class CreateKeyActivity extends ActionBarActivity { +public class CreateKeyActivity extends BaseActivity { public static final String EXTRA_NAME = "name"; public static final String EXTRA_EMAIL = "email"; @@ -37,8 +36,6 @@ public class CreateKeyActivity extends ActionBarActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.create_key_activity); - // pass extras into fragment CreateKeyInputFragment frag = CreateKeyInputFragment.newInstance( @@ -48,6 +45,11 @@ public class CreateKeyActivity extends ActionBarActivity { loadFragment(null, frag, FRAG_ACTION_START); } + @Override + protected void initLayout() { + setContentView(R.layout.create_key_activity); + } + public void loadFragment(Bundle savedInstanceState, Fragment fragment, int action) { // However, if we're being restored from a previous state, // then we don't need to do anything and should return or else diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java index 2804a5ce6..6e0115342 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java @@ -35,17 +35,16 @@ import org.spongycastle.bcpg.sig.KeyFlags; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.EditKeyResult; -import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; -import org.sufficientlysecure.keychain.util.Preferences; +import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; -import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Preferences; public class CreateKeyFinalFragment extends Fragment { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java index 6079062a7..8aa9fa6db 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java @@ -89,7 +89,7 @@ public class CreateKeyInputFragment extends Fragment { mEmailEdit.setThreshold(1); // Start working from first character mEmailEdit.setAdapter( - new ArrayAdapter<String> + new ArrayAdapter<> (getActivity(), android.R.layout.simple_spinner_dropdown_item, ContactHelper.getPossibleUserEmails(getActivity()) ) @@ -124,7 +124,7 @@ public class CreateKeyInputFragment extends Fragment { mNameEdit.setThreshold(1); // Start working from first character mNameEdit.setAdapter( - new ArrayAdapter<String> + new ArrayAdapter<> (getActivity(), android.R.layout.simple_spinner_dropdown_item, ContactHelper.getPossibleUserNames(getActivity()) ) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java deleted file mode 100644 index 681e22e1e..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> - * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.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.annotation.TargetApi; -import android.content.Intent; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Build.VERSION_CODES; -import android.os.Bundle; -import android.view.View; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; -import org.sufficientlysecure.keychain.pgp.PgpHelper; -import org.sufficientlysecure.keychain.operations.results.OperationResult; -import org.sufficientlysecure.keychain.ui.util.SubtleAttentionSeeker; - -import java.util.regex.Matcher; - -public class DecryptActivity extends DrawerActivity { - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.decrypt_activity); - - activateDrawerNavigation(savedInstanceState); - - View actionFile = findViewById(R.id.decrypt_files); - View actionFromClipboard = findViewById(R.id.decrypt_from_clipboard); - - actionFile.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent filesDecrypt = new Intent(DecryptActivity.this, DecryptFilesActivity.class); - filesDecrypt.setAction(DecryptFilesActivity.ACTION_DECRYPT_DATA_OPEN); - startActivity(filesDecrypt); - } - }); - - actionFromClipboard.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent clipboardDecrypt = new Intent(DecryptActivity.this, DecryptTextActivity.class); - clipboardDecrypt.setAction(DecryptTextActivity.ACTION_DECRYPT_FROM_CLIPBOARD); - startActivityForResult(clipboardDecrypt, 0); - } - }); - } - - @TargetApi(VERSION_CODES.HONEYCOMB) - @Override - protected void onResume() { - super.onResume(); - - // This is an eye candy ice cream sandwich feature, nvm on versions below - if (Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH) { - - // get text from clipboard - final CharSequence clipboardText = - ClipboardReflection.getClipboardText(DecryptActivity.this); - - // if it's null, nothing to do here /o/ - if (clipboardText == null) { - return; - } - - new AsyncTask<String, Void, Boolean>() { - @Override - protected Boolean doInBackground(String... clipboardText) { - - // see if it looks like a pgp thing - Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(clipboardText[0]); - boolean animate = matcher.matches(); - - // see if it looks like another pgp thing - if (!animate) { - matcher = PgpHelper.PGP_CLEARTEXT_SIGNATURE.matcher(clipboardText[0]); - animate = matcher.matches(); - } - return animate; - } - - @Override - protected void onPostExecute(Boolean animate) { - super.onPostExecute(animate); - - // if so, animate the clipboard icon just a bit~ - if (animate) { - SubtleAttentionSeeker.tada(findViewById(R.id.clipboard_icon), 1.5f).start(); - } - } - }.execute(clipboardText.toString()); - } - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - // if a result has been returned, display a notify - if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) { - OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); - result.createNotify(this).show(); - } else { - super.onActivityResult(requestCode, resultCode, data); - } - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java index 9d972d8c0..89dd4970b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java @@ -20,14 +20,13 @@ package org.sufficientlysecure.keychain.ui; import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.api.OpenKeychainIntents; import org.sufficientlysecure.keychain.util.Log; -public class DecryptFilesActivity extends ActionBarActivity { +public class DecryptFilesActivity extends BaseActivity { /* Intents */ public static final String ACTION_DECRYPT_DATA = OpenKeychainIntents.DECRYPT_DATA; @@ -41,12 +40,15 @@ public class DecryptFilesActivity extends ActionBarActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.decrypt_files_activity); - // Handle intent actions handleActions(savedInstanceState, getIntent()); } + @Override + protected void initLayout() { + setContentView(R.layout.decrypt_files_activity); + } + /** * Handles all actions with this intent * @@ -86,7 +88,7 @@ public class DecryptFilesActivity extends ActionBarActivity { loadFragment(savedInstanceState, null, true); } else if (ACTION_DECRYPT_DATA.equals(action)) { Log.e(Constants.TAG, - "Include an Uri with setData() in your Intent!"); + "Include an Uri with setInputData() in your Intent!"); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java index 691f1c240..5606523be 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java @@ -35,13 +35,13 @@ import android.widget.TextView; import org.openintents.openpgp.util.OpenPgpApi; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.util.FileHelper; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; -import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.ui.util.Notify; +import org.sufficientlysecure.keychain.util.FileHelper; +import org.sufficientlysecure.keychain.util.Log; import java.io.File; @@ -310,7 +310,7 @@ public class DecryptFilesFragment extends DecryptFragment { // A future open after decryption feature if () { Intent viewFile = new Intent(Intent.ACTION_VIEW); - viewFile.setData(mOutputUri); + viewFile.setInputData(mOutputUri); startActivity(viewFile); } */ diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java index 7f06d36e8..8723c7255 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java @@ -27,9 +27,9 @@ import android.widget.TextView; import org.openintents.openpgp.OpenPgpSignatureResult; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; public abstract class DecryptFragment extends Fragment { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java index 2da76088a..81a8a2ac4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java @@ -20,22 +20,21 @@ package org.sufficientlysecure.keychain.ui; import android.content.Intent; import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; import android.text.TextUtils; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.api.OpenKeychainIntents; import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; -import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.operations.results.SingletonResult; -import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.ui.util.Notify; +import org.sufficientlysecure.keychain.util.Log; import java.util.regex.Matcher; -public class DecryptTextActivity extends ActionBarActivity { +public class DecryptTextActivity extends BaseActivity { /* Intents */ public static final String ACTION_DECRYPT_TEXT = OpenKeychainIntents.DECRYPT_TEXT; @@ -50,12 +49,15 @@ public class DecryptTextActivity extends ActionBarActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.decrypt_text_activity); - // Handle intent actions handleActions(savedInstanceState, getIntent()); } + @Override + protected void initLayout() { + setContentView(R.layout.decrypt_text_activity); + } + /** * Fixing broken PGP MESSAGE Strings coming from GMail/AOSP Mail */ diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java index fa7abf0f5..a15b23c06 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java @@ -34,13 +34,15 @@ import org.openintents.openpgp.util.OpenPgpApi; 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.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ShareHelper; +import java.io.UnsupportedEncodingException; + public class DecryptTextFragment extends DecryptFragment { public static final String ARG_CIPHERTEXT = "ciphertext"; @@ -111,7 +113,7 @@ public class DecryptTextFragment extends DecryptFragment { Intent prototype = createSendIntent(text); String title = getString(R.string.title_share_file); - // we don't want to decrypt the decypted, no inception ;) + // we don't want to decrypt the decrypted, no inception ;) String[] blacklist = new String[]{ Constants.PACKAGE_NAME + ".ui.DecryptTextActivity", "org.thialfihar.android.apg.ui.DecryptActivity" @@ -194,7 +196,18 @@ public class DecryptTextFragment extends DecryptFragment { byte[] decryptedMessage = returnData .getByteArray(KeychainIntentService.RESULT_DECRYPTED_BYTES); - mText.setText(new String(decryptedMessage)); + String displayMessage; + if (pgpResult.getCharset() != null) { + try { + displayMessage = new String(decryptedMessage, pgpResult.getCharset()); + } catch (UnsupportedEncodingException e) { + // if we can't decode properly, just fall back to utf-8 + displayMessage = new String(decryptedMessage); + } + } else { + displayMessage = new String(decryptedMessage); + } + mText.setText(displayMessage); pgpResult.createNotify(getActivity()).show(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java deleted file mode 100644 index da46de486..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (C) 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; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.content.res.Configuration; -import android.graphics.Color; -import android.os.Bundle; -import android.support.v4.app.ActionBarDrawerToggle; -import android.support.v4.view.GravityCompat; -import android.support.v4.widget.DrawerLayout; -import android.support.v4.widget.FixedDrawerLayout; -import android.support.v7.app.ActionBarActivity; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.TextView; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; - -public class DrawerActivity extends ActionBarActivity { - private FixedDrawerLayout mDrawerLayout; - private ListView mDrawerList; - private ActionBarDrawerToggle mDrawerToggle; - - private CharSequence mDrawerTitle; - private CharSequence mTitle; - private boolean mIsDrawerLocked = false; - - private Class mSelectedItem; - - private static final int MENU_ID_PREFERENCE = 222; - private static final int MENU_ID_HELP = 223; - - protected void deactivateDrawerNavigation() { - ((DrawerLayout) findViewById(R.id.drawer_layout)).setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); - } - - protected void activateDrawerNavigation(Bundle savedInstanceState) { - mDrawerTitle = getString(R.string.app_name); - mDrawerLayout = (FixedDrawerLayout) findViewById(R.id.drawer_layout); - mDrawerList = (ListView) findViewById(R.id.left_drawer); - ViewGroup viewGroup = (ViewGroup) findViewById(R.id.content_frame); - int leftMarginLoaded = ((ViewGroup.MarginLayoutParams) viewGroup.getLayoutParams()).leftMargin; - int leftMarginInTablets = (int) getResources().getDimension(R.dimen.drawer_size); - int errorInMarginAllowed = 5; - - // if the left margin of the loaded layout is close to the - // one used in tablets then set drawer as open and locked - if (Math.abs(leftMarginLoaded - leftMarginInTablets) < errorInMarginAllowed) { - mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN, mDrawerList); - mDrawerLayout.setScrimColor(Color.TRANSPARENT); - mIsDrawerLocked = true; - } else { - // set a custom shadow that overlays the main content when the drawer opens - mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); - mIsDrawerLocked = false; - } - - NavItem mItemIconTexts[] = new NavItem[]{ - new NavItem(R.drawable.ic_action_accounts, getString(R.string.nav_keys)), - new NavItem(R.drawable.ic_action_secure, getString(R.string.nav_encrypt_text)), - new NavItem(R.drawable.ic_action_secure, getString(R.string.nav_encrypt_files)), - new NavItem(R.drawable.ic_action_not_secure, getString(R.string.nav_decrypt)), - new NavItem(R.drawable.ic_action_view_as_list, getString(R.string.nav_apps))}; - - mDrawerList.setAdapter(new NavigationDrawerAdapter(this, R.layout.drawer_list_item, - mItemIconTexts)); - - mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); - - // enable ActionBar app icon to behave as action to toggle nav drawer - // if the drawer is not locked - if (!mIsDrawerLocked) { - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setHomeButtonEnabled(true); - } - - // ActionBarDrawerToggle ties together the the proper interactions - // between the sliding drawer and the action bar app icon - mDrawerToggle = new ActionBarDrawerToggle(this, /* host Activity */ - mDrawerLayout, /* DrawerLayout object */ - R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */ - R.string.drawer_open, /* "open drawer" description for accessibility */ - R.string.drawer_close /* "close drawer" description for accessibility */ - ) { - public void onDrawerClosed(View view) { - getSupportActionBar().setTitle(mTitle); - - callIntentForDrawerItem(mSelectedItem); - } - - public void onDrawerOpened(View drawerView) { - mTitle = getSupportActionBar().getTitle(); - getSupportActionBar().setTitle(mDrawerTitle); - // creates call to onPrepareOptionsMenu() - supportInvalidateOptionsMenu(); - } - }; - - if (!mIsDrawerLocked) { - mDrawerLayout.setDrawerListener(mDrawerToggle); - } else { - // If the drawer is locked open make it un-focusable - // so that it doesn't consume all the Back button presses - mDrawerLayout.setFocusableInTouchMode(false); - } - } - - /** - * Uses startActivity to call the Intent of the given class - * - * @param drawerItem the class of the drawer item you want to load. Based on Constants.DrawerItems.* - */ - public void callIntentForDrawerItem(Class drawerItem) { - // creates call to onPrepareOptionsMenu() - supportInvalidateOptionsMenu(); - - // call intent activity if selected - if (drawerItem != null) { - finish(); - overridePendingTransition(0, 0); - - Intent intent = new Intent(this, drawerItem); - startActivity(intent); - - // disable animation of activity start - overridePendingTransition(0, 0); - } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - if (mDrawerToggle == null) { - return super.onCreateOptionsMenu(menu); - } - - menu.add(42, MENU_ID_PREFERENCE, 100, R.string.menu_preferences); - menu.add(42, MENU_ID_HELP, 101, R.string.menu_help); - - return super.onCreateOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (mDrawerToggle == null) { - return super.onOptionsItemSelected(item); - } - - // The action bar home/up action should open or close the drawer. - // ActionBarDrawerToggle will take care of this. - if (mDrawerToggle.onOptionsItemSelected(item)) { - return true; - } - - switch (item.getItemId()) { - case MENU_ID_PREFERENCE: { - Intent intent = new Intent(this, PreferencesActivity.class); - startActivity(intent); - return true; - } - case MENU_ID_HELP: { - Intent intent = new Intent(this, HelpActivity.class); - startActivity(intent); - return true; - } - default: - return super.onOptionsItemSelected(item); - } - } - - /** - * The click listener for ListView in the navigation drawer - */ - private class DrawerItemClickListener implements ListView.OnItemClickListener { - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - selectItem(position); - } - } - - private void selectItem(int position) { - // update selected item and title, then close the drawer - mDrawerList.setItemChecked(position, true); - // set selected class - mSelectedItem = Constants.DrawerItems.ARRAY[position]; - - // setTitle(mDrawerTitles[position]); - // If drawer isn't locked just close the drawer and - // it will move to the selected item by itself (via drawer toggle listener) - if (!mIsDrawerLocked) { - mDrawerLayout.closeDrawer(mDrawerList); - // else move to the selected item yourself - } else { - callIntentForDrawerItem(mSelectedItem); - } - } - - /** - * When using the ActionBarDrawerToggle, you must call it during onPostCreate() and - * onConfigurationChanged()... - */ - @Override - protected void onPostCreate(Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - // Sync the toggle state after onRestoreInstanceState has occurred. - if (mDrawerToggle != null) { - mDrawerToggle.syncState(); - } - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - // Pass any configuration change to the drawer toggles - if (mDrawerToggle != null) { - mDrawerToggle.onConfigurationChanged(newConfig); - } - } - - private class NavItem { - public int icon; // res-id - public String title; - - /** - * NavItem constructor - * - * @param icon The icons resource-id - * @param title The title of the menu entry - */ - public NavItem(int icon, String title) { - super(); - this.icon = icon; - this.title = title; - } - } - - private class NavigationDrawerAdapter extends ArrayAdapter<NavItem> { - Context mContext; - int mLayoutResourceId; - NavItem mData[] = null; - - public NavigationDrawerAdapter(Context context, int layoutResourceId, NavItem[] data) { - super(context, layoutResourceId, data); - this.mLayoutResourceId = layoutResourceId; - this.mContext = context; - this.mData = data; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View row = convertView; - NavItemHolder holder; - - if (row == null) { - LayoutInflater inflater = ((Activity) mContext).getLayoutInflater(); - row = inflater.inflate(mLayoutResourceId, parent, false); - - holder = new NavItemHolder(); - holder.mImg = (ImageView) row.findViewById(R.id.drawer_item_icon); - holder.mTxtTitle = (TextView) row.findViewById(R.id.drawer_item_text); - - row.setTag(holder); - } else { - holder = (NavItemHolder) row.getTag(); - } - - NavItem item = mData[position]; - holder.mTxtTitle.setText(item.title); - holder.mImg.setImageResource(item.icon); - - return row; - } - - } - - static class NavItemHolder { - ImageView mImg; - TextView mTxtTitle; - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 98049d89b..6dc2994cf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -19,14 +19,13 @@ package org.sufficientlysecure.keychain.ui; import android.net.Uri; import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.util.Log; -public class EditKeyActivity extends ActionBarActivity { +public class EditKeyActivity extends BaseActivity { public static final String EXTRA_SAVE_KEYRING_PARCEL = "save_keyring_parcel"; @@ -36,8 +35,6 @@ public class EditKeyActivity extends ActionBarActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.edit_key_activity); - Uri dataUri = getIntent().getData(); SaveKeyringParcel saveKeyringParcel = getIntent().getParcelableExtra(EXTRA_SAVE_KEYRING_PARCEL); if (dataUri == null && saveKeyringParcel == null) { @@ -49,6 +46,11 @@ public class EditKeyActivity extends ActionBarActivity { loadFragment(savedInstanceState, dataUri, saveKeyringParcel); } + @Override + protected void initLayout() { + setContentView(R.layout.edit_key_activity); + } + private void loadFragment(Bundle savedInstanceState, Uri dataUri, SaveKeyringParcel saveKeyringParcel) { // However, if we're being restored from a previous state, // then we don't need to do anything and should return or else diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java index afe6afb3c..25ca6e8fd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java @@ -29,7 +29,6 @@ import android.os.Messenger; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; -import android.support.v7.app.ActionBarActivity; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; @@ -55,6 +54,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange; import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter; import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; @@ -65,7 +65,6 @@ import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyExpiryDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.EditUserIdDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment; -import org.sufficientlysecure.keychain.ui.util.ActionBarHelper; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.Log; @@ -147,10 +146,8 @@ public class EditKeyFragment extends LoaderFragment implements @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - - // Inflate a "Done"/"Cancel" custom action bar view - ActionBarHelper.setTwoButtonView(((ActionBarActivity) getActivity()).getSupportActionBar(), - R.string.btn_save, R.drawable.ic_action_save, + ((EditKeyActivity) getActivity()).setFullScreenDialogDoneClose( + R.string.btn_save, new OnClickListener() { @Override public void onClick(View v) { @@ -161,16 +158,13 @@ public class EditKeyFragment extends LoaderFragment implements saveInDatabase(mCurrentPassphrase); } } - }, R.string.menu_key_edit_cancel, R.drawable.ic_action_cancel, - new OnClickListener() { + }, new OnClickListener() { @Override public void onClick(View v) { - // cancel getActivity().setResult(Activity.RESULT_CANCELED); getActivity().finish(); } - } - ); + }); Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); SaveKeyringParcel saveKeyringParcel = getArguments().getParcelable(ARG_SAVE_KEYRING_PARCEL); @@ -230,10 +224,7 @@ public class EditKeyFragment extends LoaderFragment implements mSaveKeyringParcel = new SaveKeyringParcel(masterKeyId, keyRing.getFingerprint()); mPrimaryUserId = keyRing.getPrimaryUserIdWithFallback(); - } catch (PgpKeyNotFoundException e) { - finishWithError(LogType.MSG_EK_ERROR_NOT_FOUND); - return; - } catch (NotFoundException e) { + } catch (PgpKeyNotFoundException | NotFoundException e) { finishWithError(LogType.MSG_EK_ERROR_NOT_FOUND); return; } @@ -395,8 +386,8 @@ public class EditKeyFragment extends LoaderFragment implements // cache new returned passphrase! mSaveKeyringParcel.mNewUnlock = new ChangeUnlockParcel( - data.getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE), - null + data.getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE), + null ); } } @@ -478,12 +469,13 @@ public class EditKeyFragment extends LoaderFragment implements } break; case EditSubkeyDialogFragment.MESSAGE_STRIP: - // toggle - if (mSaveKeyringParcel.mStripSubKeys.contains(keyId)) { - mSaveKeyringParcel.mStripSubKeys.remove(keyId); - } else { - mSaveKeyringParcel.mStripSubKeys.add(keyId); + SubkeyChange change = mSaveKeyringParcel.getSubkeyChange(keyId); + if (change == null) { + mSaveKeyringParcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, null)); + break; } + // toggle + change.mDummyStrip = !change.mDummyStrip; break; } getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java index b9058a37d..0d7e6056e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -8,13 +8,15 @@ import android.os.Messenger; import org.openintents.openpgp.util.OpenPgpApi; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult; +import org.sufficientlysecure.keychain.operations.results.SignEncryptResult; +import org.sufficientlysecure.keychain.pgp.SignEncryptParcel; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.operations.results.SignEncryptResult; import java.util.Date; -public abstract class EncryptActivity extends DrawerActivity { +public abstract class EncryptActivity extends BaseActivity { public static final int REQUEST_CODE_PASSPHRASE = 0x00008001; public static final int REQUEST_CODE_NFC = 0x00008002; @@ -82,7 +84,10 @@ public abstract class EncryptActivity extends DrawerActivity { // Send all information needed to service to edit key in other thread Intent intent = new Intent(this, KeychainIntentService.class); intent.setAction(KeychainIntentService.ACTION_SIGN_ENCRYPT); - intent.putExtra(KeychainIntentService.EXTRA_DATA, createEncryptBundle()); + + Bundle data = new Bundle(); + data.putParcelable(KeychainIntentService.SIGN_ENCRYPT_PARCEL, createEncryptBundle()); + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); // Message is received after encrypting is done in KeychainIntentService KeychainIntentServiceHandler serviceHandler = new KeychainIntentServiceHandler(this, @@ -92,28 +97,31 @@ public abstract class EncryptActivity extends DrawerActivity { super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { - SignEncryptResult pgpResult = + SignEncryptResult result = message.getData().getParcelable(SignEncryptResult.EXTRA_RESULT); - if (pgpResult.isPending()) { - if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) == - SignEncryptResult.RESULT_PENDING_PASSPHRASE) { + PgpSignEncryptResult pgpResult = result.getPending(); + + if (pgpResult != null && pgpResult.isPending()) { + if ((pgpResult.getResult() & PgpSignEncryptResult.RESULT_PENDING_PASSPHRASE) == + PgpSignEncryptResult.RESULT_PENDING_PASSPHRASE) { startPassphraseDialog(pgpResult.getKeyIdPassphraseNeeded()); - } else if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_NFC) == - SignEncryptResult.RESULT_PENDING_NFC) { + } else if ((pgpResult.getResult() & PgpSignEncryptResult.RESULT_PENDING_NFC) == + PgpSignEncryptResult.RESULT_PENDING_NFC) { mNfcTimestamp = pgpResult.getNfcTimestamp(); - startNfcSign(pgpResult.getNfcKeyId(), pgpResult.getNfcPassphrase(), pgpResult.getNfcHash(), pgpResult.getNfcAlgo()); + startNfcSign(pgpResult.getNfcKeyId(), pgpResult.getNfcPassphrase(), + pgpResult.getNfcHash(), pgpResult.getNfcAlgo()); } else { throw new RuntimeException("Unhandled pending result!"); } return; } - if (pgpResult.success()) { - onEncryptSuccess(message, pgpResult); + if (result.success()) { + onEncryptSuccess(result); } else { - pgpResult.createNotify(EncryptActivity.this).show(); + result.createNotify(EncryptActivity.this).show(); } // no matter the result, reset parameters @@ -136,8 +144,8 @@ public abstract class EncryptActivity extends DrawerActivity { protected abstract boolean inputIsValid(); - protected abstract void onEncryptSuccess(Message message, SignEncryptResult result); + protected abstract void onEncryptSuccess(SignEncryptResult result); - protected abstract Bundle createEncryptBundle(); + protected abstract SignEncryptParcel createEncryptBundle(); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java index 2d1b66daa..c5404094a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java @@ -28,7 +28,6 @@ import com.tokenautocomplete.TokenCompleteTextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; @@ -164,8 +163,8 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi private void updateEncryptionKeys() { List<Object> objects = mEncryptKeyView.getObjects(); - List<Long> keyIds = new ArrayList<Long>(); - List<String> userIds = new ArrayList<String>(); + List<Long> keyIds = new ArrayList<>(); + List<String> userIds = new ArrayList<>(); for (Object object : objects) { if (object instanceof EncryptKeyCompletionView.EncryptionKey) { keyIds.add(((EncryptKeyCompletionView.EncryptionKey) object).getKeyId()); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java new file mode 100644 index 000000000..a498d0763 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2014-2015 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.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.AsyncTask; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; +import org.sufficientlysecure.keychain.operations.results.OperationResult; +import org.sufficientlysecure.keychain.pgp.PgpHelper; +import org.sufficientlysecure.keychain.ui.util.SubtleAttentionSeeker; + +import java.util.regex.Matcher; + +public class EncryptDecryptOverviewFragment extends Fragment { + + View mEncryptFile; + View mEncryptText; + View mDecryptFile; + View mDecryptFromClipboard; + View mClipboardIcon; + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + } + + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.encrypt_decrypt_overview_fragment, container, false); + + mEncryptFile = view.findViewById(R.id.encrypt_files); + mEncryptText = view.findViewById(R.id.encrypt_text); + mDecryptFile = view.findViewById(R.id.decrypt_files); + mDecryptFromClipboard = view.findViewById(R.id.decrypt_from_clipboard); + mClipboardIcon = view.findViewById(R.id.clipboard_icon); + + mEncryptFile.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent encrypt = new Intent(getActivity(), EncryptFilesActivity.class); + startActivity(encrypt); + } + }); + + mEncryptText.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent encrypt = new Intent(getActivity(), EncryptTextActivity.class); + startActivity(encrypt); + } + }); + + mDecryptFile.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent filesDecrypt = new Intent(getActivity(), DecryptFilesActivity.class); + filesDecrypt.setAction(DecryptFilesActivity.ACTION_DECRYPT_DATA_OPEN); + startActivity(filesDecrypt); + } + }); + + mDecryptFromClipboard.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent clipboardDecrypt = new Intent(getActivity(), DecryptTextActivity.class); + clipboardDecrypt.setAction(DecryptTextActivity.ACTION_DECRYPT_FROM_CLIPBOARD); + startActivityForResult(clipboardDecrypt, 0); + } + }); + + return view; + } + + @Override + public void onResume() { + super.onResume(); + + // get text from clipboard + final CharSequence clipboardText = + ClipboardReflection.getClipboardText(getActivity()); + + // if it's null, nothing to do here /o/ + if (clipboardText == null) { + return; + } + + new AsyncTask<String, Void, Boolean>() { + @Override + protected Boolean doInBackground(String... clipboardText) { + + // see if it looks like a pgp thing + Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(clipboardText[0]); + boolean animate = matcher.matches(); + + // see if it looks like another pgp thing + if (!animate) { + matcher = PgpHelper.PGP_CLEARTEXT_SIGNATURE.matcher(clipboardText[0]); + animate = matcher.matches(); + } + return animate; + } + + @Override + protected void onPostExecute(Boolean animate) { + super.onPostExecute(animate); + + // if so, animate the clipboard icon just a bit~ + if (animate) { + SubtleAttentionSeeker.tada(mClipboardIcon, 1.5f).start(); + } + } + }.execute(clipboardText.toString()); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + // if a result has been returned, display a notify + if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) { + OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); + result.createNotify(getActivity()).show(); + } else { + super.onActivityResult(requestCode, resultCode, data); + } + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java index 054d85323..1286617d3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java @@ -21,7 +21,6 @@ package org.sufficientlysecure.keychain.ui; import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.os.Message; import android.support.v4.app.Fragment; import android.view.Menu; import android.view.MenuItem; @@ -29,14 +28,14 @@ import android.view.MenuItem; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.api.OpenKeychainIntents; -import org.sufficientlysecure.keychain.util.Preferences; -import org.sufficientlysecure.keychain.util.ShareHelper; -import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.operations.results.SignEncryptResult; +import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.pgp.SignEncryptParcel; import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; -import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.ui.util.Notify; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Preferences; +import org.sufficientlysecure.keychain.util.ShareHelper; import java.util.ArrayList; import java.util.HashSet; @@ -122,13 +121,13 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi @Override public ArrayList<Uri> getInputUris() { - if (mInputUris == null) mInputUris = new ArrayList<Uri>(); + if (mInputUris == null) mInputUris = new ArrayList<>(); return mInputUris; } @Override public ArrayList<Uri> getOutputUris() { - if (mOutputUris == null) mOutputUris = new ArrayList<Uri>(); + if (mOutputUris == null) mOutputUris = new ArrayList<>(); return mOutputUris; } @@ -170,7 +169,7 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi } @Override - public void onEncryptSuccess(Message message, SignEncryptResult pgpResult) { + public void onEncryptSuccess(SignEncryptResult result) { if (mDeleteAfterEncrypt) { for (Uri inputUri : mInputUris) { DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment.newInstance(inputUri); @@ -182,29 +181,25 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi if (mShareAfterEncrypt) { // Share encrypted message/file - startActivity(sendWithChooserExcludingEncrypt(message)); + startActivity(sendWithChooserExcludingEncrypt()); } else { // Save encrypted file - pgpResult.createNotify(EncryptFilesActivity.this).show(); + result.createNotify(EncryptFilesActivity.this).show(); } } @Override - protected Bundle createEncryptBundle() { + protected SignEncryptParcel createEncryptBundle() { // fill values for this action - Bundle data = new Bundle(); + SignEncryptParcel data = new SignEncryptParcel(); - data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URIS); - data.putParcelableArrayList(KeychainIntentService.ENCRYPT_INPUT_URIS, mInputUris); + data.addInputUris(mInputUris); + data.addOutputUris(mOutputUris); - data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URIS); - data.putParcelableArrayList(KeychainIntentService.ENCRYPT_OUTPUT_URIS, mOutputUris); - - data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, - Preferences.getPreferences(this).getDefaultFileCompression()); + data.setCompressionId(Preferences.getPreferences(this).getDefaultMessageCompression()); // Always use armor for messages - data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, mUseArmor); + data.setEnableAsciiArmorOutput(mUseArmor); if (isModeSymmetric()) { Log.d(Constants.TAG, "Symmetric encryption enabled!"); @@ -212,13 +207,12 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi if (passphrase.length() == 0) { passphrase = null; } - data.putString(KeychainIntentService.ENCRYPT_SYMMETRIC_PASSPHRASE, passphrase); + data.setSymmetricPassphrase(passphrase); } else { - data.putLong(KeychainIntentService.ENCRYPT_SIGNATURE_MASTER_ID, mSigningKeyId); - data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS, mEncryptionKeyIds); - data.putString(KeychainIntentService.ENCRYPT_SIGNATURE_KEY_PASSPHRASE, mSigningKeyPassphrase); - data.putSerializable(KeychainIntentService.ENCRYPT_SIGNATURE_NFC_TIMESTAMP, mNfcTimestamp); - data.putByteArray(KeychainIntentService.ENCRYPT_SIGNATURE_NFC_HASH, mNfcHash); + data.setEncryptionMasterKeyIds(mEncryptionKeyIds); + data.setSignatureMasterKeyId(mSigningKeyId); + data.setSignaturePassphrase(mSigningKeyPassphrase); + data.setNfcState(mNfcHash, mNfcTimestamp); } return data; } @@ -226,8 +220,8 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi /** * Create Intent Chooser but exclude OK's EncryptActivity. */ - private Intent sendWithChooserExcludingEncrypt(Message message) { - Intent prototype = createSendIntent(message); + private Intent sendWithChooserExcludingEncrypt() { + Intent prototype = createSendIntent(); String title = getString(R.string.title_share_file); // we don't want to encrypt the encrypted, no inception ;) @@ -239,7 +233,7 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi return new ShareHelper(this).createChooserExcluding(prototype, title, blacklist); } - private Intent createSendIntent(Message message) { + private Intent createSendIntent() { Intent sendIntent; // file if (mOutputUris.size() == 1) { @@ -252,7 +246,7 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi sendIntent.setType("application/octet-stream"); if (!isModeSymmetric() && mEncryptionUserIds != null) { - Set<String> users = new HashSet<String>(); + Set<String> users = new HashSet<>(); for (String user : mEncryptionUserIds) { String[] userId = KeyRing.splitUserId(user); if (userId[1] != null) { @@ -309,15 +303,13 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.encrypt_files_activity); - // if called with an intent action, do not init drawer navigation if (ACTION_ENCRYPT_DATA.equals(getIntent().getAction())) { // lock drawer - deactivateDrawerNavigation(); +// deactivateDrawerNavigation(); // TODO: back button to key? } else { - activateDrawerNavigation(savedInstanceState); +// activateDrawerNavigation(savedInstanceState); } // Handle intent actions @@ -328,6 +320,11 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi } @Override + protected void initLayout() { + setContentView(R.layout.encrypt_files_activity); + } + + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.encrypt_file_activity, menu); menu.findItem(R.id.check_use_armor).setChecked(mUseArmor); @@ -379,7 +376,7 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi String action = intent.getAction(); Bundle extras = intent.getExtras(); String type = intent.getType(); - ArrayList<Uri> uris = new ArrayList<Uri>(); + ArrayList<Uri> uris = new ArrayList<>(); if (extras == null) { extras = new Bundle(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java index 6961f5ee7..860bd8502 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java @@ -36,10 +36,10 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.FileHelper; -import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider; import java.io.File; import java.util.HashMap; @@ -59,7 +59,7 @@ public class EncryptFilesFragment extends Fragment implements EncryptActivityInt private View mShareFile; private ListView mSelectedFiles; private SelectedFilesAdapter mAdapter = new SelectedFilesAdapter(); - private final Map<Uri, Bitmap> thumbnailCache = new HashMap<Uri, Bitmap>(); + private final Map<Uri, Bitmap> thumbnailCache = new HashMap<>(); @Override public void onAttach(Activity activity) { @@ -224,7 +224,7 @@ public class EncryptFilesFragment extends Fragment implements EncryptActivityInt @Override public void onNotifyUpdate() { // Clear cache if needed - for (Uri uri : new HashSet<Uri>(thumbnailCache.keySet())) { + for (Uri uri : new HashSet<>(thumbnailCache.keySet())) { if (!mEncryptInterface.getInputUris().contains(uri)) { thumbnailCache.remove(uri); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java index 958daa122..2dd861d07 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java @@ -21,7 +21,6 @@ package org.sufficientlysecure.keychain.ui; import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.os.Message; import android.support.v4.app.Fragment; import android.view.Menu; import android.view.MenuItem; @@ -30,13 +29,13 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.api.OpenKeychainIntents; import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; -import org.sufficientlysecure.keychain.util.Preferences; -import org.sufficientlysecure.keychain.util.ShareHelper; -import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.operations.results.SignEncryptResult; -import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.pgp.SignEncryptParcel; import org.sufficientlysecure.keychain.ui.util.Notify; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Preferences; +import org.sufficientlysecure.keychain.util.ShareHelper; import java.util.ArrayList; import java.util.HashSet; @@ -121,13 +120,13 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv @Override public ArrayList<Uri> getInputUris() { - if (mInputUris == null) mInputUris = new ArrayList<Uri>(); + if (mInputUris == null) mInputUris = new ArrayList<>(); return mInputUris; } @Override public ArrayList<Uri> getOutputUris() { - if (mOutputUris == null) mOutputUris = new ArrayList<Uri>(); + if (mOutputUris == null) mOutputUris = new ArrayList<>(); return mOutputUris; } @@ -169,32 +168,31 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv } @Override - protected void onEncryptSuccess(Message message, SignEncryptResult pgpResult) { + protected void onEncryptSuccess(SignEncryptResult result) { if (mShareAfterEncrypt) { // Share encrypted message/file - startActivity(sendWithChooserExcludingEncrypt(message)); + startActivity(sendWithChooserExcludingEncrypt(result.getResultBytes())); } else { // Copy to clipboard - copyToClipboard(message); - pgpResult.createNotify(EncryptTextActivity.this).show(); + copyToClipboard(result.getResultBytes()); + result.createNotify(EncryptTextActivity.this).show(); // Notify.showNotify(EncryptTextActivity.this, // R.string.encrypt_sign_clipboard_successful, Notify.Style.INFO); } } @Override - protected Bundle createEncryptBundle() { + protected SignEncryptParcel createEncryptBundle() { // fill values for this action - Bundle data = new Bundle(); + SignEncryptParcel data = new SignEncryptParcel(); - data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_BYTES); - data.putByteArray(KeychainIntentService.ENCRYPT_MESSAGE_BYTES, mMessage.getBytes()); + data.setBytes(mMessage.getBytes()); + data.setCleartextSignature(true); - data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, - Preferences.getPreferences(this).getDefaultMessageCompression()); + data.setCompressionId(Preferences.getPreferences(this).getDefaultMessageCompression()); // Always use armor for messages - data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, true); + data.setEnableAsciiArmorOutput(true); if (isModeSymmetric()) { Log.d(Constants.TAG, "Symmetric encryption enabled!"); @@ -202,26 +200,25 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv if (passphrase.length() == 0) { passphrase = null; } - data.putString(KeychainIntentService.ENCRYPT_SYMMETRIC_PASSPHRASE, passphrase); + data.setSymmetricPassphrase(passphrase); } else { - data.putLong(KeychainIntentService.ENCRYPT_SIGNATURE_MASTER_ID, mSigningKeyId); - data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS, mEncryptionKeyIds); - data.putString(KeychainIntentService.ENCRYPT_SIGNATURE_KEY_PASSPHRASE, mSigningKeyPassphrase); - data.putSerializable(KeychainIntentService.ENCRYPT_SIGNATURE_NFC_TIMESTAMP, mNfcTimestamp); - data.putByteArray(KeychainIntentService.ENCRYPT_SIGNATURE_NFC_HASH, mNfcHash); + data.setEncryptionMasterKeyIds(mEncryptionKeyIds); + data.setSignatureMasterKeyId(mSigningKeyId); + data.setSignaturePassphrase(mSigningKeyPassphrase); + data.setNfcState(mNfcHash, mNfcTimestamp); } return data; } - private void copyToClipboard(Message message) { - ClipboardReflection.copyToClipboard(this, new String(message.getData().getByteArray(KeychainIntentService.RESULT_BYTES))); + private void copyToClipboard(byte[] resultBytes) { + ClipboardReflection.copyToClipboard(this, new String(resultBytes)); } /** * Create Intent Chooser but exclude OK's EncryptActivity. */ - private Intent sendWithChooserExcludingEncrypt(Message message) { - Intent prototype = createSendIntent(message); + private Intent sendWithChooserExcludingEncrypt(byte[] resultBytes) { + Intent prototype = createSendIntent(resultBytes); String title = getString(R.string.title_share_message); // we don't want to encrypt the encrypted, no inception ;) @@ -233,20 +230,21 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv return new ShareHelper(this).createChooserExcluding(prototype, title, blacklist); } - private Intent createSendIntent(Message message) { + private Intent createSendIntent(byte[] resultBytes) { Intent sendIntent; sendIntent = new Intent(Intent.ACTION_SEND); sendIntent.setType("text/plain"); - sendIntent.putExtra(Intent.EXTRA_TEXT, new String(message.getData().getByteArray(KeychainIntentService.RESULT_BYTES))); + sendIntent.putExtra(Intent.EXTRA_TEXT, new String(resultBytes)); if (!isModeSymmetric() && mEncryptionUserIds != null) { - Set<String> users = new HashSet<String>(); + Set<String> users = new HashSet<>(); for (String user : mEncryptionUserIds) { String[] userId = KeyRing.splitUserId(user); if (userId[1] != null) { users.add(userId[1]); } } + // pass trough email addresses as extra for email applications sendIntent.putExtra(Intent.EXTRA_EMAIL, users.toArray(new String[users.size()])); } return sendIntent; @@ -288,15 +286,13 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.encrypt_text_activity); - // if called with an intent action, do not init drawer navigation if (ACTION_ENCRYPT_TEXT.equals(getIntent().getAction())) { // lock drawer - deactivateDrawerNavigation(); +// deactivateDrawerNavigation(); // TODO: back button to key? } else { - activateDrawerNavigation(savedInstanceState); +// activateDrawerNavigation(savedInstanceState); } // Handle intent actions @@ -305,6 +301,11 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv } @Override + protected void initLayout() { + setContentView(R.layout.encrypt_text_activity); + } + + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.encrypt_text_activity, menu); return super.onCreateOptionsMenu(menu); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java index d71c9c465..393e15cfa 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java @@ -19,17 +19,16 @@ package org.sufficientlysecure.keychain.ui; import android.content.Intent; import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; import android.view.KeyEvent; import android.view.View; import android.view.Window; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Preferences; -public class FirstTimeActivity extends ActionBarActivity { +public class FirstTimeActivity extends BaseActivity { View mCreateKey; View mImportKey; @@ -43,8 +42,6 @@ public class FirstTimeActivity extends ActionBarActivity { super.onCreate(savedInstanceState); - setContentView(R.layout.first_time_activity); - mCreateKey = findViewById(R.id.first_time_create_key); mImportKey = findViewById(R.id.first_time_import_key); mSkipSetup = findViewById(R.id.first_time_cancel); @@ -72,7 +69,11 @@ public class FirstTimeActivity extends ActionBarActivity { startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY); } }); + } + @Override + protected void initLayout() { + setContentView(R.layout.first_time_activity); } @Override @@ -91,7 +92,7 @@ public class FirstTimeActivity extends ActionBarActivity { private void finishSetup(Intent srcData) { Preferences prefs = Preferences.getPreferences(this); prefs.setFirstTime(false); - Intent intent = new Intent(this, KeyListActivity.class); + Intent intent = new Intent(this, MainActivity.class); // give intent through to display notify if (srcData != null) { intent.putExtras(srcData); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java index bbc1e4b1f..2eb35351e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java @@ -20,14 +20,14 @@ package org.sufficientlysecure.keychain.ui; import android.content.Intent; import android.os.Bundle; import android.support.v4.view.ViewPager; -import android.support.v7.app.ActionBar; -import android.support.v7.app.ActionBarActivity; +import android.view.View; + +import com.astuetz.PagerSlidingTabStrip; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter; -import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout; -public class HelpActivity extends ActionBarActivity { +public class HelpActivity extends BaseActivity { public static final String EXTRA_SELECTED_TAB = "selected_tab"; public static final int TAB_START = 0; @@ -44,16 +44,16 @@ public class HelpActivity extends ActionBarActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - final ActionBar actionBar = getSupportActionBar(); - actionBar.setDisplayShowTitleEnabled(true); - actionBar.setDisplayHomeAsUpEnabled(false); - actionBar.setHomeButtonEnabled(false); - - setContentView(R.layout.help_activity); + mToolbar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + finish(); + } + }); mViewPager = (ViewPager) findViewById(R.id.pager); - SlidingTabLayout slidingTabLayout = - (SlidingTabLayout) findViewById(R.id.sliding_tab_layout); + PagerSlidingTabStrip slidingTabLayout = + (PagerSlidingTabStrip) findViewById(R.id.sliding_tab_layout); mTabsAdapter = new PagerTabStripAdapter(this); mViewPager.setAdapter(mTabsAdapter); @@ -98,4 +98,9 @@ public class HelpActivity extends ActionBarActivity { // switch to tab selected by extra mViewPager.setCurrentItem(selectedTab); } + + @Override + protected void initLayout() { + setContentView(R.layout.help_activity); + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index dfb7b3056..71f6fd4bf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -17,19 +17,13 @@ package org.sufficientlysecure.keychain.ui; -import android.annotation.TargetApi; import android.app.ProgressDialog; import android.content.Intent; import android.net.Uri; -import android.nfc.NdefMessage; -import android.nfc.NfcAdapter; -import android.os.Build; import android.os.Bundle; import android.os.Message; import android.os.Messenger; -import android.os.Parcelable; import android.support.v4.app.Fragment; -import android.support.v7.app.ActionBarActivity; import android.view.View; import android.view.View.OnClickListener; @@ -51,7 +45,7 @@ import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize import java.io.IOException; import java.util.ArrayList; -public class ImportKeysActivity extends ActionBarActivity { +public class ImportKeysActivity extends BaseActivity { public static final String ACTION_IMPORT_KEY = OpenKeychainIntents.IMPORT_KEY; public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER = OpenKeychainIntents.IMPORT_KEY_FROM_KEYSERVER; public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT = @@ -64,9 +58,6 @@ public class ImportKeysActivity extends ActionBarActivity { // Actions for internal use only: public static final String ACTION_IMPORT_KEY_FROM_FILE = Constants.INTENT_PREFIX + "IMPORT_KEY_FROM_FILE"; - public static final String ACTION_IMPORT_KEY_FROM_NFC = Constants.INTENT_PREFIX - + "IMPORT_KEY_FROM_NFC"; - public static final String EXTRA_RESULT = "result"; // only used by ACTION_IMPORT_KEY @@ -90,8 +81,6 @@ public class ImportKeysActivity extends ActionBarActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.import_keys_activity); - mImportButton = findViewById(R.id.import_import); mImportButton.setOnClickListener(new OnClickListener() { @Override @@ -103,6 +92,11 @@ public class ImportKeysActivity extends ActionBarActivity { handleActions(savedInstanceState, getIntent()); } + @Override + protected void initLayout() { + setContentView(R.layout.import_keys_activity); + } + protected void handleActions(Bundle savedInstanceState, Intent intent) { String action = intent.getAction(); Bundle extras = intent.getExtras(); @@ -113,105 +107,114 @@ public class ImportKeysActivity extends ActionBarActivity { extras = new Bundle(); } + if (action == null) { + startCloudFragment(savedInstanceState, null, false); + startListFragment(savedInstanceState, null, null, null); + return; + } + if (Intent.ACTION_VIEW.equals(action)) { // Android's Action when opening file associated to Keychain (see AndroidManifest.xml) // delegate action to ACTION_IMPORT_KEY action = ACTION_IMPORT_KEY; } - if (ACTION_IMPORT_KEY.equals(action)) { - /* Keychain's own Actions */ - startFileFragment(savedInstanceState); + switch (action) { + case ACTION_IMPORT_KEY: { + /* Keychain's own Actions */ + startFileFragment(savedInstanceState); - if (dataUri != null) { - // action: directly load data - startListFragment(savedInstanceState, null, dataUri, null); - } else if (extras.containsKey(EXTRA_KEY_BYTES)) { - byte[] importData = extras.getByteArray(EXTRA_KEY_BYTES); + if (dataUri != null) { + // action: directly load data + startListFragment(savedInstanceState, null, dataUri, null); + } else if (extras.containsKey(EXTRA_KEY_BYTES)) { + byte[] importData = extras.getByteArray(EXTRA_KEY_BYTES); - // action: directly load data - startListFragment(savedInstanceState, importData, null, null); + // action: directly load data + startListFragment(savedInstanceState, importData, null, null); + } + break; } - } else if (ACTION_IMPORT_KEY_FROM_KEYSERVER.equals(action) - || ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE.equals(action) - || ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT.equals(action)) { + case ACTION_IMPORT_KEY_FROM_KEYSERVER: + case ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE: + case ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT: { - // only used for OpenPgpService - if (extras.containsKey(EXTRA_PENDING_INTENT_DATA)) { - mPendingIntentData = extras.getParcelable(EXTRA_PENDING_INTENT_DATA); - } - if (extras.containsKey(EXTRA_QUERY) || extras.containsKey(EXTRA_KEY_ID)) { - /* simple search based on query or key id */ - - String query = null; - if (extras.containsKey(EXTRA_QUERY)) { - query = extras.getString(EXTRA_QUERY); - } else if (extras.containsKey(EXTRA_KEY_ID)) { - long keyId = extras.getLong(EXTRA_KEY_ID, 0); - if (keyId != 0) { - query = KeyFormattingUtils.convertKeyIdToHex(keyId); - } + // only used for OpenPgpService + if (extras.containsKey(EXTRA_PENDING_INTENT_DATA)) { + mPendingIntentData = extras.getParcelable(EXTRA_PENDING_INTENT_DATA); } + if (extras.containsKey(EXTRA_QUERY) || extras.containsKey(EXTRA_KEY_ID)) { + /* simple search based on query or key id */ + + String query = null; + if (extras.containsKey(EXTRA_QUERY)) { + query = extras.getString(EXTRA_QUERY); + } else if (extras.containsKey(EXTRA_KEY_ID)) { + long keyId = extras.getLong(EXTRA_KEY_ID, 0); + if (keyId != 0) { + query = KeyFormattingUtils.convertKeyIdToHex(keyId); + } + } - if (query != null && query.length() > 0) { - // display keyserver fragment with query - startCloudFragment(savedInstanceState, query, false); + if (query != null && query.length() > 0) { + // display keyserver fragment with query + startCloudFragment(savedInstanceState, query, false); - // action: search immediately - startListFragment(savedInstanceState, null, null, query); - } else { - Log.e(Constants.TAG, "Query is empty!"); - return; - } - } else if (extras.containsKey(EXTRA_FINGERPRINT)) { - /* - * search based on fingerprint, here we can enforce a check in the end - * if the right key has been downloaded - */ + // action: search immediately + startListFragment(savedInstanceState, null, null, query); + } else { + Log.e(Constants.TAG, "Query is empty!"); + return; + } + } else if (extras.containsKey(EXTRA_FINGERPRINT)) { + /* + * search based on fingerprint, here we can enforce a check in the end + * if the right key has been downloaded + */ - String fingerprint = extras.getString(EXTRA_FINGERPRINT); - if (isFingerprintValid(fingerprint)) { - String query = "0x" + fingerprint; + String fingerprint = extras.getString(EXTRA_FINGERPRINT); + if (isFingerprintValid(fingerprint)) { + String query = "0x" + fingerprint; - // display keyserver fragment with query - startCloudFragment(savedInstanceState, query, true); + // display keyserver fragment with query + startCloudFragment(savedInstanceState, query, true); - // action: search immediately - startListFragment(savedInstanceState, null, null, query); + // action: search immediately + startListFragment(savedInstanceState, null, null, query); + } + } else { + Log.e(Constants.TAG, + "IMPORT_KEY_FROM_KEYSERVER action needs to contain the 'query', 'key_id', or " + + "'fingerprint' extra!" + ); + return; } - } else { - Log.e(Constants.TAG, - "IMPORT_KEY_FROM_KEYSERVER action needs to contain the 'query', 'key_id', or " + - "'fingerprint' extra!" - ); - return; + break; } - } else if (ACTION_IMPORT_KEY_FROM_FILE.equals(action)) { - // NOTE: this only displays the appropriate fragment, no actions are taken - startFileFragment(savedInstanceState); - - // no immediate actions! - startListFragment(savedInstanceState, null, null, null); - } else if (ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN.equals(action)) { - // NOTE: this only displays the appropriate fragment, no actions are taken - startFileFragment(savedInstanceState); + case ACTION_IMPORT_KEY_FROM_FILE: { + // NOTE: this only displays the appropriate fragment, no actions are taken + startFileFragment(savedInstanceState); - // no immediate actions! - startListFragment(savedInstanceState, null, null, null); - } else if (ACTION_IMPORT_KEY_FROM_NFC.equals(action)) { - // NOTE: this only displays the appropriate fragment, no actions are taken - startFileFragment(savedInstanceState); - // TODO!!!!! + // no immediate actions! + startListFragment(savedInstanceState, null, null, null); + break; + } + case ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN: { + // NOTE: this only displays the appropriate fragment, no actions are taken + startFileFragment(savedInstanceState); - // no immediate actions! - startListFragment(savedInstanceState, null, null, null); - } else { - startCloudFragment(savedInstanceState, null, false); - startListFragment(savedInstanceState, null, null, null); + // no immediate actions! + startListFragment(savedInstanceState, null, null, null); + break; + } + default: { + startCloudFragment(savedInstanceState, null, false); + startListFragment(savedInstanceState, null, null, null); + break; + } } } - private void startListFragment(Bundle savedInstanceState, byte[] bytes, Uri dataUri, String serverQuery) { // However, if we're being restored from a previous state, // then we don't need to do anything and should return or else @@ -353,7 +356,7 @@ public class ImportKeysActivity extends ActionBarActivity { // We parcel this iteratively into a file - anything we can // display here, we should be able to import. ParcelableFileCache<ParcelableKeyRing> cache = - new ParcelableFileCache<ParcelableKeyRing>(this, "key_import.pcl"); + new ParcelableFileCache<>(this, "key_import.pcl"); cache.writeCache(selectedEntries); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); @@ -385,7 +388,7 @@ public class ImportKeysActivity extends ActionBarActivity { data.putString(KeychainIntentService.IMPORT_KEY_SERVER, sls.mCloudPrefs.keyserver); // get selected key entries - ArrayList<ParcelableKeyRing> keys = new ArrayList<ParcelableKeyRing>(); + ArrayList<ParcelableKeyRing> keys = new ArrayList<>(); { // change the format into ParcelableKeyRing ArrayList<ImportKeysListEntry> entries = mListFragment.getSelectedEntries(); @@ -413,50 +416,4 @@ public class ImportKeysActivity extends ActionBarActivity { } } - /** - * NFC - */ - @Override - public void onResume() { - super.onResume(); - - // Check to see if the Activity started due to an Android Beam - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { - handleActionNdefDiscovered(getIntent()); - } else { - Log.d(Constants.TAG, "NFC: No NDEF discovered!"); - } - } else { - Log.e(Constants.TAG, "Android Beam not supported by Android < 4.1"); - } - } - - /** - * NFC - */ - @Override - public void onNewIntent(Intent intent) { - // onResume gets called after this to handle the intent - setIntent(intent); - } - - /** - * NFC: Parses the NDEF Message from the intent and prints to the TextView - */ - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) - void handleActionNdefDiscovered(Intent intent) { - Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); - // 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(); - - Intent importIntent = new Intent(this, ImportKeysActivity.class); - importIntent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY); - importIntent.putExtra(ImportKeysActivity.EXTRA_KEY_BYTES, receivedKeyringBytes); - - handleActions(null, importIntent); - } - } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java index 03aba344a..91ca93c36 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java @@ -21,6 +21,7 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.preference.PreferenceActivity; import android.support.v4.app.Fragment; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -36,8 +37,8 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.util.ContactHelper; -import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Preferences; import java.util.List; @@ -81,7 +82,7 @@ public class ImportKeysCloudFragment extends Fragment { namesAndEmails.addAll(ContactHelper.getContactMails(getActivity())); mQueryEditText.setThreshold(3); mQueryEditText.setAdapter( - new ArrayAdapter<String> + new ArrayAdapter<> (getActivity(), android.R.layout.simple_spinner_dropdown_item, namesAndEmails ) @@ -110,11 +111,9 @@ public class ImportKeysCloudFragment extends Fragment { mConfigButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - Intent i = new Intent(mImportActivity, PreferencesActivity.class); - // GRR, for some reason I can’t set the Action or I get an incomprehensible - // exception about “modern two-pane layouts” - // i.setAction(PreferencesActivity.ACTION_PREFS_CLOUD); - startActivity(i); + Intent intent = new Intent(mImportActivity, SettingsActivity.class); + intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, SettingsActivity.CloudSearchPrefsFragment.class.getName()); + startActivity(intent); } }); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java index 4fe53fb09..6a6140892 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java @@ -113,7 +113,7 @@ public class ImportKeysListFragment extends ListFragment implements return mAdapter.getSelectedEntries(); } else { Log.e(Constants.TAG, "Adapter not initialized, returning empty list"); - return new ArrayList<ImportKeysListEntry>(); + return new ArrayList<>(); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeScanActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java index 5966870df..948da94d8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeScanActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java @@ -17,12 +17,17 @@ package org.sufficientlysecure.keychain.ui; +import android.annotation.TargetApi; import android.app.ProgressDialog; import android.content.Intent; import android.net.Uri; +import android.nfc.NdefMessage; +import android.nfc.NfcAdapter; +import android.os.Build; import android.os.Bundle; import android.os.Message; import android.os.Messenger; +import android.os.Parcelable; import android.support.v4.app.FragmentActivity; import android.widget.Toast; @@ -48,10 +53,13 @@ import java.util.Locale; /** * Proxy activity (just a transparent content view) to scan QR Codes using the Barcode Scanner app */ -public class QrCodeScanActivity extends FragmentActivity { +public class ImportKeysProxyActivity extends FragmentActivity { public static final String ACTION_QR_CODE_API = OpenKeychainIntents.IMPORT_KEY_FROM_QR_CODE; public static final String ACTION_SCAN_WITH_RESULT = Constants.INTENT_PREFIX + "SCAN_QR_CODE_WITH_RESULT"; + public static final String ACTION_SCAN_IMPORT = Constants.INTENT_PREFIX + "SCAN_QR_CODE_IMPORT"; + + public static final String EXTRA_FINGERPRINT = "fingerprint"; boolean returnResult; @@ -73,17 +81,35 @@ public class QrCodeScanActivity extends FragmentActivity { // Scanning a fingerprint directly with Barcode Scanner, thus we already have scanned returnResult = false; - startCertify(dataUri); + processScannedContent(dataUri); + } else if (ACTION_SCAN_IMPORT.equals(action)) { + returnResult = false; + IntentIntegrator integrator = new IntentIntegrator(this); + integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES) + .setPrompt(getString(R.string.import_qr_code_text)) + .setResultDisplayDuration(0) + .initiateScan(); } else if (ACTION_SCAN_WITH_RESULT.equals(action)) { - // scan using xzing's Barcode Scanner and return result parcel in OpenKeychain - returnResult = true; - new IntentIntegrator(this).initiateScan(); + IntentIntegrator integrator = new IntentIntegrator(this); + integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES) + .setPrompt(getString(R.string.import_qr_code_text)) + .setResultDisplayDuration(0) + .initiateScan(); } else if (ACTION_QR_CODE_API.equals(action)) { // scan using xzing's Barcode Scanner from outside OpenKeychain returnResult = false; new IntentIntegrator(this).initiateScan(); + } else if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { + // Check to see if the Activity started due to an Android Beam + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + returnResult = false; + handleActionNdefDiscovered(getIntent()); + } else { + Log.e(Constants.TAG, "Android Beam not supported by Android < 4.1"); + finish(); + } } else { Log.e(Constants.TAG, "No valid scheme or action given!"); finish(); @@ -95,16 +121,16 @@ public class QrCodeScanActivity extends FragmentActivity { if (requestCode == IntentIntegratorSupportV4.REQUEST_CODE) { IntentResult scanResult = IntentIntegratorSupportV4.parseActivityResult(requestCode, resultCode, data); - if (scanResult != null && scanResult.getFormatName() != null) { - String scannedContent = scanResult.getContents(); - Log.d(Constants.TAG, "scannedContent: " + scannedContent); - startCertify(Uri.parse(scanResult.getContents())); - } else { + if (scanResult == null || scanResult.getFormatName() == null) { Log.e(Constants.TAG, "scanResult or formatName null! Should not happen!"); finish(); + return; } + String scannedContent = scanResult.getContents(); + processScannedContent(scannedContent); + return; } // if a result has been returned, return it down to other activity @@ -112,6 +138,42 @@ public class QrCodeScanActivity extends FragmentActivity { returnResult(data); } else { super.onActivityResult(requestCode, resultCode, data); + finish(); + } + } + + private void processScannedContent(String content) { + Uri uri = Uri.parse(content); + processScannedContent(uri); + } + + private void processScannedContent(Uri uri) { + + Log.d(Constants.TAG, "scanned: " + uri); + + String fingerprint = null; + + // example: openpgp4fpr:73EE2314F65FA92EC2390D3A718C070100012282 + if (uri.getScheme().toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) { + fingerprint = uri.getEncodedSchemeSpecificPart().toLowerCase(Locale.ENGLISH); + } + + if (fingerprint == null) { + SingletonResult result = new SingletonResult( + SingletonResult.RESULT_ERROR, OperationResult.LogType.MSG_WRONG_QR_CODE); + Intent intent = new Intent(); + intent.putExtra(SingletonResult.EXTRA_RESULT, result); + returnResult(intent); + return; + } + + if (returnResult) { + Intent result = new Intent(); + result.putExtra(EXTRA_FINGERPRINT, fingerprint); + setResult(RESULT_OK, result); + finish(); + } else { + importKeys(fingerprint); } } @@ -128,21 +190,28 @@ public class QrCodeScanActivity extends FragmentActivity { } } - public void startCertify(Uri dataUri) { - // example: openpgp4fpr:73EE2314F65FA92EC2390D3A718C070100012282 - if (dataUri.getScheme().equals(Constants.FINGERPRINT_SCHEME)) { - String fingerprint = dataUri.getEncodedSchemeSpecificPart().toLowerCase(Locale.ENGLISH); - importKeys(fingerprint); - } else { - SingletonResult result = new SingletonResult( - SingletonResult.RESULT_ERROR, OperationResult.LogType.MSG_WRONG_QR_CODE); - Intent intent = new Intent(); - intent.putExtra(SingletonResult.EXTRA_RESULT, result); - returnResult(intent); - } + public void importKeys(byte[] keyringData) { + + ParcelableKeyRing keyEntry = new ParcelableKeyRing(keyringData); + ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>(); + selectedEntries.add(keyEntry); + + startImportService(selectedEntries); + } public void importKeys(String fingerprint) { + + ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null); + ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>(); + selectedEntries.add(keyEntry); + + startImportService(selectedEntries); + + } + + private void startImportService (ArrayList<ParcelableKeyRing> keyRings) { + // Message is received after importing is done in KeychainIntentService KeychainIntentServiceHandler serviceHandler = new KeychainIntentServiceHandler( this, @@ -168,7 +237,7 @@ public class QrCodeScanActivity extends FragmentActivity { return; } - if ( ! result.success()) { + if (!result.success()) { // only return if no success... Intent data = new Intent(); data.putExtras(returnData); @@ -176,34 +245,32 @@ public class QrCodeScanActivity extends FragmentActivity { return; } - Intent certifyIntent = new Intent(QrCodeScanActivity.this, CertifyKeyActivity.class); + Intent certifyIntent = new Intent(ImportKeysProxyActivity.this, + CertifyKeyActivity.class); certifyIntent.putExtra(CertifyKeyActivity.EXTRA_RESULT, result); - certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, result.getImportedMasterKeyIds()); + certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, + result.getImportedMasterKeyIds()); startActivityForResult(certifyIntent, 0); } } }; - // search config - Preferences prefs = Preferences.getPreferences(this); - Preferences.CloudSearchPrefs cloudPrefs = new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver()); - - // Send all information needed to service to query keys in other thread - Intent intent = new Intent(this, KeychainIntentService.class); - - intent.setAction(KeychainIntentService.ACTION_IMPORT_KEYRING); - // fill values for this action Bundle data = new Bundle(); - data.putString(KeychainIntentService.IMPORT_KEY_SERVER, cloudPrefs.keyserver); - - ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null); - ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<ParcelableKeyRing>(); - selectedEntries.add(keyEntry); + // search config + { + Preferences prefs = Preferences.getPreferences(this); + Preferences.CloudSearchPrefs cloudPrefs = + new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver()); + data.putString(KeychainIntentService.IMPORT_KEY_SERVER, cloudPrefs.keyserver); + } - data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, selectedEntries); + data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, keyRings); + // Send all information needed to service to query keys in other thread + Intent intent = new Intent(this, KeychainIntentService.class); + intent.setAction(KeychainIntentService.ACTION_IMPORT_KEYRING); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); // Create a new Messenger for the communication back @@ -215,6 +282,20 @@ public class QrCodeScanActivity extends FragmentActivity { // start service with intent startService(intent); + + } + + /** + * NFC: Parses the NDEF Message from the intent and prints to the TextView + */ + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + void handleActionNdefDiscovered(Intent intent) { + Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); + // 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(); + importKeys(receivedKeyringBytes); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java deleted file mode 100644 index 8d26cc955..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de> - * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.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.app.ProgressDialog; -import android.content.Intent; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -import android.view.Menu; -import android.view.MenuItem; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.operations.results.ConsolidateResult; -import org.sufficientlysecure.keychain.operations.results.OperationResult; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.provider.KeychainDatabase; -import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.ui.util.Notify; -import org.sufficientlysecure.keychain.util.ExportHelper; -import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.util.Preferences; - -import java.io.IOException; - -public class KeyListActivity extends DrawerActivity { - - public static final int REQUEST_CODE_RESULT_TO_LIST = 1; - - ExportHelper mExportHelper; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setTitle(R.string.nav_keys); - - // if this is the first time show first time activity - Preferences prefs = Preferences.getPreferences(this); - if (prefs.isFirstTime()) { - startActivity(new Intent(this, FirstTimeActivity.class)); - finish(); - return; - } - - mExportHelper = new ExportHelper(this); - - setContentView(R.layout.key_list_activity); - - Intent data = getIntent(); - // If we got an EXTRA_RESULT in the intent, show the notification - if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) { - OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); - result.createNotify(this).show(); - } - - // now setup navigation drawer in DrawerActivity... - activateDrawerNavigation(savedInstanceState); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - getMenuInflater().inflate(R.menu.key_list, menu); - - if (Constants.DEBUG) { - menu.findItem(R.id.menu_key_list_debug_cons).setVisible(true); - menu.findItem(R.id.menu_key_list_debug_read).setVisible(true); - menu.findItem(R.id.menu_key_list_debug_write).setVisible(true); - menu.findItem(R.id.menu_key_list_debug_first_time).setVisible(true); - } - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_key_list_add: - Intent scanQrCode = new Intent(this, QrCodeScanActivity.class); - scanQrCode.setAction(QrCodeScanActivity.ACTION_SCAN_WITH_RESULT); - startActivityForResult(scanQrCode, 0); - return true; - - case R.id.menu_key_list_search_cloud: - searchCloud(); - return true; - - case R.id.menu_key_list_create: - createKey(); - return true; - - case R.id.menu_key_list_import_existing_key: - Intent intentImportExisting = new Intent(this, ImportKeysActivity.class); - intentImportExisting.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN); - startActivityForResult(intentImportExisting, 0); - return true; - - case R.id.menu_key_list_export: - mExportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE, true); - return true; - - case R.id.menu_key_list_debug_cons: - consolidate(); - return true; - - case R.id.menu_key_list_debug_read: - try { - KeychainDatabase.debugBackup(this, true); - Notify.showNotify(this, "Restored debug_backup.db", Notify.Style.INFO); - getContentResolver().notifyChange(KeychainContract.KeyRings.CONTENT_URI, null); - } catch (IOException e) { - Log.e(Constants.TAG, "IO Error", e); - Notify.showNotify(this, "IO Error " + e.getMessage(), Notify.Style.ERROR); - } - return true; - - case R.id.menu_key_list_debug_write: - try { - KeychainDatabase.debugBackup(this, false); - Notify.showNotify(this, "Backup to debug_backup.db completed", Notify.Style.INFO); - } catch (IOException e) { - Log.e(Constants.TAG, "IO Error", e); - Notify.showNotify(this, "IO Error: " + e.getMessage(), Notify.Style.ERROR); - } - return true; - - case R.id.menu_key_list_debug_first_time: - Preferences prefs = Preferences.getPreferences(this); - prefs.setFirstTime(true); - Intent intent = new Intent(this, FirstTimeActivity.class); - startActivity(intent); - finish(); - return true; - - default: - return super.onOptionsItemSelected(item); - } - } - - private void searchCloud() { - Intent importIntent = new Intent(this, ImportKeysActivity.class); - importIntent.putExtra(ImportKeysActivity.EXTRA_QUERY, (String) null); // hack to show only cloud tab - startActivity(importIntent); - } - - private void createKey() { - Intent intent = new Intent(this, CreateKeyActivity.class); - startActivityForResult(intent, 0); - } - - private void consolidate() { - // Message is received after importing is done in KeychainIntentService - KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( - this, - getString(R.string.progress_importing), - ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard KeychainIntentServiceHandler first - super.handleMessage(message); - - if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { - // get returned data bundle - Bundle returnData = message.getData(); - if (returnData == null) { - return; - } - final ConsolidateResult result = - returnData.getParcelable(OperationResult.EXTRA_RESULT); - if (result == null) { - return; - } - - result.createNotify(KeyListActivity.this).show(); - } - } - }; - - // Send all information needed to service to import key in other thread - Intent intent = new Intent(this, KeychainIntentService.class); - - intent.setAction(KeychainIntentService.ACTION_CONSOLIDATE); - - // fill values for this action - Bundle data = new Bundle(); - - intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - // if a result has been returned, display a notify - if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) { - OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); - result.createNotify(this).show(); - } else { - super.onActivityResult(requestCode, resultCode, data); - } - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index d0be052d8..b56da463a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -18,6 +18,7 @@ package org.sufficientlysecure.keychain.ui; +import android.animation.ObjectAnimator; import android.annotation.TargetApi; import android.app.ProgressDialog; import android.content.Context; @@ -31,62 +32,54 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Messenger; -import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v4.view.MenuItemCompat; import android.support.v4.widget.CursorAdapter; -import android.support.v4.widget.NoScrollableSwipeRefreshLayout; -import android.support.v7.app.ActionBar; -import android.support.v7.app.ActionBarActivity; import android.support.v7.widget.SearchView; import android.view.ActionMode; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; -import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AbsListView.MultiChoiceModeListener; import android.widget.AdapterView; -import android.widget.Button; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; +import com.getbase.floatingactionbutton.FloatingActionButton; +import com.getbase.floatingactionbutton.FloatingActionsMenu; + import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; +import org.sufficientlysecure.keychain.operations.results.ConsolidateResult; import org.sufficientlysecure.keychain.operations.results.DeleteResult; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.util.ExportHelper; -import org.sufficientlysecure.keychain.util.KeyUpdateHelper; import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainDatabase; +import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; -import org.sufficientlysecure.keychain.ui.widget.ListAwareSwipeRefreshLayout; import org.sufficientlysecure.keychain.ui.util.Highlighter; -import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.Notify; -import org.sufficientlysecure.keychain.util.ParcelableFileCache; +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 java.io.IOException; -import java.util.ArrayList; import java.util.Date; import java.util.HashMap; -import edu.cmu.cylab.starslinger.exchange.ExchangeActivity; -import edu.cmu.cylab.starslinger.exchange.ExchangeConfig; import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; import se.emilsjolander.stickylistheaders.StickyListHeadersListView; @@ -96,21 +89,26 @@ import se.emilsjolander.stickylistheaders.StickyListHeadersListView; */ public class KeyListFragment extends LoaderFragment implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener, - LoaderManager.LoaderCallbacks<Cursor> { + LoaderManager.LoaderCallbacks<Cursor>, FabContainer { + + ExportHelper mExportHelper; private KeyListAdapter mAdapter; private StickyListHeadersListView mStickyList; - private ListAwareSwipeRefreshLayout mSwipeRefreshLayout; // saves the mode object for multiselect, needed for reset at some point private ActionMode mActionMode = null; - private boolean mShowAllKeys = true; - private String mQuery; - private SearchView mSearchView; - boolean hideMenu = false; + private FloatingActionsMenu mFab; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mExportHelper = new ExportHelper(getActivity()); + } /** * Load custom layout with StickyListView from library @@ -123,71 +121,36 @@ public class KeyListFragment extends LoaderFragment mStickyList = (StickyListHeadersListView) view.findViewById(R.id.key_list_list); mStickyList.setOnItemClickListener(this); - mSwipeRefreshLayout = (ListAwareSwipeRefreshLayout) view.findViewById(R.id.key_list_swipe_container); - mSwipeRefreshLayout.setOnRefreshListener(new NoScrollableSwipeRefreshLayout.OnRefreshListener() { + mFab = (FloatingActionsMenu) view.findViewById(R.id.fab_main); + + FloatingActionButton fabQrCode = (FloatingActionButton) view.findViewById(R.id.fab_add_qr_code); + FloatingActionButton fabCloud = (FloatingActionButton) view.findViewById(R.id.fab_add_cloud); + FloatingActionButton fabFile = (FloatingActionButton) view.findViewById(R.id.fab_add_file); + + fabQrCode.setOnClickListener(new OnClickListener() { @Override - public void onRefresh() { - KeychainIntentServiceHandler finishedHandler = new KeychainIntentServiceHandler(getActivity()) { - public void handleMessage(Message message) { - if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { - mSwipeRefreshLayout.setRefreshing(false); - } - } - }; - // new KeyUpdateHelper().updateAllKeys(getActivity(), finishedHandler); - updateActionbarForSwipe(false); + public void onClick(View v) { + mFab.collapse(); + scanQrCode(); } }); - mSwipeRefreshLayout.setColorScheme( - R.color.android_purple_dark, - R.color.android_purple_light, - R.color.android_purple_dark, - R.color.android_purple_light); - mSwipeRefreshLayout.setStickyListHeadersListView(mStickyList); - mSwipeRefreshLayout.setOnTouchListener(new View.OnTouchListener() { + fabCloud.setOnClickListener(new OnClickListener() { @Override - public boolean onTouch(View v, MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_MOVE) { - updateActionbarForSwipe(true); - } else { - updateActionbarForSwipe(false); - } - return false; + public void onClick(View v) { + mFab.collapse(); + searchCloud(); + } + }); + fabFile.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mFab.collapse(); + importFile(); } }); - // Just disable for now - mSwipeRefreshLayout.setIsLocked(true); - return root; - } - private void updateActionbarForSwipe(boolean show) { - ActionBarActivity activity = (ActionBarActivity) getActivity(); - ActionBar bar = activity.getSupportActionBar(); - - if (show) { - bar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); - bar.setDisplayUseLogoEnabled(false); - bar.setCustomView(R.layout.custom_actionbar); - TextView title = (TextView) getActivity().findViewById(R.id.custom_actionbar_text); - title.setText(R.string.swipe_to_update); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - hideMenu = true; - activity.invalidateOptionsMenu(); - } - } else { - bar.setTitle(getActivity().getTitle()); - bar.setDisplayHomeAsUpEnabled(true); - bar.setDisplayShowTitleEnabled(true); - bar.setDisplayUseLogoEnabled(true); - bar.setDisplayShowHomeEnabled(true); - bar.setDisplayShowCustomEnabled(false); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - hideMenu = false; - activity.invalidateOptionsMenu(); - } - } + return root; } /** @@ -198,90 +161,91 @@ public class KeyListFragment extends LoaderFragment public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); + // show app name instead of "keys" from nav drawer + getActivity().setTitle(R.string.app_name); + mStickyList.setOnItemClickListener(this); mStickyList.setAreHeadersSticky(true); mStickyList.setDrawingListUnderStickyHeader(false); mStickyList.setFastScrollEnabled(true); /* - * Multi-selection is only available for Android >= 3.0 + * Multi-selection */ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - mStickyList.setFastScrollAlwaysVisible(true); + mStickyList.setFastScrollAlwaysVisible(true); - mStickyList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); - mStickyList.getWrappedList().setMultiChoiceModeListener(new MultiChoiceModeListener() { + mStickyList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); + mStickyList.getWrappedList().setMultiChoiceModeListener(new MultiChoiceModeListener() { - @Override - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - android.view.MenuInflater inflater = getActivity().getMenuInflater(); - inflater.inflate(R.menu.key_list_multi, menu); - mActionMode = mode; - return true; - } + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + android.view.MenuInflater inflater = getActivity().getMenuInflater(); + inflater.inflate(R.menu.key_list_multi, menu); + mActionMode = mode; + return true; + } - @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - return false; - } + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + return false; + } - @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - // get IDs for checked positions as long array - long[] ids; + // get IDs for checked positions as long array + long[] ids; - switch (item.getItemId()) { - case R.id.menu_key_list_multi_encrypt: { - ids = mAdapter.getCurrentSelectedMasterKeyIds(); - encrypt(mode, ids); - break; - } - case R.id.menu_key_list_multi_delete: { - ids = mAdapter.getCurrentSelectedMasterKeyIds(); - showDeleteKeyDialog(mode, ids, mAdapter.isAnySecretSelected()); - break; - } - case R.id.menu_key_list_multi_export: { - ids = mAdapter.getCurrentSelectedMasterKeyIds(); - ExportHelper mExportHelper = new ExportHelper((ActionBarActivity) getActivity()); - mExportHelper.showExportKeysDialog(ids, Constants.Path.APP_DIR_FILE, - mAdapter.isAnySecretSelected()); - break; - } - case R.id.menu_key_list_multi_select_all: { - // select all - for (int i = 0; i < mStickyList.getCount(); i++) { - mStickyList.setItemChecked(i, true); - } - break; + switch (item.getItemId()) { + case R.id.menu_key_list_multi_encrypt: { + ids = mAdapter.getCurrentSelectedMasterKeyIds(); + encrypt(mode, ids); + break; + } + case R.id.menu_key_list_multi_delete: { + ids = mAdapter.getCurrentSelectedMasterKeyIds(); + showDeleteKeyDialog(mode, ids, mAdapter.isAnySecretSelected()); + break; + } + case R.id.menu_key_list_multi_export: { + ids = mAdapter.getCurrentSelectedMasterKeyIds(); + ExportHelper mExportHelper = new ExportHelper(getActivity()); + mExportHelper.showExportKeysDialog(ids, Constants.Path.APP_DIR_FILE, + mAdapter.isAnySecretSelected()); + break; + } + case R.id.menu_key_list_multi_select_all: { + // select all + for (int i = 0; i < mStickyList.getCount(); i++) { + mStickyList.setItemChecked(i, true); } + break; } - return true; } + return true; + } - @Override - public void onDestroyActionMode(ActionMode mode) { - mActionMode = null; - mAdapter.clearSelection(); - } + @Override + public void onDestroyActionMode(ActionMode mode) { + mActionMode = null; + mAdapter.clearSelection(); + } - @Override - public void onItemCheckedStateChanged(ActionMode mode, int position, long id, - boolean checked) { - if (checked) { - mAdapter.setNewSelection(position, checked); - } else { - mAdapter.removeSelection(position); - } - int count = mStickyList.getCheckedItemCount(); - String keysSelected = getResources().getQuantityString( - R.plurals.key_list_selected_keys, count, count); - mode.setTitle(keysSelected); + @Override + public void onItemCheckedStateChanged(ActionMode mode, int position, long id, + boolean checked) { + if (checked) { + mAdapter.setNewSelection(position, true); + } else { + mAdapter.removeSelection(position); } + int count = mStickyList.getCheckedItemCount(); + String keysSelected = getResources().getQuantityString( + R.plurals.key_list_selected_keys, count, count); + mode.setTitle(keysSelected); + } - }); - } + }); // We have a menu item to show in action bar. setHasOptionsMenu(true); @@ -340,14 +304,6 @@ public class KeyListFragment extends LoaderFragment whereArgs[i] = "%" + words[i] + "%"; } } - if (!mShowAllKeys) { - if (where == null) { - where = ""; - } else { - where += " AND "; - } - where += KeyRings.VERIFIED + " != 0"; - } // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. @@ -368,9 +324,7 @@ public class KeyListFragment extends LoaderFragment // end action mode, if any if (mActionMode != null) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - mActionMode.finish(); - } + mActionMode.finish(); } // The list should now be shown. @@ -400,7 +354,6 @@ public class KeyListFragment extends LoaderFragment startActivity(viewIntent); } - @TargetApi(Build.VERSION_CODES.HONEYCOMB) protected void encrypt(ActionMode mode, long[] masterKeyIds) { Intent intent = new Intent(getActivity(), EncryptFilesActivity.class); intent.setAction(EncryptFilesActivity.ACTION_ENCRYPT_DATA); @@ -414,10 +367,8 @@ public class KeyListFragment extends LoaderFragment /** * Show dialog to delete key * - * @param masterKeyIds * @param hasSecret must contain whether the list of masterKeyIds contains a secret key or not */ - @TargetApi(Build.VERSION_CODES.HONEYCOMB) public void showDeleteKeyDialog(final ActionMode mode, long[] masterKeyIds, boolean hasSecret) { // Can only work on singular secret keys if (hasSecret && masterKeyIds.length > 1) { @@ -455,25 +406,27 @@ public class KeyListFragment extends LoaderFragment @Override public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { + inflater.inflate(R.menu.key_list, menu); + + if (Constants.DEBUG) { + menu.findItem(R.id.menu_key_list_debug_cons).setVisible(true); + menu.findItem(R.id.menu_key_list_debug_read).setVisible(true); + menu.findItem(R.id.menu_key_list_debug_write).setVisible(true); + menu.findItem(R.id.menu_key_list_debug_first_time).setVisible(true); + } + // Get the searchview MenuItem searchItem = menu.findItem(R.id.menu_key_list_search); - mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem); + SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem); // Execute this when searching - mSearchView.setOnQueryTextListener(this); - - View searchPlate = mSearchView.findViewById(android.support.v7.appcompat.R.id.search_plate); - searchPlate.setBackgroundResource(R.drawable.keychaintheme_searchview_holo_light); + searchView.setOnQueryTextListener(this); // Erase search result without focus MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() { @Override public boolean onMenuItemActionExpand(MenuItem item) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - hideMenu = true; - getActivity().invalidateOptionsMenu(); - } // disable swipe-to-refresh // mSwipeRefreshLayout.setIsLocked(true); @@ -485,26 +438,66 @@ public class KeyListFragment extends LoaderFragment mQuery = null; getLoaderManager().restartLoader(0, null, KeyListFragment.this); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - hideMenu = false; - getActivity().invalidateOptionsMenu(); - } // enable swipe-to-refresh // mSwipeRefreshLayout.setIsLocked(false); return true; } }); - if (hideMenu) { - for (int i = 0; i < menu.size(); i++) { - menu.getItem(i).setVisible(false); - } - } - super.onCreateOptionsMenu(menu, inflater); } @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case R.id.menu_key_list_create: + createKey(); + return true; + + case R.id.menu_key_list_export: + mExportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE, true); + 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); + Notify.showNotify(getActivity(), "Restored debug_backup.db", Notify.Style.INFO); + getActivity().getContentResolver().notifyChange(KeychainContract.KeyRings.CONTENT_URI, null); + } catch (IOException e) { + Log.e(Constants.TAG, "IO Error", e); + Notify.showNotify(getActivity(), "IO Error " + e.getMessage(), Notify.Style.ERROR); + } + return true; + + case R.id.menu_key_list_debug_write: + try { + KeychainDatabase.debugBackup(getActivity(), false); + Notify.showNotify(getActivity(), "Backup to debug_backup.db completed", Notify.Style.INFO); + } catch (IOException e) { + Log.e(Constants.TAG, "IO Error", e); + Notify.showNotify(getActivity(), "IO Error: " + e.getMessage(), Notify.Style.ERROR); + } + return true; + + case R.id.menu_key_list_debug_first_time: + Preferences prefs = Preferences.getPreferences(getActivity()); + prefs.setFirstTime(true); + Intent intent = new Intent(getActivity(), FirstTimeActivity.class); + startActivity(intent); + getActivity().finish(); + return true; + + default: + return super.onOptionsItemSelected(item); + } + } + + @Override public boolean onQueryTextSubmit(String s) { return true; } @@ -526,6 +519,105 @@ public class KeyListFragment extends LoaderFragment return true; } + private void searchCloud() { + Intent importIntent = new Intent(getActivity(), ImportKeysActivity.class); + importIntent.putExtra(ImportKeysActivity.EXTRA_QUERY, (String) null); // hack to show only cloud tab + startActivity(importIntent); + } + + private void scanQrCode() { + Intent scanQrCode = new Intent(getActivity(), ImportKeysProxyActivity.class); + scanQrCode.setAction(ImportKeysProxyActivity.ACTION_SCAN_IMPORT); + startActivityForResult(scanQrCode, 0); + } + + private void importFile() { + Intent intentImportExisting = new Intent(getActivity(), ImportKeysActivity.class); + intentImportExisting.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN); + startActivityForResult(intentImportExisting, 0); + } + + private void createKey() { + Intent intent = new Intent(getActivity(), CreateKeyActivity.class); + startActivityForResult(intent, 0); + } + + private void consolidate() { + // Message is received after importing is done in KeychainIntentService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( + getActivity(), + getString(R.string.progress_importing), + ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + // get returned data bundle + Bundle returnData = message.getData(); + if (returnData == null) { + return; + } + final ConsolidateResult result = + returnData.getParcelable(OperationResult.EXTRA_RESULT); + if (result == null) { + return; + } + + result.createNotify(getActivity()).show(); + } + } + }; + + // Send all information needed to service to import key in other thread + Intent intent = new Intent(getActivity(), KeychainIntentService.class); + + intent.setAction(KeychainIntentService.ACTION_CONSOLIDATE); + + // fill values for this action + Bundle data = new Bundle(); + + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(getActivity()); + + // start service with intent + getActivity().startService(intent); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + // if a result has been returned, display a notify + if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) { + OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); + result.createNotify(getActivity()).show(); + } else { + super.onActivityResult(requestCode, resultCode, data); + } + } + + @Override + public void fabMoveUp(int height) { + ObjectAnimator anim = ObjectAnimator.ofFloat(mFab, "translationY", 0, -height); + // we're a little behind, so skip 1/10 of the time + anim.setDuration(270); + anim.start(); + } + + @Override + public void fabRestorePosition() { + ObjectAnimator anim = ObjectAnimator.ofFloat(mFab, "translationY", 0); + // we're a little ahead, so wait a few ms + anim.setStartDelay(70); + anim.setDuration(300); + anim.start(); + } + /** * Implements StickyListHeadersAdapter from library */ @@ -533,7 +625,7 @@ public class KeyListFragment extends LoaderFragment private String mQuery; private LayoutInflater mInflater; - private HashMap<Integer, Boolean> mSelection = new HashMap<Integer, Boolean>(); + private HashMap<Integer, Boolean> mSelection = new HashMap<>(); public KeyListAdapter(Context context, Cursor c, int flags) { super(context, c, flags); @@ -624,13 +716,13 @@ public class KeyListFragment extends LoaderFragment // Note: order is important! if (isRevoked) { - KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, null, KeyFormattingUtils.STATE_REVOKED, true); + KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray); h.mStatus.setVisibility(View.VISIBLE); h.mSlinger.setVisibility(View.GONE); h.mMainUserId.setTextColor(context.getResources().getColor(R.color.bg_gray)); h.mMainUserIdRest.setTextColor(context.getResources().getColor(R.color.bg_gray)); } else if (isExpired) { - KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, null, KeyFormattingUtils.STATE_EXPIRED, true); + KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, null, KeyFormattingUtils.STATE_EXPIRED, R.color.bg_gray); h.mStatus.setVisibility(View.VISIBLE); h.mSlinger.setVisibility(View.GONE); h.mMainUserId.setTextColor(context.getResources().getColor(R.color.bg_gray)); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java index 4a2b88518..0de7bb391 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java @@ -19,21 +19,18 @@ package org.sufficientlysecure.keychain.ui; import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; import android.view.View; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.ui.util.ActionBarHelper; -public class LogDisplayActivity extends ActionBarActivity { +public class LogDisplayActivity extends BaseActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Inflate a "Done" custom action bar - ActionBarHelper.setOneButtonView(getSupportActionBar(), - R.string.btn_okay, R.drawable.ic_action_done, + setFullScreenDialogClose( new View.OnClickListener() { @Override public void onClick(View v) { @@ -42,7 +39,10 @@ public class LogDisplayActivity extends ActionBarActivity { } } ); + } + @Override + protected void initLayout() { setContentView(R.layout.log_display_activity); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java index 2baebc83d..b655a7e55 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -153,11 +153,11 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe switch (subEntry.mType.mLevel) { case DEBUG: ih.mSecondImg.setBackgroundColor(Color.GRAY); break; case INFO: ih.mSecondImg.setBackgroundColor(Color.BLACK); break; - case WARN: ih.mSecondImg.setBackgroundColor(Color.YELLOW); break; - case ERROR: ih.mSecondImg.setBackgroundColor(Color.RED); break; - case START: ih.mSecondImg.setBackgroundColor(getResources().getColor(R.color.emphasis)); break; - case OK: ih.mSecondImg.setBackgroundColor(Color.GREEN); break; - case CANCELLED: ih.mSecondImg.setBackgroundColor(Color.RED); break; + case WARN: ih.mSecondImg.setBackgroundColor(getResources().getColor(R.color.android_orange_light)); break; + case ERROR: ih.mSecondImg.setBackgroundColor(getResources().getColor(R.color.android_red_light)); break; + case START: ih.mSecondImg.setBackgroundColor(Color.BLACK); break; + case OK: ih.mSecondImg.setBackgroundColor(getResources().getColor(R.color.android_green_light)); break; + case CANCELLED: ih.mSecondImg.setBackgroundColor(getResources().getColor(R.color.android_red_light)); break; } } else { ih.mSecond.setVisibility(View.GONE); @@ -184,11 +184,11 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe switch (entry.mType.mLevel) { case DEBUG: ih.mImg.setBackgroundColor(Color.GRAY); break; case INFO: ih.mImg.setBackgroundColor(Color.BLACK); break; - case WARN: ih.mImg.setBackgroundColor(Color.YELLOW); break; - case ERROR: ih.mImg.setBackgroundColor(Color.RED); break; - case START: ih.mImg.setBackgroundColor(getResources().getColor(R.color.emphasis)); break; - case OK: ih.mImg.setBackgroundColor(Color.GREEN); break; - case CANCELLED: ih.mImg.setBackgroundColor(Color.RED); break; + case WARN: ih.mImg.setBackgroundColor(getResources().getColor(R.color.android_orange_light)); break; + case ERROR: ih.mImg.setBackgroundColor(getResources().getColor(R.color.android_red_light)); break; + case START: ih.mImg.setBackgroundColor(Color.BLACK); break; + case OK: ih.mImg.setBackgroundColor(getResources().getColor(R.color.android_green_light)); break; + case CANCELLED: ih.mImg.setBackgroundColor(getResources().getColor(R.color.android_red_light)); break; } return convertView; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java new file mode 100644 index 000000000..b6b2fcb8a --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.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 org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.OperationResult; +import org.sufficientlysecure.keychain.remote.ui.AppsListFragment; +import org.sufficientlysecure.keychain.util.Preferences; +import org.sufficientlysecure.keychain.util.FabContainer; + +import it.neokree.materialnavigationdrawer.MaterialNavigationDrawer; + +public class MainActivity extends MaterialNavigationDrawer implements FabContainer { + + @Override + public void init(Bundle savedInstanceState) { + // don't open drawer on first run + disableLearningPattern(); + +// addMultiPaneSupport(); + + // set the header image + // create and set the header + setDrawerHeaderImage(R.drawable.drawer_header); + + // create sections + addSection(newSection(getString(R.string.nav_keys), R.drawable.ic_vpn_key_black_24dp, new KeyListFragment())); + addSection(newSection(getString(R.string.nav_encrypt_decrypt), R.drawable.ic_lock_black_24dp, new EncryptDecryptOverviewFragment())); + addSection(newSection(getString(R.string.title_api_registered_apps), R.drawable.ic_apps_black_24dp, new AppsListFragment())); + + // create bottom section + addBottomSection(newSection(getString(R.string.menu_preferences), R.drawable.ic_settings_black_24dp, new Intent(this, SettingsActivity.class))); + addBottomSection(newSection(getString(R.string.menu_help), R.drawable.ic_help_black_24dp, new Intent(this, HelpActivity.class))); + + // if this is the first time show first time activity + Preferences prefs = Preferences.getPreferences(this); + if (prefs.isFirstTime()) { + startActivity(new Intent(this, FirstTimeActivity.class)); + finish(); + return; + } + + Intent data = getIntent(); + // If we got an EXTRA_RESULT in the intent, show the notification + if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) { + OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); + result.createNotify(this).show(); + } + } + + @Override + public void fabMoveUp(int height) { + Object fragment = getCurrentSection().getTargetFragment(); + if (fragment instanceof FabContainer) { + ((FabContainer) fragment).fabMoveUp(height); + } + } + + @Override + public void fabRestorePosition() { + Object fragment = getCurrentSection().getTargetFragment(); + if (fragment instanceof FabContainer) { + ((FabContainer) fragment).fabRestorePosition(); + } + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcActivity.java index d0e40a9b8..7311f4879 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcActivity.java @@ -15,7 +15,6 @@ import android.nfc.Tag; import android.nfc.tech.IsoDep; import android.os.Build; import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; import android.view.WindowManager; import android.widget.Toast; @@ -23,13 +22,11 @@ import org.spongycastle.bcpg.HashAlgorithmTags; import org.spongycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Iso7816TLV; import org.sufficientlysecure.keychain.util.Log; import java.io.IOException; import java.nio.ByteBuffer; -import java.util.Locale; /** * This class provides a communication interface to OpenPGP applications on ISO SmartCard compliant @@ -38,7 +35,7 @@ import java.util.Locale; * For the full specs, see http://g10code.com/docs/openpgp-card-2.0.pdf */ @TargetApi(Build.VERSION_CODES.GINGERBREAD_MR1) -public class NfcActivity extends ActionBarActivity { +public class NfcActivity extends BaseActivity { // actions public static final String ACTION_SIGN_HASH = "sign_hash"; @@ -82,8 +79,6 @@ public class NfcActivity extends ActionBarActivity { getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - setContentView(R.layout.nfc_activity); - Intent intent = getIntent(); Bundle data = intent.getExtras(); String action = intent.getAction(); @@ -93,36 +88,46 @@ public class NfcActivity extends ActionBarActivity { mKeyId = data.getLong(EXTRA_KEY_ID); } - if (ACTION_SIGN_HASH.equals(action)) { - mAction = action; - mPin = data.getString(EXTRA_PIN); - mHashToSign = data.getByteArray(EXTRA_NFC_HASH_TO_SIGN); - mHashAlgo = data.getInt(EXTRA_NFC_HASH_ALGO); - mServiceIntent = data.getParcelable(EXTRA_DATA); - - Log.d(Constants.TAG, "NfcActivity mAction: " + mAction); - Log.d(Constants.TAG, "NfcActivity mPin: " + mPin); - Log.d(Constants.TAG, "NfcActivity mHashToSign as hex: " + getHex(mHashToSign)); - Log.d(Constants.TAG, "NfcActivity mServiceIntent: " + mServiceIntent.toString()); - } else if (ACTION_DECRYPT_SESSION_KEY.equals(action)) { - mAction = action; - mPin = data.getString(EXTRA_PIN); - mEncryptedSessionKey = data.getByteArray(EXTRA_NFC_ENC_SESSION_KEY); - mServiceIntent = data.getParcelable(EXTRA_DATA); - - Log.d(Constants.TAG, "NfcActivity mAction: " + mAction); - Log.d(Constants.TAG, "NfcActivity mPin: " + mPin); - Log.d(Constants.TAG, "NfcActivity mEncryptedSessionKey as hex: " + getHex(mEncryptedSessionKey)); - Log.d(Constants.TAG, "NfcActivity mServiceIntent: " + mServiceIntent.toString()); - } else if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) { - Log.e(Constants.TAG, "This should not happen! NfcActivity.onCreate() is being called instead of onNewIntent()!"); - toast("This should not happen! Please create a new bug report that the NFC screen is restarted!"); - finish(); - } else { - Log.d(Constants.TAG, "Action not supported: " + action); + switch (action) { + case ACTION_SIGN_HASH: + mAction = action; + mPin = data.getString(EXTRA_PIN); + mHashToSign = data.getByteArray(EXTRA_NFC_HASH_TO_SIGN); + mHashAlgo = data.getInt(EXTRA_NFC_HASH_ALGO); + mServiceIntent = data.getParcelable(EXTRA_DATA); + + Log.d(Constants.TAG, "NfcActivity mAction: " + mAction); + Log.d(Constants.TAG, "NfcActivity mPin: " + mPin); + Log.d(Constants.TAG, "NfcActivity mHashToSign as hex: " + getHex(mHashToSign)); + Log.d(Constants.TAG, "NfcActivity mServiceIntent: " + mServiceIntent.toString()); + break; + case ACTION_DECRYPT_SESSION_KEY: + mAction = action; + mPin = data.getString(EXTRA_PIN); + mEncryptedSessionKey = data.getByteArray(EXTRA_NFC_ENC_SESSION_KEY); + mServiceIntent = data.getParcelable(EXTRA_DATA); + + Log.d(Constants.TAG, "NfcActivity mAction: " + mAction); + Log.d(Constants.TAG, "NfcActivity mPin: " + mPin); + Log.d(Constants.TAG, "NfcActivity mEncryptedSessionKey as hex: " + getHex(mEncryptedSessionKey)); + Log.d(Constants.TAG, "NfcActivity mServiceIntent: " + mServiceIntent.toString()); + break; + case NfcAdapter.ACTION_TAG_DISCOVERED: + Log.e(Constants.TAG, "This should not happen! NfcActivity.onCreate() is being called instead of onNewIntent()!"); + toast("This should not happen! Please create a new bug report that the NFC screen is restarted!"); + finish(); + break; + default: + Log.d(Constants.TAG, "Action not supported: " + action); + break; } } + @Override + protected void initLayout() { + setContentView(R.layout.nfc_activity); + } + /** * Called when the system is about to start resuming a previous activity, * disables NFC Foreground Dispatch diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcIntentActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcIntentActivity.java index cb15dbec2..0ccb206d1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcIntentActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcIntentActivity.java @@ -15,15 +15,14 @@ import android.nfc.Tag; import android.nfc.tech.IsoDep; import android.os.Build; import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; import android.view.WindowManager; import android.widget.Toast; import org.spongycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.util.Iso7816TLV; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.Iso7816TLV; import org.sufficientlysecure.keychain.util.Log; import java.io.IOException; @@ -36,7 +35,7 @@ import java.nio.ByteBuffer; * For the full specs, see http://g10code.com/docs/openpgp-card-2.0.pdf */ @TargetApi(Build.VERSION_CODES.GINGERBREAD_MR1) -public class NfcIntentActivity extends ActionBarActivity { +public class NfcIntentActivity extends BaseActivity { // special extra for OpenPgpService public static final String EXTRA_DATA = "data"; @@ -54,8 +53,6 @@ public class NfcIntentActivity extends ActionBarActivity { getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - setContentView(R.layout.nfc_activity); - Intent intent = getIntent(); Bundle data = intent.getExtras(); String action = intent.getAction(); @@ -87,7 +84,11 @@ public class NfcIntentActivity extends ActionBarActivity { Log.e(Constants.TAG, "IOException!", e); finish(); } + } + @Override + protected void initLayout() { + setContentView(R.layout.nfc_activity); } /** diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java index 5bface1fb..53fd9cdfd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java @@ -41,8 +41,6 @@ import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; -import com.haibison.android.lockpattern.LockPatternActivity; - import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; @@ -108,20 +106,20 @@ public class PassphraseDialogActivity extends FragmentActivity { case RESULT_CANCELED: // The user cancelled the task break; - case LockPatternActivity.RESULT_FAILED: - // The user failed to enter the pattern - break; - case LockPatternActivity.RESULT_FORGOT_PATTERN: - // The user forgot the pattern and invoked your recovery Activity. - break; +// case LockPatternActivity.RESULT_FAILED: +// // The user failed to enter the pattern +// break; +// case LockPatternActivity.RESULT_FORGOT_PATTERN: +// // The user forgot the pattern and invoked your recovery Activity. +// break; } /* * In any case, there's always a key EXTRA_RETRY_COUNT, which holds * the number of tries that the user did. */ - int retryCount = data.getIntExtra( - LockPatternActivity.EXTRA_RETRY_COUNT, 0); +// int retryCount = data.getIntExtra( +// LockPatternActivity.EXTRA_RETRY_COUNT, 0); break; } @@ -256,9 +254,9 @@ public class PassphraseDialogActivity extends FragmentActivity { if (keyType == CanonicalizedSecretKey.SecretKeyType.PATTERN) { // start pattern dialog and show progress circle here... - Intent patternActivity = new Intent(getActivity(), LockPatternActivity.class); - patternActivity.putExtra(LockPatternActivity.EXTRA_PATTERN, "123"); - startActivityForResult(patternActivity, REQUEST_CODE_ENTER_PATTERN); +// Intent patternActivity = new Intent(getActivity(), LockPatternActivity.class); +// patternActivity.putExtra(LockPatternActivity.EXTRA_PATTERN, "123"); +// startActivityForResult(patternActivity, REQUEST_CODE_ENTER_PATTERN); mInput.setVisibility(View.GONE); mProgress.setVisibility(View.VISIBLE); } else { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseWizardActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseWizardActivity.java index 93778fd0c..2e838535d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseWizardActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseWizardActivity.java @@ -43,10 +43,6 @@ import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; -import com.haibison.android.lockpattern.LockPatternFragment; -import com.haibison.android.lockpattern.LockPatternFragmentOld; -import com.haibison.android.lockpattern.widget.LockPatternView; - import org.sufficientlysecure.keychain.R; import java.io.IOException; @@ -56,7 +52,8 @@ import java.util.Arrays; import java.util.List; @TargetApi(Build.VERSION_CODES.HONEYCOMB) -public class PassphraseWizardActivity extends FragmentActivity implements LockPatternView.OnPatternListener { +public class PassphraseWizardActivity extends FragmentActivity { +//public class PassphraseWizardActivity extends FragmentActivity implements LockPatternView.OnPatternListener { //create or authenticate public String selectedAction; //for lockpattern @@ -117,10 +114,10 @@ public class PassphraseWizardActivity extends FragmentActivity implements LockPa getActionBar().setTitle(R.string.draw_lockpattern); } // LockPatternFragmentOld lpf = LockPatternFragmentOld.newInstance(selectedAction); - LockPatternFragment lpf = LockPatternFragment.newInstance("asd"); +// LockPatternFragment lpf = LockPatternFragment.newInstance("asd"); - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.fragmentContainer, lpf).addToBackStack(null).commit(); +// FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); +// transaction.replace(R.id.fragmentContainer, lpf).addToBackStack(null).commit(); } public void cancel(View view) { @@ -205,13 +202,11 @@ public class PassphraseWizardActivity extends FragmentActivity implements LockPa writeNFC = false; //just write once Toast.makeText(this, R.string.nfc_write_succesful, Toast.LENGTH_SHORT).show(); //advance to lockpattern - LockPatternFragmentOld lpf = LockPatternFragmentOld.newInstance(selectedAction); - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.fragmentContainer, lpf).addToBackStack(null).commit(); +// LockPatternFragmentOld lpf = LockPatternFragmentOld.newInstance(selectedAction); +// FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); +// transaction.replace(R.id.fragmentContainer, lpf).addToBackStack(null).commit(); } - } catch (IOException e) { - e.printStackTrace(); - } catch (FormatException e) { + } catch (IOException | FormatException e) { e.printStackTrace(); } @@ -226,9 +221,9 @@ public class PassphraseWizardActivity extends FragmentActivity implements LockPa //passwort matches, go to next view Toast.makeText(this, R.string.passphrases_match + "!", Toast.LENGTH_SHORT).show(); - LockPatternFragmentOld lpf = LockPatternFragmentOld.newInstance(selectedAction); - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.fragmentContainer, lpf).addToBackStack(null).commit(); +// LockPatternFragmentOld lpf = LockPatternFragmentOld.newInstance(selectedAction); +// FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); +// transaction.replace(R.id.fragmentContainer, lpf).addToBackStack(null).commit(); readNFC = false; //just once } else { //passwort doesnt match @@ -236,9 +231,7 @@ public class PassphraseWizardActivity extends FragmentActivity implements LockPa nfc.setText(R.string.nfc_wrong_tag); } } - } catch (IOException e) { - e.printStackTrace(); - } catch (FormatException e) { + } catch (IOException | FormatException e) { e.printStackTrace(); } } @@ -356,26 +349,6 @@ public class PassphraseWizardActivity extends FragmentActivity implements LockPa adapter.disableForegroundDispatch(this); } - @Override - public void onPatternStart() { - - } - - @Override - public void onPatternCleared() { - - } - - @Override - public void onPatternCellAdded(List<LockPatternView.Cell> pattern) { - - } - - @Override - public void onPatternDetected(List<LockPatternView.Cell> pattern) { - - } - public static class SelectMethods extends Fragment { // private OnFragmentInteractionListener mListener; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java index cf0c3eb88..d3c1d971a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java @@ -20,57 +20,56 @@ package org.sufficientlysecure.keychain.ui; import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; +import android.support.v4.app.ActivityCompat; +import android.support.v7.widget.CardView; import android.view.View; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.ImageView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.ui.util.ActionBarHelper; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.util.Log; +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; -public class QrCodeViewActivity extends ActionBarActivity { +public class QrCodeViewActivity extends BaseActivity { - private ImageView mFingerprintQrCode; + private ImageView mQrCode; + private CardView mQrCodeLayout; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Inflate a "Done" custom action bar - ActionBarHelper.setOneButtonView(getSupportActionBar(), - R.string.btn_okay, R.drawable.ic_action_done, + setFullScreenDialogClose( new View.OnClickListener() { @Override public void onClick(View v) { // "Done" - finish(); + ActivityCompat.finishAfterTransition(QrCodeViewActivity.this); } } ); - setContentView(R.layout.qr_code_activity); - Uri dataUri = getIntent().getData(); if (dataUri == null) { Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); - finish(); + ActivityCompat.finishAfterTransition(QrCodeViewActivity.this); return; } - mFingerprintQrCode = (ImageView) findViewById(R.id.qr_code_image); + mQrCode = (ImageView) findViewById(R.id.qr_code_image); + mQrCodeLayout = (CardView) findViewById(R.id.qr_code_image_layout); - mFingerprintQrCode.setOnClickListener(new View.OnClickListener() { + mQrCodeLayout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - finish(); + ActivityCompat.finishAfterTransition(QrCodeViewActivity.this); } }); @@ -82,7 +81,7 @@ public class QrCodeViewActivity extends ActionBarActivity { if (blob == null) { Log.e(Constants.TAG, "key not found!"); Notify.showNotify(this, R.string.error_key_not_found, Style.ERROR); - finish(); + ActivityCompat.finishAfterTransition(QrCodeViewActivity.this); } String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob); @@ -91,37 +90,26 @@ public class QrCodeViewActivity extends ActionBarActivity { // create a minimal size qr code, we can keep this in ram no problem final Bitmap qrCode = QrCodeUtils.getQRCodeBitmap(qrCodeContent, 0); - mFingerprintQrCode.getViewTreeObserver().addOnGlobalLayoutListener( + mQrCode.getViewTreeObserver().addOnGlobalLayoutListener( new OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - // create actual bitmap in display dimensions - Bitmap scaled = Bitmap.createScaledBitmap(qrCode, - mFingerprintQrCode.getWidth(), mFingerprintQrCode.getWidth(), false); - mFingerprintQrCode.setImageBitmap(scaled); - } - }); + @Override + public void onGlobalLayout() { + // create actual bitmap in display dimensions + Bitmap scaled = Bitmap.createScaledBitmap(qrCode, + mQrCode.getWidth(), mQrCode.getWidth(), false); + mQrCode.setImageBitmap(scaled); + } + }); } catch (ProviderHelper.NotFoundException e) { Log.e(Constants.TAG, "key not found!", e); Notify.showNotify(this, R.string.error_key_not_found, Style.ERROR); - finish(); + ActivityCompat.finishAfterTransition(QrCodeViewActivity.this); } } @Override - protected void onResume() { - super.onResume(); - - // custom activity transition to get zoom in effect - this.overridePendingTransition(R.anim.qr_code_zoom_enter, android.R.anim.fade_out); - } - - @Override - protected void onPause() { - super.onPause(); - - // custom activity transition to get zoom out effect - this.overridePendingTransition(0, R.anim.qr_code_zoom_exit); + protected void initLayout() { + setContentView(R.layout.qr_code_activity); } }
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java index 20e1bbe97..d1df2906d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java @@ -27,13 +27,9 @@ import android.os.Bundle; import android.os.Message; import android.os.Messenger; import android.support.v4.app.FragmentActivity; -import android.support.v7.app.ActionBarActivity; import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.NumberPicker; -import android.widget.Spinner; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; @@ -50,13 +46,12 @@ import org.sufficientlysecure.keychain.util.ParcelableFileCache; import java.io.IOException; import java.util.ArrayList; -import java.util.List; import edu.cmu.cylab.starslinger.exchange.ExchangeActivity; import edu.cmu.cylab.starslinger.exchange.ExchangeConfig; @TargetApi(Build.VERSION_CODES.HONEYCOMB) -public class SafeSlingerActivity extends ActionBarActivity { +public class SafeSlingerActivity extends BaseActivity { private static final int REQUEST_CODE_SAFE_SLINGER = 211; @@ -69,51 +64,17 @@ public class SafeSlingerActivity extends ActionBarActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.safe_slinger_activity); - mMasterKeyId = getIntent().getLongExtra(EXTRA_MASTER_KEY_ID, 0); - // NOTE: there are two versions of this layout, for API >= 11 and one for < 11 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - NumberPicker picker = (NumberPicker) findViewById(R.id.safe_slinger_picker); - picker.setMinValue(2); - picker.setMaxValue(10); - picker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() { - @Override - public void onValueChange(NumberPicker picker, int oldVal, int newVal) { - mSelectedNumber = newVal; - } - }); - } else { - Spinner spinner = (Spinner) findViewById(R.id.safe_slinger_spinner); - - List<String> list = new ArrayList<String>(); - list.add("2"); - list.add("3"); - list.add("4"); - list.add("5"); - list.add("6"); - list.add("7"); - list.add("8"); - list.add("9"); - list.add("10"); - - ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this, - android.R.layout.simple_spinner_item, list); - dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - spinner.setAdapter(dataAdapter); - spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { - mSelectedNumber = position + 2; - } - - @Override - public void onNothingSelected(AdapterView<?> parent) { - - } - }); - } + NumberPicker picker = (NumberPicker) findViewById(R.id.safe_slinger_picker); + picker.setMinValue(2); + picker.setMaxValue(10); + picker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() { + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + mSelectedNumber = newVal; + } + }); ImageView buttonIcon = (ImageView) findViewById(R.id.safe_slinger_button_image); buttonIcon.setColorFilter(getResources().getColor(R.color.tertiary_text_light), @@ -128,6 +89,11 @@ public class SafeSlingerActivity extends ActionBarActivity { }); } + @Override + protected void initLayout() { + setContentView(R.layout.safe_slinger_activity); + } + private void startExchange(long masterKeyId, int number) { // retrieve public key blob and start SafeSlinger Uri uri = KeychainContract.KeyRingData.buildPublicKeyRingUri(masterKeyId); @@ -197,7 +163,7 @@ public class SafeSlingerActivity extends ActionBarActivity { certifyIntent.putExtra(CertifyKeyActivity.EXTRA_RESULT, result); certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, result.getImportedMasterKeyIds()); certifyIntent.putExtra(CertifyKeyActivity.EXTRA_CERTIFY_KEY_ID, mMasterKeyId); - startActivityForResult(certifyIntent, KeyListActivity.REQUEST_CODE_RESULT_TO_LIST); + startActivityForResult(certifyIntent, 0); // mExchangeMasterKeyId = null; } @@ -221,7 +187,7 @@ public class SafeSlingerActivity extends ActionBarActivity { // We parcel this iteratively into a file - anything we can // display here, we should be able to import. ParcelableFileCache<ParcelableKeyRing> cache = - new ParcelableFileCache<ParcelableKeyRing>(activity, "key_import.pcl"); + new ParcelableFileCache<>(activity, "key_import.pcl"); cache.writeCache(it.size(), it.iterator()); // fill values for this action @@ -249,7 +215,7 @@ public class SafeSlingerActivity extends ActionBarActivity { } private static ArrayList<ParcelableKeyRing> getSlingedKeys(Bundle extras) { - ArrayList<ParcelableKeyRing> list = new ArrayList<ParcelableKeyRing>(); + ArrayList<ParcelableKeyRing> list = new ArrayList<>(); if (extras != null) { byte[] d; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java deleted file mode 100644 index 148aa7d67..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de> - * 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.Intent; -import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; -import android.view.View; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.ui.util.ActionBarHelper; - -public class SelectPublicKeyActivity extends ActionBarActivity { - - // Actions for internal use only: - public static final String ACTION_SELECT_PUBLIC_KEYS = Constants.INTENT_PREFIX - + "SELECT_PUBLIC_KEYRINGS"; - - public static final String EXTRA_SELECTED_MASTER_KEY_IDS = "master_key_ids"; - - public static final String RESULT_EXTRA_MASTER_KEY_IDS = "master_key_ids"; - public static final String RESULT_EXTRA_USER_IDS = "user_ids"; - - SelectPublicKeyFragment mSelectFragment; - - long mSelectedMasterKeyIds[]; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Inflate a "Done"/"Cancel" custom action bar view - ActionBarHelper.setTwoButtonView(getSupportActionBar(), R.string.btn_okay, R.drawable.ic_action_done, - new View.OnClickListener() { - @Override - public void onClick(View v) { - // ok - okClicked(); - } - }, R.string.btn_do_not_save, R.drawable.ic_action_cancel, new View.OnClickListener() { - @Override - public void onClick(View v) { - // cancel - cancelClicked(); - } - } - ); - - setContentView(R.layout.select_public_key_activity); - - setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); - - handleIntent(getIntent()); - - // Check that the activity is using the layout version with - // the fragment_container FrameLayout - if (findViewById(R.id.select_public_key_fragment_container) != null) { - - // 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; - } - - // Create an instance of the fragment - mSelectFragment = SelectPublicKeyFragment.newInstance(mSelectedMasterKeyIds); - - // Add the fragment to the 'fragment_container' FrameLayout - getSupportFragmentManager().beginTransaction() - .add(R.id.select_public_key_fragment_container, mSelectFragment).commit(); - } - - // TODO: reimplement! - // mFilterLayout = findViewById(R.id.layout_filter); - // mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo); - // mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear); - // - // mClearFilterButton.setOnClickListener(new OnClickListener() { - // public void onClick(View v) { - // handleIntent(new Intent()); - // } - // }); - - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - handleIntent(intent); - } - - private void handleIntent(Intent intent) { - // TODO: reimplement search! - - // String searchString = null; - // if (Intent.ACTION_SEARCH.equals(intent.getAction())) { - // searchString = intent.getStringExtra(SearchManager.QUERY); - // if (searchString != null && searchString.trim().length() == 0) { - // searchString = null; - // } - // } - - // if (searchString == null) { - // mFilterLayout.setVisibility(View.GONE); - // } else { - // mFilterLayout.setVisibility(View.VISIBLE); - // mFilterInfo.setText(getString(R.string.filterInfo, searchString)); - // } - - // preselected master keys - mSelectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS); - } - - private void cancelClicked() { - setResult(RESULT_CANCELED, null); - finish(); - } - - private void okClicked() { - Intent data = new Intent(); - data.putExtra(RESULT_EXTRA_MASTER_KEY_IDS, mSelectFragment.getSelectedMasterKeyIds()); - data.putExtra(RESULT_EXTRA_USER_IDS, mSelectFragment.getSelectedUserIds()); - setResult(RESULT_OK, data); - finish(); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java index af583bf89..afec3bf06 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java @@ -42,7 +42,6 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.ListFragmentWorkaround; -import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.ui.adapter.SelectKeyCursorAdapter; @@ -216,7 +215,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T public long[] getSelectedMasterKeyIds() { // mListView.getCheckedItemIds() would give the row ids of the KeyRings not the master key // ids! - Vector<Long> vector = new Vector<Long>(); + Vector<Long> vector = new Vector<>(); for (int i = 0; i < getListView().getCount(); ++i) { if (getListView().isItemChecked(i)) { vector.add(mAdapter.getMasterKeyId(i)); @@ -238,7 +237,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T * @return */ public String[] getSelectedUserIds() { - Vector<String> userIds = new Vector<String>(); + Vector<String> userIds = new Vector<>(); for (int i = 0; i < getListView().getCount(); ++i) { if (getListView().isItemChecked(i)) { userIds.add(mAdapter.getUserId(i)); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java index 51fac4779..53986a392 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java @@ -17,7 +17,7 @@ package org.sufficientlysecure.keychain.ui; -import android.annotation.SuppressLint; +import android.annotation.TargetApi; import android.content.Context; import android.content.Intent; import android.os.Build; @@ -27,19 +27,22 @@ import android.preference.Preference; import android.preference.PreferenceActivity; import android.preference.PreferenceFragment; import android.preference.PreferenceScreen; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; import org.spongycastle.bcpg.CompressionAlgorithmTags; import org.spongycastle.bcpg.HashAlgorithmTags; import org.spongycastle.openpgp.PGPEncryptedData; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.ui.widget.IntegerListPreference; +import org.sufficientlysecure.keychain.util.Preferences; import java.util.List; -@SuppressLint("NewApi") -public class PreferencesActivity extends PreferenceActivity { +public class SettingsActivity extends PreferenceActivity { 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"; @@ -54,6 +57,8 @@ public class PreferencesActivity extends PreferenceActivity { sPreferences = Preferences.getPreferences(this); super.onCreate(savedInstanceState); + setupToolbar(); + String action = getIntent().getAction(); if (action != null && action.equals(ACTION_PREFS_CLOUD)) { @@ -64,9 +69,9 @@ public class PreferencesActivity extends PreferenceActivity { mKeyServerPreference .setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { public boolean onPreferenceClick(Preference preference) { - Intent intent = new Intent(PreferencesActivity.this, - PreferencesKeyServerActivity.class); - intent.putExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS, + Intent intent = new Intent(SettingsActivity.this, + SettingsKeyServerActivity.class); + intent.putExtra(SettingsKeyServerActivity.EXTRA_KEY_SERVERS, sPreferences.getKeyServers()); startActivityForResult(intent, REQUEST_CODE_KEYSERVER_PREF); return false; @@ -130,12 +135,33 @@ public class PreferencesActivity extends PreferenceActivity { initializeUseNumKeypadForYubikeyPin( (CheckBoxPreference) findPreference(Constants.Pref.USE_NUMKEYPAD_FOR_YUBIKEY_PIN)); - } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { - // Load the legacy preferences headers - addPreferencesFromResource(R.xml.preference_headers_legacy); } } + /** + * Hack to get Toolbar in PreferenceActivity. See http://stackoverflow.com/a/26614696 + */ + private void setupToolbar() { + ViewGroup root = (ViewGroup) findViewById(android.R.id.content); + LinearLayout content = (LinearLayout) root.getChildAt(0); + LinearLayout toolbarContainer = (LinearLayout) View.inflate(this, R.layout.preference_toolbar_activity, null); + + root.removeAllViews(); + toolbarContainer.addView(content); + root.addView(toolbarContainer); + + Toolbar toolbar = (Toolbar) toolbarContainer.findViewById(R.id.toolbar); + toolbar.setTitle(R.string.title_preferences); + toolbar.setNavigationIcon(getResources().getDrawable(R.drawable.ic_arrow_back_white_24dp)); + toolbar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + //What to do on back clicked + finish(); + } + }); + } + @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { @@ -144,7 +170,7 @@ public class PreferencesActivity extends PreferenceActivity { return; } String servers[] = data - .getStringArrayExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS); + .getStringArrayExtra(SettingsKeyServerActivity.EXTRA_KEY_SERVERS); sPreferences.setKeyServers(servers); mKeyServerPreference.setSummary(keyserverSummary(this)); break; @@ -157,7 +183,6 @@ public class PreferencesActivity extends PreferenceActivity { } } - /* Called only on Honeycomb and later */ @Override public void onBuildHeaders(List<Header> target) { super.onBuildHeaders(target); @@ -185,8 +210,8 @@ public class PreferencesActivity extends PreferenceActivity { .setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { public boolean onPreferenceClick(Preference preference) { Intent intent = new Intent(getActivity(), - PreferencesKeyServerActivity.class); - intent.putExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS, + SettingsKeyServerActivity.class); + intent.putExtra(SettingsKeyServerActivity.EXTRA_KEY_SERVERS, sPreferences.getKeyServers()); startActivityForResult(intent, REQUEST_CODE_KEYSERVER_PREF); return false; @@ -208,7 +233,7 @@ public class PreferencesActivity extends PreferenceActivity { return; } String servers[] = data - .getStringArrayExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS); + .getStringArrayExtra(SettingsKeyServerActivity.EXTRA_KEY_SERVERS); sPreferences.setKeyServers(servers); mKeyServerPreference.setSummary(keyserverSummary(getActivity())); break; @@ -287,6 +312,7 @@ public class PreferencesActivity extends PreferenceActivity { } } + @TargetApi(Build.VERSION_CODES.KITKAT) protected boolean isValidFragment(String fragmentName) { return AdvancedPrefsFragment.class.getName().equals(fragmentName) || CloudSearchPrefsFragment.class.getName().equals(fragmentName) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java index 2a8ef171c..080dc2495 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java @@ -20,7 +20,6 @@ package org.sufficientlysecure.keychain.ui; import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; @@ -28,14 +27,13 @@ import android.view.ViewGroup; import android.widget.TextView; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.ui.util.ActionBarHelper; import org.sufficientlysecure.keychain.ui.widget.Editor; import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener; import org.sufficientlysecure.keychain.ui.widget.KeyServerEditor; import java.util.Vector; -public class PreferencesKeyServerActivity extends ActionBarActivity implements OnClickListener, +public class SettingsKeyServerActivity extends BaseActivity implements OnClickListener, EditorListener { public static final String EXTRA_KEY_SERVERS = "key_servers"; @@ -52,23 +50,19 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O super.onCreate(savedInstanceState); // Inflate a "Done"/"Cancel" custom action bar view - ActionBarHelper.setTwoButtonView(getSupportActionBar(), R.string.btn_okay, R.drawable.ic_action_done, + setFullScreenDialogDoneClose(R.string.btn_save, new View.OnClickListener() { @Override public void onClick(View v) { - // ok okClicked(); } - }, R.string.btn_do_not_save, R.drawable.ic_action_cancel, new View.OnClickListener() { + }, + new View.OnClickListener() { @Override public void onClick(View v) { - // cancel cancelClicked(); } - } - ); - - setContentView(R.layout.key_server_preference); + }); mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); @@ -102,6 +96,11 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O makeServerList(servers); } + @Override + protected void initLayout() { + setContentView(R.layout.key_server_preference); + } + private void makeServerList(String[] servers) { if (servers != null) { mEditors.removeAllViews(); @@ -137,7 +136,7 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O } private Vector<String> serverList() { - Vector<String> servers = new Vector<String>(); + Vector<String> servers = new Vector<>(); for (int i = 0; i < mEditors.getChildCount(); ++i) { KeyServerEditor editor = (KeyServerEditor) mEditors.getChildAt(i); String tmp = editor.getValue(); @@ -150,7 +149,7 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O private void okClicked() { Intent data = new Intent(); - Vector<String> servers = new Vector<String>(); + Vector<String> servers = new Vector<>(); for (int i = 0; i < mEditors.getChildCount(); ++i) { KeyServerEditor editor = (KeyServerEditor) mEditors.getChildAt(i); String tmp = editor.getValue(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java index 497486a5e..e19793fd5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java @@ -24,7 +24,6 @@ import android.os.Bundle; import android.os.Message; import android.os.Messenger; import android.support.v4.app.NavUtils; -import android.support.v7.app.ActionBarActivity; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; @@ -34,17 +33,17 @@ import android.widget.Toast; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Preferences; /** * Sends the selected public key to a keyserver */ -public class UploadKeyActivity extends ActionBarActivity { +public class UploadKeyActivity extends BaseActivity { private View mUploadButton; private Spinner mKeyServerSpinner; @@ -54,12 +53,10 @@ public class UploadKeyActivity extends ActionBarActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.upload_key_activity); - mUploadButton = findViewById(R.id.upload_key_action_upload); mKeyServerSpinner = (Spinner) findViewById(R.id.upload_key_keyserver); - ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, + ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, Preferences.getPreferences(this) .getKeyServers() ); @@ -86,6 +83,11 @@ public class UploadKeyActivity extends ActionBarActivity { } } + @Override + protected void initLayout() { + setContentView(R.layout.upload_key_activity); + } + private void uploadKey() { // Send all information needed to service to upload key in other thread Intent intent = new Intent(this, KeychainIntentService.class); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java index 34c08a6c7..a80503591 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java @@ -27,9 +27,6 @@ import android.support.v4.app.NavUtils; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v7.app.ActionBar; -import android.support.v7.app.ActionBarActivity; -import android.text.SpannableString; -import android.text.SpannableStringBuilder; import android.text.format.DateFormat; import android.view.MenuItem; import android.view.View; @@ -37,19 +34,18 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; -import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.pgp.WrappedSignature; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; +import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; import java.util.Date; -public class ViewCertActivity extends ActionBarActivity +public class ViewCertActivity extends BaseActivity implements LoaderManager.LoaderCallbacks<Cursor> { // These are the rows that we will retrieve. @@ -86,8 +82,6 @@ public class ViewCertActivity extends ActionBarActivity ActionBar actionBar = getSupportActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); - setContentView(R.layout.view_cert_activity); - mSigneeKey = (TextView) findViewById(R.id.signee_key); mSigneeUid = (TextView) findViewById(R.id.signee_uid); mAlgorithm = (TextView) findViewById(R.id.algorithm); @@ -113,6 +107,11 @@ public class ViewCertActivity extends ActionBarActivity } @Override + protected void initLayout() { + setContentView(R.layout.view_cert_activity); + } + + @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 0bc75b3a9..742cde75c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -18,10 +18,15 @@ package org.sufficientlysecure.keychain.ui; +import android.animation.ArgbEvaluator; +import android.animation.ObjectAnimator; +import android.annotation.SuppressLint; import android.annotation.TargetApi; +import android.app.Activity; +import android.app.ActivityOptions; import android.content.Intent; import android.database.Cursor; -import android.graphics.PorterDuff; +import android.graphics.Bitmap; import android.net.Uri; import android.nfc.NdefMessage; import android.nfc.NdefRecord; @@ -32,62 +37,77 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.os.Messenger; import android.provider.ContactsContract; +import android.provider.Settings; +import android.support.v4.app.ActivityCompat; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; -import android.support.v4.view.ViewPager; -import android.support.v7.app.ActionBar; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.widget.CardView; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.Animation.AnimationListener; +import android.view.animation.AnimationUtils; +import android.widget.ImageButton; import android.widget.ImageView; -import android.widget.LinearLayout; +import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; +import com.getbase.floatingactionbutton.FloatingActionButton; + import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.util.ContactHelper; -import org.sufficientlysecure.keychain.util.ExportHelper; -import org.sufficientlysecure.keychain.util.Preferences; +import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; +import org.sufficientlysecure.keychain.operations.results.CertifyResult; +import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; +import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.operations.results.OperationResult; -import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter; -import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout; -import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout.TabColorizer; -import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.ui.util.FormattingUtils; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.Notify; +import org.sufficientlysecure.keychain.ui.util.QrCodeUtils; +import org.sufficientlysecure.keychain.ui.widget.AspectRatioImageView; +import org.sufficientlysecure.keychain.util.ContactHelper; +import org.sufficientlysecure.keychain.util.ExportHelper; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Preferences; +import java.util.ArrayList; import java.util.Date; import java.util.HashMap; -public class ViewKeyActivity extends ActionBarActivity implements +public class ViewKeyActivity extends BaseActivity implements LoaderManager.LoaderCallbacks<Cursor> { + static final int REQUEST_QR_FINGERPRINT = 1; + ExportHelper mExportHelper; ProviderHelper mProviderHelper; protected Uri mDataUri; - public static final String EXTRA_SELECTED_TAB = "selected_tab"; - public static final int TAB_MAIN = 0; - public static final int TAB_SHARE = 1; - - // view - private ViewPager mViewPager; - private SlidingTabLayout mSlidingTabLayout; - private PagerTabStripAdapter mTabsAdapter; - - private LinearLayout mStatusLayout; + private TextView mName; private TextView mStatusText; private ImageView mStatusImage; - private View mStatusDivider; + private RelativeLayout mBigToolbar; + + private ImageButton mActionEncryptFile; + private ImageButton mActionEncryptText; + private ImageButton mActionNfc; + private FloatingActionButton mFab; + private AspectRatioImageView mPhoto; + private ImageView mQrCode; + private CardView mQrCodeLayout; // NFC private NfcAdapter mNfcAdapter; @@ -98,6 +118,17 @@ public class ViewKeyActivity extends ActionBarActivity implements private static final int LOADER_ID_UNIFIED = 0; + private boolean mIsSecret = false; + private boolean mHasEncrypt = false; + private boolean mIsVerified = false; + private MenuItem mRefreshItem; + private boolean mIsRefreshing; + private Animation mRotate, mRotateSpin; + private View mRefresh; + private String mFingerprint; + private long mMasterKeyId; + + @SuppressLint("InflateParams") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -105,39 +136,65 @@ public class ViewKeyActivity extends ActionBarActivity implements mExportHelper = new ExportHelper(this); mProviderHelper = new ProviderHelper(this); - // let the actionbar look like Android's contact app - ActionBar actionBar = getSupportActionBar(); - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setIcon(android.R.color.transparent); - actionBar.setHomeButtonEnabled(true); + setTitle(null); - setContentView(R.layout.view_key_activity); - - mStatusLayout = (LinearLayout) findViewById(R.id.view_key_status_layout); - mStatusText = (TextView) findViewById(R.id.view_key_status_text); + mName = (TextView) findViewById(R.id.view_key_name); + mStatusText = (TextView) findViewById(R.id.view_key_status); mStatusImage = (ImageView) findViewById(R.id.view_key_status_image); - mStatusDivider = findViewById(R.id.view_key_status_divider); + mBigToolbar = (RelativeLayout) findViewById(R.id.toolbar_big); + + mActionEncryptFile = (ImageButton) findViewById(R.id.view_key_action_encrypt_files); + mActionEncryptText = (ImageButton) findViewById(R.id.view_key_action_encrypt_text); + mActionNfc = (ImageButton) findViewById(R.id.view_key_action_nfc); + mFab = (FloatingActionButton) findViewById(R.id.fab); + mPhoto = (AspectRatioImageView) findViewById(R.id.view_key_photo); + mQrCode = (ImageView) findViewById(R.id.view_key_qr_code); + mQrCodeLayout = (CardView) findViewById(R.id.view_key_qr_code_layout); + + mRotateSpin = AnimationUtils.loadAnimation(this, R.anim.rotate_spin); + mRotateSpin.setAnimationListener(new AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { - mViewPager = (ViewPager) findViewById(R.id.view_key_pager); - mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.view_key_sliding_tab_layout); + } - mSlidingTabLayout.setCustomTabColorizer(new TabColorizer() { @Override - public int getIndicatorColor(int position) { - return 0xFFAA66CC; + public void onAnimationEnd(Animation animation) { + mRefreshItem.getActionView().clearAnimation(); + mRefreshItem.setActionView(null); + mRefreshItem.setEnabled(true); + + // this is a deferred call + supportInvalidateOptionsMenu(); } @Override - public int getDividerColor(int position) { - return 0; + public void onAnimationRepeat(Animation animation) { + } }); + mRotate = AnimationUtils.loadAnimation(this, R.anim.rotate); + mRotate.setRepeatCount(Animation.INFINITE); + mRotate.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { - int switchToTab = TAB_MAIN; - Intent intent = getIntent(); - if (intent.getExtras() != null && intent.getExtras().containsKey(EXTRA_SELECTED_TAB)) { - switchToTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB); - } + } + + @Override + public void onAnimationEnd(Animation animation) { + + } + + @Override + public void onAnimationRepeat(Animation animation) { + if (!mIsRefreshing) { + mRefreshItem.getActionView().clearAnimation(); + mRefreshItem.getActionView().startAnimation(mRotateSpin); + } + } + }); + mRefresh = getLayoutInflater().inflate(R.layout.indeterminate_progress, null); mDataUri = getIntent().getData(); if (mDataUri == null) { @@ -155,7 +212,45 @@ public class ViewKeyActivity extends ActionBarActivity implements } } - Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString()); + Log.i(Constants.TAG, "mDataUri: " + mDataUri); + + mActionEncryptFile.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + encrypt(mDataUri, false); + } + }); + mActionEncryptText.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + encrypt(mDataUri, true); + } + }); + + mFab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mIsSecret) { + startSafeSlinger(mDataUri); + } else { + scanQrCode(); + } + } + }); + + mQrCodeLayout.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + showQrCodeDialog(); + } + }); + + mActionNfc.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + invokeNfcBeam(); + } + }); // Prepare the loaders. Either re-connect with an existing ones, // or start new ones. @@ -163,35 +258,39 @@ public class ViewKeyActivity extends ActionBarActivity implements initNfc(mDataUri); - initTabs(mDataUri); - - // switch to tab selected by extra - mViewPager.setCurrentItem(switchToTab); + startFragment(savedInstanceState, mDataUri); } - private void initTabs(Uri dataUri) { - mTabsAdapter = new PagerTabStripAdapter(this); - mViewPager.setAdapter(mTabsAdapter); + @Override + protected void initLayout() { + setContentView(R.layout.view_key_activity); + } - Bundle mainBundle = new Bundle(); - mainBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri); - mTabsAdapter.addTab(ViewKeyMainFragment.class, - mainBundle, getString(R.string.key_view_tab_main)); + private void startFragment(Bundle savedInstanceState, Uri dataUri) { + // 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; + } - Bundle shareBundle = new Bundle(); - shareBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri); - mTabsAdapter.addTab(ViewKeyShareFragment.class, - shareBundle, getString(R.string.key_view_tab_share)); + // Create an instance of the fragment + ViewKeyFragment frag = ViewKeyFragment.newInstance(dataUri); - // update layout after operations - mSlidingTabLayout.setViewPager(mViewPager); + // Add the fragment to the 'fragment_container' FrameLayout + // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! + getSupportFragmentManager().beginTransaction() + .replace(R.id.view_key_fragment, frag) + .commitAllowingStateLoss(); + // do it immediately! + getSupportFragmentManager().executePendingTransactions(); } @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.key_view, menu); - + mRefreshItem = menu.findItem(R.id.menu_key_view_refresh); return true; } @@ -200,7 +299,7 @@ public class ViewKeyActivity extends ActionBarActivity implements try { switch (item.getItemId()) { case android.R.id.home: { - Intent homeIntent = new Intent(this, KeyListActivity.class); + Intent homeIntent = new Intent(this, MainActivity.class); homeIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(homeIntent); return true; @@ -214,9 +313,26 @@ public class ViewKeyActivity extends ActionBarActivity implements return true; } case R.id.menu_key_view_advanced: { - Intent advancedIntent = new Intent(this, ViewKeyAdvancedActivity.class); + Intent advancedIntent = new Intent(this, ViewKeyAdvActivity.class); advancedIntent.setData(mDataUri); startActivity(advancedIntent); + return true; + } + case R.id.menu_key_view_refresh: { + try { + updateFromKeyserver(mDataUri, mProviderHelper); + } catch (ProviderHelper.NotFoundException e) { + Notify.showNotify(this, R.string.error_key_not_found, Notify.Style.ERROR); + } + return true; + } + case R.id.menu_key_view_edit: { + editKey(mDataUri); + return true; + } + case R.id.menu_key_view_certify_fingerprint: { + certifyFingeprint(mDataUri); + return true; } } } catch (ProviderHelper.NotFoundException e) { @@ -226,6 +342,105 @@ public class ViewKeyActivity extends ActionBarActivity implements return super.onOptionsItemSelected(item); } + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + MenuItem editKey = menu.findItem(R.id.menu_key_view_edit); + editKey.setVisible(mIsSecret); + MenuItem certifyFingerprint = menu.findItem(R.id.menu_key_view_certify_fingerprint); + certifyFingerprint.setVisible(!mIsSecret && !mIsVerified); + + return true; + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private void invokeNfcBeam() { + // Check for available NFC Adapter + mNfcAdapter = NfcAdapter.getDefaultAdapter(this); + if (mNfcAdapter == null || !mNfcAdapter.isEnabled()) { + Notify.createNotify(this, R.string.error_nfc_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() { + @Override + public void onAction() { + Intent intentSettings = new Intent(Settings.ACTION_NFC_SETTINGS); + startActivity(intentSettings); + } + }, R.string.menu_nfc_preferences).show(); + + return; + } + + if (!mNfcAdapter.isNdefPushEnabled()) { + Notify.createNotify(this, R.string.error_beam_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() { + @Override + public void onAction() { + Intent intentSettings = new Intent(Settings.ACTION_NFCSHARING_SETTINGS); + startActivity(intentSettings); + } + }, R.string.menu_beam_preferences).show(); + + return; + } + + mNfcAdapter.invokeBeam(this); + } + + private void scanQrCode() { + Intent scanQrCode = new Intent(this, ImportKeysProxyActivity.class); + scanQrCode.setAction(ImportKeysProxyActivity.ACTION_SCAN_WITH_RESULT); + startActivityForResult(scanQrCode, REQUEST_QR_FINGERPRINT); + } + + private void certifyFingeprint(Uri dataUri) { + Intent intent = new Intent(this, CertifyFingerprintActivity.class); + intent.setData(dataUri); + + startCertifyIntent(intent); + } + + private void certifyImmediate() { + Intent intent = new Intent(this, CertifyKeyActivity.class); + intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[]{ mMasterKeyId }); + + startCertifyIntent(intent); + } + + private void startCertifyIntent (Intent intent) { + // Message is received after signing is done in KeychainIntentService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + Bundle data = message.getData(); + CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT); + + result.createNotify(ViewKeyActivity.this).show(); + } + } + }; + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + startActivityForResult(intent, 0); + } + + private void showQrCodeDialog() { + Intent qrCodeIntent = new Intent(this, QrCodeViewActivity.class); + + // create the transition animation - the images in the layouts + // of both activities are defined with android:transitionName="qr_code" + Bundle opts = null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + ActivityOptions options = ActivityOptions + .makeSceneTransitionAnimation(this, mQrCodeLayout, "qr_code"); + opts = options.toBundle(); + } + + qrCodeIntent.setData(mDataUri); + ActivityCompat.startActivity(this, qrCodeIntent, opts); + } + private void exportToFile(Uri dataUri, ExportHelper exportHelper, ProviderHelper providerHelper) throws ProviderHelper.NotFoundException { Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri); @@ -258,7 +473,31 @@ public class ViewKeyActivity extends ActionBarActivity implements @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { - // if a result has been returned, display a notify + if (requestCode == REQUEST_QR_FINGERPRINT && resultCode == Activity.RESULT_OK) { + + // If there is an EXTRA_RESULT, that's an error. Just show it. + if (data.hasExtra(OperationResult.EXTRA_RESULT)) { + OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); + result.createNotify(this).show(); + return; + } + + String fp = data.getStringExtra(ImportKeysProxyActivity.EXTRA_FINGERPRINT); + if (fp == null) { + Notify.createNotify(this, "Error scanning fingerprint!", + Notify.LENGTH_LONG, Notify.Style.ERROR).show(); + return; + } + if (mFingerprint.equalsIgnoreCase(fp)) { + certifyImmediate(); + } else { + Notify.createNotify(this, "Fingerprints did not match!", + Notify.LENGTH_LONG, Notify.Style.ERROR).show(); + } + + return; + } + if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) { OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); result.createNotify(this).show(); @@ -267,6 +506,154 @@ public class ViewKeyActivity extends ActionBarActivity implements } } + private void encrypt(Uri dataUri, boolean text) { + // If there is no encryption key, don't bother. + if (!mHasEncrypt) { + Notify.showNotify(this, R.string.error_no_encrypt_subkey, Notify.Style.ERROR); + return; + } + try { + long keyId = new ProviderHelper(this) + .getCachedPublicKeyRing(dataUri) + .extractOrGetMasterKeyId(); + long[] encryptionKeyIds = new long[]{keyId}; + Intent intent; + if (text) { + intent = new Intent(this, EncryptTextActivity.class); + intent.setAction(EncryptTextActivity.ACTION_ENCRYPT_TEXT); + intent.putExtra(EncryptTextActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds); + } else { + intent = new Intent(this, EncryptFilesActivity.class); + intent.setAction(EncryptFilesActivity.ACTION_ENCRYPT_DATA); + intent.putExtra(EncryptFilesActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds); + } + // used instead of startActivity set actionbar based on callingPackage + startActivityForResult(intent, 0); + } catch (PgpKeyNotFoundException e) { + Log.e(Constants.TAG, "key not found!", e); + } + } + + private void updateFromKeyserver(Uri dataUri, ProviderHelper providerHelper) + throws ProviderHelper.NotFoundException { + + mIsRefreshing = true; + mRefreshItem.setEnabled(false); + mRefreshItem.setActionView(mRefresh); + mRefresh.startAnimation(mRotate); + + byte[] blob = (byte[]) providerHelper.getGenericData( + KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri), + KeychainContract.Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB); + String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob); + + ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null); + ArrayList<ParcelableKeyRing> entries = new ArrayList<>(); + entries.add(keyEntry); + + // Message is received after importing is done in KeychainIntentService + KeychainIntentServiceHandler serviceHandler = new KeychainIntentServiceHandler(this) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + // get returned data bundle + Bundle returnData = message.getData(); + + mIsRefreshing = false; + + if (returnData == null) { + finish(); + return; + } + final ImportKeyResult result = + returnData.getParcelable(OperationResult.EXTRA_RESULT); + result.createNotify(ViewKeyActivity.this).show(); + } + } + }; + + // fill values for this action + Bundle data = new Bundle(); + + // search config + { + Preferences prefs = Preferences.getPreferences(this); + Preferences.CloudSearchPrefs cloudPrefs = + new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver()); + data.putString(KeychainIntentService.IMPORT_KEY_SERVER, cloudPrefs.keyserver); + } + + data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, entries); + + // Send all information needed to service to query keys in other thread + Intent intent = new Intent(this, KeychainIntentService.class); + intent.setAction(KeychainIntentService.ACTION_IMPORT_KEYRING); + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(serviceHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + serviceHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + + } + + private void editKey(Uri dataUri) { + Intent editIntent = new Intent(this, EditKeyActivity.class); + editIntent.setData(KeychainContract.KeyRingData.buildSecretKeyRingUri(dataUri)); + startActivityForResult(editIntent, 0); + } + + private void startSafeSlinger(Uri dataUri) { + long keyId = 0; + try { + keyId = new ProviderHelper(this) + .getCachedPublicKeyRing(dataUri) + .extractOrGetMasterKeyId(); + } catch (PgpKeyNotFoundException e) { + Log.e(Constants.TAG, "key not found!", e); + } + Intent safeSlingerIntent = new Intent(this, SafeSlingerActivity.class); + safeSlingerIntent.putExtra(SafeSlingerActivity.EXTRA_MASTER_KEY_ID, keyId); + startActivityForResult(safeSlingerIntent, 0); + } + + /** + * Load QR Code asynchronously and with a fade in animation + */ + private void loadQrCode(final String fingerprint) { + AsyncTask<Void, Void, Bitmap> loadTask = + new AsyncTask<Void, Void, Bitmap>() { + protected Bitmap doInBackground(Void... unused) { + String qrCodeContent = Constants.FINGERPRINT_SCHEME + ":" + fingerprint; + // render with minimal size + return QrCodeUtils.getQRCodeBitmap(qrCodeContent, 0); + } + + protected void onPostExecute(Bitmap qrCode) { + // scale the image up to our actual size. we do this in code rather + // than let the ImageView do this because we don't require filtering. + Bitmap scaled = Bitmap.createScaledBitmap(qrCode, + mQrCode.getHeight(), mQrCode.getHeight(), + false); + mQrCode.setImageBitmap(scaled); + + // simple fade-in animation + AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f); + anim.setDuration(200); + mQrCode.startAnimation(anim); + } + }; + + loadTask.execute(); + } + /** * NFC: Initialize NFC sharing if OS and device supports it */ @@ -357,25 +744,34 @@ public class ViewKeyActivity extends ActionBarActivity implements } }; - static final String[] UNIFIED_PROJECTION = new String[]{ + // These are the rows that we will retrieve. + static final String[] PROJECTION = new String[]{ KeychainContract.KeyRings._ID, KeychainContract.KeyRings.MASTER_KEY_ID, KeychainContract.KeyRings.USER_ID, KeychainContract.KeyRings.IS_REVOKED, KeychainContract.KeyRings.EXPIRY, - + KeychainContract.KeyRings.VERIFIED, + KeychainContract.KeyRings.HAS_ANY_SECRET, + KeychainContract.KeyRings.FINGERPRINT, + KeychainContract.KeyRings.HAS_ENCRYPT }; - static final int INDEX_UNIFIED_MASTER_KEY_ID = 1; - static final int INDEX_UNIFIED_USER_ID = 2; - static final int INDEX_UNIFIED_IS_REVOKED = 3; - static final int INDEX_UNIFIED_EXPIRY = 4; + + static final int INDEX_MASTER_KEY_ID = 1; + static final int INDEX_USER_ID = 2; + static final int INDEX_IS_REVOKED = 3; + static final int INDEX_EXPIRY = 4; + static final int INDEX_VERIFIED = 5; + static final int INDEX_HAS_ANY_SECRET = 6; + static final int INDEX_FINGERPRINT = 7; + static final int INDEX_HAS_ENCRYPT = 8; @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { switch (id) { case LOADER_ID_UNIFIED: { Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mDataUri); - return new CursorLoader(this, baseUri, UNIFIED_PROJECTION, null, null, null); + return new CursorLoader(this, baseUri, PROJECTION, null, null, null); } default: @@ -383,6 +779,8 @@ public class ViewKeyActivity extends ActionBarActivity implements } } + int mPreviousColor = 0; + @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { /* TODO better error handling? May cause problems when a key is deleted, @@ -398,37 +796,165 @@ public class ViewKeyActivity extends ActionBarActivity implements case LOADER_ID_UNIFIED: { if (data.moveToFirst()) { // get name, email, and comment from USER_ID - String[] mainUserId = KeyRing.splitUserId(data.getString(INDEX_UNIFIED_USER_ID)); + String[] mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID)); if (mainUserId[0] != null) { - setTitle(mainUserId[0]); + mName.setText(mainUserId[0]); } else { - setTitle(R.string.user_id_no_name); + mName.setText(R.string.user_id_no_name); } - // get key id from MASTER_KEY_ID - long masterKeyId = data.getLong(INDEX_UNIFIED_MASTER_KEY_ID); - getSupportActionBar().setSubtitle(KeyFormattingUtils.beautifyKeyIdWithPrefix(this, masterKeyId)); + mMasterKeyId = data.getLong(INDEX_MASTER_KEY_ID); + mFingerprint = KeyFormattingUtils.convertFingerprintToHex(data.getBlob(INDEX_FINGERPRINT)); + + mIsSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0; + mHasEncrypt = data.getInt(INDEX_HAS_ENCRYPT) != 0; + boolean isRevoked = data.getInt(INDEX_IS_REVOKED) > 0; + boolean isExpired = !data.isNull(INDEX_EXPIRY) + && new Date(data.getLong(INDEX_EXPIRY) * 1000).before(new Date()); + mIsVerified = data.getInt(INDEX_VERIFIED) > 0; + + // if the refresh animation isn't playing + if (!mRotate.hasStarted() && !mRotateSpin.hasStarted()) { + // re-create options menu based on mIsSecret, mIsVerified + supportInvalidateOptionsMenu(); + // this is done at the end of the animation otherwise + } - boolean isRevoked = data.getInt(INDEX_UNIFIED_IS_REVOKED) > 0; - boolean isExpired = !data.isNull(INDEX_UNIFIED_EXPIRY) - && new Date(data.getLong(INDEX_UNIFIED_EXPIRY) * 1000).before(new Date()); + AsyncTask<String, Void, Bitmap> photoTask = + new AsyncTask<String, Void, Bitmap>() { + protected Bitmap doInBackground(String... fingerprint) { + return ContactHelper.photoFromFingerprint(getContentResolver(), fingerprint[0]); + } + + protected void onPostExecute(Bitmap photo) { + mPhoto.setImageBitmap(photo); + mPhoto.setVisibility(View.VISIBLE); + } + }; // Note: order is important + int color; if (isRevoked) { mStatusText.setText(R.string.view_key_revoked); - KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, KeyFormattingUtils.STATE_REVOKED); - mStatusDivider.setVisibility(View.VISIBLE); - mStatusLayout.setVisibility(View.VISIBLE); + mStatusImage.setVisibility(View.VISIBLE); + KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, + KeyFormattingUtils.STATE_REVOKED, R.color.icons, true); + color = getResources().getColor(R.color.android_red_light); + + mActionEncryptFile.setVisibility(View.GONE); + mActionEncryptText.setVisibility(View.GONE); + mActionNfc.setVisibility(View.GONE); + mFab.setVisibility(View.GONE); + mQrCodeLayout.setVisibility(View.GONE); } else if (isExpired) { - mStatusText.setText(R.string.view_key_expired); - KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, KeyFormattingUtils.STATE_EXPIRED); - mStatusDivider.setVisibility(View.VISIBLE); - mStatusLayout.setVisibility(View.VISIBLE); + if (mIsSecret) { + mStatusText.setText(R.string.view_key_expired_secret); + } else { + mStatusText.setText(R.string.view_key_expired); + } + mStatusImage.setVisibility(View.VISIBLE); + KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, + KeyFormattingUtils.STATE_EXPIRED, R.color.icons, true); + color = getResources().getColor(R.color.android_red_light); + + mActionEncryptFile.setVisibility(View.GONE); + mActionEncryptText.setVisibility(View.GONE); + mActionNfc.setVisibility(View.GONE); + mFab.setVisibility(View.GONE); + mQrCodeLayout.setVisibility(View.GONE); + } else if (mIsSecret) { + mStatusText.setText(R.string.view_key_my_key); + mStatusImage.setVisibility(View.GONE); + color = getResources().getColor(R.color.primary); + photoTask.execute(mFingerprint); + loadQrCode(mFingerprint); + mQrCodeLayout.setVisibility(View.VISIBLE); + + // and place leftOf qr code + RelativeLayout.LayoutParams nameParams = (RelativeLayout.LayoutParams) + mName.getLayoutParams(); + // remove right margin + nameParams.setMargins(FormattingUtils.dpToPx(this, 48), 0, 0, 0); + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + nameParams.setMarginEnd(0); + } + nameParams.addRule(RelativeLayout.LEFT_OF, R.id.view_key_qr_code_layout); + mName.setLayoutParams(nameParams); + + RelativeLayout.LayoutParams statusParams = (RelativeLayout.LayoutParams) + mStatusText.getLayoutParams(); + statusParams.setMargins(FormattingUtils.dpToPx(this, 48), 0, 0, 0); + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + statusParams.setMarginEnd(0); + } + statusParams.addRule(RelativeLayout.LEFT_OF, R.id.view_key_qr_code_layout); + mStatusText.setLayoutParams(statusParams); + + mActionEncryptFile.setVisibility(View.VISIBLE); + mActionEncryptText.setVisibility(View.VISIBLE); + + // invokeBeam is available from API 21 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + mActionNfc.setVisibility(View.VISIBLE); + } else { + mActionNfc.setVisibility(View.GONE); + } + mFab.setVisibility(View.VISIBLE); + mFab.setIconDrawable(getResources().getDrawable(R.drawable.ic_repeat_white_24dp)); } else { - mStatusDivider.setVisibility(View.GONE); - mStatusLayout.setVisibility(View.GONE); + mActionEncryptFile.setVisibility(View.VISIBLE); + mActionEncryptText.setVisibility(View.VISIBLE); + mQrCodeLayout.setVisibility(View.GONE); + mActionNfc.setVisibility(View.GONE); + + if (mIsVerified) { + mStatusText.setText(R.string.view_key_verified); + mStatusImage.setVisibility(View.VISIBLE); + KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, + KeyFormattingUtils.STATE_VERIFIED, R.color.icons, true); + color = getResources().getColor(R.color.primary); + photoTask.execute(mFingerprint); + + mFab.setVisibility(View.GONE); + } else { + mStatusText.setText(R.string.view_key_unverified); + mStatusImage.setVisibility(View.VISIBLE); + KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, + KeyFormattingUtils.STATE_UNVERIFIED, R.color.icons, true); + color = getResources().getColor(R.color.android_orange_light); + + mFab.setVisibility(View.VISIBLE); + } } + if (mPreviousColor == 0 || mPreviousColor == color) { + mToolbar.setBackgroundColor(color); + mStatusBar.setBackgroundColor(color); + mBigToolbar.setBackgroundColor(color); + mPreviousColor = color; + } else { + ObjectAnimator colorFade1 = + ObjectAnimator.ofObject(mToolbar, "backgroundColor", + new ArgbEvaluator(), mPreviousColor, color); + ObjectAnimator colorFade2 = + ObjectAnimator.ofObject(mStatusBar, "backgroundColor", + new ArgbEvaluator(), mPreviousColor, color); + ObjectAnimator colorFade3 = + ObjectAnimator.ofObject(mBigToolbar, "backgroundColor", + new ArgbEvaluator(), mPreviousColor, color); + + colorFade1.setDuration(1200); + colorFade2.setDuration(1200); + colorFade3.setDuration(1200); + colorFade1.start(); + colorFade2.start(); + colorFade3.start(); + mPreviousColor = color; + } + + //noinspection deprecation + mStatusImage.setAlpha(80); + break; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java new file mode 100644 index 000000000..9d79b377c --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2015 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; + +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.provider.ContactsContract; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v4.view.ViewPager; +import android.view.View; +import android.widget.Toast; + +import com.astuetz.PagerSlidingTabStrip; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.OperationResult; +import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.ContactHelper; +import org.sufficientlysecure.keychain.util.ExportHelper; +import org.sufficientlysecure.keychain.util.Log; + +import java.util.Date; + +public class ViewKeyAdvActivity extends BaseActivity implements + LoaderManager.LoaderCallbacks<Cursor> { + + ExportHelper mExportHelper; + ProviderHelper mProviderHelper; + + protected Uri mDataUri; + + public static final String EXTRA_SELECTED_TAB = "selected_tab"; + public static final int TAB_MAIN = 0; + public static final int TAB_SHARE = 1; + + // view + private ViewPager mViewPager; + private PagerSlidingTabStrip mSlidingTabLayout; + private PagerTabStripAdapter mTabsAdapter; + + private static final int LOADER_ID_UNIFIED = 0; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setFullScreenDialogClose(new View.OnClickListener() { + @Override + public void onClick(View v) { + finish(); + } + }); + + mExportHelper = new ExportHelper(this); + mProviderHelper = new ProviderHelper(this); + + mViewPager = (ViewPager) findViewById(R.id.view_key_pager); + mSlidingTabLayout = (PagerSlidingTabStrip) findViewById(R.id.view_key_sliding_tab_layout); + + int switchToTab = TAB_MAIN; + Intent intent = getIntent(); + if (intent.getExtras() != null && intent.getExtras().containsKey(EXTRA_SELECTED_TAB)) { + switchToTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB); + } + + mDataUri = getIntent().getData(); + if (mDataUri == null) { + Log.e(Constants.TAG, "Data missing. Should be uri of key!"); + finish(); + return; + } + if (mDataUri.getHost().equals(ContactsContract.AUTHORITY)) { + mDataUri = ContactHelper.dataUriFromContactUri(this, mDataUri); + if (mDataUri == null) { + Log.e(Constants.TAG, "Contact Data missing. Should be uri of key!"); + Toast.makeText(this, R.string.error_contacts_key_id_missing, Toast.LENGTH_LONG).show(); + finish(); + return; + } + } + + Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString()); + + // Prepare the loaders. Either re-connect with an existing ones, + // or start new ones. + getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); + + initTabs(mDataUri); + + // switch to tab selected by extra + mViewPager.setCurrentItem(switchToTab); + } + + @Override + protected void initLayout() { + setContentView(R.layout.view_key_adv_activity); + } + + private void initTabs(Uri dataUri) { + mTabsAdapter = new PagerTabStripAdapter(this); + mViewPager.setAdapter(mTabsAdapter); + + Bundle shareBundle = new Bundle(); + shareBundle.putParcelable(ViewKeyAdvUserIdsFragment.ARG_DATA_URI, dataUri); + mTabsAdapter.addTab(ViewKeyAdvShareFragment.class, + shareBundle, getString(R.string.key_view_tab_share)); + + Bundle userIdsBundle = new Bundle(); + userIdsBundle.putParcelable(ViewKeyAdvUserIdsFragment.ARG_DATA_URI, dataUri); + mTabsAdapter.addTab(ViewKeyAdvUserIdsFragment.class, + userIdsBundle, getString(R.string.section_user_ids)); + + Bundle keysBundle = new Bundle(); + keysBundle.putParcelable(ViewKeyAdvSubkeysFragment.ARG_DATA_URI, dataUri); + mTabsAdapter.addTab(ViewKeyAdvSubkeysFragment.class, + keysBundle, getString(R.string.key_view_tab_keys)); + + Bundle certsBundle = new Bundle(); + certsBundle.putParcelable(ViewKeyAdvCertsFragment.ARG_DATA_URI, dataUri); + mTabsAdapter.addTab(ViewKeyAdvCertsFragment.class, + certsBundle, getString(R.string.key_view_tab_certs)); + + Bundle trustBundle = new Bundle(); + trustBundle.putParcelable(ViewKeyTrustFragment.ARG_DATA_URI, dataUri); + mTabsAdapter.addTab(ViewKeyTrustFragment.class, + trustBundle, getString(R.string.key_view_tab_keybase)); + + // update layout after operations + mSlidingTabLayout.setViewPager(mViewPager); + } + + // These are the rows that we will retrieve. + static final String[] PROJECTION = new String[]{ + KeychainContract.KeyRings._ID, + KeychainContract.KeyRings.MASTER_KEY_ID, + KeychainContract.KeyRings.USER_ID, + KeychainContract.KeyRings.IS_REVOKED, + KeychainContract.KeyRings.EXPIRY, + KeychainContract.KeyRings.VERIFIED, + KeychainContract.KeyRings.HAS_ANY_SECRET + }; + + static final int INDEX_MASTER_KEY_ID = 1; + static final int INDEX_USER_ID = 2; + static final int INDEX_IS_REVOKED = 3; + static final int INDEX_EXPIRY = 4; + static final int INDEX_VERIFIED = 5; + static final int INDEX_HAS_ANY_SECRET = 6; + + @Override + public Loader<Cursor> onCreateLoader(int id, Bundle args) { + switch (id) { + case LOADER_ID_UNIFIED: { + Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mDataUri); + return new CursorLoader(this, baseUri, PROJECTION, null, null, null); + } + + default: + return null; + } + } + + @Override + public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + /* TODO better error handling? May cause problems when a key is deleted, + * because the notification triggers faster than the activity closes. + */ + // Avoid NullPointerExceptions... + if (data.getCount() == 0) { + return; + } + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + switch (loader.getId()) { + case LOADER_ID_UNIFIED: { + if (data.moveToFirst()) { + // get name, email, and comment from USER_ID + String[] mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID)); + if (mainUserId[0] != null) { + setTitle(mainUserId[0]); + } else { + setTitle(R.string.user_id_no_name); + } + + // get key id from MASTER_KEY_ID + long masterKeyId = data.getLong(INDEX_MASTER_KEY_ID); + getSupportActionBar().setSubtitle(KeyFormattingUtils.beautifyKeyIdWithPrefix(this, masterKeyId)); + + boolean isSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0; + boolean isRevoked = data.getInt(INDEX_IS_REVOKED) > 0; + boolean isExpired = !data.isNull(INDEX_EXPIRY) + && new Date(data.getLong(INDEX_EXPIRY) * 1000).before(new Date()); + boolean isVerified = data.getInt(INDEX_VERIFIED) > 0; + + // Note: order is important + int color; + if (isRevoked || isExpired) { + color = getResources().getColor(R.color.android_red_light); + } else if (isSecret) { + color = getResources().getColor(R.color.primary); + } else { + if (isVerified) { + color = getResources().getColor(R.color.primary); + } else { + color = getResources().getColor(R.color.android_orange_light); + } + } + mToolbar.setBackgroundColor(color); + mStatusBar.setBackgroundColor(color); + mSlidingTabLayout.setBackgroundColor(color); + + break; + } + } + } + } + + @Override + public void onLoaderReset(Loader<Cursor> loader) { + + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + // if a result has been returned, display a notify + if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) { + OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); + result.createNotify(this).show(); + } else { + super.onActivityResult(requestCode, resultCode, data); + } + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvancedFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvCertsFragment.java index 61bd126ce..90d7a400f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvancedFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvCertsFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2014-2015 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 @@ -30,7 +30,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.ListView; import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; @@ -39,7 +38,6 @@ import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.WrappedSignature; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainDatabase; -import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; @@ -47,23 +45,16 @@ import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; import se.emilsjolander.stickylistheaders.StickyListHeadersListView; -public class ViewKeyAdvancedFragment extends LoaderFragment implements +public class ViewKeyAdvCertsFragment extends LoaderFragment implements LoaderManager.LoaderCallbacks<Cursor>, AdapterView.OnItemClickListener { public static final String ARG_DATA_URI = "data_uri"; - private ListView mSubkeysList; - private SubkeysAdapter mSubkeysAdapter; - private StickyListHeadersListView mStickyList; private CertListAdapter mCertsAdapter; - private Uri mDataUriSubkeys; private Uri mDataUriCerts; - private static final int LOADER_SUBKEYS = 1; - private static final int LOADER_CERTS = 2; - // These are the rows that we will retrieve. static final String[] CERTS_PROJECTION = new String[]{ KeychainContract.Certs._ID, @@ -86,8 +77,8 @@ public class ViewKeyAdvancedFragment extends LoaderFragment implements /** * Creates new instance of this fragment */ - public static ViewKeyAdvancedFragment newInstance(Uri dataUri) { - ViewKeyAdvancedFragment frag = new ViewKeyAdvancedFragment(); + public static ViewKeyAdvCertsFragment newInstance(Uri dataUri) { + ViewKeyAdvCertsFragment frag = new ViewKeyAdvCertsFragment(); Bundle args = new Bundle(); args.putParcelable(ARG_DATA_URI, dataUri); @@ -99,9 +90,8 @@ public class ViewKeyAdvancedFragment extends LoaderFragment implements @Override public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { View root = super.onCreateView(inflater, superContainer, savedInstanceState); - View view = inflater.inflate(R.layout.view_key_advanced_fragment, getContainer()); + View view = inflater.inflate(R.layout.view_key_adv_certs_fragment, getContainer()); - mSubkeysList = (ListView) view.findViewById(R.id.keys); mStickyList = (StickyListHeadersListView) view.findViewById(R.id.certs_list); return root; @@ -122,7 +112,6 @@ public class ViewKeyAdvancedFragment extends LoaderFragment implements } private void loadData(Uri dataUri) { - mDataUriSubkeys = KeychainContract.Keys.buildKeysUri(dataUri); mDataUriCerts = KeychainContract.Certs.buildCertsUri(dataUri); mStickyList.setAreHeadersSticky(true); @@ -132,34 +121,23 @@ public class ViewKeyAdvancedFragment extends LoaderFragment implements mStickyList.setEmptyView(getActivity().findViewById(R.id.empty)); // Create an empty adapter we will use to display the loaded data. - mSubkeysAdapter = new SubkeysAdapter(getActivity(), null, 0); - mSubkeysList.setAdapter(mSubkeysAdapter); - mCertsAdapter = new CertListAdapter(getActivity(), null); mStickyList.setAdapter(mCertsAdapter); // Prepare the loaders. Either re-connect with an existing ones, // or start new ones. - getLoaderManager().initLoader(LOADER_SUBKEYS, null, this); - getLoaderManager().initLoader(LOADER_CERTS, null, this); + getLoaderManager().initLoader(0, null, this); } public Loader<Cursor> onCreateLoader(int id, Bundle args) { setContentShown(false); - switch (id) { - case LOADER_SUBKEYS: - return new CursorLoader(getActivity(), mDataUriSubkeys, - SubkeysAdapter.SUBKEYS_PROJECTION, null, null, null); - - case LOADER_CERTS: - // Now create and return a CursorLoader that will take care of - // creating a Cursor for the data being displayed. - return new CursorLoader(getActivity(), mDataUriCerts, - CERTS_PROJECTION, null, null, CERTS_SORT_ORDER); - - default: - return null; - } + + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + return new CursorLoader(getActivity(), mDataUriCerts, + CERTS_PROJECTION, null, null, CERTS_SORT_ORDER); + } public void onLoadFinished(Loader<Cursor> loader, Cursor data) { @@ -170,15 +148,8 @@ public class ViewKeyAdvancedFragment extends LoaderFragment implements // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) - switch (loader.getId()) { - case LOADER_SUBKEYS: - mSubkeysAdapter.swapCursor(data); - break; - case LOADER_CERTS: - mCertsAdapter.swapCursor(data); - mStickyList.setAdapter(mCertsAdapter); - break; - } + mCertsAdapter.swapCursor(data); + mStickyList.setAdapter(mCertsAdapter); // TODO: maybe show not before both are loaded! setContentShown(true); @@ -189,14 +160,7 @@ public class ViewKeyAdvancedFragment extends LoaderFragment implements * We need to make sure we are no longer using it. */ public void onLoaderReset(Loader<Cursor> loader) { - switch (loader.getId()) { - case LOADER_SUBKEYS: - mSubkeysAdapter.swapCursor(null); - break; - case LOADER_CERTS: - mCertsAdapter.swapCursor(null); - break; - } + mCertsAdapter.swapCursor(null); } /** @@ -307,7 +271,7 @@ public class ViewKeyAdvancedFragment extends LoaderFragment implements @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - return mInflater.inflate(R.layout.view_key_certs_item, parent, false); + return mInflater.inflate(R.layout.view_key_adv_certs_item, parent, false); } /** @@ -322,7 +286,7 @@ public class ViewKeyAdvancedFragment extends LoaderFragment implements HeaderViewHolder holder; if (convertView == null) { holder = new HeaderViewHolder(); - convertView = mInflater.inflate(R.layout.view_key_certs_header, parent, false); + convertView = mInflater.inflate(R.layout.view_key_adv_certs_header, parent, false); holder.text = (TextView) convertView.findViewById(R.id.stickylist_header_text); holder.count = (TextView) convertView.findViewById(R.id.certs_num); convertView.setTag(holder); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java index aa260b654..8d0a2dd1d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java @@ -17,7 +17,7 @@ package org.sufficientlysecure.keychain.ui; -import android.annotation.TargetApi; +import android.app.ActivityOptions; import android.content.Intent; import android.database.Cursor; import android.graphics.Bitmap; @@ -26,10 +26,11 @@ import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; -import android.provider.Settings; +import android.support.v4.app.ActivityCompat; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; +import android.support.v7.widget.CardView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -47,7 +48,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.ui.dialog.ShareNfcDialogFragment; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.QrCodeUtils; @@ -56,20 +56,19 @@ import org.sufficientlysecure.keychain.util.Log; import java.io.IOException; -public class ViewKeyShareFragment extends LoaderFragment implements +public class ViewKeyAdvShareFragment extends LoaderFragment implements LoaderManager.LoaderCallbacks<Cursor> { public static final String ARG_DATA_URI = "uri"; private TextView mFingerprint; - private ImageView mFingerprintQrCode; + private ImageView mQrCode; + private CardView mQrCodeLayout; private View mFingerprintShareButton; private View mFingerprintClipboardButton; private View mKeyShareButton; private View mKeyClipboardButton; private ImageButton mKeySafeSlingerButton; - private View mNfcHelpButton; - private View mNfcPrefsButton; private View mKeyUploadButton; ProviderHelper mProviderHelper; @@ -81,31 +80,24 @@ public class ViewKeyShareFragment extends LoaderFragment implements @Override public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { View root = super.onCreateView(inflater, superContainer, savedInstanceState); - View view = inflater.inflate(R.layout.view_key_share_fragment, getContainer()); + View view = inflater.inflate(R.layout.view_key_adv_share_fragment, getContainer()); - mProviderHelper = new ProviderHelper(ViewKeyShareFragment.this.getActivity()); + mProviderHelper = new ProviderHelper(ViewKeyAdvShareFragment.this.getActivity()); mFingerprint = (TextView) view.findViewById(R.id.view_key_fingerprint); - mFingerprintQrCode = (ImageView) view.findViewById(R.id.view_key_fingerprint_qr_code_image); + mQrCode = (ImageView) view.findViewById(R.id.view_key_qr_code); + mQrCodeLayout = (CardView) view.findViewById(R.id.view_key_qr_code_layout); mFingerprintShareButton = view.findViewById(R.id.view_key_action_fingerprint_share); mFingerprintClipboardButton = view.findViewById(R.id.view_key_action_fingerprint_clipboard); mKeyShareButton = view.findViewById(R.id.view_key_action_key_share); mKeyClipboardButton = view.findViewById(R.id.view_key_action_key_clipboard); mKeySafeSlingerButton = (ImageButton) view.findViewById(R.id.view_key_action_key_safeslinger); - mNfcHelpButton = view.findViewById(R.id.view_key_action_nfc_help); - mNfcPrefsButton = view.findViewById(R.id.view_key_action_nfc_prefs); mKeyUploadButton = view.findViewById(R.id.view_key_action_upload); mKeySafeSlingerButton.setColorFilter(getResources().getColor(R.color.tertiary_text_light), PorterDuff.Mode.SRC_IN); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - mNfcPrefsButton.setVisibility(View.VISIBLE); - } else { - mNfcPrefsButton.setVisibility(View.GONE); - } - - mFingerprintQrCode.setOnClickListener(new View.OnClickListener() { + mQrCodeLayout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showQrCodeDialog(); @@ -142,18 +134,6 @@ public class ViewKeyShareFragment extends LoaderFragment implements startSafeSlinger(mDataUri); } }); - mNfcHelpButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - showNfcHelpDialog(); - } - }); - mNfcPrefsButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - showNfcPrefs(); - } - }); mKeyUploadButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -228,10 +208,7 @@ public class ViewKeyShareFragment extends LoaderFragment implements } startActivity(Intent.createChooser(sendIntent, title)); } - } catch (PgpGeneralException e) { - Log.e(Constants.TAG, "error processing key!", e); - Notify.showNotify(getActivity(), R.string.error_key_processing, Notify.Style.ERROR); - } catch (IOException e) { + } catch (PgpGeneralException | IOException e) { Log.e(Constants.TAG, "error processing key!", e); Notify.showNotify(getActivity(), R.string.error_key_processing, Notify.Style.ERROR); } catch (ProviderHelper.NotFoundException e) { @@ -242,20 +219,18 @@ public class ViewKeyShareFragment extends LoaderFragment implements private void showQrCodeDialog() { Intent qrCodeIntent = new Intent(getActivity(), QrCodeViewActivity.class); - qrCodeIntent.setData(mDataUri); - startActivity(qrCodeIntent); - } - private void showNfcHelpDialog() { - ShareNfcDialogFragment dialog = ShareNfcDialogFragment.newInstance(); - dialog.show(getActivity().getSupportFragmentManager(), "shareNfcDialog"); - } + // create the transition animation - the images in the layouts + // of both activities are defined with android:transitionName="qr_code" + Bundle opts = null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + ActivityOptions options = ActivityOptions + .makeSceneTransitionAnimation(getActivity(), mQrCodeLayout, "qr_code"); + opts = options.toBundle(); + } - @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) - private void showNfcPrefs() { - Intent intentSettings = new Intent( - Settings.ACTION_NFCSHARING_SETTINGS); - startActivity(intentSettings); + qrCodeIntent.setData(mDataUri); + ActivityCompat.startActivity(getActivity(), qrCodeIntent, opts); } @Override @@ -361,19 +336,19 @@ public class ViewKeyShareFragment extends LoaderFragment implements protected void onPostExecute(Bitmap qrCode) { // only change view, if fragment is attached to activity - if (ViewKeyShareFragment.this.isAdded()) { + if (ViewKeyAdvShareFragment.this.isAdded()) { // scale the image up to our actual size. we do this in code rather // than let the ImageView do this because we don't require filtering. Bitmap scaled = Bitmap.createScaledBitmap(qrCode, - mFingerprintQrCode.getHeight(), mFingerprintQrCode.getHeight(), + mQrCode.getHeight(), mQrCode.getHeight(), false); - mFingerprintQrCode.setImageBitmap(scaled); + mQrCode.setImageBitmap(scaled); // simple fade-in animation AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f); anim.setDuration(200); - mFingerprintQrCode.startAnimation(anim); + mQrCode.startAnimation(anim); } } }; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java new file mode 100644 index 000000000..bd00c6780 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2014-2015 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; + +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ListView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter; +import org.sufficientlysecure.keychain.util.Log; + +public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements + LoaderManager.LoaderCallbacks<Cursor> { + + public static final String ARG_DATA_URI = "data_uri"; + + private ListView mSubkeysList; + private SubkeysAdapter mSubkeysAdapter; + + private Uri mDataUriSubkeys; + + /** + * Creates new instance of this fragment + */ + public static ViewKeyAdvSubkeysFragment newInstance(Uri dataUri) { + ViewKeyAdvSubkeysFragment frag = new ViewKeyAdvSubkeysFragment(); + + Bundle args = new Bundle(); + args.putParcelable(ARG_DATA_URI, dataUri); + + frag.setArguments(args); + return frag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { + View root = super.onCreateView(inflater, superContainer, savedInstanceState); + View view = inflater.inflate(R.layout.view_key_adv_subkeys_fragment, getContainer()); + + mSubkeysList = (ListView) view.findViewById(R.id.keys); + + return root; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); + if (dataUri == null) { + Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); + getActivity().finish(); + return; + } + + loadData(dataUri); + } + + private void loadData(Uri dataUri) { + mDataUriSubkeys = KeychainContract.Keys.buildKeysUri(dataUri); + + // Create an empty adapter we will use to display the loaded data. + mSubkeysAdapter = new SubkeysAdapter(getActivity(), null, 0); + mSubkeysList.setAdapter(mSubkeysAdapter); + + // Prepare the loaders. Either re-connect with an existing ones, + // or start new ones. + getLoaderManager().initLoader(0, null, this); + } + + public Loader<Cursor> onCreateLoader(int id, Bundle args) { + setContentShown(false); + + return new CursorLoader(getActivity(), mDataUriSubkeys, + SubkeysAdapter.SUBKEYS_PROJECTION, null, null, null); + } + + public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + // Avoid NullPointerExceptions, if we get an empty result set. + if (data.getCount() == 0) { + return; + } + + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mSubkeysAdapter.swapCursor(data); + + // TODO: maybe show not before both are loaded! + setContentShown(true); + } + + /** + * This is called when the last Cursor provided to onLoadFinished() above is about to be closed. + * We need to make sure we are no longer using it. + */ + public void onLoaderReset(Loader<Cursor> loader) { + mSubkeysAdapter.swapCursor(null); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java new file mode 100644 index 000000000..c4e6639a8 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.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.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ListView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; +import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; +import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment; +import org.sufficientlysecure.keychain.util.Log; + +public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements + LoaderManager.LoaderCallbacks<Cursor> { + + public static final String ARG_DATA_URI = "uri"; + + private ListView mUserIds; + + private static final int LOADER_ID_UNIFIED = 0; + private static final int LOADER_ID_USER_IDS = 1; + + private UserIdsAdapter mUserIdsAdapter; + + private Uri mDataUri; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { + View root = super.onCreateView(inflater, superContainer, savedInstanceState); + View view = inflater.inflate(R.layout.view_key_adv_main_fragment, getContainer()); + + mUserIds = (ListView) view.findViewById(R.id.view_key_user_ids); + + mUserIds.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + showUserIdInfo(position); + } + }); + + return root; + } + + private void showUserIdInfo(final int position) { + final boolean isRevoked = mUserIdsAdapter.getIsRevoked(position); + final int isVerified = mUserIdsAdapter.getIsVerified(position); + + DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { + public void run() { + UserIdInfoDialogFragment dialogFragment = + UserIdInfoDialogFragment.newInstance(isRevoked, isVerified); + + dialogFragment.show(getActivity().getSupportFragmentManager(), "userIdInfoDialog"); + } + }); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); + if (dataUri == null) { + Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); + getActivity().finish(); + return; + } + + loadData(dataUri); + } + + private void loadData(Uri dataUri) { + mDataUri = dataUri; + + Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString()); + + mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0); + mUserIds.setAdapter(mUserIdsAdapter); + + // Prepare the loaders. Either re-connect with an existing ones, + // or start new ones. + getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); + getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this); + } + + static final String[] UNIFIED_PROJECTION = new String[]{ + KeyRings._ID, KeyRings.MASTER_KEY_ID, + KeyRings.HAS_ANY_SECRET, KeyRings.IS_REVOKED, KeyRings.EXPIRY, KeyRings.HAS_ENCRYPT + }; + static final int INDEX_UNIFIED_MASTER_KEY_ID = 1; + static final int INDEX_UNIFIED_HAS_ANY_SECRET = 2; + static final int INDEX_UNIFIED_IS_REVOKED = 3; + static final int INDEX_UNIFIED_EXPIRY = 4; + static final int INDEX_UNIFIED_HAS_ENCRYPT = 5; + + public Loader<Cursor> onCreateLoader(int id, Bundle args) { + setContentShown(false); + + switch (id) { + case LOADER_ID_UNIFIED: { + Uri baseUri = KeyRings.buildUnifiedKeyRingUri(mDataUri); + return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null); + } + case LOADER_ID_USER_IDS: { + Uri baseUri = UserPackets.buildUserIdsUri(mDataUri); + return new CursorLoader(getActivity(), baseUri, + UserIdsAdapter.USER_IDS_PROJECTION, null, null, null); + } + + default: + return null; + } + } + + public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + /* TODO better error handling? May cause problems when a key is deleted, + * because the notification triggers faster than the activity closes. + */ + // Avoid NullPointerExceptions... + if (data.getCount() == 0) { + return; + } + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + switch (loader.getId()) { + case LOADER_ID_UNIFIED: { + if (data.moveToFirst()) { + + + break; + } + } + + case LOADER_ID_USER_IDS: { + mUserIdsAdapter.swapCursor(data); + break; + } + + } + setContentShown(true); + } + + /** + * This is called when the last Cursor provided to onLoadFinished() above is about to be closed. + * We need to make sure we are no longer using it. + */ + public void onLoaderReset(Loader<Cursor> loader) { + switch (loader.getId()) { + case LOADER_ID_USER_IDS: + mUserIdsAdapter.swapCursor(null); + break; + } + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java new file mode 100644 index 000000000..453bfd499 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.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.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ListView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; +import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment; +import org.sufficientlysecure.keychain.util.Log; + +import java.util.Date; + +public class ViewKeyFragment extends LoaderFragment implements + LoaderManager.LoaderCallbacks<Cursor> { + + public static final String ARG_DATA_URI = "uri"; + + private ListView mUserIds; + + boolean mIsSecret = false; + + private static final int LOADER_ID_UNIFIED = 0; + private static final int LOADER_ID_USER_IDS = 1; + + private UserIdsAdapter mUserIdsAdapter; + + private Uri mDataUri; + + ProviderHelper mProviderHelper; + + /** + * Creates new instance of this fragment + */ + public static ViewKeyFragment newInstance(Uri dataUri) { + ViewKeyFragment frag = new ViewKeyFragment(); + Bundle args = new Bundle(); + args.putParcelable(ARG_DATA_URI, dataUri); + + frag.setArguments(args); + + return frag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { + View root = super.onCreateView(inflater, superContainer, savedInstanceState); + View view = inflater.inflate(R.layout.view_key_fragment, getContainer()); + + mProviderHelper = new ProviderHelper(getActivity()); + + mUserIds = (ListView) view.findViewById(R.id.view_key_user_ids); + + mUserIds.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + showUserIdInfo(position); + } + }); + + return root; + } + + private void showUserIdInfo(final int position) { + if (!mIsSecret) { + final boolean isRevoked = mUserIdsAdapter.getIsRevoked(position); + final int isVerified = mUserIdsAdapter.getIsVerified(position); + + DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { + public void run() { + UserIdInfoDialogFragment dialogFragment = + UserIdInfoDialogFragment.newInstance(isRevoked, isVerified); + + dialogFragment.show(getActivity().getSupportFragmentManager(), "userIdInfoDialog"); + } + }); + } + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); + if (dataUri == null) { + Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); + getActivity().finish(); + return; + } + + loadData(dataUri); + } + + + // These are the rows that we will retrieve. + static final String[] UNIFIED_PROJECTION = new String[]{ + KeychainContract.KeyRings._ID, + KeychainContract.KeyRings.MASTER_KEY_ID, + KeychainContract.KeyRings.USER_ID, + KeychainContract.KeyRings.IS_REVOKED, + KeychainContract.KeyRings.EXPIRY, + KeychainContract.KeyRings.VERIFIED, + KeychainContract.KeyRings.HAS_ANY_SECRET, + KeychainContract.KeyRings.FINGERPRINT, + KeychainContract.KeyRings.HAS_ENCRYPT + }; + + static final int INDEX_MASTER_KEY_ID = 1; + static final int INDEX_USER_ID = 2; + static final int INDEX_IS_REVOKED = 3; + static final int INDEX_EXPIRY = 4; + static final int INDEX_VERIFIED = 5; + static final int INDEX_HAS_ANY_SECRET = 6; + static final int INDEX_FINGERPRINT = 7; + static final int INDEX_HAS_ENCRYPT = 8; + + private void loadData(Uri dataUri) { + mDataUri = dataUri; + + Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString()); + + // Prepare the loaders. Either re-connect with an existing ones, + // or start new ones. + getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); + } + + // don't show revoked user ids here, irrelevant for average users + public static final String USER_IDS_WHERE = UserPackets.IS_REVOKED + " = 0"; + + public Loader<Cursor> onCreateLoader(int id, Bundle args) { + setContentShown(false); + + switch (id) { + case LOADER_ID_UNIFIED: { + Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mDataUri); + return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null); + } + case LOADER_ID_USER_IDS: { + Uri baseUri = UserPackets.buildUserIdsUri(mDataUri); + return new CursorLoader(getActivity(), baseUri, + UserIdsAdapter.USER_IDS_PROJECTION, USER_IDS_WHERE, null, null); + } + + default: + return null; + } + } + + public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + /* TODO better error handling? May cause problems when a key is deleted, + * because the notification triggers faster than the activity closes. + */ + // Avoid NullPointerExceptions... + if (data.getCount() == 0) { + return; + } + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + switch (loader.getId()) { + case LOADER_ID_UNIFIED: { + if (data.moveToFirst()) { + + mIsSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0; + boolean hasEncrypt = data.getInt(INDEX_HAS_ENCRYPT) != 0; + boolean isRevoked = data.getInt(INDEX_IS_REVOKED) > 0; + boolean isExpired = !data.isNull(INDEX_EXPIRY) + && new Date(data.getLong(INDEX_EXPIRY) * 1000).before(new Date()); + boolean isVerified = data.getInt(INDEX_VERIFIED) > 0; + + // load user ids after we know if it's a secret key + mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0, false, !mIsSecret, null); + mUserIds.setAdapter(mUserIdsAdapter); + getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this); + + break; + } + } + + case LOADER_ID_USER_IDS: { + mUserIdsAdapter.swapCursor(data); + break; + } + + } + setContentShown(true); + } + + /** + * This is called when the last Cursor provided to onLoadFinished() above is about to be closed. + * We need to make sure we are no longer using it. + */ + public void onLoaderReset(Loader<Cursor> loader) { + switch (loader.getId()) { + case LOADER_ID_USER_IDS: { + mUserIdsAdapter.swapCursor(null); + break; + } + } + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java deleted file mode 100644 index 8101006e1..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> - * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.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.database.Cursor; -import android.graphics.PorterDuff; -import android.net.Uri; -import android.os.Bundle; -import android.support.v4.app.LoaderManager; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ImageView; -import android.widget.ListView; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; -import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; -import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; -import org.sufficientlysecure.keychain.ui.linked.LinkedIdWizard; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; -import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; -import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment; -import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.ui.util.Notify; - -import java.util.Date; - -public class ViewKeyMainFragment extends LoaderFragment implements - LoaderManager.LoaderCallbacks<Cursor> { - - public static final String ARG_DATA_URI = "uri"; - - private View mActionLink, mActionLinkDivider; - private View mActionEdit; - private View mActionEditDivider; - private View mActionEncryptFiles; - private View mActionEncryptText; - private View mActionEncryptTextText; - private View mActionCertify; - private View mActionCertifyText; - private ImageView mActionCertifyImage; - private View mActionUpdate; - - private ListView mUserIds; - - private static final int LOADER_ID_UNIFIED = 0; - private static final int LOADER_ID_USER_IDS = 1; - - // conservative attitude - private boolean mHasEncrypt = true; - - private UserIdsAdapter mUserIdsAdapter; - - private Uri mDataUri; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { - View root = super.onCreateView(inflater, superContainer, savedInstanceState); - View view = inflater.inflate(R.layout.view_key_main_fragment, getContainer()); - - mUserIds = (ListView) view.findViewById(R.id.view_key_user_ids); - mActionLink = view.findViewById(R.id.view_key_action_link); - mActionLinkDivider = view.findViewById(R.id.view_key_action_link); - mActionEdit = view.findViewById(R.id.view_key_action_edit); - mActionEditDivider = view.findViewById(R.id.view_key_action_edit_divider); - mActionEncryptText = view.findViewById(R.id.view_key_action_encrypt_text); - mActionEncryptTextText = view.findViewById(R.id.view_key_action_encrypt_text_text); - mActionEncryptFiles = view.findViewById(R.id.view_key_action_encrypt_files); - mActionCertify = view.findViewById(R.id.view_key_action_certify); - mActionCertifyText = view.findViewById(R.id.view_key_action_certify_text); - mActionCertifyImage = (ImageView) view.findViewById(R.id.view_key_action_certify_image); - // make certify image gray, like action icons - mActionCertifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), - PorterDuff.Mode.SRC_IN); - mActionUpdate = view.findViewById(R.id.view_key_action_update); - - mUserIds.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - showUserIdInfo(position); - } - }); - - return root; - } - - private void showUserIdInfo(final int position) { - final boolean isRevoked = mUserIdsAdapter.getIsRevoked(position); - final int isVerified = mUserIdsAdapter.getIsVerified(position); - - DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { - public void run() { - UserIdInfoDialogFragment dialogFragment = - UserIdInfoDialogFragment.newInstance(isRevoked, isVerified); - - dialogFragment.show(getActivity().getSupportFragmentManager(), "userIdInfoDialog"); - } - }); - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); - if (dataUri == null) { - Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); - getActivity().finish(); - return; - } - - loadData(dataUri); - } - - private void loadData(Uri dataUri) { - mDataUri = dataUri; - - Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString()); - - mActionEncryptFiles.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - encrypt(mDataUri, false); - } - }); - mActionEncryptText.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - encrypt(mDataUri, true); - } - }); - mActionCertify.setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - certify(mDataUri); - } - }); - mActionLink.setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - linkKey(mDataUri); - } - }); - mActionEdit.setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - editKey(mDataUri); - } - }); - mActionUpdate.setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - try { - updateFromKeyserver(mDataUri, new ProviderHelper(getActivity())); - } catch (NotFoundException e) { - Notify.showNotify(getActivity(), R.string.error_key_not_found, Notify.Style.ERROR); - } - } - }); - - mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0); - mUserIds.setAdapter(mUserIdsAdapter); - - // Prepare the loaders. Either re-connect with an existing ones, - // or start new ones. - getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); - getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this); - } - - static final String[] UNIFIED_PROJECTION = new String[]{ - KeyRings._ID, KeyRings.MASTER_KEY_ID, - KeyRings.HAS_ANY_SECRET, KeyRings.IS_REVOKED, KeyRings.EXPIRY, KeyRings.HAS_ENCRYPT - }; - static final int INDEX_UNIFIED_MASTER_KEY_ID = 1; - static final int INDEX_UNIFIED_HAS_ANY_SECRET = 2; - static final int INDEX_UNIFIED_IS_REVOKED = 3; - static final int INDEX_UNIFIED_EXPIRY = 4; - static final int INDEX_UNIFIED_HAS_ENCRYPT = 5; - - public Loader<Cursor> onCreateLoader(int id, Bundle args) { - setContentShown(false); - - switch (id) { - case LOADER_ID_UNIFIED: { - Uri baseUri = KeyRings.buildUnifiedKeyRingUri(mDataUri); - return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null); - } - case LOADER_ID_USER_IDS: { - Uri baseUri = UserPackets.buildUserIdsUri(mDataUri); - return new CursorLoader(getActivity(), baseUri, - UserIdsAdapter.USER_IDS_PROJECTION, null, null, null); - } - - default: - return null; - } - } - - public void onLoadFinished(Loader<Cursor> loader, Cursor data) { - /* TODO better error handling? May cause problems when a key is deleted, - * because the notification triggers faster than the activity closes. - */ - // Avoid NullPointerExceptions... - if (data.getCount() == 0) { - return; - } - // Swap the new cursor in. (The framework will take care of closing the - // old cursor once we return.) - switch (loader.getId()) { - case LOADER_ID_UNIFIED: { - if (data.moveToFirst()) { - if (data.getInt(INDEX_UNIFIED_HAS_ANY_SECRET) != 0) { - // edit button - mActionLink.setVisibility(View.VISIBLE); - mActionLinkDivider.setVisibility(View.VISIBLE); - mActionEdit.setVisibility(View.VISIBLE); - mActionEditDivider.setVisibility(View.VISIBLE); - } else { - // edit button - mActionLink.setVisibility(View.GONE); - mActionLinkDivider.setVisibility(View.GONE); - mActionEdit.setVisibility(View.GONE); - mActionEditDivider.setVisibility(View.GONE); - } - - // If this key is revoked, it cannot be used for anything! - if (data.getInt(INDEX_UNIFIED_IS_REVOKED) != 0) { - mActionEdit.setEnabled(false); - mActionCertify.setEnabled(false); - mActionCertifyText.setEnabled(false); - mActionEncryptText.setEnabled(false); - mActionEncryptTextText.setEnabled(false); - mActionEncryptFiles.setEnabled(false); - } else { - mActionEdit.setEnabled(true); - - Date expiryDate = new Date(data.getLong(INDEX_UNIFIED_EXPIRY) * 1000); - if (!data.isNull(INDEX_UNIFIED_EXPIRY) && expiryDate.before(new Date())) { - mActionCertify.setEnabled(false); - mActionCertifyText.setEnabled(false); - mActionEncryptText.setEnabled(false); - mActionEncryptTextText.setEnabled(false); - mActionEncryptFiles.setEnabled(false); - } else { - mActionCertify.setEnabled(true); - mActionCertifyText.setEnabled(true); - mActionEncryptText.setEnabled(true); - mActionEncryptTextText.setEnabled(true); - mActionEncryptFiles.setEnabled(true); - } - } - - mHasEncrypt = data.getInt(INDEX_UNIFIED_HAS_ENCRYPT) != 0; - - break; - } - } - - case LOADER_ID_USER_IDS: - mUserIdsAdapter.swapCursor(data); - break; - - } - setContentShown(true); - } - - /** - * This is called when the last Cursor provided to onLoadFinished() above is about to be closed. - * We need to make sure we are no longer using it. - */ - public void onLoaderReset(Loader<Cursor> loader) { - switch (loader.getId()) { - case LOADER_ID_USER_IDS: - mUserIdsAdapter.swapCursor(null); - break; - } - } - - private void encrypt(Uri dataUri, boolean text) { - // If there is no encryption key, don't bother. - if (!mHasEncrypt) { - Notify.showNotify(getActivity(), R.string.error_no_encrypt_subkey, Notify.Style.ERROR); - return; - } - try { - long keyId = new ProviderHelper(getActivity()) - .getCachedPublicKeyRing(dataUri) - .extractOrGetMasterKeyId(); - long[] encryptionKeyIds = new long[]{keyId}; - Intent intent; - if (text) { - intent = new Intent(getActivity(), EncryptTextActivity.class); - intent.setAction(EncryptTextActivity.ACTION_ENCRYPT_TEXT); - intent.putExtra(EncryptTextActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds); - } else { - intent = new Intent(getActivity(), EncryptFilesActivity.class); - intent.setAction(EncryptFilesActivity.ACTION_ENCRYPT_DATA); - intent.putExtra(EncryptFilesActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds); - } - // used instead of startActivity set actionbar based on callingPackage - startActivityForResult(intent, 0); - } catch (PgpKeyNotFoundException e) { - Log.e(Constants.TAG, "key not found!", e); - } - } - - private void updateFromKeyserver(Uri dataUri, ProviderHelper providerHelper) - throws ProviderHelper.NotFoundException { - byte[] blob = (byte[]) providerHelper.getGenericData( - KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri), - KeychainContract.Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB); - String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob); - - Intent queryIntent = new Intent(getActivity(), ImportKeysActivity.class); - queryIntent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT); - queryIntent.putExtra(ImportKeysActivity.EXTRA_FINGERPRINT, fingerprint); - - startActivityForResult(queryIntent, 0); - } - - private void certify(Uri dataUri) { - long keyId = 0; - try { - keyId = new ProviderHelper(getActivity()) - .getCachedPublicKeyRing(dataUri) - .extractOrGetMasterKeyId(); - } catch (PgpKeyNotFoundException e) { - Log.e(Constants.TAG, "key not found!", e); - } - Intent certifyIntent = new Intent(getActivity(), CertifyKeyActivity.class); - certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[]{keyId}); - startActivityForResult(certifyIntent, 0); - } - - private void editKey(Uri dataUri) { - Intent editIntent = new Intent(getActivity(), EditKeyActivity.class); - editIntent.setData(KeychainContract.KeyRingData.buildSecretKeyRingUri(dataUri)); - startActivityForResult(editIntent, 0); - } - - private void linkKey(Uri dataUri) { - Intent editIntent = new Intent(getActivity(), LinkedIdWizard.class); - editIntent.setData(KeyRings.buildUnifiedKeyRingUri(dataUri)); - startActivityForResult(editIntent, 0); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java new file mode 100644 index 000000000..25edc7a02 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java @@ -0,0 +1,460 @@ +/* + * Copyright (C) 2014 Tim Bray <tbray@textuality.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.app.ProgressDialog; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Typeface; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.method.LinkMovementMethod; +import android.text.style.ClickableSpan; +import android.text.style.StyleSpan; +import android.text.style.URLSpan; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TableLayout; +import android.widget.TableRow; +import android.widget.TextView; + +import com.textuality.keybase.lib.KeybaseException; +import com.textuality.keybase.lib.Proof; +import com.textuality.keybase.lib.User; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.Log; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Hashtable; +import java.util.List; + +public class ViewKeyTrustFragment extends LoaderFragment implements + LoaderManager.LoaderCallbacks<Cursor> { + + public static final String ARG_DATA_URI = "uri"; + + private View mStartSearch; + private TextView mTrustReadout; + private TextView mReportHeader; + private TableLayout mProofListing; + private LayoutInflater mInflater; + private View mProofVerifyHeader; + private TextView mProofVerifyDetail; + + private static final int LOADER_ID_DATABASE = 1; + + // for retrieving the key we’re working on + private Uri mDataUri; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { + View root = super.onCreateView(inflater, superContainer, savedInstanceState); + View view = inflater.inflate(R.layout.view_key_adv_keybase_fragment, getContainer()); + mInflater = inflater; + + mTrustReadout = (TextView) view.findViewById(R.id.view_key_trust_readout); + mStartSearch = view.findViewById(R.id.view_key_trust_search_cloud); + mStartSearch.setEnabled(false); + mReportHeader = (TextView) view.findViewById(R.id.view_key_trust_cloud_narrative); + mProofListing = (TableLayout) view.findViewById(R.id.view_key_proof_list); + mProofVerifyHeader = view.findViewById(R.id.view_key_proof_verify_header); + mProofVerifyDetail = (TextView) view.findViewById(R.id.view_key_proof_verify_detail); + mReportHeader.setVisibility(View.GONE); + mProofListing.setVisibility(View.GONE); + mProofVerifyHeader.setVisibility(View.GONE); + mProofVerifyDetail.setVisibility(View.GONE); + + return root; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); + if (dataUri == null) { + Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); + getActivity().finish(); + return; + } + mDataUri = dataUri; + + // retrieve the key from the database + getLoaderManager().initLoader(LOADER_ID_DATABASE, null, this); + } + + static final String[] TRUST_PROJECTION = new String[]{ + KeyRings._ID, KeyRings.FINGERPRINT, KeyRings.IS_REVOKED, KeyRings.EXPIRY, + KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED + }; + static final int INDEX_TRUST_FINGERPRINT = 1; + static final int INDEX_TRUST_IS_REVOKED = 2; + static final int INDEX_TRUST_EXPIRY = 3; + static final int INDEX_UNIFIED_HAS_ANY_SECRET = 4; + static final int INDEX_VERIFIED = 5; + + public Loader<Cursor> onCreateLoader(int id, Bundle args) { + setContentShown(false); + + switch (id) { + case LOADER_ID_DATABASE: { + Uri baseUri = KeyRings.buildUnifiedKeyRingUri(mDataUri); + return new CursorLoader(getActivity(), baseUri, TRUST_PROJECTION, null, null, null); + } + // decided to just use an AsyncTask for keybase, but maybe later + default: + return null; + } + } + + public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + /* TODO better error handling? May cause problems when a key is deleted, + * because the notification triggers faster than the activity closes. + */ + // Avoid NullPointerExceptions... + if (data.getCount() == 0) { + return; + } + + boolean nothingSpecial = true; + StringBuilder message = new StringBuilder(); + + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + if (data.moveToFirst()) { + + if (data.getInt(INDEX_UNIFIED_HAS_ANY_SECRET) != 0) { + message.append(getString(R.string.key_trust_it_is_yours)).append("\n"); + nothingSpecial = false; + } else if (data.getInt(INDEX_VERIFIED) != 0) { + message.append(getString(R.string.key_trust_already_verified)).append("\n"); + nothingSpecial = false; + } + + // If this key is revoked, don’t trust it! + if (data.getInt(INDEX_TRUST_IS_REVOKED) != 0) { + message.append(getString(R.string.key_trust_revoked)). + append(getString(R.string.key_trust_old_keys)); + + nothingSpecial = false; + } else { + Date expiryDate = new Date(data.getLong(INDEX_TRUST_EXPIRY) * 1000); + if (!data.isNull(INDEX_TRUST_EXPIRY) && expiryDate.before(new Date())) { + + // if expired, don’t trust it! + message.append(getString(R.string.key_trust_expired)). + append(getString(R.string.key_trust_old_keys)); + + nothingSpecial = false; + } + } + + if (nothingSpecial) { + message.append(getString(R.string.key_trust_maybe)); + } + + final byte[] fp = data.getBlob(INDEX_TRUST_FINGERPRINT); + final String fingerprint = KeyFormattingUtils.convertFingerprintToHex(fp); + if (fingerprint != null) { + + mStartSearch.setEnabled(true); + mStartSearch.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + mStartSearch.setEnabled(false); + new DescribeKey().execute(fingerprint); + } + }); + } + } + + mTrustReadout.setText(message); + setContentShown(true); + } + + /** + * This is called when the last Cursor provided to onLoadFinished() above is about to be closed. + * We need to make sure we are no longer using it. + */ + public void onLoaderReset(Loader<Cursor> loader) { + // no-op in this case I think + } + + class ResultPage { + String mHeader; + final List<CharSequence> mProofs; + + public ResultPage(String header, List<CharSequence> proofs) { + mHeader = header; + mProofs = proofs; + } + } + + // look for evidence from keybase in the background, make tabular version of result + // + private class DescribeKey extends AsyncTask<String, Void, ResultPage> { + + @Override + protected ResultPage doInBackground(String... args) { + String fingerprint = args[0]; + + final ArrayList<CharSequence> proofList = new ArrayList<CharSequence>(); + final Hashtable<Integer, ArrayList<Proof>> proofs = new Hashtable<Integer, ArrayList<Proof>>(); + try { + User keybaseUser = User.findByFingerprint(fingerprint); + for (Proof proof : keybaseUser.getProofs()) { + Integer proofType = proof.getType(); + appendIfOK(proofs, proofType, proof); + } + + // a one-liner in a modern programming language + for (Integer proofType : proofs.keySet()) { + Proof[] x = {}; + Proof[] proofsFor = proofs.get(proofType).toArray(x); + if (proofsFor.length > 0) { + SpannableStringBuilder ssb = new SpannableStringBuilder(); + ssb.append(getProofNarrative(proofType)).append(" "); + + int i = 0; + while (i < proofsFor.length - 1) { + appendProofLinks(ssb, fingerprint, proofsFor[i]); + ssb.append(", "); + i++; + } + appendProofLinks(ssb, fingerprint, proofsFor[i]); + proofList.add(ssb); + } + } + + } catch (KeybaseException ignored) { + } + + return new ResultPage(getString(R.string.key_trust_results_prefix), proofList); + } + + private SpannableStringBuilder appendProofLinks(SpannableStringBuilder ssb, final String fingerprint, final Proof proof) throws KeybaseException { + int startAt = ssb.length(); + String handle = proof.getHandle(); + ssb.append(handle); + ssb.setSpan(new URLSpan(proof.getServiceUrl()), startAt, startAt + handle.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + if (haveProofFor(proof.getType())) { + ssb.append("\u00a0["); + startAt = ssb.length(); + String verify = getString(R.string.keybase_verify); + ssb.append(verify); + ClickableSpan clicker = new ClickableSpan() { + @Override + public void onClick(View view) { + verify(proof, fingerprint); + } + }; + ssb.setSpan(clicker, startAt, startAt + verify.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + ssb.append("]"); + } + return ssb; + } + + @Override + protected void onPostExecute(ResultPage result) { + super.onPostExecute(result); + if (result.mProofs.isEmpty()) { + result.mHeader = getActivity().getString(R.string.key_trust_no_cloud_evidence); + } + + mStartSearch.setVisibility(View.GONE); + mReportHeader.setVisibility(View.VISIBLE); + mProofListing.setVisibility(View.VISIBLE); + mReportHeader.setText(result.mHeader); + + int rowNumber = 1; + for (CharSequence s : result.mProofs) { + TableRow row = (TableRow) mInflater.inflate(R.layout.view_key_adv_keybase_proof, null); + TextView number = (TextView) row.findViewById(R.id.proof_number); + TextView text = (TextView) row.findViewById(R.id.proof_text); + number.setText(Integer.toString(rowNumber++) + ". "); + text.setText(s); + text.setMovementMethod(LinkMovementMethod.getInstance()); + mProofListing.addView(row); + } + + // mSearchReport.loadDataWithBaseURL("file:///android_res/drawable/", s, "text/html", "UTF-8", null); + } + } + + private String getProofNarrative(int proofType) { + int stringIndex; + switch (proofType) { + case Proof.PROOF_TYPE_TWITTER: stringIndex = R.string.keybase_narrative_twitter; break; + case Proof.PROOF_TYPE_GITHUB: stringIndex = R.string.keybase_narrative_github; break; + case Proof.PROOF_TYPE_DNS: stringIndex = R.string.keybase_narrative_dns; break; + case Proof.PROOF_TYPE_WEB_SITE: stringIndex = R.string.keybase_narrative_web_site; break; + case Proof.PROOF_TYPE_HACKERNEWS: stringIndex = R.string.keybase_narrative_hackernews; break; + case Proof.PROOF_TYPE_COINBASE: stringIndex = R.string.keybase_narrative_coinbase; break; + case Proof.PROOF_TYPE_REDDIT: stringIndex = R.string.keybase_narrative_reddit; break; + default: stringIndex = R.string.keybase_narrative_unknown; + } + return getActivity().getString(stringIndex); + } + + private void appendIfOK(Hashtable<Integer, ArrayList<Proof>> table, Integer proofType, Proof proof) throws KeybaseException { + ArrayList<Proof> list = table.get(proofType); + if (list == null) { + list = new ArrayList<Proof>(); + table.put(proofType, list); + } + list.add(proof); + } + + // which proofs do we have working verifiers for? + private boolean haveProofFor(int proofType) { + switch (proofType) { + case Proof.PROOF_TYPE_TWITTER: return true; + case Proof.PROOF_TYPE_GITHUB: return true; + case Proof.PROOF_TYPE_DNS: return true; + case Proof.PROOF_TYPE_WEB_SITE: return true; + case Proof.PROOF_TYPE_HACKERNEWS: return true; + case Proof.PROOF_TYPE_COINBASE: return true; + case Proof.PROOF_TYPE_REDDIT: return true; + default: return false; + } + } + + private void verify(final Proof proof, final String fingerprint) { + Intent intent = new Intent(getActivity(), KeychainIntentService.class); + Bundle data = new Bundle(); + intent.setAction(KeychainIntentService.ACTION_VERIFY_KEYBASE_PROOF); + + data.putString(KeychainIntentService.KEYBASE_PROOF, proof.toString()); + data.putString(KeychainIntentService.KEYBASE_REQUIRED_FINGERPRINT, fingerprint); + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + mProofVerifyDetail.setVisibility(View.GONE); + + // Create a new Messenger for the communication back after proof work is done + // + KeychainIntentServiceHandler handler = new KeychainIntentServiceHandler(getActivity(), + getString(R.string.progress_verifying_signature), ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + Bundle returnData = message.getData(); + String msg = returnData.getString(KeychainIntentServiceHandler.DATA_MESSAGE); + SpannableStringBuilder ssb = new SpannableStringBuilder(); + + if ((msg != null) && msg.equals("OK")) { + + //yay + String proofUrl = returnData.getString(KeychainIntentServiceHandler.KEYBASE_PROOF_URL); + String presenceUrl = returnData.getString(KeychainIntentServiceHandler.KEYBASE_PRESENCE_URL); + String presenceLabel = returnData.getString(KeychainIntentServiceHandler.KEYBASE_PRESENCE_LABEL); + + String proofLabel; + switch (proof.getType()) { + case Proof.PROOF_TYPE_TWITTER: + proofLabel = getString(R.string.keybase_twitter_proof); + break; + case Proof.PROOF_TYPE_DNS: + proofLabel = getString(R.string.keybase_dns_proof); + break; + case Proof.PROOF_TYPE_WEB_SITE: + proofLabel = getString(R.string.keybase_web_site_proof); + break; + case Proof.PROOF_TYPE_GITHUB: + proofLabel = getString(R.string.keybase_github_proof); + break; + case Proof.PROOF_TYPE_REDDIT: + proofLabel = getString(R.string.keybase_reddit_proof); + break; + default: + proofLabel = getString(R.string.keybase_a_post); + break; + } + + ssb.append(getString(R.string.keybase_proof_succeeded)); + StyleSpan bold = new StyleSpan(Typeface.BOLD); + ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + ssb.append("\n\n"); + int length = ssb.length(); + ssb.append(proofLabel); + if (proofUrl != null) { + URLSpan postLink = new URLSpan(proofUrl); + ssb.setSpan(postLink, length, length + proofLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + if (Proof.PROOF_TYPE_DNS == proof.getType()) { + ssb.append(" ").append(getString(R.string.keybase_for_the_domain)).append(" "); + } else { + ssb.append(" ").append(getString(R.string.keybase_fetched_from)).append(" "); + } + length = ssb.length(); + URLSpan presenceLink = new URLSpan(presenceUrl); + ssb.append(presenceLabel); + ssb.setSpan(presenceLink, length, length + presenceLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + if (Proof.PROOF_TYPE_REDDIT == proof.getType()) { + ssb.append(", "). + append(getString(R.string.keybase_reddit_attribution)). + append(" “").append(proof.getHandle()).append("”, "); + } + ssb.append(" ").append(getString(R.string.keybase_contained_signature)); + } else { + // verification failed! + msg = returnData.getString(KeychainIntentServiceHandler.DATA_ERROR); + ssb.append(getString(R.string.keybase_proof_failure)); + if (msg == null) { + msg = getString(R.string.keybase_unknown_proof_failure); + } + StyleSpan bold = new StyleSpan(Typeface.BOLD); + ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + ssb.append("\n\n").append(msg); + } + mProofVerifyHeader.setVisibility(View.VISIBLE); + mProofVerifyDetail.setVisibility(View.VISIBLE); + mProofVerifyDetail.setMovementMethod(LinkMovementMethod.getInstance()); + mProofVerifyDetail.setText(ssb); + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(handler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + handler.showProgressDialog(getActivity()); + + // start service with intent + getActivity().startService(intent); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java index 6c81e9193..6ba9e26ad 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java @@ -95,8 +95,8 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> { * @see org.sufficientlysecure.keychain.operations.ImportExportOperation */ public ArrayList<ImportKeysListEntry> getSelectedEntries() { - ArrayList<ImportKeysListEntry> result = new ArrayList<ImportKeysListEntry>(); - ArrayList<ImportKeysListEntry> secrets = new ArrayList<ImportKeysListEntry>(); + ArrayList<ImportKeysListEntry> result = new ArrayList<>(); + ArrayList<ImportKeysListEntry> secrets = new ArrayList<>(); if (mData == null) { return result; } @@ -175,9 +175,9 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> { } if (entry.isRevoked()) { - KeyFormattingUtils.setStatusImage(getContext(), holder.status, null, KeyFormattingUtils.STATE_REVOKED, true); + KeyFormattingUtils.setStatusImage(getContext(), holder.status, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray); } else if (entry.isExpired()) { - KeyFormattingUtils.setStatusImage(getContext(), holder.status, null, KeyFormattingUtils.STATE_EXPIRED, true); + KeyFormattingUtils.setStatusImage(getContext(), holder.status, null, KeyFormattingUtils.STATE_EXPIRED, R.color.bg_gray); } if (entry.isRevoked() || entry.isExpired()) { @@ -213,8 +213,24 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> { // destroyLoader view from holder holder.userIdsList.removeAllViews(); + // we want conventional gpg UserIDs first, then Keybase ”proofs” HashMap<String, HashSet<String>> mergedUserIds = entry.getMergedUserIds(); - for (Map.Entry<String, HashSet<String>> pair : mergedUserIds.entrySet()) { + ArrayList<Map.Entry<String, HashSet<String>>> sortedIds = new ArrayList<Map.Entry<String, HashSet<String>>>(mergedUserIds.entrySet()); + java.util.Collections.sort(sortedIds, new java.util.Comparator<Map.Entry<String, HashSet<String>>>() { + @Override + public int compare(Map.Entry<String, HashSet<String>> entry1, Map.Entry<String, HashSet<String>> entry2) { + + // sort keybase UserIds after non-Keybase + boolean e1IsKeybase = entry1.getKey().contains(":"); + boolean e2IsKeybase = entry2.getKey().contains(":"); + if (e1IsKeybase != e2IsKeybase) { + return (e1IsKeybase) ? 1 : -1; + } + return entry1.getKey().compareTo(entry2.getKey()); + } + }); + + for (Map.Entry<String, HashSet<String>> pair : sortedIds) { String cUserId = pair.getKey(); HashSet<String> cEmails = pair.getValue(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java index 176c3ff5b..4781864dd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java @@ -21,13 +21,13 @@ import android.content.Context; import android.support.v4.content.AsyncTaskLoader; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.keyimport.CloudSearch; +import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; 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.Preferences; -import org.sufficientlysecure.keychain.keyimport.CloudSearch; -import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Preferences; import java.util.ArrayList; @@ -39,7 +39,7 @@ public class ImportKeysListCloudLoader Preferences.CloudSearchPrefs mCloudPrefs; String mServerQuery; - private ArrayList<ImportKeysListEntry> mEntryList = new ArrayList<ImportKeysListEntry>(); + private ArrayList<ImportKeysListEntry> mEntryList = new ArrayList<>(); private AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper; public ImportKeysListCloudLoader(Context context, String serverQuery, Preferences.CloudSearchPrefs cloudPrefs) { @@ -51,7 +51,7 @@ public class ImportKeysListCloudLoader @Override public AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> loadInBackground() { - mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, null); + mEntryListWrapper = new AsyncTaskResultWrapper<>(mEntryList, null); if (mServerQuery == null) { Log.e(Constants.TAG, "mServerQuery is null!"); @@ -119,7 +119,7 @@ public class ImportKeysListCloudLoader mEntryList.addAll(searchResult); } GetKeyResult getKeyResult = new GetKeyResult(GetKeyResult.RESULT_OK, null); - mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, getKeyResult); + mEntryListWrapper = new AsyncTaskResultWrapper<>(mEntryList, getKeyResult); } catch (Keyserver.CloudSearchFailureException e) { // convert exception to result parcel int error = GetKeyResult.RESULT_ERROR; @@ -140,7 +140,7 @@ public class ImportKeysListCloudLoader OperationResult.OperationLog log = new OperationResult.OperationLog(); log.add(logType, 0); GetKeyResult getKeyResult = new GetKeyResult(error, log); - mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, getKeyResult); + mEntryListWrapper = new AsyncTaskResultWrapper<>(mEntryList, getKeyResult); } } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java index cecad2716..5447c5f96 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java @@ -24,9 +24,9 @@ import android.support.v4.util.LongSparseArray; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; -import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.operations.results.GetKeyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; +import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing.IteratorWithIOThrow; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; @@ -35,28 +35,15 @@ import org.sufficientlysecure.keychain.util.PositionAwareInputStream; import java.io.BufferedInputStream; import java.io.IOException; import java.util.ArrayList; -import java.util.Iterator; public class ImportKeysListLoader extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> { - public static class NonPgpPartException extends Exception { - private int mCount; - - public NonPgpPartException(int count) { - this.mCount = count; - } - - public int getCount() { - return mCount; - } - } - final Context mContext; final InputData mInputData; - ArrayList<ImportKeysListEntry> mData = new ArrayList<ImportKeysListEntry>(); - LongSparseArray<ParcelableKeyRing> mParcelableRings = new LongSparseArray<ParcelableKeyRing>(); + ArrayList<ImportKeysListEntry> mData = new ArrayList<>(); + LongSparseArray<ParcelableKeyRing> mParcelableRings = new LongSparseArray<>(); AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper; public ImportKeysListLoader(Context context, InputData inputData) { @@ -73,7 +60,7 @@ public class ImportKeysListLoader } GetKeyResult getKeyResult = new GetKeyResult(GetKeyResult.RESULT_OK, null); - mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, getKeyResult); + mEntryListWrapper = new AsyncTaskResultWrapper<>(mData, getKeyResult); if (mInputData == null) { Log.e(Constants.TAG, "Input data is null!"); @@ -140,7 +127,7 @@ public class ImportKeysListLoader OperationResult.OperationLog log = new OperationResult.OperationLog(); log.add(OperationResult.LogType.MSG_GET_NO_VALID_KEYS, 0); GetKeyResult getKeyResult = new GetKeyResult(GetKeyResult.RESULT_ERROR_NO_VALID_KEYS, log); - mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> + mEntryListWrapper = new AsyncTaskResultWrapper<> (mData, getKeyResult); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java index 80d605fb9..b8fe21941 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java @@ -33,7 +33,7 @@ public class KeyValueSpinnerAdapter extends ArrayAdapter<String> { static <K, V extends Comparable<? super V>> SortedSet<Map.Entry<K, V>> entriesSortedByValues( Map<K, V> map) { - SortedSet<Map.Entry<K, V>> sortedEntries = new TreeSet<Map.Entry<K, V>>( + SortedSet<Map.Entry<K, V>> sortedEntries = new TreeSet<>( new Comparator<Map.Entry<K, V>>() { @Override public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java index 9f29826ef..015775669 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java @@ -21,13 +21,13 @@ import android.content.Context; import android.database.Cursor; import android.os.Parcel; import android.support.v4.util.LongSparseArray; +import android.support.v4.widget.CursorAdapter; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.CompoundButton; -import android.support.v4.widget.CursorAdapter; import android.widget.TextView; import org.sufficientlysecure.keychain.R; @@ -44,7 +44,7 @@ public class MultiUserIdsAdapter extends CursorAdapter { public MultiUserIdsAdapter(Context context, Cursor c, int flags) { super(context, c, flags); mInflater = LayoutInflater.from(context); - mCheckStates = new ArrayList<Boolean>(); + mCheckStates = new ArrayList<>(); } @Override @@ -148,7 +148,7 @@ public class MultiUserIdsAdapter extends CursorAdapter { } public ArrayList<CertifyAction> getSelectedCertifyActions() { - LongSparseArray<CertifyAction> actions = new LongSparseArray<CertifyAction>(); + LongSparseArray<CertifyAction> actions = new LongSparseArray<>(); for (int i = 0; i < mCheckStates.size(); i++) { if (mCheckStates.get(i)) { mCursor.moveToPosition(i); @@ -171,7 +171,7 @@ public class MultiUserIdsAdapter extends CursorAdapter { } } - ArrayList<CertifyAction> result = new ArrayList<CertifyAction>(actions.size()); + ArrayList<CertifyAction> result = new ArrayList<>(actions.size()); for (int i = 0; i < actions.size(); i++) { result.add(actions.valueAt(i)); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java index 330254a8f..963e77fe9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java @@ -27,7 +27,7 @@ import java.util.ArrayList; public class PagerTabStripAdapter extends FragmentPagerAdapter { protected final Activity mActivity; - protected final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>(); + protected final ArrayList<TabInfo> mTabs = new ArrayList<>(); static final class TabInfo { public final Class<?> clss; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java index 60c130969..a836b35df 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java @@ -30,9 +30,9 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.ui.util.Highlighter; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; /** @@ -133,11 +133,11 @@ abstract public class SelectKeyCursorAdapter extends CursorAdapter { boolean enabled; if (cursor.getInt(mIndexIsRevoked) != 0) { h.statusIcon.setVisibility(View.VISIBLE); - KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, null, KeyFormattingUtils.STATE_REVOKED, true); + KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray); enabled = false; } else if (cursor.getInt(mIndexIsExpiry) != 0) { h.statusIcon.setVisibility(View.VISIBLE); - KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, null, KeyFormattingUtils.STATE_EXPIRED, true); + KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, null, KeyFormattingUtils.STATE_EXPIRED, R.color.bg_gray); enabled = false; } else { h.statusIcon.setVisibility(View.GONE); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java index a032e96fc..ff5fbb49a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java @@ -35,11 +35,11 @@ import android.widget.ImageView; import android.widget.TextView; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import java.util.Calendar; import java.util.Date; @@ -160,7 +160,11 @@ public class SubkeysAdapter extends CursorAdapter { cursor.getString(INDEX_KEY_CURVE_OID) )); - if (mSaveKeyringParcel != null && mSaveKeyringParcel.mStripSubKeys.contains(keyId)) { + SubkeyChange change = mSaveKeyringParcel != null + ? mSaveKeyringParcel.getSubkeyChange(keyId) + : null; + + if (change != null && change.mDummyStrip) { algorithmStr.append(", "); final SpannableString boldStripped = new SpannableString( context.getString(R.string.key_stripped) @@ -268,12 +272,12 @@ public class SubkeysAdapter extends CursorAdapter { PorterDuff.Mode.SRC_IN); if (isRevoked) { - vStatus.setImageResource(R.drawable.status_signature_revoked_cutout); + vStatus.setImageResource(R.drawable.status_signature_revoked_cutout_24px); vStatus.setColorFilter( mContext.getResources().getColor(R.color.bg_gray), PorterDuff.Mode.SRC_IN); } else if (isExpired) { - vStatus.setImageResource(R.drawable.status_signature_expired_cutout); + vStatus.setImageResource(R.drawable.status_signature_expired_cutout_24px); vStatus.setColorFilter( mContext.getResources().getColor(R.color.bg_gray), PorterDuff.Mode.SRC_IN); @@ -297,7 +301,7 @@ public class SubkeysAdapter extends CursorAdapter { @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - View view = mInflater.inflate(R.layout.view_key_subkey_item, null); + View view = mInflater.inflate(R.layout.view_key_adv_subkey_item, null); if (mDefaultTextColor == null) { TextView keyId = (TextView) view.findViewById(R.id.subkey_item_key_id); mDefaultTextColor = keyId.getTextColors(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java index 9f5d5341e..d2359a387 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java @@ -31,8 +31,8 @@ import android.widget.TextView; import org.spongycastle.bcpg.sig.KeyFlags; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import java.util.Calendar; import java.util.Date; @@ -68,7 +68,7 @@ public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAd public View getView(final int position, View convertView, ViewGroup parent) { if (convertView == null) { // Not recycled, inflate a new view - convertView = mInflater.inflate(R.layout.view_key_subkey_item, null); + convertView = mInflater.inflate(R.layout.view_key_adv_subkey_item, null); final ViewHolder holder = new ViewHolder(); holder.vKeyId = (TextView) convertView.findViewById(R.id.subkey_item_key_id); holder.vKeyDetails = (TextView) convertView.findViewById(R.id.subkey_item_details); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java index 9ddfa90be..44afed351 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java @@ -33,7 +33,7 @@ public class TabsAdapter extends FragmentStatePagerAdapter implements ActionBar. private final Context mContext; private final ActionBar mActionBar; private final ViewPager mViewPager; - private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>(); + private final ArrayList<TabInfo> mTabs = new ArrayList<>(); static final class TabInfo { public final Class<?> clss; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java index 7b8670f22..73c42b1be 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java @@ -34,6 +34,9 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; +import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; @@ -43,6 +46,7 @@ public class UserAttributesAdapter extends CursorAdapter implements AdapterView. private LayoutInflater mInflater; private final ArrayList<Boolean> mCheckStates; private SaveKeyringParcel mSaveKeyringParcel; + private boolean mShowStatusImages; public static final String[] USER_PACKETS_PROJECTION = new String[]{ UserPackets._ID, @@ -64,12 +68,13 @@ public class UserAttributesAdapter extends CursorAdapter implements AdapterView. private static final int INDEX_IS_REVOKED = 7; public UserAttributesAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes, - SaveKeyringParcel saveKeyringParcel) { + boolean showStatusImages, SaveKeyringParcel saveKeyringParcel) { super(context, c, flags); mInflater = LayoutInflater.from(context); mCheckStates = showCheckBoxes ? new ArrayList<Boolean>() : null; mSaveKeyringParcel = saveKeyringParcel; + mShowStatusImages = showStatusImages; } public UserAttributesAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes) { @@ -161,12 +166,17 @@ public class UserAttributesAdapter extends CursorAdapter implements AdapterView. vVerifiedLayout.setVisibility(View.GONE); } else { vEditImage.setVisibility(View.GONE); - vVerifiedLayout.setVisibility(View.VISIBLE); + + if (mShowStatusImages) { + vVerifiedLayout.setVisibility(View.VISIBLE); + } else { + vVerifiedLayout.setVisibility(View.GONE); + } } if (isRevoked) { // set revocation icon (can this even be primary?) - KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_REVOKED, true); + KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray); // disable revoked user ids vName.setEnabled(false); @@ -188,13 +198,13 @@ public class UserAttributesAdapter extends CursorAdapter implements AdapterView. int isVerified = cursor.getInt(INDEX_VERIFIED); switch (isVerified) { case Certs.VERIFIED_SECRET: - KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_VERIFIED, false); + KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_VERIFIED, KeyFormattingUtils.DEFAULT_COLOR); break; case Certs.VERIFIED_SELF: - KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_UNVERIFIED, false); + KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR); break; default: - KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_INVALID, false); + KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_INVALID, KeyFormattingUtils.DEFAULT_COLOR); break; } } @@ -225,7 +235,7 @@ public class UserAttributesAdapter extends CursorAdapter implements AdapterView. } public ArrayList<String> getSelectedUserIds() { - ArrayList<String> result = new ArrayList<String>(); + ArrayList<String> result = new ArrayList<>(); for (int i = 0; i < mCheckStates.size(); i++) { if (mCheckStates.get(i)) { mCursor.moveToPosition(i); @@ -267,7 +277,7 @@ public class UserAttributesAdapter extends CursorAdapter implements AdapterView. @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - View view = mInflater.inflate(R.layout.view_key_user_id_item, null); + View view = mInflater.inflate(R.layout.view_key_adv_user_id_item, null); // only need to do this once ever, since mShowCheckBoxes is final view.findViewById(R.id.user_id_item_check_box).setVisibility(mCheckStates != null ? View.VISIBLE : View.GONE); return view; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAddedAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAddedAdapter.java index 4c0e7a492..01218a4e4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAddedAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAddedAdapter.java @@ -64,7 +64,7 @@ public class UserIdsAddedAdapter extends ArrayAdapter<String> { public View getView(final int position, View convertView, ViewGroup parent) { if (convertView == null) { // Not recycled, inflate a new view - convertView = mInflater.inflate(R.layout.view_key_user_id_item, null); + convertView = mInflater.inflate(R.layout.view_key_adv_user_id_item, null); final ViewHolder holder = new ViewHolder(); holder.vAddress = (TextView) convertView.findViewById(R.id.user_id_item_address); holder.vName = (TextView) convertView.findViewById(R.id.user_id_item_name); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java index b4119a5eb..d5376cbdc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java @@ -145,20 +145,20 @@ public class AddSubkeyDialogFragment extends DialogFragment { } { - ArrayList<Choice<Algorithm>> choices = new ArrayList<Choice<Algorithm>>(); - choices.add(new Choice<Algorithm>(Algorithm.DSA, getResources().getString( + ArrayList<Choice<Algorithm>> choices = new ArrayList<>(); + choices.add(new Choice<>(Algorithm.DSA, getResources().getString( R.string.dsa))); if (!mWillBeMasterKey) { - choices.add(new Choice<Algorithm>(Algorithm.ELGAMAL, getResources().getString( + choices.add(new Choice<>(Algorithm.ELGAMAL, getResources().getString( R.string.elgamal))); } - choices.add(new Choice<Algorithm>(Algorithm.RSA, getResources().getString( + choices.add(new Choice<>(Algorithm.RSA, getResources().getString( R.string.rsa))); - choices.add(new Choice<Algorithm>(Algorithm.ECDSA, getResources().getString( + choices.add(new Choice<>(Algorithm.ECDSA, getResources().getString( R.string.ecdsa))); - choices.add(new Choice<Algorithm>(Algorithm.ECDH, getResources().getString( + choices.add(new Choice<>(Algorithm.ECDH, getResources().getString( R.string.ecdh))); - ArrayAdapter<Choice<Algorithm>> adapter = new ArrayAdapter<Choice<Algorithm>>(context, + ArrayAdapter<Choice<Algorithm>> adapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item, choices); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); mAlgorithmSpinner.setAdapter(adapter); @@ -172,20 +172,20 @@ public class AddSubkeyDialogFragment extends DialogFragment { } // dynamic ArrayAdapter must be created (instead of ArrayAdapter.getFromResource), because it's content may change - ArrayAdapter<CharSequence> keySizeAdapter = new ArrayAdapter<CharSequence>(context, android.R.layout.simple_spinner_item, + ArrayAdapter<CharSequence> keySizeAdapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item, new ArrayList<CharSequence>(Arrays.asList(getResources().getStringArray(R.array.rsa_key_size_spinner_values)))); keySizeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); mKeySizeSpinner.setAdapter(keySizeAdapter); mKeySizeSpinner.setSelection(1); // Default to 4096 for the key length { - ArrayList<Choice<Curve>> choices = new ArrayList<Choice<Curve>>(); + ArrayList<Choice<Curve>> choices = new ArrayList<>(); - choices.add(new Choice<Curve>(Curve.NIST_P256, getResources().getString( + choices.add(new Choice<>(Curve.NIST_P256, getResources().getString( R.string.key_curve_nist_p256))); - choices.add(new Choice<Curve>(Curve.NIST_P384, getResources().getString( + choices.add(new Choice<>(Curve.NIST_P384, getResources().getString( R.string.key_curve_nist_p384))); - choices.add(new Choice<Curve>(Curve.NIST_P521, getResources().getString( + choices.add(new Choice<>(Curve.NIST_P521, getResources().getString( R.string.key_curve_nist_p521))); /* @see SaveKeyringParcel @@ -197,7 +197,7 @@ public class AddSubkeyDialogFragment extends DialogFragment { R.string.key_curve_bp_p512))); */ - ArrayAdapter<Choice<Curve>> adapter = new ArrayAdapter<Choice<Curve>>(context, + ArrayAdapter<Choice<Curve>> adapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item, choices); mCurveSpinner.setAdapter(adapter); // make NIST P-256 the default diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java index 3eef04aa7..094c4d8a9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java @@ -45,8 +45,8 @@ import android.widget.TextView.OnEditorActionListener; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.util.ContactHelper; import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.util.ContactHelper; import org.sufficientlysecure.keychain.util.Log; import java.util.regex.Matcher; @@ -85,7 +85,7 @@ public class AddUserIdDialogFragment extends DialogFragment implements OnEditorA mMessenger = getArguments().getParcelable(ARG_MESSENGER); String predefinedName = getArguments().getString(ARG_NAME); - ArrayAdapter<String> autoCompleteEmailAdapter = new ArrayAdapter<String> + ArrayAdapter<String> autoCompleteEmailAdapter = new ArrayAdapter<> (getActivity(), android.R.layout.simple_spinner_dropdown_item, ContactHelper.getPossibleUserEmails(getActivity()) ); @@ -150,7 +150,7 @@ public class AddUserIdDialogFragment extends DialogFragment implements OnEditorA mName.setThreshold(1); // Start working from first character mName.setAdapter( - new ArrayAdapter<String> + new ArrayAdapter<> (getActivity(), android.R.layout.simple_spinner_dropdown_item, ContactHelper.getPossibleUserNames(getActivity()) ) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AdvancedAppSettingsDialogFragment.java index 19cf27259..d2fa37cf7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AdvancedAppSettingsDialogFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2015 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 @@ -25,43 +25,48 @@ import android.support.v4.app.FragmentActivity; import org.sufficientlysecure.keychain.R; -public class BadImportKeyDialogFragment extends DialogFragment { - private static final String ARG_BAD_IMPORT = "bad_import"; +public class AdvancedAppSettingsDialogFragment extends DialogFragment { + private static final String ARG_PACKAGE_NAME = "package_name"; + private static final String ARG_SIGNATURE = "signature"; /** - * Creates a new instance of this Bad Import Key DialogFragment - * - * @param bad - * @return + * Creates new instance of this fragment */ - public static BadImportKeyDialogFragment newInstance(int bad) { - BadImportKeyDialogFragment frag = new BadImportKeyDialogFragment(); + public static AdvancedAppSettingsDialogFragment newInstance(String packageName, String digest) { + AdvancedAppSettingsDialogFragment frag = new AdvancedAppSettingsDialogFragment(); Bundle args = new Bundle(); + args.putString(ARG_PACKAGE_NAME, packageName); + args.putString(ARG_SIGNATURE, digest); - args.putInt(ARG_BAD_IMPORT, bad); frag.setArguments(args); - return frag; } + /** + * Creates dialog + */ @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final FragmentActivity activity = getActivity(); - final int badImport = getArguments().getInt(ARG_BAD_IMPORT); CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity); - alert.setIcon(R.drawable.ic_dialog_alert_holo_light); - alert.setTitle(R.string.warning); - alert.setMessage(activity.getResources() - .getQuantityString(R.plurals.bad_keys_encountered, badImport, badImport)); - alert.setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - }); + + alert.setTitle(R.string.api_settings_advanced); alert.setCancelable(true); + alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + dismiss(); + } + }); + + String packageName = getArguments().getString(ARG_PACKAGE_NAME); + String signature = getArguments().getString(ARG_SIGNATURE); + + alert.setMessage(getString(R.string.api_settings_package_name) + ": " + packageName + "\n\n" + + getString(R.string.api_settings_package_signature) + ": " + signature); + return alert.show(); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java index aa27e8391..d405b1dda 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java @@ -26,13 +26,13 @@ public class CustomAlertDialogBuilder extends AlertDialog.Builder { int dividerId = dialog.getContext().getResources().getIdentifier("android:id/titleDivider", null, null); View divider = dialog.findViewById(dividerId); if (divider != null) { - divider.setBackgroundColor(dialog.getContext().getResources().getColor(R.color.emphasis)); + divider.setBackgroundColor(dialog.getContext().getResources().getColor(R.color.header_text)); } int textViewId = dialog.getContext().getResources().getIdentifier("android:id/alertTitle", null, null); TextView tv = (TextView) dialog.findViewById(textViewId); if (tv != null) { - tv.setTextColor(dialog.getContext().getResources().getColor(R.color.emphasis)); + tv.setTextColor(dialog.getContext().getResources().getColor(R.color.header_text)); } return dialog; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java index 42e21cd57..879e3f6da 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java @@ -18,6 +18,7 @@ package org.sufficientlysecure.keychain.ui.dialog; import android.app.Dialog; +import android.content.ContentResolver; import android.content.DialogInterface; import android.net.Uri; import android.os.Build; @@ -30,6 +31,8 @@ import android.widget.Toast; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.util.FileHelper; +import java.io.File; + public class DeleteFileDialogFragment extends DialogFragment { private static final String ARG_DELETE_URI = "delete_uri"; @@ -69,21 +72,38 @@ public class DeleteFileDialogFragment extends DialogFragment { @Override public void onClick(DialogInterface dialog, int id) { dismiss(); + String scheme = deleteUri.getScheme(); - // We can not securely delete Uris, so just use usual delete on them - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - if (DocumentsContract.deleteDocument(getActivity().getContentResolver(), deleteUri)) { + if(scheme.equals(ContentResolver.SCHEME_FILE)) { + if(new File(deleteUri.getPath()).delete()) { Toast.makeText(getActivity(), R.string.file_delete_successful, Toast.LENGTH_SHORT).show(); return; } } + else if(scheme.equals(ContentResolver.SCHEME_CONTENT)) { + // We can not securely delete Uris, so just use usual delete on them + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + if (DocumentsContract.deleteDocument(getActivity().getContentResolver(), deleteUri)) { + Toast.makeText(getActivity(), R.string.file_delete_successful, Toast.LENGTH_SHORT).show(); + return; + } + } - if (getActivity().getContentResolver().delete(deleteUri, null, null) > 0) { - Toast.makeText(getActivity(), R.string.file_delete_successful, Toast.LENGTH_SHORT).show(); - return; + if (getActivity().getContentResolver().delete(deleteUri, null, null) > 0) { + Toast.makeText(getActivity(), R.string.file_delete_successful, Toast.LENGTH_SHORT).show(); + return; + } + + // some Uri's a ContentResolver fails to delete is handled by the java.io.File's delete + // via the path of the Uri + if(new File(deleteUri.getPath()).delete()) { + Toast.makeText(getActivity(), R.string.file_delete_successful, Toast.LENGTH_SHORT).show(); + return; + } } - Toast.makeText(getActivity(), getActivity().getString(R.string.error_file_delete_failed, deleteFilename), Toast.LENGTH_SHORT).show(); + Toast.makeText(getActivity(), getActivity().getString(R.string.error_file_delete_failed, + deleteFilename), Toast.LENGTH_SHORT).show(); // Note: We can't delete every file... // If possible we should find out if deletion is possible before even showing the option to do so. diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java index 5b96ea231..802f0c11b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java @@ -33,6 +33,7 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; @@ -100,14 +101,20 @@ public class DeleteKeyDialogFragment extends DialogFragment { ProviderHelper.FIELD_TYPE_INTEGER } ); - String userId = (String) data.get(KeyRings.USER_ID); + String name; + String[] mainUserId = KeyRing.splitUserId((String) data.get(KeyRings.USER_ID)); + if (mainUserId[0] != null) { + name = mainUserId[0]; + } else { + name = getString(R.string.user_id_no_name); + } hasSecret = ((Long) data.get(KeyRings.HAS_ANY_SECRET)) == 1; // Set message depending on which key it is. mMainMessage.setText(getString( hasSecret ? R.string.secret_key_deletion_confirmation : R.string.public_key_deletetion_confirmation, - userId + name )); } catch (ProviderHelper.NotFoundException e) { dismiss(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java index 1cc800b2b..7ac85781f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java @@ -37,9 +37,9 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.FileHelper; import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.ui.util.Notify; import java.io.File; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java deleted file mode 100644 index 961f92f03..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2013-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.annotation.TargetApi; -import android.app.Dialog; -import android.content.DialogInterface; -import android.content.Intent; -import android.nfc.NfcAdapter; -import android.os.Build; -import android.os.Bundle; -import android.provider.Settings; -import android.support.v4.app.DialogFragment; -import android.support.v4.app.FragmentActivity; - -import org.sufficientlysecure.htmltextview.HtmlTextView; -import org.sufficientlysecure.keychain.R; - -@TargetApi(Build.VERSION_CODES.JELLY_BEAN) -public class ShareNfcDialogFragment extends DialogFragment { - - /** - * Creates new instance of this fragment - */ - public static ShareNfcDialogFragment newInstance() { - ShareNfcDialogFragment frag = new ShareNfcDialogFragment(); - - return frag; - } - - /** - * Creates dialog - */ - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final FragmentActivity activity = getActivity(); - - CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity); - - alert.setTitle(R.string.share_nfc_dialog); - alert.setCancelable(true); - - alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - dismiss(); - } - }); - - HtmlTextView textView = new HtmlTextView(getActivity()); - textView.setPadding(8, 8, 8, 8); - alert.setView(textView); - - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { - textView.setText(getString(R.string.error) + ": " - + getString(R.string.error_jelly_bean_needed)); - } else { - // check if NFC Adapter is available - NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); - if (nfcAdapter == null) { - textView.setText(getString(R.string.error) + ": " - + getString(R.string.error_nfc_needed)); - } else { - // nfc works... - textView.setHtmlFromRawResource(getActivity(), R.raw.nfc_beam_share, true); - - alert.setNegativeButton(R.string.menu_beam_preferences, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - Intent intentSettings = new Intent( - Settings.ACTION_NFCSHARING_SETTINGS); - startActivity(intentSettings); - } - } - ); - } - } - - return alert.show(); - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ActionBarHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ActionBarHelper.java deleted file mode 100644 index edd12ec73..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ActionBarHelper.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2013-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.support.v7.app.ActionBar; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.TextView; - -import org.sufficientlysecure.keychain.R; - -public class ActionBarHelper { - - /** - * Sets custom view on ActionBar for Done/Cancel activities - * - * @param actionBar - * @param firstText - * @param firstDrawableId - * @param firstOnClickListener - * @param secondText - * @param secondDrawableId - * @param secondOnClickListener - */ - public static void setTwoButtonView(ActionBar actionBar, - int firstText, int firstDrawableId, OnClickListener firstOnClickListener, - int secondText, int secondDrawableId, OnClickListener secondOnClickListener) { - - // Inflate the custom action bar view - final LayoutInflater inflater = (LayoutInflater) actionBar.getThemedContext() - .getSystemService(Activity.LAYOUT_INFLATER_SERVICE); - final View customActionBarView = inflater.inflate( - R.layout.actionbar_custom_view_done_cancel, null); - - TextView firstTextView = ((TextView) customActionBarView.findViewById(R.id.actionbar_done_text)); - firstTextView.setText(firstText); - firstTextView.setCompoundDrawablesWithIntrinsicBounds(firstDrawableId, 0, 0, 0); - customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener( - firstOnClickListener); - TextView secondTextView = ((TextView) customActionBarView.findViewById(R.id.actionbar_cancel_text)); - secondTextView.setText(secondText); - secondTextView.setCompoundDrawablesWithIntrinsicBounds(secondDrawableId, 0, 0, 0); - customActionBarView.findViewById(R.id.actionbar_cancel).setOnClickListener( - secondOnClickListener); - - // Show the custom action bar view and hide the normal Home icon and title. - actionBar.setDisplayShowTitleEnabled(false); - actionBar.setDisplayShowHomeEnabled(false); - actionBar.setDisplayShowCustomEnabled(true); - actionBar.setCustomView(customActionBarView, new ActionBar.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); - } - - /** - * Sets custom view on ActionBar for Done activities - * - * @param actionBar - * @param firstText - * @param firstOnClickListener - */ - public static void setOneButtonView(ActionBar actionBar, int firstText, int firstDrawableId, - OnClickListener firstOnClickListener) { - // Inflate a "Done" custom action bar view to serve as the "Up" affordance. - final LayoutInflater inflater = (LayoutInflater) actionBar.getThemedContext() - .getSystemService(Activity.LAYOUT_INFLATER_SERVICE); - final View customActionBarView = inflater - .inflate(R.layout.actionbar_custom_view_done, null); - - TextView firstTextView = ((TextView) customActionBarView.findViewById(R.id.actionbar_done_text)); - firstTextView.setText(firstText); - firstTextView.setCompoundDrawablesWithIntrinsicBounds(firstDrawableId, 0, 0, 0); - customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener( - firstOnClickListener); - - // Show the custom action bar view and hide the normal Home icon and title. - actionBar.setDisplayShowTitleEnabled(false); - actionBar.setDisplayShowHomeEnabled(false); - actionBar.setDisplayShowCustomEnabled(true); - actionBar.setCustomView(customActionBarView); - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/FormattingUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/FormattingUtils.java index 3f84bf490..eb5c3df45 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/FormattingUtils.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/FormattingUtils.java @@ -24,18 +24,12 @@ import android.text.style.StrikethroughSpan; public class FormattingUtils { - public static SpannableStringBuilder strikeOutText(CharSequence text) { - SpannableStringBuilder sb = new SpannableStringBuilder(text); - sb.setSpan(new StrikethroughSpan(), 0, text.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - return sb; - } - public static int dpToPx(Context context, int dp) { - return (int) ((dp * context.getResources().getDisplayMetrics().density) + 0.5); + return (int) ((dp * context.getResources().getDisplayMetrics().density) + 0.5f); } public static int pxToDp(Context context, int px) { - return (int) ((px / context.getResources().getDisplayMetrics().density) + 0.5); + return (int) ((px / context.getResources().getDisplayMetrics().density) + 0.5f); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java index bff7d6b27..38ed88b9c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java @@ -377,6 +377,8 @@ public class KeyFormattingUtils { ((int) digest[2] + 256) % 256}; } + public static final int DEFAULT_COLOR = -1; + public static final int STATE_REVOKED = 1; public static final int STATE_EXPIRED = 2; public static final int STATE_VERIFIED = 3; @@ -393,20 +395,32 @@ public class KeyFormattingUtils { } public static void setStatusImage(Context context, ImageView statusIcon, TextView statusText, int state) { - setStatusImage(context, statusIcon, statusText, state, false); + setStatusImage(context, statusIcon, statusText, state, KeyFormattingUtils.DEFAULT_COLOR, false); + } + + public static void setStatusImage(Context context, ImageView statusIcon, TextView statusText, + int state, int color) { + setStatusImage(context, statusIcon, statusText, state, color, false); } /** * Sets status image based on constant */ public static void setStatusImage(Context context, ImageView statusIcon, TextView statusText, - int state, boolean unobtrusive) { + int state, int color, boolean big) { switch (state) { /** GREEN: everything is good **/ case STATE_VERIFIED: { - statusIcon.setImageDrawable( - context.getResources().getDrawable(R.drawable.status_signature_verified_cutout)); - int color = R.color.android_green_light; + if (big) { + statusIcon.setImageDrawable( + context.getResources().getDrawable(R.drawable.status_signature_verified_cutout_96px)); + } else { + statusIcon.setImageDrawable( + context.getResources().getDrawable(R.drawable.status_signature_verified_cutout_24px)); + } + if (color == KeyFormattingUtils.DEFAULT_COLOR) { + color = R.color.android_green_light; + } statusIcon.setColorFilter(context.getResources().getColor(color), PorterDuff.Mode.SRC_IN); if (statusText != null) { @@ -416,8 +430,10 @@ public class KeyFormattingUtils { } case STATE_ENCRYPTED: { statusIcon.setImageDrawable( - context.getResources().getDrawable(R.drawable.status_lock_closed)); - int color = R.color.android_green_light; + context.getResources().getDrawable(R.drawable.status_lock_closed_24px)); + if (color == KeyFormattingUtils.DEFAULT_COLOR) { + color = R.color.android_green_light; + } statusIcon.setColorFilter(context.getResources().getColor(color), PorterDuff.Mode.SRC_IN); if (statusText != null) { @@ -427,9 +443,16 @@ public class KeyFormattingUtils { } /** ORANGE: mostly bad... **/ case STATE_UNVERIFIED: { - statusIcon.setImageDrawable( - context.getResources().getDrawable(R.drawable.status_signature_unverified_cutout)); - int color = R.color.android_orange_light; + if (big) { + statusIcon.setImageDrawable( + context.getResources().getDrawable(R.drawable.status_signature_unverified_cutout_96px)); + } else { + statusIcon.setImageDrawable( + context.getResources().getDrawable(R.drawable.status_signature_unverified_cutout_24px)); + } + if (color == KeyFormattingUtils.DEFAULT_COLOR) { + color = R.color.android_orange_light; + } statusIcon.setColorFilter(context.getResources().getColor(color), PorterDuff.Mode.SRC_IN); if (statusText != null) { @@ -439,8 +462,10 @@ public class KeyFormattingUtils { } case STATE_UNKNOWN_KEY: { statusIcon.setImageDrawable( - context.getResources().getDrawable(R.drawable.status_signature_unknown_cutout)); - int color = R.color.android_orange_light; + context.getResources().getDrawable(R.drawable.status_signature_unknown_cutout_24px)); + if (color == KeyFormattingUtils.DEFAULT_COLOR) { + color = R.color.android_orange_light; + } statusIcon.setColorFilter(context.getResources().getColor(color), PorterDuff.Mode.SRC_IN); if (statusText != null) { @@ -450,11 +475,15 @@ public class KeyFormattingUtils { } /** RED: really bad... **/ case STATE_REVOKED: { - statusIcon.setImageDrawable( - context.getResources().getDrawable(R.drawable.status_signature_revoked_cutout)); - int color = R.color.android_red_light; - if (unobtrusive) { - color = R.color.bg_gray; + if (big) { + statusIcon.setImageDrawable( + context.getResources().getDrawable(R.drawable.status_signature_revoked_cutout_96px)); + } else { + statusIcon.setImageDrawable( + context.getResources().getDrawable(R.drawable.status_signature_revoked_cutout_24px)); + } + if (color == KeyFormattingUtils.DEFAULT_COLOR) { + color = R.color.android_red_light; } statusIcon.setColorFilter(context.getResources().getColor(color), PorterDuff.Mode.SRC_IN); @@ -464,11 +493,15 @@ public class KeyFormattingUtils { break; } case STATE_EXPIRED: { - statusIcon.setImageDrawable( - context.getResources().getDrawable(R.drawable.status_signature_expired_cutout)); - int color = R.color.android_red_light; - if (unobtrusive) { - color = R.color.bg_gray; + if (big) { + statusIcon.setImageDrawable( + context.getResources().getDrawable(R.drawable.status_signature_expired_cutout_96px)); + } else { + statusIcon.setImageDrawable( + context.getResources().getDrawable(R.drawable.status_signature_expired_cutout_24px)); + } + if (color == KeyFormattingUtils.DEFAULT_COLOR) { + color = R.color.android_red_light; } statusIcon.setColorFilter(context.getResources().getColor(color), PorterDuff.Mode.SRC_IN); @@ -479,8 +512,10 @@ public class KeyFormattingUtils { } case STATE_NOT_ENCRYPTED: { statusIcon.setImageDrawable( - context.getResources().getDrawable(R.drawable.status_lock_open)); - int color = R.color.android_red_light; + context.getResources().getDrawable(R.drawable.status_lock_open_24px)); + if (color == KeyFormattingUtils.DEFAULT_COLOR) { + color = R.color.android_red_light; + } statusIcon.setColorFilter(context.getResources().getColor(color), PorterDuff.Mode.SRC_IN); if (statusText != null) { @@ -490,8 +525,10 @@ public class KeyFormattingUtils { } case STATE_NOT_SIGNED: { statusIcon.setImageDrawable( - context.getResources().getDrawable(R.drawable.status_signature_unknown_cutout)); - int color = R.color.android_red_light; + context.getResources().getDrawable(R.drawable.status_signature_unknown_cutout_24px)); + if (color == KeyFormattingUtils.DEFAULT_COLOR) { + color = R.color.android_red_light; + } statusIcon.setColorFilter(context.getResources().getColor(color), PorterDuff.Mode.SRC_IN); if (statusText != null) { @@ -501,8 +538,10 @@ public class KeyFormattingUtils { } case STATE_INVALID: { statusIcon.setImageDrawable( - context.getResources().getDrawable(R.drawable.status_signature_invalid_cutout)); - int color = R.color.android_red_light; + context.getResources().getDrawable(R.drawable.status_signature_invalid_cutout_24px)); + if (color == KeyFormattingUtils.DEFAULT_COLOR) { + color = R.color.android_red_light; + } statusIcon.setColorFilter(context.getResources().getColor(color), PorterDuff.Mode.SRC_IN); if (statusText != null) { @@ -513,8 +552,10 @@ public class KeyFormattingUtils { /** special **/ case STATE_UNAVAILABLE: { statusIcon.setImageDrawable( - context.getResources().getDrawable(R.drawable.status_signature_invalid_cutout)); - int color = R.color.bg_gray; + context.getResources().getDrawable(R.drawable.status_signature_invalid_cutout_24px)); + if (color == KeyFormattingUtils.DEFAULT_COLOR) { + color = R.color.bg_gray; + } statusIcon.setColorFilter(context.getResources().getColor(color), PorterDuff.Mode.SRC_IN); if (statusText != null) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java index 551ac039d..3bc29edb6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java @@ -20,46 +20,135 @@ package org.sufficientlysecure.keychain.ui.util; import android.app.Activity; import android.content.res.Resources; -import com.github.johnpersano.supertoasts.SuperCardToast; -import com.github.johnpersano.supertoasts.SuperToast; +import com.nispok.snackbar.Snackbar; +import com.nispok.snackbar.Snackbar.SnackbarDuration; +import com.nispok.snackbar.SnackbarManager; +import com.nispok.snackbar.enums.SnackbarType; +import com.nispok.snackbar.listeners.ActionClickListener; +import com.nispok.snackbar.listeners.EventListenerAdapter; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.util.FabContainer; /** - * @author danielhass * Notify wrapper which allows a more easy use of different notification libraries */ public class Notify { public static enum Style {OK, WARN, INFO, ERROR} + public static final int LENGTH_INDEFINITE = 0; + public static final int LENGTH_LONG = 3500; + /** * Shows a simple in-layout notification with the CharSequence given as parameter - * @param activity * @param text Text to show * @param style Notification styling */ - public static void showNotify(Activity activity, CharSequence text, Style style) { + public static void showNotify(final Activity activity, CharSequence text, Style style) { + + Snackbar bar = getSnackbar(activity) + .text(text); + + switch (style) { + case OK: + break; + case WARN: + bar.textColor(activity.getResources().getColor(R.color.android_orange_light)); + break; + case ERROR: + bar.textColor(activity.getResources().getColor(R.color.android_red_light)); + break; + } + + SnackbarManager.show(bar); + + } + + public static Showable createNotify (Activity activity, int resId, int duration, Style style) { + final Snackbar bar = getSnackbar(activity) + .text(resId); + + if (duration == LENGTH_INDEFINITE) { + bar.duration(SnackbarDuration.LENGTH_INDEFINITE); + } else { + bar.duration(duration); + } + + switch (style) { + case OK: + bar.actionColor(activity.getResources().getColor(R.color.android_green_light)); + break; + case WARN: + bar.textColor(activity.getResources().getColor(R.color.android_orange_light)); + break; + case ERROR: + bar.textColor(activity.getResources().getColor(R.color.android_red_light)); + break; + } - SuperCardToast st = new SuperCardToast(activity); - st.setText(text); - st.setDuration(SuperToast.Duration.MEDIUM); - switch (style){ + return new Showable () { + @Override + public void show() { + SnackbarManager.show(bar); + } + }; + } + + public static Showable createNotify(Activity activity, int resId, int duration, Style style, + final ActionListener listener, int resIdAction) { + return createNotify(activity, activity.getString(resId), duration, style, listener, resIdAction); + } + + public static Showable createNotify(Activity activity, String msg, int duration, Style style) { + return createNotify(activity, msg, duration, style, null, 0); + } + + public static Showable createNotify(Activity activity, String msg, int duration, Style style, + final ActionListener listener, int resIdAction) { + + final Snackbar bar = getSnackbar(activity) + .text(msg); + + if (listener != null) { + bar.actionLabel(resIdAction); + bar.actionListener(new ActionClickListener() { + @Override + public void onActionClicked(Snackbar snackbar) { + listener.onAction(); + } + }); + } + + if (duration == LENGTH_INDEFINITE) { + bar.duration(SnackbarDuration.LENGTH_INDEFINITE); + } else { + bar.duration(duration); + } + + switch (style) { case OK: - st.setBackground(SuperToast.Background.GREEN); + bar.actionColor(activity.getResources().getColor(R.color.android_green_light)); break; case WARN: - st.setBackground(SuperToast.Background.ORANGE); + bar.textColor(activity.getResources().getColor(R.color.android_orange_light)); break; case ERROR: - st.setBackground(SuperToast.Background.RED); + bar.textColor(activity.getResources().getColor(R.color.android_red_light)); break; } - st.show(); + + return new Showable () { + @Override + public void show() { + SnackbarManager.show(bar); + } + }; } /** * Shows a simple in-layout notification with the resource text from given id - * @param activity * @param resId ResourceId of notification text * @param style Notification styling * @throws Resources.NotFoundException @@ -67,4 +156,36 @@ public class Notify { public static void showNotify(Activity activity, int resId, Style style) throws Resources.NotFoundException { showNotify(activity, activity.getResources().getText(resId), style); } + + private static Snackbar getSnackbar(final Activity activity) { + Snackbar bar = Snackbar.with(activity) + .type(SnackbarType.MULTI_LINE) + .duration(SnackbarDuration.LENGTH_LONG); + + if (activity instanceof FabContainer) { + bar.eventListener(new EventListenerAdapter() { + @Override + public void onShow(Snackbar snackbar) { + ((FabContainer) activity).fabMoveUp(snackbar.getHeight()); + } + + @Override + public void onDismiss(Snackbar snackbar) { + ((FabContainer) activity).fabRestorePosition(); + } + }); + } + return bar; + } + + public interface Showable { + public void show(); + + } + + public interface ActionListener { + public void onAction(); + + } + }
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java index 36f38045f..b8d4ea7d2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java @@ -47,7 +47,7 @@ public class QrCodeUtils { */ public static Bitmap getQRCodeBitmap(final String input, final int size) { try { - final Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>(); + final Hashtable<EncodeHintType, Object> hints = new Hashtable<>(); hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M); final BitMatrix result = new QRCodeWriter().encode(input, BarcodeFormat.QR_CODE, size, size, hints); @@ -59,7 +59,7 @@ public class QrCodeUtils { for (int y = 0; y < height; y++) { final int offset = y * width; for (int x = 0; x < width; x++) { - pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.WHITE; + pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.TRANSPARENT; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/AspectRatioImageView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/AspectRatioImageView.java new file mode 100644 index 000000000..0df5ba5e8 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/AspectRatioImageView.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2015 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.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.widget.ImageView; + +import org.sufficientlysecure.keychain.R; + +/** + * Maintains an aspect ratio based on either width or height. Disabled by default. + * + * from https://gist.github.com/JakeWharton/2856179 + */ +public class AspectRatioImageView extends ImageView { + // NOTE: These must be kept in sync with the AspectRatioImageView attributes in attrs.xml. + public static final int MEASUREMENT_WIDTH = 0; + public static final int MEASUREMENT_HEIGHT = 1; + + private static final float DEFAULT_ASPECT_RATIO = 1f; + private static final boolean DEFAULT_ASPECT_RATIO_ENABLED = false; + private static final int DEFAULT_DOMINANT_MEASUREMENT = MEASUREMENT_WIDTH; + + private float aspectRatio; + private boolean aspectRatioEnabled; + private int dominantMeasurement; + + public AspectRatioImageView(Context context) { + this(context, null); + } + + public AspectRatioImageView(Context context, AttributeSet attrs) { + super(context, attrs); + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AspectRatioImageView); + aspectRatio = a.getFloat(R.styleable.AspectRatioImageView_aspectRatio, DEFAULT_ASPECT_RATIO); + aspectRatioEnabled = a.getBoolean(R.styleable.AspectRatioImageView_aspectRatioEnabled, + DEFAULT_ASPECT_RATIO_ENABLED); + dominantMeasurement = a.getInt(R.styleable.AspectRatioImageView_dominantMeasurement, + DEFAULT_DOMINANT_MEASUREMENT); + a.recycle(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (!aspectRatioEnabled) return; + + int newWidth; + int newHeight; + switch (dominantMeasurement) { + case MEASUREMENT_WIDTH: + newWidth = getMeasuredWidth(); + newHeight = (int) (newWidth * aspectRatio); + break; + + case MEASUREMENT_HEIGHT: + newHeight = getMeasuredHeight(); + newWidth = (int) (newHeight * aspectRatio); + break; + + default: + throw new IllegalStateException("Unknown measurement with ID " + dominantMeasurement); + } + + setMeasuredDimension(newWidth, newHeight); + } + + /** + * Get the aspect ratio for this image view. + */ + public float getAspectRatio() { + return aspectRatio; + } + + /** + * Set the aspect ratio for this image view. This will update the view instantly. + */ + public void setAspectRatio(float aspectRatio) { + this.aspectRatio = aspectRatio; + if (aspectRatioEnabled) { + requestLayout(); + } + } + + /** + * Get whether or not forcing the aspect ratio is enabled. + */ + public boolean getAspectRatioEnabled() { + return aspectRatioEnabled; + } + + /** + * set whether or not forcing the aspect ratio is enabled. This will re-layout the view. + */ + public void setAspectRatioEnabled(boolean aspectRatioEnabled) { + this.aspectRatioEnabled = aspectRatioEnabled; + requestLayout(); + } + + /** + * Get the dominant measurement for the aspect ratio. + */ + public int getDominantMeasurement() { + return dominantMeasurement; + } + + /** + * Set the dominant measurement for the aspect ratio. + * + * @see #MEASUREMENT_WIDTH + * @see #MEASUREMENT_HEIGHT + */ + public void setDominantMeasurement(int dominantMeasurement) { + if (dominantMeasurement != MEASUREMENT_HEIGHT && dominantMeasurement != MEASUREMENT_WIDTH) { + throw new IllegalArgumentException("Invalid measurement type."); + } + this.dominantMeasurement = dominantMeasurement; + requestLayout(); + } +}
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java index 14f42eb04..6d0e6556f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java @@ -27,9 +27,9 @@ import android.util.AttributeSet; import android.widget.ImageView; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainDatabase; -import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; public class CertifyKeySpinner extends KeySpinner { @@ -86,32 +86,33 @@ public class CertifyKeySpinner extends KeySpinner { super.onLoadFinished(loader, data); if (loader.getId() == LOADER_ID) { + mIndexHasCertify = data.getColumnIndex(KeychainContract.KeyRings.HAS_CERTIFY); + mIndexIsRevoked = data.getColumnIndex(KeychainContract.KeyRings.IS_REVOKED); + mIndexIsExpired = data.getColumnIndex(KeychainContract.KeyRings.IS_EXPIRED); + // If there is only one choice, pick it by default if (mAdapter.getCount() == 2) { // preselect if key can certify - if (data.moveToPosition(1) && !data.isNull(mIndexHasCertify)) { + if (data.moveToPosition(0) && !data.isNull(mIndexHasCertify)) { setSelection(1); } } - mIndexHasCertify = data.getColumnIndex(KeychainContract.KeyRings.HAS_CERTIFY); - mIndexIsRevoked = data.getColumnIndex(KeychainContract.KeyRings.IS_REVOKED); - mIndexIsExpired = data.getColumnIndex(KeychainContract.KeyRings.IS_EXPIRED); } } @Override boolean setStatus(Context context, Cursor cursor, ImageView statusView) { if (cursor.getInt(mIndexIsRevoked) != 0) { - KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_REVOKED, true); + KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray); return false; } if (cursor.getInt(mIndexIsExpired) != 0) { - KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_EXPIRED, true); + KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_EXPIRED, R.color.bg_gray); return false; } // don't invalidate the "None" entry, which is also null! if (cursor.getPosition() != 0 && cursor.isNull(mIndexHasCertify)) { - KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_UNAVAILABLE, true); + KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_UNAVAILABLE, R.color.bg_gray); return false; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java index e03a14989..f05f5f96b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java @@ -28,7 +28,6 @@ import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; -import android.text.SpannableStringBuilder; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; @@ -42,14 +41,13 @@ import com.tokenautocomplete.TokenCompleteTextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.util.ContactHelper; import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; +import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.ContactHelper; import org.sufficientlysecure.keychain.util.Log; import java.util.ArrayList; @@ -165,7 +163,7 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView { setAdapter(new EncryptKeyAdapter(Collections.<EncryptionKey>emptyList())); return; } - ArrayList<EncryptionKey> keys = new ArrayList<EncryptionKey>(); + ArrayList<EncryptionKey> keys = new ArrayList<>(); while (cursor.moveToNext()) { try { EncryptionKey key = new EncryptionKey(cursor); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java index b456b61ab..34e7b639a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java @@ -33,17 +33,16 @@ import org.sufficientlysecure.keychain.R; /** * Class representing a LinearLayout that can fold and hide it's content when pressed * To use just add the following to your xml layout - - <org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - custom:foldedLabel="@string/TEXT_TO_DISPLAY_WHEN_FOLDED" - custom:unFoldedLabel="@string/TEXT_TO_DISPLAY_WHEN_UNFOLDED"> - - <include layout="@layout/ELEMENTS_TO_BE_FOLDED"/> - - </org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout> - + * <p/> + * <org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout + * android:layout_width="wrap_content" + * android:layout_height="wrap_content" + * custom:foldedLabel="@string/TEXT_TO_DISPLAY_WHEN_FOLDED" + * custom:unFoldedLabel="@string/TEXT_TO_DISPLAY_WHEN_UNFOLDED"> + * <p/> + * <include layout="@layout/ELEMENTS_TO_BE_FOLDED"/> + * <p/> + * </org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout> */ public class FoldableLinearLayout extends LinearLayout { @@ -75,6 +74,7 @@ public class FoldableLinearLayout extends LinearLayout { /** * Load given attributes to inner variables, + * * @param context * @param attrs */ @@ -87,8 +87,8 @@ public class FoldableLinearLayout extends LinearLayout { a.recycle(); } // If any attribute isn't found then set a default one - mFoldedLabel = (mFoldedLabel == null) ? context.getString(R.id.none) : mFoldedLabel; - mUnFoldedLabel = (mUnFoldedLabel == null) ? context.getString(R.id.none) : mUnFoldedLabel; + mFoldedLabel = (mFoldedLabel == null) ? context.getString(R.string.none) : mFoldedLabel; + mUnFoldedLabel = (mUnFoldedLabel == null) ? context.getString(R.string.none) : mUnFoldedLabel; } @Override @@ -138,7 +138,7 @@ public class FoldableLinearLayout extends LinearLayout { private void initialiseInnerViews() { mFoldableIcon = (ImageView) mFoldableLayout.findViewById(R.id.foldableIcon); - mFoldableIcon.setImageResource(R.drawable.ic_action_expand); + mFoldableIcon.setImageResource(R.drawable.ic_expand_more_black_24dp); mFoldableTextView = (TextView) mFoldableLayout.findViewById(R.id.foldableText); mFoldableTextView.setText(mFoldedLabel); @@ -151,7 +151,7 @@ public class FoldableLinearLayout extends LinearLayout { public void onClick(View view) { mFolded = !mFolded; if (mFolded) { - mFoldableIcon.setImageResource(R.drawable.ic_action_collapse); + mFoldableIcon.setImageResource(R.drawable.ic_expand_less_black_24dp); mFoldableContainer.setVisibility(View.VISIBLE); AlphaAnimation animation = new AlphaAnimation(0f, 1f); animation.setDuration(mShortAnimationDuration); @@ -159,12 +159,13 @@ public class FoldableLinearLayout extends LinearLayout { mFoldableTextView.setText(mUnFoldedLabel); } else { - mFoldableIcon.setImageResource(R.drawable.ic_action_expand); + mFoldableIcon.setImageResource(R.drawable.ic_expand_more_black_24dp); AlphaAnimation animation = new AlphaAnimation(1f, 0f); animation.setDuration(mShortAnimationDuration); animation.setAnimationListener(new Animation.AnimationListener() { @Override - public void onAnimationStart(Animation animation) { } + public void onAnimationStart(Animation animation) { + } @Override public void onAnimationEnd(Animation animation) { @@ -173,7 +174,8 @@ public class FoldableLinearLayout extends LinearLayout { } @Override - public void onAnimationRepeat(Animation animation) { } + public void onAnimationRepeat(Animation animation) { + } }); mFoldableContainer.startAnimation(animation); mFoldableTextView.setText(mFoldedLabel); @@ -185,6 +187,7 @@ public class FoldableLinearLayout extends LinearLayout { /** * Adds provided child view to foldableContainer View + * * @param child */ @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java index 7ec6ffe95..c8eceea50 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java @@ -24,24 +24,28 @@ import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; import android.support.v4.widget.CursorAdapter; +import android.support.v7.internal.widget.TintSpinner; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ImageView; -import android.widget.Spinner; import android.widget.SpinnerAdapter; import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; -public abstract class KeySpinner extends Spinner implements LoaderManager.LoaderCallbacks<Cursor> { +/** + * Use TintSpinner from AppCompat lib instead of Spinner. Fixes white dropdown icon. + * Related: http://stackoverflow.com/a/27713090 + */ +public abstract class KeySpinner extends TintSpinner implements LoaderManager.LoaderCallbacks<Cursor> { public interface OnKeyChangedListener { public void onKeyChanged(long masterKeyId); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/ListAwareSwipeRefreshLayout.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/ListAwareSwipeRefreshLayout.java deleted file mode 100644 index b3c3eb417..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/ListAwareSwipeRefreshLayout.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2014 Daniel Albert - * - * 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.widget; - -import android.content.Context; -import android.support.v4.widget.NoScrollableSwipeRefreshLayout; -import android.util.AttributeSet; -import android.view.InputDevice; -import android.view.InputDevice.MotionRange; -import android.view.MotionEvent; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.util.Log; - -import se.emilsjolander.stickylistheaders.StickyListHeadersListView; - -public class ListAwareSwipeRefreshLayout extends NoScrollableSwipeRefreshLayout { - - private StickyListHeadersListView mStickyListHeadersListView = null; - private boolean mIsLocked = false; - - /** - * Constructors - */ - public ListAwareSwipeRefreshLayout(Context context) { - super(context); - } - - public ListAwareSwipeRefreshLayout(Context context, AttributeSet attrs) { - super(context, attrs); - } - - /** - * Getters / Setters - */ - public void setStickyListHeadersListView(StickyListHeadersListView stickyListHeadersListView) { - mStickyListHeadersListView = stickyListHeadersListView; - } - - public StickyListHeadersListView getStickyListHeadersListView() { - return mStickyListHeadersListView; - } - - public void setIsLocked(boolean locked) { - mIsLocked = locked; - } - - public boolean getIsLocked() { - return mIsLocked; - } - - @Override - public boolean canChildScrollUp() { - if (mStickyListHeadersListView == null) { - return super.canChildScrollUp(); - } - - return (mIsLocked || ( - mStickyListHeadersListView.getWrappedList().getChildCount() > 0 - && (mStickyListHeadersListView.getWrappedList().getChildAt(0).getTop() < 0 - || mStickyListHeadersListView.getFirstVisiblePosition() > 0) - ) - ); - } - - /** Called on a touch event, this method exempts a small area in the upper right from pull to - * refresh handling. - * - * If the touch event happens somewhere in the upper right corner of the screen, we return false - * to indicate that the event was not handled. This ensures events in that area are always - * handed through to the list scrollbar handle. For all other cases, we pass the message through - * to the pull to refresh handler. - */ - @Override - public boolean onTouchEvent(MotionEvent event) { - // The device may be null. This actually happens - if (event.getDevice() != null) { - // MotionEvent.AXIS_X is api level 12, for some reason, so we use a constant 0 here - float ratioX = event.getX() / event.getDevice().getMotionRange(0).getMax(); - float ratioY = event.getY() / event.getDevice().getMotionRange(1).getMax(); - // if this is the upper right corner, don't handle as pull to refresh event - if (ratioX > 0.85f && ratioY < 0.15f) { - return false; - } - } - return super.onTouchEvent(event); - } - -}
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java index 59d05a62e..fe91e306e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java @@ -84,15 +84,15 @@ public class SignKeySpinner extends KeySpinner { @Override boolean setStatus(Context context, Cursor cursor, ImageView statusView) { if (cursor.getInt(mIndexIsRevoked) != 0) { - KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_REVOKED, true); + KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray); return false; } if (cursor.getInt(mIndexIsExpired) != 0) { - KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_EXPIRED, true); + KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_EXPIRED, R.color.bg_gray); return false; } if (cursor.getInt(mIndexHasSign) == 0) { - KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_UNAVAILABLE, true); + KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_UNAVAILABLE, R.color.bg_gray); return false; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabLayout.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabLayout.java deleted file mode 100644 index 17471c86c..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabLayout.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sufficientlysecure.keychain.ui.widget; - -import android.content.Context; -import android.graphics.Typeface; -import android.os.Build; -import android.support.v4.view.PagerAdapter; -import android.support.v4.view.ViewPager; -import android.util.AttributeSet; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.HorizontalScrollView; -import android.widget.TextView; - -/** - * Copied from http://developer.android.com/samples/SlidingTabsColors/index.html - */ - -/** - * To be used with ViewPager to provide a tab indicator component which give constant feedback as to - * the user's scroll progress. - * <p/> - * To use the component, simply add it to your view hierarchy. Then in your - * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call - * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for. - * <p/> - * The colors can be customized in two ways. The first and simplest is to provide an array of colors - * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The - * alternative is via the {@link TabColorizer} interface which provides you complete control over - * which color is used for any individual position. - * <p/> - * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)}, - * providing the layout ID of your custom layout. - */ -public class SlidingTabLayout extends HorizontalScrollView { - - /** - * Allows complete control over the colors drawn in the tab layout. Set with - * {@link #setCustomTabColorizer(TabColorizer)}. - */ - public interface TabColorizer { - - /** - * @return return the color of the indicator used when {@code position} is selected. - */ - int getIndicatorColor(int position); - - /** - * @return return the color of the divider drawn to the right of {@code position}. - */ - int getDividerColor(int position); - - } - - private static final int TITLE_OFFSET_DIPS = 24; - private static final int TAB_VIEW_PADDING_DIPS = 16; - private static final int TAB_VIEW_TEXT_SIZE_SP = 12; - - private int mTitleOffset; - - private int mTabViewLayoutId; - private int mTabViewTextViewId; - - private ViewPager mViewPager; - private ViewPager.OnPageChangeListener mViewPagerPageChangeListener; - - private final SlidingTabStrip mTabStrip; - - public SlidingTabLayout(Context context) { - this(context, null); - } - - public SlidingTabLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - // Disable the Scroll Bar - setHorizontalScrollBarEnabled(false); - // Make sure that the Tab Strips fills this View - setFillViewport(true); - - mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density); - - mTabStrip = new SlidingTabStrip(context); - addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); - } - - /** - * Set the custom {@link TabColorizer} to be used. - * <p/> - * If you only require simple custmisation then you can use - * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve - * similar effects. - */ - public void setCustomTabColorizer(TabColorizer tabColorizer) { - mTabStrip.setCustomTabColorizer(tabColorizer); - } - - /** - * Sets the colors to be used for indicating the selected tab. These colors are treated as a - * circular array. Providing one color will mean that all tabs are indicated with the same color. - */ - public void setSelectedIndicatorColors(int... colors) { - mTabStrip.setSelectedIndicatorColors(colors); - } - - /** - * Sets the colors to be used for tab dividers. These colors are treated as a circular array. - * Providing one color will mean that all tabs are indicated with the same color. - */ - public void setDividerColors(int... colors) { - mTabStrip.setDividerColors(colors); - } - - /** - * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are - * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so - * that the layout can update it's scroll position correctly. - * - * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener) - */ - public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { - mViewPagerPageChangeListener = listener; - } - - /** - * Set the custom layout to be inflated for the tab views. - * - * @param layoutResId Layout id to be inflated - * @param textViewId id of the {@link TextView} in the inflated view - */ - public void setCustomTabView(int layoutResId, int textViewId) { - mTabViewLayoutId = layoutResId; - mTabViewTextViewId = textViewId; - } - - /** - * Sets the associated view pager. Note that the assumption here is that the pager content - * (number of tabs and tab titles) does not change after this call has been made. - */ - public void setViewPager(ViewPager viewPager) { - mTabStrip.removeAllViews(); - - mViewPager = viewPager; - if (viewPager != null) { - viewPager.setOnPageChangeListener(new InternalViewPagerListener()); - populateTabStrip(); - } - } - - /** - * Create a default view to be used for tabs. This is called if a custom tab view is not set via - * {@link #setCustomTabView(int, int)}. - */ - protected TextView createDefaultTabView(Context context) { - TextView textView = new TextView(context); - textView.setGravity(Gravity.CENTER); - textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP); - textView.setTypeface(Typeface.DEFAULT_BOLD); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - // If we're running on Honeycomb or newer, then we can use the Theme's - // selectableItemBackground to ensure that the View has a pressed state - TypedValue outValue = new TypedValue(); - getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, - outValue, true); - textView.setBackgroundResource(outValue.resourceId); - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style - textView.setAllCaps(true); - } - - int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density); - textView.setPadding(padding, padding, padding, padding); - - return textView; - } - - private void populateTabStrip() { - final PagerAdapter adapter = mViewPager.getAdapter(); - final View.OnClickListener tabClickListener = new TabClickListener(); - - for (int i = 0; i < adapter.getCount(); i++) { - View tabView = null; - TextView tabTitleView = null; - - if (mTabViewLayoutId != 0) { - // If there is a custom tab view layout id set, try and inflate it - tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip, - false); - tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId); - } - - if (tabView == null) { - tabView = createDefaultTabView(getContext()); - } - - if (tabTitleView == null && TextView.class.isInstance(tabView)) { - tabTitleView = (TextView) tabView; - } - - tabTitleView.setText(adapter.getPageTitle(i)); - tabView.setOnClickListener(tabClickListener); - - mTabStrip.addView(tabView); - } - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - - if (mViewPager != null) { - scrollToTab(mViewPager.getCurrentItem(), 0); - } - } - - private void scrollToTab(int tabIndex, int positionOffset) { - final int tabStripChildCount = mTabStrip.getChildCount(); - if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) { - return; - } - - View selectedChild = mTabStrip.getChildAt(tabIndex); - if (selectedChild != null) { - int targetScrollX = selectedChild.getLeft() + positionOffset; - - if (tabIndex > 0 || positionOffset > 0) { - // If we're not at the first child and are mid-scroll, make sure we obey the offset - targetScrollX -= mTitleOffset; - } - - scrollTo(targetScrollX, 0); - } - } - - private class InternalViewPagerListener implements ViewPager.OnPageChangeListener { - private int mScrollState; - - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - int tabStripChildCount = mTabStrip.getChildCount(); - if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) { - return; - } - - mTabStrip.onViewPagerPageChanged(position, positionOffset); - - View selectedTitle = mTabStrip.getChildAt(position); - int extraOffset = (selectedTitle != null) - ? (int) (positionOffset * selectedTitle.getWidth()) - : 0; - scrollToTab(position, extraOffset); - - if (mViewPagerPageChangeListener != null) { - mViewPagerPageChangeListener.onPageScrolled(position, positionOffset, - positionOffsetPixels); - } - } - - @Override - public void onPageScrollStateChanged(int state) { - mScrollState = state; - - if (mViewPagerPageChangeListener != null) { - mViewPagerPageChangeListener.onPageScrollStateChanged(state); - } - } - - @Override - public void onPageSelected(int position) { - if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { - mTabStrip.onViewPagerPageChanged(position, 0f); - scrollToTab(position, 0); - } - - if (mViewPagerPageChangeListener != null) { - mViewPagerPageChangeListener.onPageSelected(position); - } - } - - } - - private class TabClickListener implements View.OnClickListener { - @Override - public void onClick(View v) { - for (int i = 0; i < mTabStrip.getChildCount(); i++) { - if (v == mTabStrip.getChildAt(i)) { - mViewPager.setCurrentItem(i); - return; - } - } - } - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabStrip.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabStrip.java deleted file mode 100644 index 4c41e12c5..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabStrip.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sufficientlysecure.keychain.ui.widget; - -import android.R; -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.util.AttributeSet; -import android.util.TypedValue; -import android.view.View; -import android.widget.LinearLayout; - -/** - * Copied from http://developer.android.com/samples/SlidingTabsColors/index.html - */ -class SlidingTabStrip extends LinearLayout { - - private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2; - private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26; - private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8; - private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFFAA66CC; - - private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1; - private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20; - private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f; - - private final int mBottomBorderThickness; - private final Paint mBottomBorderPaint; - - private final int mSelectedIndicatorThickness; - private final Paint mSelectedIndicatorPaint; - - private final int mDefaultBottomBorderColor; - - private final Paint mDividerPaint; - private final float mDividerHeight; - - private int mSelectedPosition; - private float mSelectionOffset; - - private SlidingTabLayout.TabColorizer mCustomTabColorizer; - private final SimpleTabColorizer mDefaultTabColorizer; - - SlidingTabStrip(Context context) { - this(context, null); - } - - SlidingTabStrip(Context context, AttributeSet attrs) { - super(context, attrs); - setWillNotDraw(false); - - final float density = getResources().getDisplayMetrics().density; - - TypedValue outValue = new TypedValue(); - context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true); - final int themeForegroundColor = outValue.data; - - mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor, - DEFAULT_BOTTOM_BORDER_COLOR_ALPHA); - - mDefaultTabColorizer = new SimpleTabColorizer(); - mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR); - mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor, - DEFAULT_DIVIDER_COLOR_ALPHA)); - - mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density); - mBottomBorderPaint = new Paint(); - mBottomBorderPaint.setColor(mDefaultBottomBorderColor); - - mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density); - mSelectedIndicatorPaint = new Paint(); - - mDividerHeight = DEFAULT_DIVIDER_HEIGHT; - mDividerPaint = new Paint(); - mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density)); - } - - void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) { - mCustomTabColorizer = customTabColorizer; - invalidate(); - } - - void setSelectedIndicatorColors(int... colors) { - // Make sure that the custom colorizer is removed - mCustomTabColorizer = null; - mDefaultTabColorizer.setIndicatorColors(colors); - invalidate(); - } - - void setDividerColors(int... colors) { - // Make sure that the custom colorizer is removed - mCustomTabColorizer = null; - mDefaultTabColorizer.setDividerColors(colors); - invalidate(); - } - - void onViewPagerPageChanged(int position, float positionOffset) { - mSelectedPosition = position; - mSelectionOffset = positionOffset; - invalidate(); - } - - @Override - protected void onDraw(Canvas canvas) { - final int height = getHeight(); - final int childCount = getChildCount(); - final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height); - final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null - ? mCustomTabColorizer - : mDefaultTabColorizer; - - // Thick colored underline below the current selection - if (childCount > 0) { - View selectedTitle = getChildAt(mSelectedPosition); - int left = selectedTitle.getLeft(); - int right = selectedTitle.getRight(); - int color = tabColorizer.getIndicatorColor(mSelectedPosition); - - if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) { - int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1); - if (color != nextColor) { - color = blendColors(nextColor, color, mSelectionOffset); - } - - // Draw the selection partway between the tabs - View nextTitle = getChildAt(mSelectedPosition + 1); - left = (int) (mSelectionOffset * nextTitle.getLeft() + - (1.0f - mSelectionOffset) * left); - right = (int) (mSelectionOffset * nextTitle.getRight() + - (1.0f - mSelectionOffset) * right); - } - - mSelectedIndicatorPaint.setColor(color); - - canvas.drawRect(left, height - mSelectedIndicatorThickness, right, - height, mSelectedIndicatorPaint); - } - - // Thin underline along the entire bottom edge - canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint); - - // Vertical separators between the titles - int separatorTop = (height - dividerHeightPx) / 2; - for (int i = 0; i < childCount - 1; i++) { - View child = getChildAt(i); - mDividerPaint.setColor(tabColorizer.getDividerColor(i)); - canvas.drawLine(child.getRight(), separatorTop, child.getRight(), - separatorTop + dividerHeightPx, mDividerPaint); - } - } - - /** - * Set the alpha value of the {@code color} to be the given {@code alpha} value. - */ - private static int setColorAlpha(int color, byte alpha) { - return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color)); - } - - /** - * Blend {@code color1} and {@code color2} using the given ratio. - * - * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend, - * 0.0 will return {@code color2}. - */ - private static int blendColors(int color1, int color2, float ratio) { - final float inverseRation = 1f - ratio; - float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation); - float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation); - float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation); - return Color.rgb((int) r, (int) g, (int) b); - } - - private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer { - private int[] mIndicatorColors; - private int[] mDividerColors; - - @Override - public final int getIndicatorColor(int position) { - return mIndicatorColors[position % mIndicatorColors.length]; - } - - @Override - public final int getDividerColor(int position) { - return mDividerColors[position % mDividerColors.length]; - } - - void setIndicatorColors(int... colors) { - mIndicatorColors = colors; - } - - void setDividerColors(int... colors) { - mDividerColors = colors; - } - } -}
\ No newline at end of file |