merged dialog
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user