Merge branch 'issue-1115--no-versions-multisig' into 'master'

Be more helpful when no versions available due to mismatching signatures.

Closes #1115

See merge request !577
This commit is contained in:
Hans-Christoph Steiner 2017-09-05 10:10:14 +00:00
commit 5144aedec9
3 changed files with 129 additions and 21 deletions

View File

@ -3,6 +3,7 @@ package org.fdroid.fdroid.views;
import android.annotation.SuppressLint;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
@ -12,6 +13,7 @@ import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.TextViewCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.GridLayout;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
@ -47,6 +49,7 @@ import org.fdroid.fdroid.data.InstalledAppProvider;
import org.fdroid.fdroid.data.RepoProvider;
import org.fdroid.fdroid.privileged.views.AppDiff;
import org.fdroid.fdroid.privileged.views.AppSecurityPermissions;
import org.fdroid.fdroid.views.main.MainActivity;
import java.text.NumberFormat;
import java.util.ArrayList;
@ -94,8 +97,9 @@ public class AppDetailsRecyclerViewAdapter
private App app;
private final AppDetailsRecyclerViewAdapterCallbacks callbacks;
private RecyclerView recyclerView;
private ArrayList<Object> items;
private ArrayList<Apk> versions;
private List<Object> items;
private List<Apk> versions;
private List<Apk> compatibleVersionsDifferentSig;
private boolean showVersions;
private HeaderViewHolder headerView;
@ -112,14 +116,19 @@ public class AppDetailsRecyclerViewAdapter
// Get versions
versions = new ArrayList<>();
compatibleVersionsDifferentSig = new ArrayList<>();
final List<Apk> apks = ApkProvider.Helper.findByPackageName(context, this.app.packageName);
boolean showIncompatibleVersions = Preferences.get().showIncompatibleVersions();
for (final Apk apk : apks) {
boolean allowByCompatability = apk.compatible || Preferences.get().showIncompatibleVersions();
boolean allowBySig = this.app.installedSig == null || TextUtils.equals(this.app.installedSig, apk.sig);
if (allowByCompatability && allowBySig) {
boolean allowByCompatability = apk.compatible || showIncompatibleVersions;
boolean allowBySig = this.app.installedSig == null || showIncompatibleVersions || TextUtils.equals(this.app.installedSig, apk.sig);
if (allowByCompatability) {
compatibleVersionsDifferentSig.add(apk);
if (allowBySig) {
versions.add(apk);
}
}
}
if (items == null) {
items = new ArrayList<>();
@ -226,8 +235,12 @@ public class AppDetailsRecyclerViewAdapter
View permissions = inflater.inflate(R.layout.app_details2_links, parent, false);
return new PermissionsViewHolder(permissions);
case VIEWTYPE_VERSIONS:
View versions = inflater.inflate(R.layout.app_details2_links, parent, false);
return new VersionsViewHolder(versions);
View versionsView = inflater.inflate(R.layout.app_details2_links, parent, false);
if (versions.size() == 0) {
return new NoVersionsViewHolder(versionsView);
} else {
return new VersionsViewHolder(versionsView);
}
case VIEWTYPE_VERSION:
View version = inflater.inflate(R.layout.apklistitem, parent, false);
return new VersionViewHolder(version);
@ -244,21 +257,16 @@ public class AppDetailsRecyclerViewAdapter
headerView = header;
header.bindModel();
break;
// These don't have any specific requirements, they all get their state from the outer class.
case VIEWTYPE_SCREENSHOTS:
((ScreenShotsViewHolder) holder).bindModel();
break;
case VIEWTYPE_DONATE:
((DonateViewHolder) holder).bindModel();
break;
case VIEWTYPE_LINKS:
((LinksViewHolder) holder).bindModel();
break;
case VIEWTYPE_PERMISSIONS:
((PermissionsViewHolder) holder).bindModel();
break;
case VIEWTYPE_VERSIONS:
((VersionsViewHolder) holder).bindModel();
((AppDetailsViewHolder) holder).bindModel();
break;
case VIEWTYPE_VERSION:
final Apk apk = (Apk) items.get(position);
((VersionViewHolder) holder).bindModel(apk);
@ -562,7 +570,15 @@ public class AppDetailsRecyclerViewAdapter
super.onDetachedFromRecyclerView(recyclerView);
}
private class ScreenShotsViewHolder extends RecyclerView.ViewHolder {
private abstract class AppDetailsViewHolder extends RecyclerView.ViewHolder {
AppDetailsViewHolder(View itemView) {
super(itemView);
}
public abstract void bindModel();
}
private class ScreenShotsViewHolder extends AppDetailsViewHolder {
final RecyclerView recyclerView;
LinearLayoutManagerSnapHelper snapHelper;
@ -571,6 +587,7 @@ public class AppDetailsRecyclerViewAdapter
recyclerView = (RecyclerView) view.findViewById(R.id.screenshots);
}
@Override
public void bindModel() {
LinearLayoutManager lm = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(lm);
@ -586,7 +603,7 @@ public class AppDetailsRecyclerViewAdapter
}
}
private class DonateViewHolder extends RecyclerView.ViewHolder {
private class DonateViewHolder extends AppDetailsViewHolder {
final TextView donateHeading;
final GridLayout donationOptionsLayout;
@ -596,6 +613,7 @@ public class AppDetailsRecyclerViewAdapter
donationOptionsLayout = (GridLayout) view.findViewById(R.id.donation_options);
}
@Override
public void bindModel() {
if (TextUtils.isEmpty(app.authorName)) {
donateHeading.setText(context.getString(R.string.app_details_donate_prompt_unknown_author, app.name));
@ -640,7 +658,7 @@ public class AppDetailsRecyclerViewAdapter
}
}
private abstract class ExpandableLinearLayoutViewHolder extends RecyclerView.ViewHolder {
private abstract class ExpandableLinearLayoutViewHolder extends AppDetailsViewHolder {
final TextView headerView;
final LinearLayout contentView;
@ -667,6 +685,7 @@ public class AppDetailsRecyclerViewAdapter
super(view);
}
@Override
public void bindModel() {
itemView.setOnClickListener(new View.OnClickListener() {
@Override
@ -684,12 +703,80 @@ public class AppDetailsRecyclerViewAdapter
}
}
private class NoVersionsViewHolder extends AppDetailsViewHolder {
final TextView headerView;
NoVersionsViewHolder(View view) {
super(view);
headerView = (TextView) view.findViewById(R.id.information);
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(headerView, R.drawable.ic_access_time_24dp_grey600, 0, 0, 0);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
explainIncompatibleVersions();
}
});
}
@Override
public void bindModel() {
if (hasCompatibleApksDifferentSigs()) {
headerView.setText("No versions with compatible signature");
} else {
headerView.setText("No versions compatible with device");
}
}
/**
* Show a dialog to the user explaining the reaons there are no compatible versions.
* This will either be due to device features (e.g. NFC, API levels, etc) or being signed
* by a different certificate (as is often the case with apps from Google Play signed by
* upstream).
*/
private void explainIncompatibleVersions() {
String preferenceName = context.getString(R.string.show_incompat_versions);
String showIncompatible = context.getString(
R.string.app_details__no_versions__show_incompat_versions, preferenceName);
String message;
String title;
if (hasCompatibleApksDifferentSigs()) {
title = context.getString(R.string.app_details__no_versions__no_compatible_signatures);
message = context.getString(R.string.app_details__no_versions__explain_incompatible_signatures) +
"\n\n" + showIncompatible;
} else {
title = context.getString(R.string.app_details__no_versions__none_compatible_with_device);
message = showIncompatible;
}
new AlertDialog.Builder(context)
.setTitle(title)
.setMessage(message)
.setPositiveButton(R.string.menu_settings, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(context, MainActivity.class);
intent.putExtra(MainActivity.EXTRA_VIEW_SETTINGS, true);
context.startActivity(intent);
}
})
.setNegativeButton(R.string.cancel, null)
.show();
}
private boolean hasCompatibleApksDifferentSigs() {
return compatibleVersionsDifferentSig != null && compatibleVersionsDifferentSig.size() > 0;
}
}
private class PermissionsViewHolder extends ExpandableLinearLayoutViewHolder {
PermissionsViewHolder(View view) {
super(view);
}
@Override
public void bindModel() {
itemView.setOnClickListener(new View.OnClickListener() {
@Override
@ -721,6 +808,7 @@ public class AppDetailsRecyclerViewAdapter
super(view);
}
@Override
public void bindModel() {
itemView.setOnClickListener(new View.OnClickListener() {
@Override
@ -875,12 +963,18 @@ public class AppDetailsRecyclerViewAdapter
nativecode.setVisibility(View.GONE);
}
boolean mismatchedSig = app.installedSig != null && !TextUtils.equals(app.installedSig, apk.sig);
if (apk.incompatibleReasons != null) {
incompatibleReasons.setText(
context.getResources().getString(
R.string.requires_features,
TextUtils.join(", ", apk.incompatibleReasons)));
incompatibleReasons.setVisibility(View.VISIBLE);
} else if (mismatchedSig) {
incompatibleReasons.setText(
context.getString(R.string.app_details__incompatible_mismatched_signature));
incompatibleReasons.setVisibility(View.VISIBLE);
} else {
incompatibleReasons.setVisibility(View.GONE);
}
@ -898,7 +992,7 @@ public class AppDetailsRecyclerViewAdapter
nativecode,
};
for (final View v : views) {
v.setEnabled(apk.compatible);
v.setEnabled(apk.compatible && !mismatchedSig);
}
itemView.setOnClickListener(new View.OnClickListener() {
@Override

View File

@ -54,6 +54,7 @@ public class MainActivity extends AppCompatActivity implements BottomNavigationB
private static final String TAG = "MainActivity";
public static final String EXTRA_VIEW_UPDATES = "org.fdroid.fdroid.views.main.MainActivity.VIEW_UPDATES";
public static final String EXTRA_VIEW_SETTINGS = "org.fdroid.fdroid.views.main.MainActivity.VIEW_SETTINGS";
private static final String ADD_REPO_INTENT_HANDLED = "addRepoIntentHandled";
@ -156,6 +157,11 @@ public class MainActivity extends AppCompatActivity implements BottomNavigationB
pager.scrollToPosition(adapter.adapterPositionFromItemId(R.id.updates));
selectedMenuId = R.id.updates;
setSelectedMenuInNav();
} else if (getIntent().hasExtra(EXTRA_VIEW_SETTINGS)) {
getIntent().removeExtra(EXTRA_VIEW_SETTINGS);
pager.scrollToPosition(adapter.adapterPositionFromItemId(R.id.settings));
selectedMenuId = R.id.settings;
setSelectedMenuInNav();
}
// AppDetails2 and RepoDetailsActivity set different NFC actions, so reset here

View File

@ -54,6 +54,14 @@
<string name="app_details_donate_prompt_unknown_author">Buy the developers of %1$s a coffee!</string>
<string name="app_details_donate_prompt">%1$s is created by %2$s. Buy them a coffee!</string>
<string name="app_details__incompatible_mismatched_signature">Different signature to installed version</string>
<string name="app_details__no_versions__show_incompat_versions">To show incompatible versions here anyway, enable the \"%1$s\" setting.</string>
<string name="app_details__no_versions__no_compatible_signatures">No versions with compatible signature</string>
<string name="app_details__no_versions__none_compatible_with_device">No versions compatible with device</string>
<string name="app_details__no_versions__explain_incompatible_signatures">The installed version is not compatible with any available versions. Uninstalling the app will enable you to view and install compatible versions.
This often occurs with apps installed via Google Play or other sources, if they are signed by a different certificate.</string>
<string name="about_title">About F-Droid</string>
<string name="about_version">Version</string>
<string name="about_site">Website</string>