Merge branch 'master' of github.com:open-keychain/open-keychain
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
## 3.0
|
## 3.0
|
||||||
* Fix Yubikey signature generation and decryption
|
* Full support for Yubikey signature generation and decryption!
|
||||||
* Propose installable compatible apps in apps list
|
* Propose installable compatible apps in apps list
|
||||||
* New design for decryption screens
|
* New design for decryption screens
|
||||||
* Many fixes for key import, also fixes stripped keys
|
* Many fixes for key import, also fixes stripped keys
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
* User interface to generate custom keys
|
* User interface to generate custom keys
|
||||||
* Fixing user id revocation certificates
|
* Fixing user id revocation certificates
|
||||||
* New cloud search (searches over traditional keyservers and keybase.io)
|
* New cloud search (searches over traditional keyservers and keybase.io)
|
||||||
* Support to strip keys inside OpenKeychain
|
* Support for stripping keys inside OpenKeychain
|
||||||
|
|
||||||
## 2.9.2
|
## 2.9.2
|
||||||
* Fix keys broken in 2.9.1
|
* Fix keys broken in 2.9.1
|
||||||
@@ -228,4 +228,4 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
|
|||||||
* Allow signing only
|
* Allow signing only
|
||||||
|
|
||||||
## 0.7.0
|
## 0.7.0
|
||||||
* Initial public release
|
* Initial public release
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="org.sufficientlysecure.keychain"
|
package="org.sufficientlysecure.keychain"
|
||||||
android:installLocation="auto"
|
android:installLocation="auto"
|
||||||
android:versionCode="29200"
|
android:versionCode="29201"
|
||||||
android:versionName="2.9.2">
|
android:versionName="3.0 beta1">
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
General remarks
|
General remarks
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ public class ImportKeysList extends ArrayList<ImportKeysListEntry> {
|
|||||||
// being a little anal about the ArrayList#addAll contract here
|
// being a little anal about the ArrayList#addAll contract here
|
||||||
private boolean mergeDupes(ImportKeysListEntry incoming, ImportKeysListEntry existing) {
|
private boolean mergeDupes(ImportKeysListEntry incoming, ImportKeysListEntry existing) {
|
||||||
boolean modified = false;
|
boolean modified = false;
|
||||||
|
|
||||||
|
// if any source thinks it’s expired/revoked, it is
|
||||||
if (incoming.isRevoked()) {
|
if (incoming.isRevoked()) {
|
||||||
existing.setRevoked(true);
|
existing.setRevoked(true);
|
||||||
modified = true;
|
modified = true;
|
||||||
@@ -67,8 +69,16 @@ public class ImportKeysList extends ArrayList<ImportKeysListEntry> {
|
|||||||
existing.setExpired(true);
|
existing.setExpired(true);
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we’re going to want to try to fetch the key from everywhere we found it, so remember
|
||||||
|
// all the origins
|
||||||
for (String origin : incoming.getOrigins()) {
|
for (String origin : incoming.getOrigins()) {
|
||||||
existing.addOrigin(origin);
|
existing.addOrigin(origin);
|
||||||
|
|
||||||
|
// to work properly, Keybase-sourced entries need to pass along the extra
|
||||||
|
if (KeybaseKeyserver.ORIGIN.equals(origin)) {
|
||||||
|
existing.setExtraData(incoming.getExtraData());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ArrayList<String> incomingIDs = incoming.getUserIds();
|
ArrayList<String> incomingIDs = incoming.getUserIds();
|
||||||
ArrayList<String> existingIDs = existing.getUserIds();
|
ArrayList<String> existingIDs = existing.getUserIds();
|
||||||
|
|||||||
@@ -91,7 +91,6 @@ public class AppSettingsActivity extends ActionBarActivity {
|
|||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: breaks Yubikey NFC Foreground dispatching
|
|
||||||
private void startApp() {
|
private void startApp() {
|
||||||
Intent i;
|
Intent i;
|
||||||
PackageManager manager = getPackageManager();
|
PackageManager manager = getPackageManager();
|
||||||
|
|||||||
@@ -40,9 +40,11 @@ import android.widget.AdapterView.OnItemClickListener;
|
|||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
public class AppsListFragment extends ListFragment implements
|
public class AppsListFragment extends ListFragment implements
|
||||||
LoaderManager.LoaderCallbacks<Cursor> {
|
LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
@@ -57,14 +59,31 @@ public class AppsListFragment extends ListFragment implements
|
|||||||
getListView().setOnItemClickListener(new OnItemClickListener() {
|
getListView().setOnItemClickListener(new OnItemClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
|
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
|
||||||
boolean isInstalled = mAdapter.getItemIsInstalled(position);
|
|
||||||
String selectedPackageName = mAdapter.getItemPackageName(position);
|
String selectedPackageName = mAdapter.getItemPackageName(position);
|
||||||
|
boolean installed = mAdapter.getItemIsInstalled(position);
|
||||||
|
boolean registered = mAdapter.getItemIsRegistered(position);
|
||||||
|
|
||||||
if (isInstalled) {
|
if (installed) {
|
||||||
// edit app settings
|
if (registered) {
|
||||||
Intent intent = new Intent(getActivity(), AppSettingsActivity.class);
|
// edit app settings
|
||||||
intent.setData(KeychainContract.ApiApps.buildByPackageNameUri(selectedPackageName));
|
Intent intent = new Intent(getActivity(), AppSettingsActivity.class);
|
||||||
startActivity(intent);
|
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 {
|
} else {
|
||||||
try {
|
try {
|
||||||
startActivity(new Intent(Intent.ACTION_VIEW,
|
startActivity(new Intent(Intent.ACTION_VIEW,
|
||||||
@@ -88,26 +107,34 @@ public class AppsListFragment extends ListFragment implements
|
|||||||
mAdapter = new RegisteredAppsAdapter(getActivity(), null, 0);
|
mAdapter = new RegisteredAppsAdapter(getActivity(), null, 0);
|
||||||
setListAdapter(mAdapter);
|
setListAdapter(mAdapter);
|
||||||
|
|
||||||
// Prepare the loader. Either re-connect with an existing one,
|
// Loader is started in onResume!
|
||||||
// or start a new one.
|
}
|
||||||
getLoaderManager().initLoader(0, null, this);
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
// after coming back from Google Play -> reload
|
||||||
|
getLoaderManager().restartLoader(0, null, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String TEMP_COLUMN_INSTALLED = "INSTALLED";
|
|
||||||
private static final String TEMP_COLUMN_NAME = "NAME";
|
private static final String TEMP_COLUMN_NAME = "NAME";
|
||||||
|
private static final String TEMP_COLUMN_INSTALLED = "INSTALLED";
|
||||||
|
private static final String TEMP_COLUMN_REGISTERED = "REGISTERED";
|
||||||
|
|
||||||
// These are the Contacts rows that we will retrieve.
|
// These are the Contacts rows that we will retrieve.
|
||||||
static final String[] PROJECTION = new String[]{
|
static final String[] PROJECTION = new String[]{
|
||||||
ApiApps._ID, // 0
|
ApiApps._ID, // 0
|
||||||
ApiApps.PACKAGE_NAME, // 1
|
ApiApps.PACKAGE_NAME, // 1
|
||||||
|
"null as " + TEMP_COLUMN_NAME, // installed apps can retrieve app name from Android OS
|
||||||
"0 as " + TEMP_COLUMN_INSTALLED, // changed later in cursor joiner
|
"0 as " + TEMP_COLUMN_INSTALLED, // changed later in cursor joiner
|
||||||
"null as " + TEMP_COLUMN_NAME // installed apps can retrieve app name from Android OS
|
"1 as " + TEMP_COLUMN_REGISTERED // if it is in db it is registered
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final int INDEX_ID = 0;
|
private static final int INDEX_ID = 0;
|
||||||
private static final int INDEX_PACKAGE_NAME = 1;
|
private static final int INDEX_PACKAGE_NAME = 1;
|
||||||
private static final int INDEX_INSTALLED = 2;
|
private static final int INDEX_NAME = 2;
|
||||||
private static final int INDEX_NAME = 3;
|
private static final int INDEX_INSTALLED = 3;
|
||||||
|
private static final int INDEX_REGISTERED = 4;
|
||||||
|
|
||||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||||
// This is called when a new Loader needs to be created. This
|
// This is called when a new Loader needs to be created. This
|
||||||
@@ -126,18 +153,20 @@ public class AppsListFragment extends ListFragment implements
|
|||||||
MatrixCursor availableAppsCursor = new MatrixCursor(new String[]{
|
MatrixCursor availableAppsCursor = new MatrixCursor(new String[]{
|
||||||
ApiApps._ID,
|
ApiApps._ID,
|
||||||
ApiApps.PACKAGE_NAME,
|
ApiApps.PACKAGE_NAME,
|
||||||
|
TEMP_COLUMN_NAME,
|
||||||
TEMP_COLUMN_INSTALLED,
|
TEMP_COLUMN_INSTALLED,
|
||||||
TEMP_COLUMN_NAME
|
TEMP_COLUMN_REGISTERED
|
||||||
});
|
});
|
||||||
availableAppsCursor.addRow(new Object[]{1, "com.fsck.k9", 0, "K-9 Mail"});
|
availableAppsCursor.addRow(new Object[]{1, "com.fsck.k9", "K-9 Mail", 0, 0});
|
||||||
availableAppsCursor.addRow(new Object[]{1, "eu.siacs.conversations", 0, "Conversations (Instant Messaging)"});
|
availableAppsCursor.addRow(new Object[]{1, "eu.siacs.conversations", "Conversations (Instant Messaging)", 0, 0});
|
||||||
// availableAppsCursor.addRow(new Object[]{1, "org.sufficientlysecure.keychain.demo", 0, "API Example"});
|
// availableAppsCursor.addRow(new Object[]{1, "org.sufficientlysecure.keychain.demo", "API Example", 0, 0});
|
||||||
|
|
||||||
MatrixCursor mergedCursor = new MatrixCursor(new String[]{
|
MatrixCursor mergedCursor = new MatrixCursor(new String[]{
|
||||||
ApiApps._ID,
|
ApiApps._ID,
|
||||||
ApiApps.PACKAGE_NAME,
|
ApiApps.PACKAGE_NAME,
|
||||||
|
TEMP_COLUMN_NAME,
|
||||||
TEMP_COLUMN_INSTALLED,
|
TEMP_COLUMN_INSTALLED,
|
||||||
TEMP_COLUMN_NAME
|
TEMP_COLUMN_REGISTERED
|
||||||
});
|
});
|
||||||
|
|
||||||
CursorJoiner joiner = new CursorJoiner(
|
CursorJoiner joiner = new CursorJoiner(
|
||||||
@@ -148,45 +177,39 @@ public class AppsListFragment extends ListFragment implements
|
|||||||
for (CursorJoiner.Result joinerResult : joiner) {
|
for (CursorJoiner.Result joinerResult : joiner) {
|
||||||
switch (joinerResult) {
|
switch (joinerResult) {
|
||||||
case LEFT: {
|
case LEFT: {
|
||||||
// handle case where a row in cursorA is unique
|
// handle case where a row in availableAppsCursor is unique
|
||||||
|
String packageName = availableAppsCursor.getString(INDEX_PACKAGE_NAME);
|
||||||
|
|
||||||
mergedCursor.addRow(new Object[]{
|
mergedCursor.addRow(new Object[]{
|
||||||
availableAppsCursor.getLong(INDEX_ID),
|
availableAppsCursor.getLong(INDEX_ID),
|
||||||
availableAppsCursor.getString(INDEX_PACKAGE_NAME),
|
packageName,
|
||||||
availableAppsCursor.getInt(INDEX_INSTALLED),
|
availableAppsCursor.getString(INDEX_NAME),
|
||||||
availableAppsCursor.getString(INDEX_NAME)
|
isInstalled(packageName),
|
||||||
|
0
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RIGHT: {
|
case RIGHT: {
|
||||||
// handle case where a row in cursorB is unique
|
// handle case where a row in data is unique
|
||||||
String packageName = data.getString(INDEX_PACKAGE_NAME);
|
String packageName = data.getString(INDEX_PACKAGE_NAME);
|
||||||
int installed;
|
|
||||||
try {
|
|
||||||
getActivity().getPackageManager().getApplicationInfo(packageName, 0);
|
|
||||||
installed = 1;
|
|
||||||
} catch (final PackageManager.NameNotFoundException e) {
|
|
||||||
installed = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
mergedCursor.addRow(new Object[]{
|
mergedCursor.addRow(new Object[]{
|
||||||
data.getLong(INDEX_ID),
|
data.getLong(INDEX_ID),
|
||||||
packageName,
|
packageName,
|
||||||
installed,
|
null,
|
||||||
null
|
isInstalled(packageName),
|
||||||
|
1
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BOTH: {
|
case BOTH: {
|
||||||
// handle case where a row with the same key is in both cursors
|
// handle case where a row with the same key is in both cursors
|
||||||
String packageName = data.getString(INDEX_PACKAGE_NAME);
|
String packageName = data.getString(INDEX_PACKAGE_NAME);
|
||||||
|
|
||||||
String name;
|
String name;
|
||||||
int installed;
|
if (isInstalled(packageName) == 1) {
|
||||||
try {
|
|
||||||
getActivity().getPackageManager().getApplicationInfo(packageName, 0);
|
|
||||||
installed = 1;
|
|
||||||
name = data.getString(INDEX_NAME);
|
name = data.getString(INDEX_NAME);
|
||||||
} catch (final PackageManager.NameNotFoundException e) {
|
} else {
|
||||||
installed = 0;
|
|
||||||
// if not installed take name from available apps list
|
// if not installed take name from available apps list
|
||||||
name = availableAppsCursor.getString(INDEX_NAME);
|
name = availableAppsCursor.getString(INDEX_NAME);
|
||||||
}
|
}
|
||||||
@@ -194,20 +217,29 @@ public class AppsListFragment extends ListFragment implements
|
|||||||
mergedCursor.addRow(new Object[]{
|
mergedCursor.addRow(new Object[]{
|
||||||
data.getLong(INDEX_ID),
|
data.getLong(INDEX_ID),
|
||||||
packageName,
|
packageName,
|
||||||
installed,
|
name,
|
||||||
name
|
isInstalled(packageName),
|
||||||
|
1
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Swap the new cursor in. (The framework will take care of closing the
|
// Swap the new cursor in. (The framework will take care of closing the
|
||||||
// old cursor once we return.)
|
// old cursor once we return.)
|
||||||
mAdapter.swapCursor(mergedCursor);
|
mAdapter.swapCursor(mergedCursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int isInstalled(String packageName) {
|
||||||
|
try {
|
||||||
|
getActivity().getPackageManager().getApplicationInfo(packageName, 0);
|
||||||
|
return 1;
|
||||||
|
} catch (final PackageManager.NameNotFoundException e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void onLoaderReset(Loader<Cursor> loader) {
|
public void onLoaderReset(Loader<Cursor> loader) {
|
||||||
// This is called when the last Cursor provided to onLoadFinished()
|
// This is called when the last Cursor provided to onLoadFinished()
|
||||||
// above is about to be closed. We need to make sure we are no
|
// above is about to be closed. We need to make sure we are no
|
||||||
@@ -258,6 +290,18 @@ public class AppsListFragment extends ListFragment implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bindView(View view, Context context, Cursor cursor) {
|
public void bindView(View view, Context context, Cursor cursor) {
|
||||||
TextView text = (TextView) view.findViewById(R.id.api_apps_adapter_item_name);
|
TextView text = (TextView) view.findViewById(R.id.api_apps_adapter_item_name);
|
||||||
|
|||||||
@@ -5,6 +5,20 @@ And don't add newlines before or after p tags because of transifex -->
|
|||||||
<head>
|
<head>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
<h2>3.0</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Full support for Yubikey signature generation and decryption!</li>
|
||||||
|
<li>Propose installable compatible apps in apps list</li>
|
||||||
|
<li>New design for decryption screens</li>
|
||||||
|
<li>Many fixes for key import, also fixes stripped keys</li>
|
||||||
|
<li>Honor and display key authenticate flags</li>
|
||||||
|
<li>User interface to generate custom keys</li>
|
||||||
|
<li>Fixing user id revocation certificates</li>
|
||||||
|
<li>New cloud search (searches over traditional keyservers and keybase.io)</li>
|
||||||
|
<li>Support for stripping keys inside OpenKeychain</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h2>2.9.2</h2>
|
<h2>2.9.2</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Fix keys broken in 2.9.1</li>
|
<li>Fix keys broken in 2.9.1</li>
|
||||||
|
|||||||
Reference in New Issue
Block a user