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:
commit
5144aedec9
@ -3,6 +3,7 @@ package org.fdroid.fdroid.views;
|
|||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
@ -12,6 +13,7 @@ import android.support.annotation.LayoutRes;
|
|||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.view.ViewCompat;
|
import android.support.v4.view.ViewCompat;
|
||||||
import android.support.v4.widget.TextViewCompat;
|
import android.support.v4.widget.TextViewCompat;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.support.v7.widget.GridLayout;
|
import android.support.v7.widget.GridLayout;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
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.data.RepoProvider;
|
||||||
import org.fdroid.fdroid.privileged.views.AppDiff;
|
import org.fdroid.fdroid.privileged.views.AppDiff;
|
||||||
import org.fdroid.fdroid.privileged.views.AppSecurityPermissions;
|
import org.fdroid.fdroid.privileged.views.AppSecurityPermissions;
|
||||||
|
import org.fdroid.fdroid.views.main.MainActivity;
|
||||||
|
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -94,8 +97,9 @@ public class AppDetailsRecyclerViewAdapter
|
|||||||
private App app;
|
private App app;
|
||||||
private final AppDetailsRecyclerViewAdapterCallbacks callbacks;
|
private final AppDetailsRecyclerViewAdapterCallbacks callbacks;
|
||||||
private RecyclerView recyclerView;
|
private RecyclerView recyclerView;
|
||||||
private ArrayList<Object> items;
|
private List<Object> items;
|
||||||
private ArrayList<Apk> versions;
|
private List<Apk> versions;
|
||||||
|
private List<Apk> compatibleVersionsDifferentSig;
|
||||||
private boolean showVersions;
|
private boolean showVersions;
|
||||||
|
|
||||||
private HeaderViewHolder headerView;
|
private HeaderViewHolder headerView;
|
||||||
@ -112,12 +116,17 @@ public class AppDetailsRecyclerViewAdapter
|
|||||||
|
|
||||||
// Get versions
|
// Get versions
|
||||||
versions = new ArrayList<>();
|
versions = new ArrayList<>();
|
||||||
|
compatibleVersionsDifferentSig = new ArrayList<>();
|
||||||
final List<Apk> apks = ApkProvider.Helper.findByPackageName(context, this.app.packageName);
|
final List<Apk> apks = ApkProvider.Helper.findByPackageName(context, this.app.packageName);
|
||||||
|
boolean showIncompatibleVersions = Preferences.get().showIncompatibleVersions();
|
||||||
for (final Apk apk : apks) {
|
for (final Apk apk : apks) {
|
||||||
boolean allowByCompatability = apk.compatible || Preferences.get().showIncompatibleVersions();
|
boolean allowByCompatability = apk.compatible || showIncompatibleVersions;
|
||||||
boolean allowBySig = this.app.installedSig == null || TextUtils.equals(this.app.installedSig, apk.sig);
|
boolean allowBySig = this.app.installedSig == null || showIncompatibleVersions || TextUtils.equals(this.app.installedSig, apk.sig);
|
||||||
if (allowByCompatability && allowBySig) {
|
if (allowByCompatability) {
|
||||||
versions.add(apk);
|
compatibleVersionsDifferentSig.add(apk);
|
||||||
|
if (allowBySig) {
|
||||||
|
versions.add(apk);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,8 +235,12 @@ public class AppDetailsRecyclerViewAdapter
|
|||||||
View permissions = inflater.inflate(R.layout.app_details2_links, parent, false);
|
View permissions = inflater.inflate(R.layout.app_details2_links, parent, false);
|
||||||
return new PermissionsViewHolder(permissions);
|
return new PermissionsViewHolder(permissions);
|
||||||
case VIEWTYPE_VERSIONS:
|
case VIEWTYPE_VERSIONS:
|
||||||
View versions = inflater.inflate(R.layout.app_details2_links, parent, false);
|
View versionsView = inflater.inflate(R.layout.app_details2_links, parent, false);
|
||||||
return new VersionsViewHolder(versions);
|
if (versions.size() == 0) {
|
||||||
|
return new NoVersionsViewHolder(versionsView);
|
||||||
|
} else {
|
||||||
|
return new VersionsViewHolder(versionsView);
|
||||||
|
}
|
||||||
case VIEWTYPE_VERSION:
|
case VIEWTYPE_VERSION:
|
||||||
View version = inflater.inflate(R.layout.apklistitem, parent, false);
|
View version = inflater.inflate(R.layout.apklistitem, parent, false);
|
||||||
return new VersionViewHolder(version);
|
return new VersionViewHolder(version);
|
||||||
@ -244,21 +257,16 @@ public class AppDetailsRecyclerViewAdapter
|
|||||||
headerView = header;
|
headerView = header;
|
||||||
header.bindModel();
|
header.bindModel();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// These don't have any specific requirements, they all get their state from the outer class.
|
||||||
case VIEWTYPE_SCREENSHOTS:
|
case VIEWTYPE_SCREENSHOTS:
|
||||||
((ScreenShotsViewHolder) holder).bindModel();
|
|
||||||
break;
|
|
||||||
case VIEWTYPE_DONATE:
|
case VIEWTYPE_DONATE:
|
||||||
((DonateViewHolder) holder).bindModel();
|
|
||||||
break;
|
|
||||||
case VIEWTYPE_LINKS:
|
case VIEWTYPE_LINKS:
|
||||||
((LinksViewHolder) holder).bindModel();
|
|
||||||
break;
|
|
||||||
case VIEWTYPE_PERMISSIONS:
|
case VIEWTYPE_PERMISSIONS:
|
||||||
((PermissionsViewHolder) holder).bindModel();
|
|
||||||
break;
|
|
||||||
case VIEWTYPE_VERSIONS:
|
case VIEWTYPE_VERSIONS:
|
||||||
((VersionsViewHolder) holder).bindModel();
|
((AppDetailsViewHolder) holder).bindModel();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIEWTYPE_VERSION:
|
case VIEWTYPE_VERSION:
|
||||||
final Apk apk = (Apk) items.get(position);
|
final Apk apk = (Apk) items.get(position);
|
||||||
((VersionViewHolder) holder).bindModel(apk);
|
((VersionViewHolder) holder).bindModel(apk);
|
||||||
@ -562,7 +570,15 @@ public class AppDetailsRecyclerViewAdapter
|
|||||||
super.onDetachedFromRecyclerView(recyclerView);
|
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;
|
final RecyclerView recyclerView;
|
||||||
LinearLayoutManagerSnapHelper snapHelper;
|
LinearLayoutManagerSnapHelper snapHelper;
|
||||||
|
|
||||||
@ -571,6 +587,7 @@ public class AppDetailsRecyclerViewAdapter
|
|||||||
recyclerView = (RecyclerView) view.findViewById(R.id.screenshots);
|
recyclerView = (RecyclerView) view.findViewById(R.id.screenshots);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void bindModel() {
|
public void bindModel() {
|
||||||
LinearLayoutManager lm = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
|
LinearLayoutManager lm = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
|
||||||
recyclerView.setLayoutManager(lm);
|
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 TextView donateHeading;
|
||||||
final GridLayout donationOptionsLayout;
|
final GridLayout donationOptionsLayout;
|
||||||
|
|
||||||
@ -596,6 +613,7 @@ public class AppDetailsRecyclerViewAdapter
|
|||||||
donationOptionsLayout = (GridLayout) view.findViewById(R.id.donation_options);
|
donationOptionsLayout = (GridLayout) view.findViewById(R.id.donation_options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void bindModel() {
|
public void bindModel() {
|
||||||
if (TextUtils.isEmpty(app.authorName)) {
|
if (TextUtils.isEmpty(app.authorName)) {
|
||||||
donateHeading.setText(context.getString(R.string.app_details_donate_prompt_unknown_author, app.name));
|
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 TextView headerView;
|
||||||
final LinearLayout contentView;
|
final LinearLayout contentView;
|
||||||
|
|
||||||
@ -667,6 +685,7 @@ public class AppDetailsRecyclerViewAdapter
|
|||||||
super(view);
|
super(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void bindModel() {
|
public void bindModel() {
|
||||||
itemView.setOnClickListener(new View.OnClickListener() {
|
itemView.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@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 {
|
private class PermissionsViewHolder extends ExpandableLinearLayoutViewHolder {
|
||||||
|
|
||||||
PermissionsViewHolder(View view) {
|
PermissionsViewHolder(View view) {
|
||||||
super(view);
|
super(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void bindModel() {
|
public void bindModel() {
|
||||||
itemView.setOnClickListener(new View.OnClickListener() {
|
itemView.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
@ -721,6 +808,7 @@ public class AppDetailsRecyclerViewAdapter
|
|||||||
super(view);
|
super(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void bindModel() {
|
public void bindModel() {
|
||||||
itemView.setOnClickListener(new View.OnClickListener() {
|
itemView.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
@ -875,12 +963,18 @@ public class AppDetailsRecyclerViewAdapter
|
|||||||
nativecode.setVisibility(View.GONE);
|
nativecode.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean mismatchedSig = app.installedSig != null && !TextUtils.equals(app.installedSig, apk.sig);
|
||||||
|
|
||||||
if (apk.incompatibleReasons != null) {
|
if (apk.incompatibleReasons != null) {
|
||||||
incompatibleReasons.setText(
|
incompatibleReasons.setText(
|
||||||
context.getResources().getString(
|
context.getResources().getString(
|
||||||
R.string.requires_features,
|
R.string.requires_features,
|
||||||
TextUtils.join(", ", apk.incompatibleReasons)));
|
TextUtils.join(", ", apk.incompatibleReasons)));
|
||||||
incompatibleReasons.setVisibility(View.VISIBLE);
|
incompatibleReasons.setVisibility(View.VISIBLE);
|
||||||
|
} else if (mismatchedSig) {
|
||||||
|
incompatibleReasons.setText(
|
||||||
|
context.getString(R.string.app_details__incompatible_mismatched_signature));
|
||||||
|
incompatibleReasons.setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
incompatibleReasons.setVisibility(View.GONE);
|
incompatibleReasons.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
@ -898,7 +992,7 @@ public class AppDetailsRecyclerViewAdapter
|
|||||||
nativecode,
|
nativecode,
|
||||||
};
|
};
|
||||||
for (final View v : views) {
|
for (final View v : views) {
|
||||||
v.setEnabled(apk.compatible);
|
v.setEnabled(apk.compatible && !mismatchedSig);
|
||||||
}
|
}
|
||||||
itemView.setOnClickListener(new View.OnClickListener() {
|
itemView.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -54,6 +54,7 @@ public class MainActivity extends AppCompatActivity implements BottomNavigationB
|
|||||||
private static final String TAG = "MainActivity";
|
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_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";
|
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));
|
pager.scrollToPosition(adapter.adapterPositionFromItemId(R.id.updates));
|
||||||
selectedMenuId = R.id.updates;
|
selectedMenuId = R.id.updates;
|
||||||
setSelectedMenuInNav();
|
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
|
// AppDetails2 and RepoDetailsActivity set different NFC actions, so reset here
|
||||||
|
@ -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_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_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_title">About F-Droid</string>
|
||||||
<string name="about_version">Version</string>
|
<string name="about_version">Version</string>
|
||||||
<string name="about_site">Website</string>
|
<string name="about_site">Website</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user