merged dialog
@@ -67,8 +67,10 @@
|
||||
<uses-permission android:name="android.permission.READ_PROFILE" />
|
||||
|
||||
<!-- android:allowBackup="false": Don't allow backup over adb backup or other apps! -->
|
||||
<!-- tools:replace="android:allowBackup" is a workaround for https://github.com/geftimov/android-patternview/pull/2 -->
|
||||
<application
|
||||
android:name=".KeychainApplication"
|
||||
tools:replace="android:allowBackup"
|
||||
android:allowBackup="false"
|
||||
android:hardwareAccelerated="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
@@ -145,7 +147,11 @@
|
||||
android:name=".ui.EncryptFilesActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||
android:label="@string/title_encrypt_files"
|
||||
android:windowSoftInputMode="stateHidden">
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:parentActivityName=".ui.MainActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".ui.MainActivity" />
|
||||
|
||||
<!-- Keychain's own Actions -->
|
||||
<!-- ENCRYPT with data Uri -->
|
||||
@@ -174,7 +180,11 @@
|
||||
android:name=".ui.EncryptTextActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||
android:label="@string/title_encrypt_text"
|
||||
android:windowSoftInputMode="stateHidden">
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:parentActivityName=".ui.MainActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".ui.MainActivity" />
|
||||
|
||||
<!-- Keychain's own Actions -->
|
||||
<!-- ENCRYPT with text as extra -->
|
||||
@@ -403,10 +413,9 @@
|
||||
android:value=".ui.ViewKeyActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.ViewKeyAdvancedActivity"
|
||||
android:name=".ui.ViewKeyAdvActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||
android:label="@string/title_advanced_key_info">
|
||||
</activity>
|
||||
android:label="@string/title_advanced_key_info"/>
|
||||
<activity
|
||||
android:name=".ui.SettingsActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||
@@ -433,6 +442,14 @@
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".ui.MainActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.CertifyFingerprintActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||
android:label="@string/title_certify_key">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".ui.MainActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.QrCodeScanActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||
@@ -646,14 +663,7 @@
|
||||
<activity
|
||||
android:name=".ui.PassphraseDialogActivity"
|
||||
android:theme="@android:style/Theme.NoDisplay" />
|
||||
<activity
|
||||
android:name="com.haibison.android.lockpattern.LockPatternActivity"
|
||||
android:label="@string/title_unlock"
|
||||
android:configChanges="orientation|screenSize|keyboard|keyboardHidden"
|
||||
android:screenOrientation="user"
|
||||
android:theme="@style/Alp.42447968.Theme.Dialog.Light" />
|
||||
<activity
|
||||
android:name=".ui.PassphraseWizardActivity" />
|
||||
<activity android:name=".ui.PassphraseWizardActivity" />
|
||||
<!--
|
||||
NOTE: singleTop is set to get NFC foreground dispatch to work.
|
||||
Then, all NFC intents will be broadcasted to onNewIntent() of this activity!
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
revokeAccess();
|
||||
return true;
|
||||
case R.id.menu_api_save:
|
||||
case R.id.menu_api_save: {
|
||||
save();
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_api_settings_revoke: {
|
||||
revokeAccess();
|
||||
return true;
|
||||
}
|
||||
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();
|
||||
|
||||
@@ -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);
|
||||
getListView().setOnItemClickListener(this);
|
||||
|
||||
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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
startFragment(savedInstanceState, dataUri);
|
||||
Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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()));
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
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 mainBundle = new Bundle();
|
||||
mainBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri);
|
||||
mTabsAdapter.addTab(ViewKeyMainFragment.class,
|
||||
mainBundle, getString(R.string.key_view_tab_main));
|
||||
// Create an instance of the fragment
|
||||
ViewKeyFragment frag = ViewKeyFragment.newInstance(dataUri);
|
||||
|
||||
Bundle shareBundle = new Bundle();
|
||||
shareBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri);
|
||||
mTabsAdapter.addTab(ViewKeyShareFragment.class,
|
||||
shareBundle, getString(R.string.key_view_tab_share));
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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.
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
// 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)) {
|
||||
setSelection(1);
|
||||
}
|
||||
}
|
||||
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(0) && !data.isNull(mIndexHasCertify)) {
|
||||
setSelection(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
|
After Width: | Height: | Size: 623 B |
|
After Width: | Height: | Size: 787 B |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
BIN
OpenKeychain/src/main/res/drawable-hdpi/ic_folder_white_24dp.png
Normal file
|
After Width: | Height: | Size: 224 B |
BIN
OpenKeychain/src/main/res/drawable-hdpi/ic_lock_white_24dp.png
Normal file
|
After Width: | Height: | Size: 399 B |
|
After Width: | Height: | Size: 351 B |
BIN
OpenKeychain/src/main/res/drawable-hdpi/ic_nfc_white_24dp.png
Normal file
|
After Width: | Height: | Size: 740 B |
BIN
OpenKeychain/src/main/res/drawable-hdpi/ic_qrcode_white_24dp.png
Normal file
|
After Width: | Height: | Size: 427 B |
BIN
OpenKeychain/src/main/res/drawable-hdpi/ic_repeat_grey_24dp.png
Normal file
|
After Width: | Height: | Size: 297 B |
BIN
OpenKeychain/src/main/res/drawable-hdpi/ic_repeat_white_24dp.png
Normal file
|
After Width: | Height: | Size: 296 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 398 B |
|
Before Width: | Height: | Size: 361 B |
|
Before Width: | Height: | Size: 413 B |
|
Before Width: | Height: | Size: 242 B |
|
Before Width: | Height: | Size: 423 B |
|
Before Width: | Height: | Size: 792 B |
|
Before Width: | Height: | Size: 624 B |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 803 B |
|
Before Width: | Height: | Size: 327 B |
|
Before Width: | Height: | Size: 422 B |
|
Before Width: | Height: | Size: 398 B |
|
Before Width: | Height: | Size: 406 B |
|
Before Width: | Height: | Size: 390 B |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 717 B |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 790 B |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 395 B |
|
Before Width: | Height: | Size: 419 B |
|
Before Width: | Height: | Size: 387 B |
|
Before Width: | Height: | Size: 459 B |
|
Before Width: | Height: | Size: 419 B |
|
Before Width: | Height: | Size: 420 B |
|
Before Width: | Height: | Size: 514 B |
|
Before Width: | Height: | Size: 428 B |
|
Before Width: | Height: | Size: 523 B |
|
Before Width: | Height: | Size: 556 B |
|
Before Width: | Height: | Size: 260 B |
|
Before Width: | Height: | Size: 467 B |
|
Before Width: | Height: | Size: 115 B |