diff options
author | Adithya Abraham Philip <adithyaphilip@gmail.com> | 2015-02-26 22:26:40 +0530 |
---|---|---|
committer | Adithya Abraham Philip <adithyaphilip@gmail.com> | 2015-02-26 22:26:40 +0530 |
commit | 99ff9d2340689e1ebc274e463c86af0a03806491 (patch) | |
tree | 7b3071c21dab1d026adfd5462ceaea31651c029e /OpenKeychain/src/main/java | |
parent | ec353b0c390f7b3f45b950171e56c6ddcd0a9607 (diff) | |
parent | 1210a80a96d9f862829ac1122e94b68fe8b8b5d7 (diff) | |
download | open-keychain-99ff9d2340689e1ebc274e463c86af0a03806491.tar.gz open-keychain-99ff9d2340689e1ebc274e463c86af0a03806491.tar.bz2 open-keychain-99ff9d2340689e1ebc274e463c86af0a03806491.zip |
merged dialog
Diffstat (limited to 'OpenKeychain/src/main/java')
44 files changed, 1931 insertions, 1603 deletions
diff --git a/OpenKeychain/src/main/java/android/support/v4/widget/FixedDrawerLayout.java b/OpenKeychain/src/main/java/android/support/v4/widget/FixedDrawerLayout.java deleted file mode 100644 index 6924e0b06..000000000 --- a/OpenKeychain/src/main/java/android/support/v4/widget/FixedDrawerLayout.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> - * - * 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 android.support.v4.widget; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.View; - -/** - * Fix for NullPointerException at android.support.v4.widget.DrawerLayout.isContentView(DrawerLayout.java:840) - * <p/> - * http://stackoverflow.com/a/18107942 - */ -public class FixedDrawerLayout extends DrawerLayout { - public FixedDrawerLayout(Context context) { - super(context); - } - - public FixedDrawerLayout(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public FixedDrawerLayout(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - boolean isContentView(View child) { - if (child == null) { - return false; - } - return ((LayoutParams) child.getLayoutParams()).gravity == Gravity.NO_GRAVITY; - } -} diff --git a/OpenKeychain/src/main/java/com/haibison/android/lockpattern/LockPatternFragment.java b/OpenKeychain/src/main/java/com/haibison/android/lockpattern/LockPatternFragment.java deleted file mode 100644 index 318a7c2e5..000000000 --- a/OpenKeychain/src/main/java/com/haibison/android/lockpattern/LockPatternFragment.java +++ /dev/null @@ -1,55 +0,0 @@ - - -package com.haibison.android.lockpattern; - -import android.app.Activity; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.haibison.android.lockpattern.widget.LockPatternUtils; -import com.haibison.android.lockpattern.widget.LockPatternView; - - -public class LockPatternFragment extends Fragment { - public static final String NUMBER_OF_MEASUREMENTS = "number_of_measurements"; - public static final String PATTERN_STRING = "pattern_string"; - - private String mPatternString; - private LockPatternView.OnPatternListener mEvents; - - public static LockPatternFragment newInstance(String pattern) { - LockPatternFragment fragment = new LockPatternFragment(); - Bundle args = new Bundle(); - args.putString(PATTERN_STRING, pattern); - fragment.setArguments(args); - return fragment; - } - - public LockPatternFragment() { - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - - mEvents = (LockPatternView.OnPatternListener) activity; - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - // Get the number of measurements from the bundle, or load the default: - mPatternString = getArguments().getString(PATTERN_STRING); - - View rootView = inflater.inflate(R.layout.alp_42447968_lock_pattern_activity, container, false); - - final LockPatternView lpv = (LockPatternView) rootView.findViewById(R.id.alp_42447968_view_lock_pattern); - lpv.setPattern(LockPatternView.DisplayMode.Correct, LockPatternUtils.stringToPattern(mPatternString)); - - lpv.setOnPatternListener(mEvents); - - return rootView; - } -} diff --git a/OpenKeychain/src/main/java/com/haibison/android/lockpattern/LockPatternFragmentOld.java b/OpenKeychain/src/main/java/com/haibison/android/lockpattern/LockPatternFragmentOld.java deleted file mode 100644 index 439cf3562..000000000 --- a/OpenKeychain/src/main/java/com/haibison/android/lockpattern/LockPatternFragmentOld.java +++ /dev/null @@ -1,926 +0,0 @@ -/* - * Copyright 2012 Hai Bison - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use getActivity() file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.haibison.android.lockpattern; - -import android.app.Activity; -import android.app.PendingIntent; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.res.Configuration; -import android.os.Bundle; -import android.os.ResultReceiver; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentActivity; -import android.text.TextUtils; -import android.text.format.DateUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; -import android.widget.Button; -import android.widget.TextView; -import android.widget.Toast; - -import com.haibison.android.lockpattern.util.IEncrypter; -import com.haibison.android.lockpattern.util.InvalidEncrypterException; -import com.haibison.android.lockpattern.util.LoadingDialog; -import com.haibison.android.lockpattern.util.Settings; -import com.haibison.android.lockpattern.util.UI; -import com.haibison.android.lockpattern.widget.LockPatternUtils; -import com.haibison.android.lockpattern.widget.LockPatternView; -import com.haibison.android.lockpattern.widget.LockPatternView.Cell; -import com.haibison.android.lockpattern.widget.LockPatternView.DisplayMode; - -import org.sufficientlysecure.keychain.ui.PassphraseWizardActivity; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import static com.haibison.android.lockpattern.util.Settings.Display.METADATA_CAPTCHA_WIRED_DOTS; -import static com.haibison.android.lockpattern.util.Settings.Display.METADATA_MAX_RETRIES; -import static com.haibison.android.lockpattern.util.Settings.Display.METADATA_MIN_WIRED_DOTS; -import static com.haibison.android.lockpattern.util.Settings.Display.METADATA_STEALTH_MODE; -import static com.haibison.android.lockpattern.util.Settings.Security.METADATA_AUTO_SAVE_PATTERN; -import static com.haibison.android.lockpattern.util.Settings.Security.METADATA_ENCRYPTER_CLASS; - -/** - * Main activity for getActivity() library. - * <p> - * You can deliver result to {@link android.app.PendingIntent}'s and/ or - * {@link android.os.ResultReceiver} too. See {@link #EXTRA_PENDING_INTENT_OK}, - * {@link #EXTRA_PENDING_INTENT_CANCELLED} and {@link #EXTRA_RESULT_RECEIVER} - * for more details. - * </p> - * - * <h1>NOTES</h1> - * <ul> - * <li> - * You must use one of built-in actions when calling getActivity() activity. They start - * with {@code ACTION_*}. Otherwise the library might behave strangely (we don't - * cover those cases).</li> - * <li>You must use one of the themes that getActivity() library supports. They start - * with {@code R.style.Alp_42447968_Theme_*}. The reason is the themes contain - * resources that the library needs.</li> - * <li>With {@link #ACTION_COMPARE_PATTERN}, there are <b><i>4 possible result - * codes</i></b>: {@link android.app.Activity#RESULT_OK}, {@link android.app.Activity#RESULT_CANCELED}, - * {@link #RESULT_FAILED} and {@link #RESULT_FORGOT_PATTERN}.</li> - * <li>With {@link #ACTION_VERIFY_CAPTCHA}, there are <b><i>3 possible result - * codes</i></b>: {@link android.app.Activity#RESULT_OK}, {@link android.app.Activity#RESULT_CANCELED}, - * and {@link #RESULT_FAILED}.</li> - * </ul> - * - * @author Hai Bison - * @since v1.0 - */ -public class LockPatternFragmentOld extends Fragment { - - private static final String CLASSNAME = LockPatternFragmentOld.class.getName(); - - public static final String ACTION_CREATE_PATTERN = "create"; - - /** - * Use getSelectedMethod() to compare pattern. You provide the pattern to be - * compared with {@link #EXTRA_PATTERN}. - * <p/> - * If you enabled feature auto-save pattern before (with - * {@link com.haibison.android.lockpattern.util.Settings.Security#setAutoSavePattern(android.content.Context, boolean)} ), - * then you don't need {@link #EXTRA_PATTERN} at getActivity() time. - * <p/> - * You can use {@link #EXTRA_PENDING_INTENT_FORGOT_PATTERN} to help your - * users in case they forgot the patterns. - * <p/> - * If the user passes, {@link android.app.Activity#RESULT_OK} returns. If not, - * {@link #RESULT_FAILED} returns. - * <p/> - * If the user cancels the task, {@link android.app.Activity#RESULT_CANCELED} returns. - * <p/> - * In any case, there will have extra {@link #EXTRA_RETRY_COUNT} available - * in the intent result. - * - * @see #EXTRA_PATTERN - * @see #EXTRA_PENDING_INTENT_OK - * @see #EXTRA_PENDING_INTENT_CANCELLED - * @see #RESULT_FAILED - * @see #EXTRA_RETRY_COUNT - * @since v2.4 beta - */ - public static final String ACTION_COMPARE_PATTERN = "authenticate";//CLASSNAME + ".compare_pattern"; - - /** - * Use getActivity() action to let the activity generate a random pattern and ask the - * user to re-draw it to verify. - * <p/> - * The default length of the auto-generated pattern is {@code 4}. You can - * change it with - * {@link com.haibison.android.lockpattern.util.Settings.Display#setCaptchaWiredDots(android.content.Context, int)}. - * - * @since v2.7 beta - */ - public static final String ACTION_VERIFY_CAPTCHA = CLASSNAME + ".verify_captcha"; - - /** - * If you use {@link #ACTION_COMPARE_PATTERN} and the user fails to "login" - * after a number of tries, getActivity() activity will finish with getActivity() result code. - * - * @see #ACTION_COMPARE_PATTERN - * @see #EXTRA_RETRY_COUNT - */ - public final int RESULT_FAILED = Activity.RESULT_FIRST_USER + 1; - - /** - * If you use {@link #ACTION_COMPARE_PATTERN} and the user forgot his/ her - * pattern and decided to ask for your help with recovering the pattern ( - * {@link #EXTRA_PENDING_INTENT_FORGOT_PATTERN}), getActivity() activity will finish - * with getActivity() result code. - * - * @see #ACTION_COMPARE_PATTERN - * @see #EXTRA_RETRY_COUNT - * @see #EXTRA_PENDING_INTENT_FORGOT_PATTERN - * @since v2.8 beta - */ - public static final int RESULT_FORGOT_PATTERN = Activity.RESULT_FIRST_USER + 2; - - /** - * For actions {@link #ACTION_COMPARE_PATTERN} and - * {@link #ACTION_VERIFY_CAPTCHA}, getActivity() key holds the number of tries that - * the user attempted to verify the input pattern. - */ - public static final String EXTRA_RETRY_COUNT = CLASSNAME + ".retry_count"; - - /** - * Sets value of getActivity() key to a theme in {@code R.style.Alp_42447968_Theme_*} - * . Default is the one you set in your {@code AndroidManifest.xml}. Note - * that theme {@link R.style#Alp_42447968_Theme_Light_DarkActionBar} is - * available in API 4+, but it only works in API 14+. - * - * @since v1.5.3 beta - */ - public static final String EXTRA_THEME = CLASSNAME + ".theme"; - - /** - * Key to hold the pattern. It must be a {@code char[]} array. - * <p/> - * <ul> - * <li>If you use encrypter, it should be an encrypted array.</li> - * <li>If you don't use encrypter, it should be the SHA-1 value of the - * actual pattern. You can generate the value by - * {@link com.haibison.android.lockpattern.widget.LockPatternUtils#patternToSha1(java.util.List)}.</li> - * </ul> - * - * @since v2 beta - */ - public static final String EXTRA_PATTERN = CLASSNAME + ".pattern"; - - /** - * You can provide an {@link android.os.ResultReceiver} with getActivity() key. The activity - * will notify your receiver the same result code and intent data as you - * will receive them in {@link #onActivityResult(int, int, android.content.Intent)}. - * - * @since v2.4 beta - */ - public static final String EXTRA_RESULT_RECEIVER = CLASSNAME - + ".result_receiver"; - - /** - * Put a {@link android.app.PendingIntent} into getActivity() key. It will be sent before - * {@link android.app.Activity#RESULT_OK} will be returning. If you were calling getActivity() - * activity with {@link #ACTION_CREATE_PATTERN}, key {@link #EXTRA_PATTERN} - * will be attached to the original intent which the pending intent holds. - * - * <h1>Notes</h1> - * <ul> - * <li>If you're going to use an activity, you don't need - * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} for the intent, since the library - * will call it inside {@link LockPatternFragmentOld} .</li> - * </ul> - */ - public static final String EXTRA_PENDING_INTENT_OK = CLASSNAME - + ".pending_intent_ok"; - - /** - * Put a {@link android.app.PendingIntent} into getActivity() key. It will be sent before - * {@link android.app.Activity#RESULT_CANCELED} will be returning. - * - * <h1>Notes</h1> - * <ul> - * <li>If you're going to use an activity, you don't need - * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} for the intent, since the library - * will call it inside {@link LockPatternFragmentOld} .</li> - * </ul> - */ - public static final String EXTRA_PENDING_INTENT_CANCELLED = CLASSNAME - + ".pending_intent_cancelled"; - - /** - * You put a {@link android.app.PendingIntent} into getActivity() extra. The library will show a - * button <i>"Forgot pattern?"</i> and call your intent later when the user - * taps it. - * <p/> - * <h1>Notes</h1> - * <ul> - * <li>If you use an activity, you don't need - * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} for the intent, since the library - * will call it inside {@link LockPatternFragmentOld} .</li> - * <li>{@link LockPatternFragmentOld} will finish with - * {@link #RESULT_FORGOT_PATTERN} <i><b>after</b> making a call</i> to start - * your pending intent.</li> - * <li>It is your responsibility to make sure the Intent is good. The - * library doesn't cover any errors when calling your intent.</li> - * </ul> - * - * @see #ACTION_COMPARE_PATTERN - * @since v2.8 beta - */ - public static final String EXTRA_PENDING_INTENT_FORGOT_PATTERN = CLASSNAME - + ".pending_intent_forgot_pattern"; - - /** - * Helper enum for button OK commands. (Because we use only one "OK" button - * for different commands). - * - * @author Hai Bison - */ - private static enum ButtonOkCommand { - CONTINUE,DONE - }// ButtonOkCommand - - /** - * Delay time to reload the lock pattern view after a wrong pattern. - */ - private static final long DELAY_TIME_TO_RELOAD_LOCK_PATTERN_VIEW = DateUtils.SECOND_IN_MILLIS; - - /* - * FIELDS - */ - private int mMaxRetries, mMinWiredDots, mRetryCount = 0, mCaptchaWiredDots; - private boolean mAutoSave, mStealthMode; - private IEncrypter mEncrypter; - private ButtonOkCommand mBtnOkCmd; - private Intent mIntentResult; - - /* - * CONTROLS - */ - private TextView mTextInfo; - private LockPatternView mLockPatternView; - private Button mBtnConfirm; - - /* - * FRAGMENTS - */ - private FragmentActivity fa; - - /** - * Called when the activity is first created. - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - - fa = getActivity(); - - /* - * EXTRA_THEME - */ - if (fa.getIntent().hasExtra(EXTRA_THEME)) - fa.setTheme(fa.getIntent().getIntExtra(EXTRA_THEME, - R.style.Alp_42447968_Theme_Dark)); - View view = inflater.inflate(R.layout.alp_42447968_lock_pattern_activity, container, false); - loadSettings(); - - mIntentResult = new Intent(); - fa.setResult(Activity.RESULT_CANCELED, mIntentResult); - initContentView(view); - return view; - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - } - - /** - * Loads settings, either from manifest or {@link com.haibison.android.lockpattern.util.Settings}. - */ - private void loadSettings() { - Bundle metaData = null; - try { - metaData = fa.getPackageManager().getActivityInfo(fa.getComponentName(), - PackageManager.GET_META_DATA).metaData; - } catch (NameNotFoundException e) { - /* - * Never catch getActivity(). - */ - e.printStackTrace(); - } - - if (metaData != null && metaData.containsKey(METADATA_MIN_WIRED_DOTS)) - mMinWiredDots = Settings.Display.validateMinWiredDots(getActivity(), - metaData.getInt(METADATA_MIN_WIRED_DOTS)); - else - mMinWiredDots = Settings.Display.getMinWiredDots(getActivity()); - - if (metaData != null && metaData.containsKey(METADATA_MAX_RETRIES)) - mMaxRetries = Settings.Display.validateMaxRetries(getActivity(), - metaData.getInt(METADATA_MAX_RETRIES)); - else - mMaxRetries = Settings.Display.getMaxRetries(getActivity()); - - if (metaData != null - && metaData.containsKey(METADATA_AUTO_SAVE_PATTERN)) - mAutoSave = metaData.getBoolean(METADATA_AUTO_SAVE_PATTERN); - else - mAutoSave = Settings.Security.isAutoSavePattern(getActivity()); - - if (metaData != null - && metaData.containsKey(METADATA_CAPTCHA_WIRED_DOTS)) - mCaptchaWiredDots = Settings.Display.validateCaptchaWiredDots(getActivity(), - metaData.getInt(METADATA_CAPTCHA_WIRED_DOTS)); - else - mCaptchaWiredDots = Settings.Display.getCaptchaWiredDots(getActivity()); - - if (metaData != null && metaData.containsKey(METADATA_STEALTH_MODE)) - mStealthMode = metaData.getBoolean(METADATA_STEALTH_MODE); - else - mStealthMode = Settings.Display.isStealthMode(getActivity()); - - /* - * Encrypter. - */ - char[] encrypterClass; - if (metaData != null && metaData.containsKey(METADATA_ENCRYPTER_CLASS)) - encrypterClass = metaData.getString(METADATA_ENCRYPTER_CLASS) - .toCharArray(); - else - encrypterClass = Settings.Security.getEncrypterClass(getActivity()); - - if (encrypterClass != null) { - try { - mEncrypter = (IEncrypter) Class.forName( - new String(encrypterClass), false, fa.getClassLoader()) - .newInstance(); - } catch (Throwable t) { - throw new InvalidEncrypterException(); - } - } - } - - /** - * Initializes UI... - */ - private void initContentView(View view) { - - /* - * Save all controls' state to restore later. - */ - CharSequence infoText = mTextInfo != null ? mTextInfo.getText() : null; - Boolean btnOkEnabled = mBtnConfirm != null ? mBtnConfirm.isEnabled() - : null; - DisplayMode lastDisplayMode = mLockPatternView != null ? mLockPatternView - .getDisplayMode() : null; - List<Cell> lastPattern = mLockPatternView != null ? mLockPatternView - .getPattern() : null; - - UI.adjustDialogSizeForLargeScreens(fa.getWindow()); - - View mFooter; - Button mBtnCancel; - - mTextInfo = (TextView) view.findViewById(R.id.alp_42447968_textview_info); - mLockPatternView = (LockPatternView) view.findViewById(R.id.alp_42447968_view_lock_pattern); - - mFooter = view.findViewById(R.id.alp_42447968_viewgroup_footer); - mBtnCancel = (Button) view.findViewById(R.id.alp_42447968_button_cancel); - mBtnConfirm = (Button) view.findViewById(R.id.alp_42447968_button_confirm); - - /* - * LOCK PATTERN VIEW - */ - - switch (getResources().getConfiguration().screenLayout - & Configuration.SCREENLAYOUT_SIZE_MASK) { - case Configuration.SCREENLAYOUT_SIZE_LARGE: - case Configuration.SCREENLAYOUT_SIZE_XLARGE: { - final int size = getResources().getDimensionPixelSize( - R.dimen.alp_42447968_lockpatternview_size); - LayoutParams lp = mLockPatternView.getLayoutParams(); - lp.width = size; - lp.height = size; - mLockPatternView.setLayoutParams(lp); - - break; - } - } - - /* - * Haptic feedback. - */ - boolean hapticFeedbackEnabled = false; - try { - hapticFeedbackEnabled = android.provider.Settings.System - .getInt(fa.getContentResolver(), - android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED, - 0) != 0; - } catch (Throwable t) { - /* - * Ignore it. - */ - } - mLockPatternView.setTactileFeedbackEnabled(hapticFeedbackEnabled); - - mLockPatternView.setInStealthMode(mStealthMode - && !ACTION_VERIFY_CAPTCHA.equals(fa.getIntent().getAction())); - mLockPatternView.setOnPatternListener(mLockPatternViewListener); - if (lastPattern != null && lastDisplayMode != null - && !ACTION_VERIFY_CAPTCHA.equals(fa.getIntent().getAction())) - mLockPatternView.setPattern(lastDisplayMode, lastPattern); - /* - * COMMAND BUTTONS - */ - - if (ACTION_CREATE_PATTERN.equals(getSelectedMethod())) { - mBtnCancel.setOnClickListener(mBtnCancelOnClickListener); - mBtnConfirm.setOnClickListener(mBtnConfirmOnClickListener); - - mBtnCancel.setVisibility(View.VISIBLE); - mFooter.setVisibility(View.VISIBLE); - mTextInfo.setVisibility(View.VISIBLE); - if (infoText != null) - mTextInfo.setText(infoText); - else - mTextInfo //TODO nfc text glaube ich hier oder so - .setText(R.string.alp_42447968_msg_draw_an_unlock_pattern); - - /* - * BUTTON OK - */ - if (mBtnOkCmd == null) - mBtnOkCmd = ButtonOkCommand.CONTINUE; - switch (mBtnOkCmd) { - case CONTINUE: - mBtnConfirm.setText(R.string.alp_42447968_cmd_continue); - break; - case DONE: - mBtnConfirm.setText(R.string.alp_42447968_cmd_confirm); - break; - default: - break; - } - if (btnOkEnabled != null) - mBtnConfirm.setEnabled(btnOkEnabled); - } - else if (ACTION_COMPARE_PATTERN.equals(getSelectedMethod())) { - if (TextUtils.isEmpty(infoText)) - mTextInfo - .setText(R.string.alp_42447968_msg_draw_pattern_to_unlock); - else - mTextInfo.setText(infoText); - if (fa.getIntent().hasExtra(EXTRA_PENDING_INTENT_FORGOT_PATTERN)) { - mBtnConfirm.setOnClickListener(mBtnConfirmOnClickListener); - mBtnConfirm.setText(R.string.alp_42447968_cmd_forgot_pattern); - mBtnConfirm.setEnabled(true); - mFooter.setVisibility(View.VISIBLE); - } - } - else if (ACTION_VERIFY_CAPTCHA.equals(fa.getIntent().getAction())) { - mTextInfo - .setText(R.string.alp_42447968_msg_redraw_pattern_to_confirm); - - /* - * NOTE: EXTRA_PATTERN should hold a char[] array. In getActivity() case we - * use it as a temporary variable to hold a list of Cell. - */ - - final ArrayList<Cell> pattern; - if (fa.getIntent().hasExtra(EXTRA_PATTERN)) - pattern = fa.getIntent() - .getParcelableArrayListExtra(EXTRA_PATTERN); - else - fa.getIntent().putParcelableArrayListExtra( - EXTRA_PATTERN, - pattern = LockPatternUtils - .genCaptchaPattern(mCaptchaWiredDots)); - - mLockPatternView.setPattern(DisplayMode.Animate, pattern); - } - } - - /** - * Compares {@code pattern} to the given pattern ( - * {@link #ACTION_COMPARE_PATTERN}) or to the generated "CAPTCHA" pattern ( - * {@link #ACTION_VERIFY_CAPTCHA}). Then finishes the activity if they - * match. - * - * @param pattern - * the pattern to be compared. - */ - private void doComparePattern(final List<Cell> pattern) { - if (pattern == null) - return; - - /* - * Use a LoadingDialog because decrypting pattern might take time... - */ - new LoadingDialog<Void, Void, Boolean>(getActivity(), false) { - - @Override - protected Boolean doInBackground(Void... params) { - if (ACTION_COMPARE_PATTERN.equals(getSelectedMethod())) { - char[] currentPattern = PassphraseWizardActivity.pattern; - if (currentPattern == null) - currentPattern = Settings.Security - .getPattern(getActivity()); - if (currentPattern != null) { - if (mEncrypter != null) { - return pattern.equals(mEncrypter.decrypt( - getActivity(), currentPattern)); - } else - return Arrays.equals(currentPattern, - LockPatternUtils.patternToSha1(pattern) - .toCharArray()); - } - } - else if (ACTION_VERIFY_CAPTCHA.equals(fa.getIntent().getAction())) { - return pattern.equals(fa.getIntent() - .getParcelableArrayListExtra(EXTRA_PATTERN)); - } - return false; - } - - @Override - protected void onPostExecute(Boolean result) { - super.onPostExecute(result); - if (result) { - Toast.makeText(getActivity(), "unlocked", Toast.LENGTH_SHORT).show(); - finishWithResultOk(null); - }else { - mRetryCount++; - mIntentResult.putExtra(EXTRA_RETRY_COUNT, mRetryCount); - - if (mRetryCount >= mMaxRetries) - finishWithNegativeResult(RESULT_FAILED); - else { - mLockPatternView.setDisplayMode(DisplayMode.Wrong); - mTextInfo.setText(R.string.alp_42447968_msg_try_again); - mLockPatternView.postDelayed(mLockPatternViewReloader, - DELAY_TIME_TO_RELOAD_LOCK_PATTERN_VIEW); - } - } - } - - }.execute(); - } - - /** - * Checks and creates the pattern. - * - * @param pattern - * the current pattern of lock pattern view. - */ - private void doCheckAndCreatePattern(final List<Cell> pattern) { - if (pattern.size() < mMinWiredDots) { - mLockPatternView.setDisplayMode(DisplayMode.Wrong); - mTextInfo.setText(getResources().getQuantityString( - R.plurals.alp_42447968_pmsg_connect_x_dots, mMinWiredDots, - mMinWiredDots)); - mLockPatternView.postDelayed(mLockPatternViewReloader, - DELAY_TIME_TO_RELOAD_LOCK_PATTERN_VIEW); - return; - } - - if (fa.getIntent().hasExtra(EXTRA_PATTERN)) { - /* - * Use a LoadingDialog because decrypting pattern might take time... - */ - new LoadingDialog<Void, Void, Boolean>(getActivity(), false) { - - @Override - protected Boolean doInBackground(Void... params) { - if (mEncrypter != null) - return pattern.equals(mEncrypter.decrypt( - getActivity(), fa.getIntent() - .getCharArrayExtra(EXTRA_PATTERN))); - else - return Arrays.equals( - fa.getIntent().getCharArrayExtra(EXTRA_PATTERN), - LockPatternUtils.patternToSha1(pattern) - .toCharArray()); - } - - @Override - protected void onPostExecute(Boolean result) { - super.onPostExecute(result); - - if (result) { - mTextInfo - .setText(R.string.alp_42447968_msg_your_new_unlock_pattern); - mBtnConfirm.setEnabled(true); - PassphraseWizardActivity.pattern = fa.getIntent() - .getCharArrayExtra(EXTRA_PATTERN); - } else { - mTextInfo - .setText(R.string.alp_42447968_msg_redraw_pattern_to_confirm); - mBtnConfirm.setEnabled(false); - mLockPatternView.setDisplayMode(DisplayMode.Wrong); - mLockPatternView.postDelayed(mLockPatternViewReloader, - DELAY_TIME_TO_RELOAD_LOCK_PATTERN_VIEW); - } - } - }.execute(); - } else { - /* - * Use a LoadingDialog because encrypting pattern might take time... - */ - new LoadingDialog<Void, Void, char[]>(getActivity(), false) { - - @Override - protected char[] doInBackground(Void... params) { - return mEncrypter != null ? mEncrypter.encrypt( - getActivity(), pattern) - : LockPatternUtils.patternToSha1(pattern) - .toCharArray(); - } - - @Override - protected void onPostExecute(char[] result) { - super.onPostExecute(result); - - fa.getIntent().putExtra(EXTRA_PATTERN, result); - mTextInfo - .setText(R.string.alp_42447968_msg_pattern_recorded); - mBtnConfirm.setEnabled(true); - } - - }.execute(); - } - } - - /** - * Finishes activity with {@link android.app.Activity#RESULT_OK}. - * - * @param pattern - * the pattern, if getActivity() is in mode creating pattern. In any - * cases, it can be set to {@code null}. - */ - private void finishWithResultOk(char[] pattern) { - if (ACTION_CREATE_PATTERN.equals(getSelectedMethod())) - mIntentResult.putExtra(EXTRA_PATTERN, pattern); - else { - /* - * If the user was "logging in", minimum try count can not be zero. - */ - mIntentResult.putExtra(EXTRA_RETRY_COUNT, mRetryCount + 1); - } - - fa.setResult(fa.RESULT_OK, mIntentResult); - - /* - * ResultReceiver - */ - ResultReceiver receiver = fa.getIntent().getParcelableExtra( - EXTRA_RESULT_RECEIVER); - if (receiver != null) { - Bundle bundle = new Bundle(); - if (ACTION_CREATE_PATTERN.equals(getSelectedMethod())) - bundle.putCharArray(EXTRA_PATTERN, pattern); - else { - /* - * If the user was "logging in", minimum try count can not be - * zero. - */ - bundle.putInt(EXTRA_RETRY_COUNT, mRetryCount + 1); - } - receiver.send(fa.RESULT_OK, bundle); - } - - /* - * PendingIntent - */ - PendingIntent pi = fa.getIntent().getParcelableExtra( - EXTRA_PENDING_INTENT_OK); - if (pi != null) { - try { - pi.send(getActivity(), fa.RESULT_OK, mIntentResult); - } catch (Throwable t) { - t.printStackTrace(); - } - } - - fa.finish(); - } - - /** - * Finishes the activity with negative result ( - * {@link android.app.Activity#RESULT_CANCELED}, {@link #RESULT_FAILED} or - * {@link #RESULT_FORGOT_PATTERN}). - */ - private void finishWithNegativeResult(int resultCode) { - if (ACTION_COMPARE_PATTERN.equals(getSelectedMethod())) - mIntentResult.putExtra(EXTRA_RETRY_COUNT, mRetryCount); - - fa.setResult(resultCode, mIntentResult); - - /* - * ResultReceiver - */ - ResultReceiver receiver = fa.getIntent().getParcelableExtra( - EXTRA_RESULT_RECEIVER); - if (receiver != null) { - Bundle resultBundle = null; - if (ACTION_COMPARE_PATTERN.equals(getSelectedMethod())) { - resultBundle = new Bundle(); - resultBundle.putInt(EXTRA_RETRY_COUNT, mRetryCount); - } - receiver.send(resultCode, resultBundle); - } - - /* - * PendingIntent - */ - PendingIntent pi = fa.getIntent().getParcelableExtra( - EXTRA_PENDING_INTENT_CANCELLED); - if (pi != null) { - try { - pi.send(getActivity(), resultCode, mIntentResult); - } catch (Throwable t) { - t.printStackTrace(); - } - } - - fa.finish(); - } - - /* - * LISTENERS - */ - - private final LockPatternView.OnPatternListener mLockPatternViewListener = new LockPatternView.OnPatternListener() { - - @Override - public void onPatternStart() { - mLockPatternView.removeCallbacks(mLockPatternViewReloader); - mLockPatternView.setDisplayMode(DisplayMode.Correct); - - if (ACTION_CREATE_PATTERN.equals(getSelectedMethod())) { - mTextInfo - .setText(R.string.alp_42447968_msg_release_finger_when_done); - mBtnConfirm.setEnabled(false); - if (mBtnOkCmd == ButtonOkCommand.CONTINUE) - fa.getIntent().removeExtra(EXTRA_PATTERN); - } - else if (ACTION_COMPARE_PATTERN.equals(getSelectedMethod())) { - mTextInfo - .setText(R.string.alp_42447968_msg_draw_pattern_to_unlock); - } - else if (ACTION_VERIFY_CAPTCHA.equals(getSelectedMethod())) { - mTextInfo - .setText(R.string.alp_42447968_msg_redraw_pattern_to_confirm); - } - } - - @Override - public void onPatternDetected(List<Cell> pattern) { - if (ACTION_CREATE_PATTERN.equals(getSelectedMethod())) { - doCheckAndCreatePattern(pattern); - } - else if (ACTION_COMPARE_PATTERN.equals(getSelectedMethod())) { - doComparePattern(pattern); - } - else if (ACTION_VERIFY_CAPTCHA.equals(getSelectedMethod())) { - if (!DisplayMode.Animate.equals(mLockPatternView - .getDisplayMode())) - doComparePattern(pattern); - } - } - - @Override - public void onPatternCleared() { - mLockPatternView.removeCallbacks(mLockPatternViewReloader); - - if (ACTION_CREATE_PATTERN.equals(getSelectedMethod())) { - mLockPatternView.setDisplayMode(DisplayMode.Correct); - mBtnConfirm.setEnabled(false); - if (mBtnOkCmd == ButtonOkCommand.CONTINUE) { - fa.getIntent().removeExtra(EXTRA_PATTERN); - mTextInfo - .setText(R.string.alp_42447968_msg_draw_an_unlock_pattern); - } else - mTextInfo - .setText(R.string.alp_42447968_msg_redraw_pattern_to_confirm); - } - else if (ACTION_COMPARE_PATTERN.equals(getSelectedMethod())) { - mLockPatternView.setDisplayMode(DisplayMode.Correct); - mTextInfo - .setText(R.string.alp_42447968_msg_draw_pattern_to_unlock); - } - else if (ACTION_VERIFY_CAPTCHA.equals(fa.getIntent().getAction())) { - mTextInfo - .setText(R.string.alp_42447968_msg_redraw_pattern_to_confirm); - List<Cell> pattern = fa.getIntent().getParcelableArrayListExtra( - EXTRA_PATTERN); - mLockPatternView.setPattern(DisplayMode.Animate, pattern); - } - } - - @Override - public void onPatternCellAdded(List<Cell> pattern) { - } - }; - - private final View.OnClickListener mBtnCancelOnClickListener = new View.OnClickListener() { - - @Override - public void onClick(View v) { - finishWithNegativeResult(fa.RESULT_CANCELED); - } - }; - - private final View.OnClickListener mBtnConfirmOnClickListener = new View.OnClickListener() { - - @Override - public void onClick(View v) { - if (ACTION_CREATE_PATTERN.equals(getSelectedMethod())) { - if (mBtnOkCmd == ButtonOkCommand.CONTINUE) { - mBtnOkCmd = ButtonOkCommand.DONE; - mLockPatternView.clearPattern(); - mTextInfo - .setText(R.string.alp_42447968_msg_redraw_pattern_to_confirm); - mBtnConfirm.setText(R.string.alp_42447968_cmd_confirm); - mBtnConfirm.setEnabled(false); - } else { - final char[] pattern = fa.getIntent().getCharArrayExtra( - EXTRA_PATTERN); - if (mAutoSave) - Settings.Security.setPattern(getActivity(), - pattern); - finishWithResultOk(pattern); - } - } - else if (ACTION_COMPARE_PATTERN.equals(getSelectedMethod())) { - /* - * We don't need to verify the extra. First, getActivity() button is only - * visible if there is getActivity() extra in the intent. Second, it is - * the responsibility of the caller to make sure the extra is - * good. - */ - PendingIntent pi; - try { - pi = fa.getIntent().getParcelableExtra( - EXTRA_PENDING_INTENT_FORGOT_PATTERN); - pi.send(); - } catch (Throwable t) { - t.printStackTrace(); - } - finishWithNegativeResult(RESULT_FORGOT_PATTERN); - } - } - }; - - /** - * getActivity() reloads the {@link #mLockPatternView} after a wrong pattern. - */ - private final Runnable mLockPatternViewReloader = new Runnable() { - - @Override - public void run() { - mLockPatternView.clearPattern(); - mLockPatternViewListener.onPatternCleared(); - } - }; - - /** - * Fragment constructor allowing to add a bundle with all necessary information to the fragment - * @param method contains information about which method to choose (set - * @return LockPatternFragment with bundle - */ - public static LockPatternFragmentOld newInstance(String method){ - LockPatternFragmentOld fragment = new LockPatternFragmentOld(); - Bundle args = new Bundle(); - args.putString("ACTION", method); - fragment.setArguments(args); - return fragment; - } - - /** - * Getter for the method string saved in fragment arguments - * @return String telling which method was selected - */ - public String getSelectedMethod () { - return getArguments().getString("ACTION"); - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java index 957ce4b23..01f6735ea 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java @@ -86,7 +86,7 @@ public class KeychainApplication extends Application { } brandGlowEffect(getApplicationContext(), - getApplicationContext().getResources().getColor(R.color.emphasis)); + getApplicationContext().getResources().getColor(R.color.primary)); setupAccountAsNeeded(this); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index 078eb7354..ec26d4bbe 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -555,6 +555,7 @@ public abstract class OperationResult implements Parcelable { MSG_DC_CLEAR_META_FILE (LogLevel.DEBUG, R.string.msg_dc_clear_meta_file), MSG_DC_CLEAR_META_MIME (LogLevel.DEBUG, R.string.msg_dc_clear_meta_mime), MSG_DC_CLEAR_META_SIZE (LogLevel.DEBUG, R.string.msg_dc_clear_meta_size), + MSG_DC_CLEAR_META_SIZE_UNKNOWN (LogLevel.DEBUG, R.string.msg_dc_clear_meta_size_unknown), MSG_DC_CLEAR_META_TIME (LogLevel.DEBUG, R.string.msg_dc_clear_meta_time), MSG_DC_CLEAR (LogLevel.DEBUG, R.string.msg_dc_clear), MSG_DC_CLEAR_SIGNATURE_BAD (LogLevel.WARN, R.string.msg_dc_clear_signature_bad), diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java index ad9b1900e..2ee923e42 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java @@ -160,9 +160,6 @@ public class PgpDecryptVerify extends BaseOperation { /** * If detachedSignature != null, it will be used exclusively to verify the signature - * - * @param detachedSignature - * @return */ public Builder setDetachedSignature(byte[] detachedSignature) { mDetachedSignature = detachedSignature; @@ -540,12 +537,8 @@ public class PgpDecryptVerify extends BaseOperation { PGPLiteralData literalData = (PGPLiteralData) dataChunk; - // TODO: how to get the real original size? - // this is the encrypted size so if we enable compression this value is wrong! - long originalSize = mData.getSize() - mData.getStreamPosition(); - if (originalSize < 0) { - originalSize = 0; - } + // reported size may be null if partial packets are involved (highly unlikely though) + Long originalSize = literalData.getDataLengthIfAvailable(); String originalFilename = literalData.getFileName(); String mimeType = null; @@ -573,18 +566,20 @@ public class PgpDecryptVerify extends BaseOperation { originalFilename, mimeType, literalData.getModificationTime().getTime(), - originalSize); + originalSize == null ? 0 : originalSize); - if (!originalFilename.equals("")) { + if (!"".equals(originalFilename)) { log.add(LogType.MSG_DC_CLEAR_META_FILE, indent + 1, originalFilename); } log.add(LogType.MSG_DC_CLEAR_META_MIME, indent + 1, mimeType); log.add(LogType.MSG_DC_CLEAR_META_TIME, indent + 1, new Date(literalData.getModificationTime().getTime()).toString()); - if (originalSize != 0) { + if (originalSize != null) { log.add(LogType.MSG_DC_CLEAR_META_SIZE, indent + 1, Long.toString(originalSize)); + } else { + log.add(LogType.MSG_DC_CLEAR_META_SIZE_UNKNOWN, indent + 1); } // return here if we want to decrypt the metadata only @@ -633,9 +628,8 @@ public class PgpDecryptVerify extends BaseOperation { progress = 100; } progressScaler.setProgress((int) progress, 100); - } else { - // TODO: slow annealing to fake a progress? } + // TODO: slow annealing to fake a progress? } if (signature != null) { @@ -851,9 +845,8 @@ public class PgpDecryptVerify extends BaseOperation { progress = 100; } progressScaler.setProgress((int) progress, 100); - } else { - // TODO: slow annealing to fake a progress? } + // TODO: slow annealing to fake a progress? } updateProgress(R.string.progress_verifying_signature, 90, 100); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java index c6b8b186c..407480c98 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java @@ -40,7 +40,10 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.remote.AppSettings; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.ui.BaseActivity; +import org.sufficientlysecure.keychain.ui.dialog.AddSubkeyDialogFragment; +import org.sufficientlysecure.keychain.ui.dialog.AdvancedAppSettingsDialogFragment; import org.sufficientlysecure.keychain.util.Log; import java.security.MessageDigest; @@ -130,16 +133,40 @@ public class AppSettingsActivity extends BaseActivity { @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case R.id.menu_api_settings_revoke: + case R.id.menu_api_save: { + save(); + return true; + } + case R.id.menu_api_settings_revoke: { revokeAccess(); return true; - case R.id.menu_api_save: - save(); + } + case R.id.menu_api_settings_advanced: { + showAdvancedInfo(); return true; + } } return super.onOptionsItemSelected(item); } + private void showAdvancedInfo() { + String signature = null; + // advanced info: package signature SHA-256 + try { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + md.update(mAppSettings.getPackageSignature()); + byte[] digest = md.digest(); + signature = new String(Hex.encode(digest)); + } catch (NoSuchAlgorithmException e) { + Log.e(Constants.TAG, "Should not happen!", e); + } + + AdvancedAppSettingsDialogFragment dialogFragment = + AdvancedAppSettingsDialogFragment.newInstance(mAppSettings.getPackageName(), signature); + + dialogFragment.show(getSupportFragmentManager(), "advancedDialog"); + } + private void startApp() { Intent i; PackageManager manager = getPackageManager(); @@ -175,21 +202,6 @@ public class AppSettingsActivity extends BaseActivity { mAppNameView.setText(appName); mAppIconView.setImageDrawable(appIcon); - // advanced info: package name - mPackageName.setText(mAppSettings.getPackageName()); - - // advanced info: package signature SHA-256 - try { - MessageDigest md = MessageDigest.getInstance("SHA-256"); - md.update(mAppSettings.getPackageSignature()); - byte[] digest = md.digest(); - String signature = new String(Hex.encode(digest)); - - mPackageSignature.setText(signature); - } catch (NoSuchAlgorithmException e) { - Log.e(Constants.TAG, "Should not happen!", e); - } - Uri accountsUri = appUri.buildUpon().appendPath(KeychainContract.PATH_ACCOUNTS).build(); Log.d(Constants.TAG, "accountsUri: " + accountsUri); Uri allowedKeysUri = appUri.buildUpon().appendPath(KeychainContract.PATH_ALLOWED_KEYS).build(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java index 976ce20d6..2deb33a67 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2013-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 @@ -48,82 +48,83 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; import org.sufficientlysecure.keychain.util.Log; public class AppsListFragment extends ListFragment implements - LoaderManager.LoaderCallbacks<Cursor> { + LoaderManager.LoaderCallbacks<Cursor>, OnItemClickListener { - // This is the Adapter being used to display the list's data. - RegisteredAppsAdapter mAdapter; + AppsAdapter mAdapter; @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - getListView().setOnItemClickListener(new OnItemClickListener() { - @Override - public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { - String selectedPackageName = mAdapter.getItemPackageName(position); - boolean installed = mAdapter.getItemIsInstalled(position); - boolean registered = mAdapter.getItemIsRegistered(position); - - if (installed) { - if (registered) { - // edit app settings - Intent intent = new Intent(getActivity(), AppSettingsActivity.class); - intent.setData(KeychainContract.ApiApps.buildByPackageNameUri(selectedPackageName)); - startActivity(intent); - } else { - Intent i; - PackageManager manager = getActivity().getPackageManager(); - try { - i = manager.getLaunchIntentForPackage(selectedPackageName); - if (i == null) - throw new PackageManager.NameNotFoundException(); - // start like the Android launcher would do - i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - i.addCategory(Intent.CATEGORY_LAUNCHER); - startActivity(i); - } catch (PackageManager.NameNotFoundException e) { - Log.e(Constants.TAG, "startApp", e); - } - } - } else { - try { - startActivity(new Intent(Intent.ACTION_VIEW, - Uri.parse("market://details?id=" + selectedPackageName))); - } catch (ActivityNotFoundException anfe) { - startActivity(new Intent(Intent.ACTION_VIEW, - Uri.parse("http://play.google.com/store/apps/details?id=" + selectedPackageName))); - } - } - } - }); + getListView().setOnItemClickListener(this); - // Give some text to display if there is no data. In a real - // application this would come from a resource. - setEmptyText(getString(R.string.api_no_apps)); + // NOTE: No setEmptyText(), we always have the default entries // We have a menu item to show in action bar. setHasOptionsMenu(true); // Create an empty adapter we will use to display the loaded data. - mAdapter = new RegisteredAppsAdapter(getActivity(), null, 0); + mAdapter = new AppsAdapter(getActivity(), null, 0); setListAdapter(mAdapter); - // Loader is started in onResume! + // NOTE: Loader is started in onResume! } @Override public void onResume() { super.onResume(); - // after coming back from Google Play -> reload + + // Start out with a progress indicator. + setListShown(false); + + // After coming back from Google Play -> reload getLoaderManager().restartLoader(0, null, this); } + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + String selectedPackageName = mAdapter.getItemPackageName(position); + boolean installed = mAdapter.getItemIsInstalled(position); + boolean registered = mAdapter.getItemIsRegistered(position); + + if (installed) { + if (registered) { + // Edit app settings + Intent intent = new Intent(getActivity(), AppSettingsActivity.class); + intent.setData(KeychainContract.ApiApps.buildByPackageNameUri(selectedPackageName)); + startActivity(intent); + } else { + Intent i; + PackageManager manager = getActivity().getPackageManager(); + try { + i = manager.getLaunchIntentForPackage(selectedPackageName); + if (i == null) { + throw new PackageManager.NameNotFoundException(); + } + // Start like the Android launcher would do + i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + i.addCategory(Intent.CATEGORY_LAUNCHER); + startActivity(i); + } catch (PackageManager.NameNotFoundException e) { + Log.e(Constants.TAG, "startApp", e); + } + } + } else { + try { + startActivity(new Intent(Intent.ACTION_VIEW, + Uri.parse("market://details?id=" + selectedPackageName))); + } catch (ActivityNotFoundException anfe) { + startActivity(new Intent(Intent.ACTION_VIEW, + Uri.parse("https://play.google.com/store/apps/details?id=" + selectedPackageName))); + } + } + } + private static final String TEMP_COLUMN_NAME = "NAME"; private static final String TEMP_COLUMN_INSTALLED = "INSTALLED"; private static final String TEMP_COLUMN_REGISTERED = "REGISTERED"; private static final String TEMP_COLUMN_ICON_RES_ID = "ICON_RES_ID"; - // These are the Contacts rows that we will retrieve. static final String[] PROJECTION = new String[]{ ApiApps._ID, // 0 ApiApps.PACKAGE_NAME, // 1 @@ -149,106 +150,17 @@ public class AppsListFragment extends ListFragment implements // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. - return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, + return new AppsLoader(getActivity(), baseUri, PROJECTION, null, null, ApiApps.PACKAGE_NAME + " COLLATE LOCALIZED ASC"); } public void onLoadFinished(Loader<Cursor> loader, Cursor data) { - MatrixCursor availableAppsCursor = new MatrixCursor(new String[]{ - ApiApps._ID, - ApiApps.PACKAGE_NAME, - TEMP_COLUMN_NAME, - TEMP_COLUMN_INSTALLED, - TEMP_COLUMN_REGISTERED, - TEMP_COLUMN_ICON_RES_ID - }); - // NOTE: SORT ascending by package name, this is REQUIRED for CursorJoiner! - // Drawables taken from projects res/drawables-xxhdpi/ic_launcher.png - availableAppsCursor.addRow(new Object[]{1, "com.fsck.k9", "K-9 Mail", 0, 0, R.drawable.apps_k9}); - availableAppsCursor.addRow(new Object[]{1, "com.zeapo.pwdstore", "Password Store", 0, 0, R.drawable.apps_password_store}); - availableAppsCursor.addRow(new Object[]{1, "eu.siacs.conversations", "Conversations (Instant Messaging)", 0, 0, R.drawable.apps_conversations}); - - MatrixCursor mergedCursor = new MatrixCursor(new String[]{ - ApiApps._ID, - ApiApps.PACKAGE_NAME, - TEMP_COLUMN_NAME, - TEMP_COLUMN_INSTALLED, - TEMP_COLUMN_REGISTERED, - TEMP_COLUMN_ICON_RES_ID - }); - - CursorJoiner joiner = new CursorJoiner( - availableAppsCursor, - new String[]{ApiApps.PACKAGE_NAME}, - data, - new String[]{ApiApps.PACKAGE_NAME}); - for (CursorJoiner.Result joinerResult : joiner) { - switch (joinerResult) { - case LEFT: { - // handle case where a row in availableAppsCursor is unique - String packageName = availableAppsCursor.getString(INDEX_PACKAGE_NAME); - - mergedCursor.addRow(new Object[]{ - 1, // no need for unique _ID - packageName, - availableAppsCursor.getString(INDEX_NAME), - isInstalled(packageName), - 0, - availableAppsCursor.getInt(INDEX_ICON_RES_ID) - }); - break; - } - case RIGHT: { - // handle case where a row in data is unique - String packageName = data.getString(INDEX_PACKAGE_NAME); - - mergedCursor.addRow(new Object[]{ - 1, // no need for unique _ID - packageName, - null, - isInstalled(packageName), - 1, // registered! - R.drawable.ic_launcher // icon is retrieved later - }); - break; - } - case BOTH: { - // handle case where a row with the same key is in both cursors - String packageName = data.getString(INDEX_PACKAGE_NAME); - - String name; - if (isInstalled(packageName) == 1) { - name = data.getString(INDEX_NAME); - } else { - // if not installed take name from available apps list - name = availableAppsCursor.getString(INDEX_NAME); - } - - mergedCursor.addRow(new Object[]{ - 1, // no need for unique _ID - packageName, - name, - isInstalled(packageName), - 1, // registered! - R.drawable.ic_launcher // icon is retrieved later - }); - break; - } - } - } - // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) - mAdapter.swapCursor(mergedCursor); - } + mAdapter.swapCursor(data); - private int isInstalled(String packageName) { - try { - getActivity().getPackageManager().getApplicationInfo(packageName, 0); - return 1; - } catch (final PackageManager.NameNotFoundException e) { - return 0; - } + // The list should now be shown. + setListShown(true); } public void onLoaderReset(Loader<Cursor> loader) { @@ -258,12 +170,127 @@ public class AppsListFragment extends ListFragment implements mAdapter.swapCursor(null); } - private class RegisteredAppsAdapter extends CursorAdapter { + /** + * Besides the queried cursor with all registered apps, this loader also returns non-installed + * proposed apps using a MatrixCursor. + */ + private static class AppsLoader extends CursorLoader { + + public AppsLoader(Context context) { + super(context); + } + + public AppsLoader(Context context, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + super(context, uri, projection, selection, selectionArgs, sortOrder); + } + + @Override + public Cursor loadInBackground() { + // Load registered apps from content provider + Cursor data = super.loadInBackground(); + + MatrixCursor availableAppsCursor = new MatrixCursor(new String[]{ + ApiApps._ID, + ApiApps.PACKAGE_NAME, + TEMP_COLUMN_NAME, + TEMP_COLUMN_INSTALLED, + TEMP_COLUMN_REGISTERED, + TEMP_COLUMN_ICON_RES_ID + }); + // NOTE: SORT ascending by package name, this is REQUIRED for CursorJoiner! + // Drawables taken from projects res/drawables-xxhdpi/ic_launcher.png + availableAppsCursor.addRow(new Object[]{1, "com.fsck.k9", "K-9 Mail", 0, 0, R.drawable.apps_k9}); + availableAppsCursor.addRow(new Object[]{1, "com.zeapo.pwdstore", "Password Store", 0, 0, R.drawable.apps_password_store}); + availableAppsCursor.addRow(new Object[]{1, "eu.siacs.conversations", "Conversations (Instant Messaging)", 0, 0, R.drawable.apps_conversations}); + + MatrixCursor mergedCursor = new MatrixCursor(new String[]{ + ApiApps._ID, + ApiApps.PACKAGE_NAME, + TEMP_COLUMN_NAME, + TEMP_COLUMN_INSTALLED, + TEMP_COLUMN_REGISTERED, + TEMP_COLUMN_ICON_RES_ID + }); + + CursorJoiner joiner = new CursorJoiner( + availableAppsCursor, + new String[]{ApiApps.PACKAGE_NAME}, + data, + new String[]{ApiApps.PACKAGE_NAME}); + for (CursorJoiner.Result joinerResult : joiner) { + switch (joinerResult) { + case LEFT: { + // handle case where a row in availableAppsCursor is unique + String packageName = availableAppsCursor.getString(INDEX_PACKAGE_NAME); + + mergedCursor.addRow(new Object[]{ + 1, // no need for unique _ID + packageName, + availableAppsCursor.getString(INDEX_NAME), + isInstalled(packageName), + 0, + availableAppsCursor.getInt(INDEX_ICON_RES_ID) + }); + break; + } + case RIGHT: { + // handle case where a row in data is unique + String packageName = data.getString(INDEX_PACKAGE_NAME); + + mergedCursor.addRow(new Object[]{ + 1, // no need for unique _ID + packageName, + null, + isInstalled(packageName), + 1, // registered! + R.drawable.ic_launcher // icon is retrieved later + }); + break; + } + case BOTH: { + // handle case where a row with the same key is in both cursors + String packageName = data.getString(INDEX_PACKAGE_NAME); + + String name; + if (isInstalled(packageName) == 1) { + name = data.getString(INDEX_NAME); + } else { + // if not installed take name from available apps list + name = availableAppsCursor.getString(INDEX_NAME); + } + + mergedCursor.addRow(new Object[]{ + 1, // no need for unique _ID + packageName, + name, + isInstalled(packageName), + 1, // registered! + R.drawable.ic_launcher // icon is retrieved later + }); + break; + } + } + } + + return mergedCursor; + } + + private int isInstalled(String packageName) { + try { + getContext().getPackageManager().getApplicationInfo(packageName, 0); + return 1; + } catch (final PackageManager.NameNotFoundException e) { + return 0; + } + } + } + + private class AppsAdapter extends CursorAdapter { private LayoutInflater mInflater; private PackageManager mPM; - public RegisteredAppsAdapter(Context context, Cursor c, int flags) { + public AppsAdapter(Context context, Cursor c, int flags) { super(context, c, flags); mInflater = LayoutInflater.from(context); @@ -273,44 +300,23 @@ public class AppsListFragment extends ListFragment implements /** * Similar to CursorAdapter.getItemId(). * Required to build Uris for api apps, which are not based on row ids - * - * @param position - * @return */ public String getItemPackageName(int position) { - if (mDataValid && mCursor != null) { - if (mCursor.moveToPosition(position)) { - return mCursor.getString(INDEX_PACKAGE_NAME); - } else { - return null; - } + if (mDataValid && mCursor != null && mCursor.moveToPosition(position)) { + return mCursor.getString(INDEX_PACKAGE_NAME); } else { return null; } } public boolean getItemIsInstalled(int position) { - if (mDataValid && mCursor != null) { - if (mCursor.moveToPosition(position)) { - return (mCursor.getInt(INDEX_INSTALLED) == 1); - } else { - return false; - } - } else { - return false; - } + return mDataValid && mCursor != null + && mCursor.moveToPosition(position) && (mCursor.getInt(INDEX_INSTALLED) == 1); } public boolean getItemIsRegistered(int position) { - if (mDataValid && mCursor != null) { - if (mCursor.moveToPosition(position)) { - return (mCursor.getInt(INDEX_REGISTERED) == 1); - } else { - return false; - } - } else { - return false; - } + return mDataValid && mCursor != null + && mCursor.moveToPosition(position) && (mCursor.getInt(INDEX_REGISTERED) == 1); } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BaseActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BaseActivity.java index 03bb2f733..e6c2542a2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BaseActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BaseActivity.java @@ -35,6 +35,7 @@ import org.sufficientlysecure.keychain.R; */ public abstract class BaseActivity extends ActionBarActivity { protected Toolbar mToolbar; + protected View mStatusBar; @Override protected void onCreate(Bundle savedInstanceState) { @@ -51,6 +52,7 @@ public abstract class BaseActivity extends ActionBarActivity { setSupportActionBar(mToolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); } + mStatusBar = findViewById(R.id.status_bar); } protected void setActionBarIcon(int iconRes) { 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 471f55c47..777288e69 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 @@ -18,54 +17,46 @@ package org.sufficientlysecure.keychain.ui; +import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.view.View; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.util.ExportHelper; +import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.util.Log; -public class ViewKeyAdvancedActivity extends BaseActivity { +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 - setFullScreenDialogClose( - new View.OnClickListener() { - @Override - public void onClick(View v) { - // "Done" - finish(); - } - } - ); - - 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(); + } + }); + + Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString()); - startFragment(savedInstanceState, dataUri); + startFragment(savedInstanceState, mDataUri); } @Override protected void initLayout() { - setContentView(R.layout.view_key_advanced_activity); + setContentView(R.layout.certify_fingerprint_activity); } private void startFragment(Bundle savedInstanceState, Uri dataUri) { @@ -77,15 +68,25 @@ public class ViewKeyAdvancedActivity extends BaseActivity { } // 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(); } + @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/CertifyFingerprintFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java new file mode 100644 index 000000000..aef705ee9 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java @@ -0,0 +1,184 @@ +/* + * 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> { + + 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.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[]{keyId}); + startActivityForResult(certifyIntent, 0); + } + +} 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 be7653b52..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; @@ -110,11 +111,9 @@ public class ImportKeysCloudFragment extends Fragment { mConfigButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - Intent i = new Intent(mImportActivity, SettingsActivity.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/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 079ebb729..3da185dd2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -56,6 +56,8 @@ import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; +import com.getbase.floatingactionbutton.FloatingActionButton; + import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.ConsolidateResult; @@ -105,6 +107,10 @@ public class KeyListFragment extends LoaderFragment private String mQuery; private SearchView mSearchView; + private FloatingActionButton mFabQrCode; + private FloatingActionButton mFabCloud; + private FloatingActionButton mFabFile; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -123,6 +129,29 @@ public class KeyListFragment extends LoaderFragment mStickyList = (StickyListHeadersListView) view.findViewById(R.id.key_list_list); mStickyList.setOnItemClickListener(this); + mFabQrCode = (FloatingActionButton) view.findViewById(R.id.fab_add_qr_code); + mFabCloud = (FloatingActionButton) view.findViewById(R.id.fab_add_cloud); + mFabFile = (FloatingActionButton) view.findViewById(R.id.fab_add_file); + + mFabQrCode.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + scanQrCode(); + } + }); + mFabCloud.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + searchCloud(); + } + }); + mFabFile.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + importFile(); + } + }); + mSwipeRefreshLayout = (ListAwareSwipeRefreshLayout) view.findViewById(R.id.key_list_swipe_container); mSwipeRefreshLayout.setOnRefreshListener(new NoScrollableSwipeRefreshLayout.OnRefreshListener() { @Override @@ -198,6 +227,9 @@ 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); @@ -466,9 +498,6 @@ public class KeyListFragment extends LoaderFragment // 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); - // Erase search result without focus MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() { @Override @@ -496,26 +525,11 @@ public class KeyListFragment extends LoaderFragment @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case R.id.menu_key_list_add: - Intent scanQrCode = new Intent(getActivity(), 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(getActivity(), 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; @@ -587,6 +601,18 @@ public class KeyListFragment extends LoaderFragment startActivity(importIntent); } + private void scanQrCode() { + Intent scanQrCode = new Intent(getActivity(), QrCodeScanActivity.class); + scanQrCode.setAction(QrCodeScanActivity.ACTION_SCAN_WITH_RESULT); + 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); @@ -749,13 +775,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/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java index a44274c67..ec7eee3ac 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -255,11 +255,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); @@ -286,11 +286,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 index f3bf337c9..2f29f9360 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java @@ -31,8 +31,6 @@ public class MainActivity extends NavDrawerActivity { public void init(Bundle savedInstanceState) { super.init(savedInstanceState); - setTitle(R.string.nav_keys); - // if this is the first time show first time activity Preferences prefs = Preferences.getPreferences(this); if (prefs.isFirstTime()) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NavDrawerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NavDrawerActivity.java index d82e1c246..fb23c7d3a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NavDrawerActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NavDrawerActivity.java @@ -39,7 +39,7 @@ public abstract class NavDrawerActivity extends MaterialNavigationDrawer { setDrawerHeaderImage(R.drawable.drawer_header); // create sections - addSection(newSection(getString(R.string.title_keys), R.drawable.ic_vpn_key_black_24dp, new KeyListFragment())); + 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())); 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 fd9324992..d5ca08936 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; } @@ -253,9 +251,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 872e888a8..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,9 +202,9 @@ 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 | FormatException e) { e.printStackTrace(); @@ -224,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 @@ -352,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 204ea1d38..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,6 +20,8 @@ package org.sufficientlysecure.keychain.ui; import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; +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; @@ -36,7 +38,8 @@ import org.sufficientlysecure.keychain.util.Log; public class QrCodeViewActivity extends BaseActivity { - private ImageView mFingerprintQrCode; + private ImageView mQrCode; + private CardView mQrCodeLayout; @Override public void onCreate(Bundle savedInstanceState) { @@ -48,7 +51,7 @@ public class QrCodeViewActivity extends BaseActivity { @Override public void onClick(View v) { // "Done" - finish(); + ActivityCompat.finishAfterTransition(QrCodeViewActivity.this); } } ); @@ -56,16 +59,17 @@ public class QrCodeViewActivity extends BaseActivity { 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); } }); @@ -77,7 +81,7 @@ public class QrCodeViewActivity extends BaseActivity { 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); @@ -86,20 +90,20 @@ public class QrCodeViewActivity extends BaseActivity { // 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); } } @@ -108,20 +112,4 @@ public class QrCodeViewActivity extends BaseActivity { setContentView(R.layout.qr_code_activity); } - @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); - } - }
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java index e2fc44689..53986a392 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java @@ -183,7 +183,6 @@ public class SettingsActivity extends PreferenceActivity { } } - /* Called only on Honeycomb and later */ @Override public void onBuildHeaders(List<Header> target) { super.onBuildHeaders(target); 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 575df01b7..e1a8981c4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -19,8 +19,10 @@ 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; import android.net.Uri; import android.nfc.NdefMessage; import android.nfc.NdefRecord; @@ -32,31 +34,37 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; 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.widget.CardView; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.view.animation.AlphaAnimation; +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.astuetz.PagerSlidingTabStrip; +import com.getbase.floatingactionbutton.FloatingActionButton; 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.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter; +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; @@ -72,19 +80,18 @@ public class ViewKeyActivity extends BaseActivity implements 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 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; @@ -95,6 +102,10 @@ public class ViewKeyActivity extends BaseActivity implements private static final int LOADER_ID_UNIFIED = 0; + private boolean mIsSecret = false; + private boolean mHasEncrypt = false; + private boolean mIsVerified = false; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -102,25 +113,20 @@ public class ViewKeyActivity extends BaseActivity 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); - 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); - 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); - } + 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); mDataUri = getIntent().getData(); if (mDataUri == null) { @@ -140,16 +146,52 @@ public class ViewKeyActivity extends BaseActivity implements Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString()); + 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. getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); initNfc(mDataUri); - initTabs(mDataUri); - - // switch to tab selected by extra - mViewPager.setCurrentItem(switchToTab); + startFragment(savedInstanceState, mDataUri); } @Override @@ -157,22 +199,24 @@ public class ViewKeyActivity extends BaseActivity implements setContentView(R.layout.view_key_activity); } - private void initTabs(Uri dataUri) { - mTabsAdapter = new PagerTabStripAdapter(this); - mViewPager.setAdapter(mTabsAdapter); - - 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 @@ -202,9 +246,26 @@ public class ViewKeyActivity extends BaseActivity 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) { @@ -214,6 +275,75 @@ public class ViewKeyActivity extends BaseActivity 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, QrCodeScanActivity.class); + scanQrCode.setAction(QrCodeScanActivity.ACTION_SCAN_WITH_RESULT); + startActivityForResult(scanQrCode, 0); + } + + private void certifyFingeprint(Uri dataUri) { + Intent intent = new Intent(this, CertifyFingerprintActivity.class); + intent.setData(dataUri); + 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); @@ -255,6 +385,101 @@ public class ViewKeyActivity extends BaseActivity 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 { + 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(this, ImportKeysActivity.class); + queryIntent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT); + queryIntent.putExtra(ImportKeysActivity.EXTRA_FINGERPRINT, fingerprint); + + startActivityForResult(queryIntent, 0); + } + + 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 + * + * @param fingerprint + */ + 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 */ @@ -345,25 +570,34 @@ public class ViewKeyActivity extends BaseActivity 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: @@ -386,36 +620,136 @@ public class ViewKeyActivity extends BaseActivity 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)); + String fingerprint = KeyFormattingUtils.convertFingerprintToHex(data.getBlob(INDEX_FINGERPRINT)); - 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()); + 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; + + // re-create options menu based on mIsSecret, mIsVerified + supportInvalidateOptionsMenu(); + + 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(fingerprint); + loadQrCode(fingerprint); + 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(fingerprint); + + 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); + } } + mToolbar.setBackgroundColor(color); + mStatusBar.setBackgroundColor(color); + mBigToolbar.setBackgroundColor(color); + + 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..37f9113bb --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java @@ -0,0 +1,252 @@ +/* + * 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 mainBundle = new Bundle(); + mainBundle.putParcelable(ViewKeyAdvMainFragment.ARG_DATA_URI, dataUri); + mTabsAdapter.addTab(ViewKeyAdvMainFragment.class, + mainBundle, getString(R.string.key_view_tab_main)); + + Bundle shareBundle = new Bundle(); + shareBundle.putParcelable(ViewKeyAdvMainFragment.ARG_DATA_URI, dataUri); + mTabsAdapter.addTab(ViewKeyAdvShareFragment.class, + shareBundle, getString(R.string.key_view_tab_share)); + + 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)); + + // 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/ViewKeyMainFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvMainFragment.java index 8e957a36f..c9d20f9f4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvMainFragment.java @@ -50,7 +50,7 @@ import org.sufficientlysecure.keychain.util.Log; import java.util.Date; -public class ViewKeyMainFragment extends LoaderFragment implements +public class ViewKeyAdvMainFragment extends LoaderFragment implements LoaderManager.LoaderCallbacks<Cursor> { public static final String ARG_DATA_URI = "uri"; @@ -80,7 +80,7 @@ public class ViewKeyMainFragment 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_main_fragment, getContainer()); + View view = inflater.inflate(R.layout.view_key_adv_main_fragment, getContainer()); mUserIds = (ListView) view.findViewById(R.id.view_key_user_ids); mActionEdit = view.findViewById(R.id.view_key_action_edit); @@ -265,9 +265,10 @@ public class ViewKeyMainFragment extends LoaderFragment implements } } - case LOADER_ID_USER_IDS: + case LOADER_ID_USER_IDS: { mUserIdsAdapter.swapCursor(data); break; + } } setContentShown(true); 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 7c47cbd0d..6208cff4e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java @@ -56,7 +56,7 @@ 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"; @@ -81,9 +81,9 @@ 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); @@ -358,7 +358,7 @@ 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. 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/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/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java index 8e82dd7d0..598793233 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 @@ -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()) { 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 e90b57c1c..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 @@ -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 c286218ed..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 @@ -272,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); @@ -301,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 abc1d8816..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 @@ -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/UserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java index 9f3096b23..ad7c9e430 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java @@ -43,6 +43,7 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC private LayoutInflater mInflater; private final ArrayList<Boolean> mCheckStates; private SaveKeyringParcel mSaveKeyringParcel; + private boolean mShowStatusImages; public static final String[] USER_IDS_PROJECTION = new String[]{ UserPackets._ID, @@ -60,24 +61,30 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC private static final int INDEX_IS_REVOKED = 5; public UserIdsAdapter(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 UserIdsAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes, + SaveKeyringParcel saveKeyringParcel) { + this(context, c, flags, showCheckBoxes, false, saveKeyringParcel); } public UserIdsAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes) { - this(context, c, flags, showCheckBoxes, null); + this(context, c, flags, showCheckBoxes, false, null); } public UserIdsAdapter(Context context, Cursor c, int flags, SaveKeyringParcel saveKeyringParcel) { - this(context, c, flags, false, saveKeyringParcel); + this(context, c, flags, false, false, saveKeyringParcel); } public UserIdsAdapter(Context context, Cursor c, int flags) { - this(context, c, flags, false, null); + this(context, c, flags, false, false, null); } @Override @@ -157,12 +164,17 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC 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); @@ -184,13 +196,13 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC 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; } } @@ -263,7 +275,7 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC @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/AdvancedAppSettingsDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AdvancedAppSettingsDialogFragment.java new file mode 100644 index 000000000..d2fa37cf7 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AdvancedAppSettingsDialogFragment.java @@ -0,0 +1,72 @@ +/* + * 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.dialog; + +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentActivity; + +import org.sufficientlysecure.keychain.R; + +public class AdvancedAppSettingsDialogFragment extends DialogFragment { + private static final String ARG_PACKAGE_NAME = "package_name"; + private static final String ARG_SIGNATURE = "signature"; + + /** + * Creates new instance of this fragment + */ + 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); + + frag.setArguments(args); + return frag; + } + + /** + * Creates dialog + */ + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final FragmentActivity activity = getActivity(); + + CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity); + + 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/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 66d6bf9e3..6f8c477c0 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 @@ -26,6 +26,8 @@ import com.nispok.snackbar.Snackbar.SnackbarDuration; import com.nispok.snackbar.SnackbarManager; import com.nispok.snackbar.listeners.ActionClickListener; +import org.sufficientlysecure.keychain.R; + /** * Notify wrapper which allows a more easy use of different notification libraries */ @@ -52,10 +54,10 @@ public class Notify { case OK: break; case WARN: - bar.textColor(Color.YELLOW); + bar.textColor(activity.getResources().getColor(R.color.android_orange_light)); break; case ERROR: - bar.textColor(Color.RED); + bar.textColor(activity.getResources().getColor(R.color.android_red_light)); break; } @@ -74,13 +76,13 @@ public class Notify { switch (style) { case OK: - bar.actionColor(Color.GREEN); + bar.actionColor(activity.getResources().getColor(R.color.android_green_light)); break; case WARN: - bar.textColor(Color.YELLOW); + bar.textColor(activity.getResources().getColor(R.color.android_orange_light)); break; case ERROR: - bar.textColor(Color.RED); + bar.textColor(activity.getResources().getColor(R.color.android_red_light)); break; } @@ -116,13 +118,13 @@ public class Notify { switch (style) { case OK: - bar.actionColor(Color.GREEN); + bar.actionColor(activity.getResources().getColor(R.color.android_green_light)); break; case WARN: - bar.textColor(Color.YELLOW); + bar.textColor(activity.getResources().getColor(R.color.android_orange_light)); break; case ERROR: - bar.textColor(Color.RED); + bar.textColor(activity.getResources().getColor(R.color.android_red_light)); break; } 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 0bb4100c5..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 @@ -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 904cde47e..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,6 +27,7 @@ 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.ui.util.KeyFormattingUtils; @@ -85,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/KeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java index 17bfbef57..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,13 +24,13 @@ 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; @@ -41,7 +41,11 @@ 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/SignKeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java index 9c8e4aedb..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 @@ -26,6 +26,7 @@ import android.support.v4.content.Loader; import android.util.AttributeSet; import android.widget.ImageView; +import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; @@ -83,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/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java index 8d4af58d7..a36af5c87 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java @@ -172,7 +172,7 @@ public class Preferences { } public boolean useDefaultYubikeyPin() { - return mSharedPreferences.getBoolean(Pref.USE_DEFAULT_YUBIKEY_PIN, true); + return mSharedPreferences.getBoolean(Pref.USE_DEFAULT_YUBIKEY_PIN, false); } public void setUseDefaultYubikeyPin(boolean useDefaultYubikeyPin) { |