Merge branch 'versions-overhaul' into 'master'
App version list overhaul Closes #1564, #1327, #1525, #1333, and #1322 See merge request fdroid/fdroidclient!725
This commit is contained in:
commit
070d9af9a8
@ -427,6 +427,7 @@ public class AppDetails2 extends AppCompatActivity
|
||||
case Downloading:
|
||||
if (newStatus.progressMax == 0) {
|
||||
// The first progress notification we get telling us our status is "Downloading"
|
||||
adapter.notifyAboutDownloadedApk(newStatus.apk);
|
||||
adapter.setIndeterminateProgress(R.string.download_pending);
|
||||
} else {
|
||||
adapter.setProgress(newStatus.progressCurrent, newStatus.progressMax);
|
||||
|
@ -18,6 +18,7 @@ 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.LinearSmoothScroller;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.Html;
|
||||
import android.text.Spannable;
|
||||
@ -26,11 +27,12 @@ import android.text.TextUtils;
|
||||
import android.text.format.DateFormat;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.URLSpan;
|
||||
import android.util.Log;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
@ -45,7 +47,7 @@ import org.fdroid.fdroid.Utils;
|
||||
import org.fdroid.fdroid.data.Apk;
|
||||
import org.fdroid.fdroid.data.ApkProvider;
|
||||
import org.fdroid.fdroid.data.App;
|
||||
import org.fdroid.fdroid.data.InstalledAppProvider;
|
||||
import org.fdroid.fdroid.data.Repo;
|
||||
import org.fdroid.fdroid.data.RepoProvider;
|
||||
import org.fdroid.fdroid.installer.Installer;
|
||||
import org.fdroid.fdroid.privileged.views.AppDiff;
|
||||
@ -54,6 +56,7 @@ import org.fdroid.fdroid.views.main.MainActivity;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@ -103,6 +106,9 @@ public class AppDetailsRecyclerViewAdapter
|
||||
|
||||
private HeaderViewHolder headerView;
|
||||
|
||||
private Apk downloadedApk;
|
||||
private final HashMap<Integer, Boolean> versionsExpandTracker = new HashMap<>();
|
||||
|
||||
public AppDetailsRecyclerViewAdapter(Context context, @NonNull App app, AppDetailsRecyclerViewAdapterCallbacks callbacks) {
|
||||
this.context = context;
|
||||
this.callbacks = callbacks;
|
||||
@ -125,6 +131,9 @@ public class AppDetailsRecyclerViewAdapter
|
||||
compatibleVersionsDifferentSig.add(apk);
|
||||
if (allowBySig) {
|
||||
versions.add(apk);
|
||||
if (!versionsExpandTracker.containsKey(apk.versionCode)) {
|
||||
versionsExpandTracker.put(apk.versionCode, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -142,11 +151,18 @@ public class AppDetailsRecyclerViewAdapter
|
||||
addItem(VIEWTYPE_LINKS);
|
||||
addItem(VIEWTYPE_PERMISSIONS);
|
||||
addItem(VIEWTYPE_VERSIONS);
|
||||
if (showVersions) {
|
||||
setShowVersions(true);
|
||||
}
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
void setShowVersions(boolean showVersions) {
|
||||
setShowVersions(showVersions, false);
|
||||
}
|
||||
|
||||
void setShowVersions(boolean showVersions, boolean scrollTo) {
|
||||
this.showVersions = showVersions;
|
||||
boolean itemsWereRemoved = items.removeAll(versions);
|
||||
int startIndex = items.indexOf(VIEWTYPE_VERSIONS) + 1;
|
||||
@ -158,11 +174,26 @@ public class AppDetailsRecyclerViewAdapter
|
||||
if (showVersions) {
|
||||
items.addAll(startIndex, versions);
|
||||
notifyItemRangeInserted(startIndex, versions.size());
|
||||
if (recyclerView != null) {
|
||||
((LinearLayoutManager) recyclerView.getLayoutManager()).scrollToPositionWithOffset(startIndex - 1, 0);
|
||||
if (recyclerView != null && scrollTo) {
|
||||
final LinearSmoothScroller smoothScroller = new LinearSmoothScroller(context) {
|
||||
@Override
|
||||
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
|
||||
// The default speed of smooth scrolling doesn't look good
|
||||
// and it's too fast when it happens while inserting
|
||||
// multiple recycler view items
|
||||
return 75f / displayMetrics.densityDpi;
|
||||
}
|
||||
};
|
||||
// Expanding the version list reveals up to 5 items by default
|
||||
int visibleVersionLimit = Math.min(versions.size(), 5);
|
||||
smoothScroller.setTargetPosition(startIndex + visibleVersionLimit - 1);
|
||||
recyclerView.getLayoutManager().startSmoothScroll(smoothScroller);
|
||||
}
|
||||
} else if (itemsWereRemoved) {
|
||||
notifyItemRangeRemoved(startIndex, versions.size());
|
||||
if (recyclerView != null && scrollTo) {
|
||||
recyclerView.smoothScrollToPosition(startIndex - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,10 +236,24 @@ public class AppDetailsRecyclerViewAdapter
|
||||
uriIsSetAndCanBeOpened(app.getLiberapayUri());
|
||||
}
|
||||
|
||||
private void notifyVersionViewsChanged() {
|
||||
int startIndex = items.indexOf(VIEWTYPE_VERSIONS) + 1;
|
||||
notifyItemRangeChanged(startIndex, versions.size());
|
||||
}
|
||||
|
||||
public void notifyAboutDownloadedApk(final Apk apk) {
|
||||
downloadedApk = apk;
|
||||
notifyVersionViewsChanged();
|
||||
}
|
||||
|
||||
public void clearProgress() {
|
||||
if (headerView != null) {
|
||||
headerView.clearProgress();
|
||||
}
|
||||
if (downloadedApk != null) {
|
||||
notifyVersionViewsChanged();
|
||||
downloadedApk = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setIndeterminateProgress(int resIdString) {
|
||||
@ -757,7 +802,7 @@ public class AppDetailsRecyclerViewAdapter
|
||||
itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
setShowVersions(!showVersions);
|
||||
setShowVersions(!showVersions, true);
|
||||
updateExpandableItem(showVersions);
|
||||
}
|
||||
});
|
||||
@ -945,135 +990,225 @@ public class AppDetailsRecyclerViewAdapter
|
||||
|
||||
private class VersionViewHolder extends RecyclerView.ViewHolder {
|
||||
final TextView version;
|
||||
final TextView status;
|
||||
final TextView statusInstalled;
|
||||
final TextView statusSuggested;
|
||||
final TextView statusIncompatible;
|
||||
final TextView added;
|
||||
final ImageView expandArrow;
|
||||
final View expandedLayout;
|
||||
final TextView repository;
|
||||
final TextView size;
|
||||
final TextView api;
|
||||
final Button buttonInstallUpgrade;
|
||||
final Button buttonDowngrade;
|
||||
Button buttonAction;
|
||||
final View busyIndicator;
|
||||
final TextView incompatibleReasons;
|
||||
final TextView buildtype;
|
||||
final TextView added;
|
||||
final TextView nativecode;
|
||||
|
||||
private Apk apk;
|
||||
|
||||
VersionViewHolder(View view) {
|
||||
super(view);
|
||||
version = (TextView) view.findViewById(R.id.version);
|
||||
status = (TextView) view.findViewById(R.id.status);
|
||||
statusInstalled = (TextView) view.findViewById(R.id.status_installed);
|
||||
statusSuggested = (TextView) view.findViewById(R.id.status_suggested);
|
||||
statusIncompatible = (TextView) view.findViewById(R.id.status_incompatible);
|
||||
added = (TextView) view.findViewById(R.id.added);
|
||||
expandArrow = (ImageView) view.findViewById(R.id.expand_arrow);
|
||||
expandedLayout = (View) view.findViewById(R.id.expanded_layout);
|
||||
repository = (TextView) view.findViewById(R.id.repository);
|
||||
size = (TextView) view.findViewById(R.id.size);
|
||||
api = (TextView) view.findViewById(R.id.api);
|
||||
buttonInstallUpgrade = (Button) view.findViewById(R.id.button_install_upgrade);
|
||||
buttonDowngrade = (Button) view.findViewById(R.id.button_downgrade);
|
||||
busyIndicator = (View) view.findViewById(R.id.busy_indicator);
|
||||
incompatibleReasons = (TextView) view.findViewById(R.id.incompatible_reasons);
|
||||
buildtype = (TextView) view.findViewById(R.id.buildtype);
|
||||
added = (TextView) view.findViewById(R.id.added);
|
||||
nativecode = (TextView) view.findViewById(R.id.nativecode);
|
||||
|
||||
int margin = context.getResources().getDimensionPixelSize(R.dimen.layout_horizontal_margin);
|
||||
int padding = context.getResources().getDimensionPixelSize(R.dimen.details_activity_padding);
|
||||
ViewCompat.setPaddingRelative(view, margin + padding + ViewCompat.getPaddingStart(view), view.getPaddingTop(), margin + padding + ViewCompat.getPaddingEnd(view), view.getPaddingBottom());
|
||||
ViewCompat.setPaddingRelative(view, margin + padding + ViewCompat.getPaddingStart(view), view.getPaddingTop(), margin + ViewCompat.getPaddingEnd(view), view.getPaddingBottom());
|
||||
}
|
||||
|
||||
public void bindModel(final Apk apk) {
|
||||
java.text.DateFormat df = DateFormat.getDateFormat(context);
|
||||
this.apk = apk;
|
||||
|
||||
boolean isSuggested = apk.versionCode == app.suggestedVersionCode &&
|
||||
boolean isAppInstalled = app.isInstalled(context);
|
||||
boolean isApkInstalled = apk.versionCode == app.installedVersionCode;
|
||||
boolean isApkSuggested = apk.versionCode == app.suggestedVersionCode &&
|
||||
TextUtils.equals(apk.sig, app.getMostAppropriateSignature());
|
||||
boolean isApkDownloading = callbacks.isAppDownloading() && downloadedApk != null &&
|
||||
downloadedApk.compareTo(apk) == 0;
|
||||
|
||||
version.setText(context.getString(R.string.version)
|
||||
+ " " + apk.versionName
|
||||
+ (isSuggested ? " ☆" : ""));
|
||||
// Version name and statuses
|
||||
version.setText(apk.versionName);
|
||||
statusSuggested.setVisibility(isApkSuggested && apk.compatible ? View.VISIBLE : View.GONE);
|
||||
statusInstalled.setVisibility(isApkInstalled ? View.VISIBLE : View.GONE);
|
||||
statusIncompatible.setVisibility(!apk.compatible ? View.VISIBLE : View.GONE);
|
||||
|
||||
String statusText = getInstalledStatus(apk);
|
||||
status.setText(statusText);
|
||||
|
||||
if ("Installed".equals(statusText)) {
|
||||
version.setTextColor(ContextCompat.getColor(context, R.color.fdroid_blue));
|
||||
}
|
||||
|
||||
repository.setText(context.getString(R.string.repo_provider,
|
||||
RepoProvider.Helper.findById(context, apk.repoId).getName()));
|
||||
|
||||
if (apk.size > 0) {
|
||||
size.setText(Utils.getFriendlySize(apk.size));
|
||||
size.setVisibility(View.VISIBLE);
|
||||
// Version name width correction in case it's
|
||||
// too long to prevent truncating the statuses
|
||||
if (statusSuggested.getVisibility() == View.VISIBLE ||
|
||||
statusInstalled.getVisibility() == View.VISIBLE ||
|
||||
statusIncompatible.getVisibility() == View.VISIBLE) {
|
||||
int maxWidth = (int) (Resources.getSystem().getDisplayMetrics().widthPixels * 0.4);
|
||||
version.setMaxWidth(maxWidth);
|
||||
} else {
|
||||
size.setVisibility(View.GONE);
|
||||
version.setMaxWidth(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
if (!Preferences.get().expertMode()) {
|
||||
api.setVisibility(View.GONE);
|
||||
} else if (apk.minSdkVersion > 0 && apk.maxSdkVersion < Apk.SDK_VERSION_MAX_VALUE) {
|
||||
api.setText(context.getString(R.string.minsdk_up_to_maxsdk,
|
||||
Utils.getAndroidVersionName(apk.minSdkVersion),
|
||||
Utils.getAndroidVersionName(apk.maxSdkVersion)));
|
||||
api.setVisibility(View.VISIBLE);
|
||||
} else if (apk.minSdkVersion > 0) {
|
||||
api.setText(context.getString(R.string.minsdk_or_later,
|
||||
Utils.getAndroidVersionName(apk.minSdkVersion)));
|
||||
api.setVisibility(View.VISIBLE);
|
||||
} else if (apk.maxSdkVersion > 0) {
|
||||
api.setText(context.getString(R.string.up_to_maxsdk,
|
||||
Utils.getAndroidVersionName(apk.maxSdkVersion)));
|
||||
api.setVisibility(View.VISIBLE);
|
||||
// Added date
|
||||
java.text.DateFormat df = DateFormat.getDateFormat(context);
|
||||
added.setText(context.getString(R.string.added_on, df.format(apk.added)));
|
||||
|
||||
// Repository name, APK size and required Android version
|
||||
Repo repo = RepoProvider.Helper.findById(context, apk.repoId);
|
||||
repository.setText(repo != null ? repo.getName() : context.getString(R.string.unknown));
|
||||
size.setText(context.getString(R.string.app_size, Utils.getFriendlySize(apk.size)));
|
||||
api.setText(getApiText(apk));
|
||||
|
||||
// Figuring out whether to show Install/Upgrade button or Downgrade button
|
||||
buttonDowngrade.setVisibility(View.GONE);
|
||||
buttonInstallUpgrade.setVisibility(View.GONE);
|
||||
buttonInstallUpgrade.setText(context.getString(R.string.menu_install));
|
||||
showActionButton(buttonInstallUpgrade, isApkInstalled, isApkDownloading);
|
||||
if (isAppInstalled && !isApkInstalled) {
|
||||
if (apk.versionCode > app.installedVersionCode) {
|
||||
// Change the label to indicate that pressing this
|
||||
// button will result in upgrading the installed app
|
||||
buttonInstallUpgrade.setText(R.string.menu_upgrade);
|
||||
} else {
|
||||
// The Downgrade button should be shown in this case
|
||||
buttonInstallUpgrade.setVisibility(View.GONE);
|
||||
showActionButton(buttonDowngrade, false, isApkDownloading);
|
||||
}
|
||||
}
|
||||
|
||||
if (apk.srcname != null) {
|
||||
buildtype.setText("source");
|
||||
} else {
|
||||
buildtype.setText("bin");
|
||||
}
|
||||
// Show busy indicator when the APK is being downloaded
|
||||
busyIndicator.setVisibility(isApkDownloading ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (apk.added != null) {
|
||||
added.setText(context.getString(R.string.added_on,
|
||||
df.format(apk.added)));
|
||||
added.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
added.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (Preferences.get().expertMode() && apk.nativecode != null) {
|
||||
nativecode.setText(TextUtils.join(" ", apk.nativecode));
|
||||
nativecode.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
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);
|
||||
// Display incompatible reasons when the app
|
||||
// isn't compatible and the expert mode is enabled
|
||||
if (Preferences.get().expertMode() && !apk.compatible) {
|
||||
String incompatibleReasonsText = getIncompatibleReasonsText(apk);
|
||||
if (incompatibleReasonsText != null) {
|
||||
incompatibleReasons.setVisibility(View.VISIBLE);
|
||||
incompatibleReasons.setText(incompatibleReasonsText);
|
||||
} else {
|
||||
incompatibleReasons.setVisibility(View.GONE);
|
||||
}
|
||||
} else {
|
||||
incompatibleReasons.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// Disable it all if it isn't compatible...
|
||||
final View[] views = {
|
||||
itemView,
|
||||
version,
|
||||
status,
|
||||
repository,
|
||||
size,
|
||||
api,
|
||||
buildtype,
|
||||
added,
|
||||
nativecode,
|
||||
};
|
||||
for (final View v : views) {
|
||||
v.setEnabled(apk.compatible && !mismatchedSig);
|
||||
}
|
||||
// Expand the view if it was previously expanded or when downloading
|
||||
expand(versionsExpandTracker.get(apk.versionCode) || isApkDownloading);
|
||||
|
||||
// Toggle expanded view when clicking the whole version item
|
||||
itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
callbacks.installApk(apk);
|
||||
toggleExpanded();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String getApiText(final Apk apk) {
|
||||
String apiText = "Android ";
|
||||
if (apk.minSdkVersion > 0 && apk.maxSdkVersion < Apk.SDK_VERSION_MAX_VALUE) {
|
||||
apiText += context.getString(R.string.minsdk_up_to_maxsdk,
|
||||
Utils.getAndroidVersionName(apk.minSdkVersion),
|
||||
Utils.getAndroidVersionName(apk.maxSdkVersion));
|
||||
} else if (apk.minSdkVersion > 0) {
|
||||
apiText += context.getString(R.string.minsdk_or_later,
|
||||
Utils.getAndroidVersionName(apk.minSdkVersion));
|
||||
} else if (apk.maxSdkVersion > 0) {
|
||||
apiText += context.getString(R.string.up_to_maxsdk,
|
||||
Utils.getAndroidVersionName(apk.maxSdkVersion));
|
||||
}
|
||||
return apiText;
|
||||
}
|
||||
|
||||
private String getIncompatibleReasonsText(final Apk apk) {
|
||||
if (apk.incompatibleReasons != null) {
|
||||
return context.getResources().getString(R.string.requires_features,
|
||||
TextUtils.join(", ", apk.incompatibleReasons));
|
||||
} else {
|
||||
boolean mismatchedSig = app.installedSig != null
|
||||
&& !TextUtils.equals(app.installedSig, apk.sig);
|
||||
if (mismatchedSig) {
|
||||
return context.getString(R.string.app_details__incompatible_mismatched_signature);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void showActionButton(Button button, boolean isApkInstalled, boolean isApkDownloading) {
|
||||
buttonAction = button;
|
||||
if (isApkDownloading) {
|
||||
// Don't show the button in this case
|
||||
// as the busy indicator will take its place
|
||||
buttonAction.setVisibility(View.GONE);
|
||||
} else {
|
||||
// The button should be shown but it should be also disabled
|
||||
// if either the APK isn't compatible or it's already installed
|
||||
// or also when some other APK is currently being downloaded
|
||||
buttonAction.setVisibility(View.VISIBLE);
|
||||
boolean buttonActionDisabled = !apk.compatible || isApkInstalled ||
|
||||
callbacks.isAppDownloading();
|
||||
buttonAction.setEnabled(!buttonActionDisabled);
|
||||
buttonAction.setAlpha(buttonActionDisabled ? 0.15f : 1f);
|
||||
buttonAction.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
callbacks.installApk(apk);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void expand(boolean expand) {
|
||||
versionsExpandTracker.put(apk.versionCode, expand);
|
||||
expandedLayout.setVisibility(expand ? View.VISIBLE : View.GONE);
|
||||
expandArrow.setImageDrawable(ContextCompat.getDrawable(context, expand ?
|
||||
R.drawable.ic_expand_less_grey600 : R.drawable.ic_expand_more_grey600));
|
||||
|
||||
// This is required to make these labels
|
||||
// auto-scrollable when they are too long
|
||||
version.setSelected(expand);
|
||||
repository.setSelected(expand);
|
||||
size.setSelected(expand);
|
||||
api.setSelected(expand);
|
||||
}
|
||||
|
||||
private void toggleExpanded() {
|
||||
if (busyIndicator.getVisibility() == View.VISIBLE) {
|
||||
// Don't allow collapsing the view when the busy indicator
|
||||
// is shown because the APK is being downloaded and it's quite important
|
||||
return;
|
||||
}
|
||||
|
||||
boolean expand = !versionsExpandTracker.get(apk.versionCode);
|
||||
expand(expand);
|
||||
|
||||
if (expand) {
|
||||
// Scroll the versions view to a correct position so it can show the whole item
|
||||
final LinearLayoutManager lm = (LinearLayoutManager) recyclerView.getLayoutManager();
|
||||
final int currentPosition = getAdapterPosition();
|
||||
if (currentPosition >= lm.findLastCompletelyVisibleItemPosition()) {
|
||||
// Do it only if the item is near the bottom of current viewport
|
||||
recyclerView.getViewTreeObserver()
|
||||
.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
// Expanded item dimensions should be already calculated at this moment
|
||||
// so it's possible to correctly scroll to a given position
|
||||
recyclerView.smoothScrollToPosition(currentPosition);
|
||||
recyclerView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addLinkItemView(ViewGroup parent, int resIdText, int resIdDrawable, final String url) {
|
||||
@ -1098,31 +1233,6 @@ public class AppDetailsRecyclerViewAdapter
|
||||
});
|
||||
}
|
||||
|
||||
private String getInstalledStatus(final Apk apk) {
|
||||
// Definitely not installed.
|
||||
if (apk.versionCode != app.installedVersionCode) {
|
||||
return context.getString(R.string.app_not_installed);
|
||||
}
|
||||
// Definitely installed this version.
|
||||
if (apk.sig != null && apk.sig.equals(app.installedSig)) {
|
||||
return context.getString(R.string.app_installed);
|
||||
}
|
||||
// Installed the same version, but from someplace else.
|
||||
final String installerPkgName;
|
||||
try {
|
||||
installerPkgName = context.getPackageManager().getInstallerPackageName(app.packageName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.w("AppDetailsAdapter", "Application " + app.packageName + " is not installed anymore");
|
||||
return context.getString(R.string.app_not_installed);
|
||||
}
|
||||
if (TextUtils.isEmpty(installerPkgName)) {
|
||||
return context.getString(R.string.app_inst_unknown_source);
|
||||
}
|
||||
final String installerLabel = InstalledAppProvider
|
||||
.getApplicationLabel(context, installerPkgName);
|
||||
return context.getString(R.string.app_inst_known_source, installerLabel);
|
||||
}
|
||||
|
||||
private void onLinkClicked(String url) {
|
||||
if (!TextUtils.isEmpty(url)) {
|
||||
callbacks.openUrl(url);
|
||||
|
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="24dp" />
|
||||
<solid android:color="#b87032" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="24dp" />
|
||||
<solid android:color="#22ffffff" />
|
||||
<stroke android:color="#b87032" android:width="2dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="24dp" />
|
||||
<solid android:color="@color/fdroid_blue" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="24dp" />
|
||||
<solid android:color="#22ffffff" />
|
||||
<stroke android:color="@color/fdroid_blue" android:width="2dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||
<stroke android:width="1dp" android:color="#d86758" />
|
||||
<corners android:radius="2dp" />
|
||||
</shape>
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||
<stroke android:width="1dp" android:color="#19c878" />
|
||||
<corners android:radius="2dp" />
|
||||
</shape>
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||
<stroke android:width="1dp" android:color="#429fff" />
|
||||
<corners android:radius="2dp" />
|
||||
</shape>
|
@ -1,112 +1,171 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="5dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="4dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
|
||||
<LinearLayout android:id="@+id/basic_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView android:id="@+id/version"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:textStyle="bold"
|
||||
android:textSize="18sp"
|
||||
tools:text="1.2.3"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="3dp">
|
||||
|
||||
<TextView android:id="@+id/status_suggested"
|
||||
style="@style/AppVersionStatus"
|
||||
android:background="@drawable/app_version_item_status_suggested"
|
||||
android:textColor="#429fff"
|
||||
android:text="@string/app_suggested" />
|
||||
|
||||
<TextView android:id="@+id/status_installed"
|
||||
style="@style/AppVersionStatus"
|
||||
android:background="@drawable/app_version_item_status_installed"
|
||||
android:textColor="#19c878"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/app_installed" />
|
||||
|
||||
<TextView android:id="@+id/status_incompatible"
|
||||
style="@style/AppVersionStatus"
|
||||
android:background="@drawable/app_version_item_status_incompatible"
|
||||
android:textColor="#d86758"
|
||||
android:text="@string/app_incompatible" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<TextView android:id="@+id/added"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:textSize="13sp"
|
||||
tools:text="Added on 31/12/2050" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView android:id="@+id/expand_arrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:src="@drawable/ic_expand_more_grey600" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout android:id="@+id/expanded_layout"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="5dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
android:layout_below="@id/basic_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<TextView android:id="@+id/version"
|
||||
android:textStyle="bold"
|
||||
android:maxLines="2"
|
||||
android:ellipsize="end"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="18sp"
|
||||
tools:text="Version 1.0 (as if!)"/>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView android:id="@+id/status"
|
||||
android:textSize="13sp"
|
||||
android:maxLines="2"
|
||||
android:ellipsize="end"
|
||||
android:layout_below="@id/version"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
tools:text="Installed" />
|
||||
|
||||
<TextView android:id="@+id/repository"
|
||||
android:textSize="13sp"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:layout_below="@id/status"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
tools:text="Provided By F-Droid archive" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/container_added_nativecode"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_below="@id/repository"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView android:id="@+id/added"
|
||||
android:textSize="13sp"
|
||||
android:layout_weight="0"
|
||||
android:ellipsize="end"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:gravity="start"
|
||||
android:textAlignment="viewStart"
|
||||
tools:text="Added on 1/1/2050" />
|
||||
|
||||
<TextView android:id="@+id/nativecode"
|
||||
android:textSize="13sp"
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp">
|
||||
|
||||
<TextView android:id="@+id/repository"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:textColor="?attr/lightGrayTextColor"
|
||||
android:textSize="12sp"
|
||||
tools:text="F-Droid" />
|
||||
|
||||
<TextView android:id="@+id/size"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:textColor="?attr/lightGrayTextColor"
|
||||
android:textSize="12sp"
|
||||
tools:text="Size: 4.8 MiB" />
|
||||
|
||||
<TextView android:id="@+id/api"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:textColor="?attr/lightGrayTextColor"
|
||||
android:textSize="12sp"
|
||||
tools:text="Android 4.1 or later" />
|
||||
</LinearLayout>
|
||||
|
||||
<android.support.v7.widget.AppCompatButton android:id="@+id/button_install_upgrade"
|
||||
style="@style/DetailsButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:gravity="end"
|
||||
android:textAlignment="viewEnd"
|
||||
tools:text=""
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
app:textAllCaps="true"
|
||||
tools:text="@string/menu_install"
|
||||
android:textColor="@color/fdroid_blue"
|
||||
android:background="@drawable/app_version_button_bg_selector_install_upgrade" />
|
||||
|
||||
<android.support.v7.widget.AppCompatButton android:id="@+id/button_downgrade"
|
||||
style="@style/DetailsButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
app:textAllCaps="true"
|
||||
android:text="@string/menu_downgrade"
|
||||
android:textColor="#b87032"
|
||||
android:background="@drawable/app_version_button_bg_selector_downgrade"
|
||||
tools:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
<ProgressBar android:id="@+id/busy_indicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone"
|
||||
tools:visibility="gone" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView android:id="@+id/buildtype"
|
||||
android:textSize="13sp"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_height="wrap_content"
|
||||
<TextView android:id="@+id/incompatible_reasons"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_marginBottom="4sp"
|
||||
tools:text="source" />
|
||||
|
||||
<TextView android:id="@+id/size"
|
||||
android:textSize="13sp"
|
||||
android:layout_below="@id/buildtype"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_marginBottom="4sp"
|
||||
tools:text="12mb" />
|
||||
|
||||
<TextView android:id="@+id/api"
|
||||
android:textSize="13sp"
|
||||
android:layout_below="@id/buildtype"
|
||||
android:layout_toLeftOf="@id/size"
|
||||
android:layout_toStartOf="@id/size"
|
||||
android:layout_marginRight="16sp"
|
||||
android:layout_marginEnd="16sp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
tools:text=""
|
||||
tools:visibility="gone" />
|
||||
|
||||
<TextView android:id="@+id/incompatible_reasons"
|
||||
android:textSize="13sp"
|
||||
android:layout_below="@id/container_added_nativecode"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
tools:text=""
|
||||
tools:visibility="gone" />
|
||||
|
||||
android:textColor="#d86758"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
tools:text="Requires: armeabi-v7a" />
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
|
@ -87,6 +87,7 @@ This often occurs with apps installed via Google Play or other sources, if they
|
||||
<string name="about_source">Source code</string>
|
||||
<string name="about_license">License</string>
|
||||
|
||||
<string name="app_suggested">Suggested</string>
|
||||
<string name="app_incompatible">Incompatible</string>
|
||||
<string name="app_installed">Installed</string>
|
||||
<string name="app_not_installed">Not Installed</string>
|
||||
@ -101,6 +102,7 @@ This often occurs with apps installed via Google Play or other sources, if they
|
||||
<string name="app__install_downloaded_update">Update</string>
|
||||
<string name="app_installed_media">File installed to %s</string>
|
||||
<string name="app_permission_storage">F-Droid needs the storage permission to install this to storage. Please allow it on the next screen to proceed with installation.</string>
|
||||
<string name="app_size">Size: %1$s</string>
|
||||
|
||||
<string name="app_list__name__downloading_in_progress">Downloading %1$s</string>
|
||||
<string name="app_list__name__successfully_installed">%1$s installed</string>
|
||||
@ -187,6 +189,7 @@ This often occurs with apps installed via Google Play or other sources, if they
|
||||
<string name="menu_license">License: %s</string>
|
||||
<string name="menu_source">Source Code</string>
|
||||
<string name="menu_upgrade">Upgrade</string>
|
||||
<string name="menu_downgrade">Downgrade</string>
|
||||
<string name="menu_donate">Donate</string>
|
||||
<string name="menu_bitcoin">Bitcoin</string>
|
||||
<string name="menu_litecoin">Litecoin</string>
|
||||
|
@ -170,6 +170,18 @@
|
||||
<item name="android:drawablePadding">12dp</item>
|
||||
</style>
|
||||
|
||||
<style name="AppVersionStatus">
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_marginTop">1dp</item>
|
||||
<item name="android:layout_marginLeft">6dp</item>
|
||||
<item name="android:paddingLeft">4dp</item>
|
||||
<item name="android:paddingRight">4dp</item>
|
||||
<item name="android:paddingBottom">1dp</item>
|
||||
<item name="android:maxLines">1</item>
|
||||
<item name="android:textSize">12sp</item>
|
||||
</style>
|
||||
|
||||
<style name="AppDetailsSubheaderText" parent="AppDetailsSubheaderTextBase"/>
|
||||
|
||||
<style name="AppThemeTransparent" parent="@android:style/Theme.NoDisplay"/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user