mirror of
https://github.com/f-droid/fdroidclient.git
synced 2026-04-20 06:47:06 -04:00
[app] App list search with new DB
This commit is contained in:
committed by
Hans-Christoph Steiner
parent
c4e92fba86
commit
ad2700a0db
@@ -27,6 +27,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import org.apache.commons.io.filefilter.RegexFileFilter;
|
||||
import org.fdroid.database.AppListItem;
|
||||
import org.fdroid.database.Repository;
|
||||
import org.fdroid.database.UpdatableApp;
|
||||
import org.fdroid.download.DownloadRequest;
|
||||
@@ -514,6 +515,19 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
|
||||
setInstalled(packageInfo);
|
||||
}
|
||||
|
||||
public App(AppListItem item) {
|
||||
repoId = item.getRepoId();
|
||||
packageName = item.getPackageName();
|
||||
name = item.getName() == null ? "" : item.getName();
|
||||
summary = item.getSummary() == null ? "" : item.getSummary();
|
||||
FileV2 iconFile = item.getIcon(getLocales());
|
||||
iconFromApk = iconFile == null ? null : iconFile.getName();
|
||||
installedVersionCode = item.getInstalledVersionCode() == null ? 0 : item.getInstalledVersionCode().intValue();
|
||||
installedVersionName = item.getInstalledVersionName();
|
||||
antiFeatures = item.getAntiFeatureKeys().toArray(new String[0]);
|
||||
compatible = item.isCompatible();
|
||||
}
|
||||
|
||||
public void setInstalled(@Nullable PackageInfo packageInfo) {
|
||||
installedVersionCode = packageInfo == null ? 0 : packageInfo.versionCode;
|
||||
installedVersionName = packageInfo == null ? null : packageInfo.versionName;
|
||||
|
||||
@@ -23,9 +23,6 @@ package org.fdroid.fdroid.views.apps;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.KeyEvent;
|
||||
@@ -39,12 +36,14 @@ import android.widget.TextView;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
|
||||
import org.fdroid.database.AppListItem;
|
||||
import org.fdroid.database.AppListSortOrder;
|
||||
import org.fdroid.database.FDroidDatabase;
|
||||
import org.fdroid.fdroid.FDroidApp;
|
||||
import org.fdroid.fdroid.Preferences;
|
||||
import org.fdroid.fdroid.R;
|
||||
import org.fdroid.fdroid.Utils;
|
||||
import org.fdroid.fdroid.data.AppProvider;
|
||||
import org.fdroid.fdroid.data.Schema.AppMetadataTable;
|
||||
import org.fdroid.fdroid.data.DBHelper;
|
||||
import org.fdroid.fdroid.data.Schema.AppMetadataTable.Cols;
|
||||
import org.fdroid.fdroid.views.main.MainActivity;
|
||||
|
||||
@@ -52,18 +51,16 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.CursorLoader;
|
||||
import androidx.loader.content.Loader;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides scrollable listing of apps for search and category views.
|
||||
*/
|
||||
public class AppListActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor>,
|
||||
CategoryTextWatcher.SearchTermsChangedListener {
|
||||
public class AppListActivity extends AppCompatActivity implements CategoryTextWatcher.SearchTermsChangedListener {
|
||||
|
||||
public static final String TAG = "AppListActivity";
|
||||
|
||||
@@ -85,7 +82,9 @@ public class AppListActivity extends AppCompatActivity implements LoaderManager.
|
||||
private EditText searchInput;
|
||||
private ImageView sortImage;
|
||||
private View hiddenAppNotice;
|
||||
private FDroidDatabase db;
|
||||
private Utils.KeyboardStateMonitor keyboardStateMonitor;
|
||||
private LiveData<List<AppListItem>> itemsLiveData;
|
||||
|
||||
private interface SortClause {
|
||||
String WORDS = Cols.NAME;
|
||||
@@ -101,6 +100,7 @@ public class AppListActivity extends AppCompatActivity implements LoaderManager.
|
||||
|
||||
setContentView(R.layout.activity_app_list);
|
||||
|
||||
db = DBHelper.getDb(this.getApplicationContext());
|
||||
keyboardStateMonitor = new Utils.KeyboardStateMonitor(findViewById(R.id.app_list_root));
|
||||
|
||||
savedSearchSettings = getSavedSearchSettings(this);
|
||||
@@ -128,28 +128,24 @@ public class AppListActivity extends AppCompatActivity implements LoaderManager.
|
||||
});
|
||||
|
||||
sortImage = (ImageView) findViewById(R.id.sort);
|
||||
final Drawable lastUpdated = DrawableCompat.wrap(ContextCompat.getDrawable(this,
|
||||
R.drawable.ic_last_updated)).mutate();
|
||||
final Drawable words = DrawableCompat.wrap(ContextCompat.getDrawable(AppListActivity.this,
|
||||
R.drawable.ic_sort)).mutate();
|
||||
sortImage.setImageDrawable(SortClause.WORDS.equals(sortClauseSelected) ? words : lastUpdated);
|
||||
sortImage.setImageResource(
|
||||
SortClause.WORDS.equals(sortClauseSelected) ? R.drawable.ic_sort : R.drawable.ic_last_updated
|
||||
);
|
||||
sortImage.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
switch (sortClauseSelected) {
|
||||
case SortClause.WORDS:
|
||||
sortClauseSelected = SortClause.LAST_UPDATED;
|
||||
DrawableCompat.setTint(lastUpdated, FDroidApp.isAppThemeLight() ? Color.BLACK : Color.WHITE);
|
||||
sortImage.setImageDrawable(lastUpdated);
|
||||
sortImage.setImageResource(R.drawable.ic_last_updated);
|
||||
break;
|
||||
case SortClause.LAST_UPDATED:
|
||||
sortClauseSelected = SortClause.WORDS;
|
||||
DrawableCompat.setTint(words, FDroidApp.isAppThemeLight() ? Color.BLACK : Color.WHITE);
|
||||
sortImage.setImageDrawable(words);
|
||||
sortImage.setImageResource(R.drawable.ic_sort);
|
||||
break;
|
||||
}
|
||||
putSavedSearchSettings(getApplicationContext(), SORT_CLAUSE_KEY, sortClauseSelected);
|
||||
getSupportLoaderManager().restartLoader(0, null, AppListActivity.this);
|
||||
loadItems();
|
||||
appView.scrollToPosition(0);
|
||||
}
|
||||
});
|
||||
@@ -197,6 +193,7 @@ public class AppListActivity extends AppCompatActivity implements LoaderManager.
|
||||
appView.setAdapter(appAdapter);
|
||||
|
||||
parseIntentForSearchQuery();
|
||||
loadItems();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -219,8 +216,22 @@ public class AppListActivity extends AppCompatActivity implements LoaderManager.
|
||||
// experience where the user scrolls through the apps in the category.
|
||||
appView.requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
getSupportLoaderManager().initLoader(0, null, this);
|
||||
private void loadItems() {
|
||||
if (itemsLiveData != null) {
|
||||
itemsLiveData.removeObserver(this::onAppsLoaded);
|
||||
}
|
||||
AppListSortOrder sortOrder =
|
||||
SortClause.WORDS.equals(sortClauseSelected) ? AppListSortOrder.NAME : AppListSortOrder.LAST_UPDATED;
|
||||
if (category == null) {
|
||||
itemsLiveData = db.getAppDao().getAppListItems(getPackageManager(), searchTerms,
|
||||
sortOrder);
|
||||
} else {
|
||||
itemsLiveData = db.getAppDao().getAppListItems(getPackageManager(), category,
|
||||
searchTerms, sortOrder);
|
||||
}
|
||||
itemsLiveData.observe(this, this::onAppsLoaded);
|
||||
}
|
||||
|
||||
private CharSequence getSearchText(@Nullable String category, @Nullable String searchTerms) {
|
||||
@@ -240,25 +251,11 @@ public class AppListActivity extends AppCompatActivity implements LoaderManager.
|
||||
hiddenAppNotice.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||
return new CursorLoader(
|
||||
this,
|
||||
AppProvider.getSearchUri(searchTerms, category),
|
||||
AppMetadataTable.Cols.ALL,
|
||||
null,
|
||||
null,
|
||||
getSortOrder()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor cursor) {
|
||||
private void onAppsLoaded(List<AppListItem> items) {
|
||||
setShowHiddenAppsNotice(false);
|
||||
appAdapter.setHasHiddenAppsCallback(() -> setShowHiddenAppsNotice(true));
|
||||
appAdapter.setAppCursor(cursor);
|
||||
if (cursor.getCount() > 0) {
|
||||
appAdapter.setItems(items);
|
||||
if (items.size() > 0) {
|
||||
emptyState.setVisibility(View.GONE);
|
||||
appView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
@@ -267,17 +264,12 @@ public class AppListActivity extends AppCompatActivity implements LoaderManager.
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
|
||||
appAdapter.setAppCursor(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSearchTermsChanged(@Nullable String category, @NonNull String searchTerms) {
|
||||
this.category = category;
|
||||
this.searchTerms = searchTerms;
|
||||
appView.scrollToPosition(0);
|
||||
getSupportLoaderManager().restartLoader(0, null, this);
|
||||
loadItems();
|
||||
if (TextUtils.isEmpty(searchTerms)) {
|
||||
removeSavedSearchSettings(this, SEARCH_TERMS_KEY);
|
||||
} else {
|
||||
@@ -285,87 +277,6 @@ public class AppListActivity extends AppCompatActivity implements LoaderManager.
|
||||
}
|
||||
}
|
||||
|
||||
private String getSortOrder() {
|
||||
final String table = AppMetadataTable.NAME;
|
||||
final String nameCol = table + "." + AppMetadataTable.Cols.NAME;
|
||||
final String summaryCol = table + "." + AppMetadataTable.Cols.SUMMARY;
|
||||
final String packageCol = Cols.Package.PACKAGE_NAME;
|
||||
|
||||
if (sortClauseSelected.equals(SortClause.LAST_UPDATED)) {
|
||||
return table + "." + Cols.LAST_UPDATED + " DESC"
|
||||
+ ", " + table + "." + Cols.IS_LOCALIZED + " DESC"
|
||||
+ ", " + table + "." + Cols.ADDED + " ASC"
|
||||
+ ", " + table + "." + Cols.NAME + " IS NULL ASC"
|
||||
+ ", CASE WHEN " + table + "." + Cols.ICON + " IS NULL"
|
||||
+ " AND " + table + "." + Cols.ICON_URL + " IS NULL"
|
||||
+ " THEN 1 ELSE 0 END"
|
||||
+ ", " + table + "." + Cols.SUMMARY + " IS NULL ASC"
|
||||
+ ", " + table + "." + Cols.DESCRIPTION + " IS NULL ASC"
|
||||
+ ", " + table + "." + Cols.WHATSNEW + " IS NULL ASC"
|
||||
+ ", CASE WHEN " + table + "." + Cols.PHONE_SCREENSHOTS + " IS NULL"
|
||||
+ " AND " + table + "." + Cols.SEVEN_INCH_SCREENSHOTS + " IS NULL"
|
||||
+ " AND " + table + "." + Cols.TEN_INCH_SCREENSHOTS + " IS NULL"
|
||||
+ " AND " + table + "." + Cols.TV_SCREENSHOTS + " IS NULL"
|
||||
+ " AND " + table + "." + Cols.WEAR_SCREENSHOTS + " IS NULL"
|
||||
+ " AND " + table + "." + Cols.FEATURE_GRAPHIC + " IS NULL"
|
||||
+ " AND " + table + "." + Cols.PROMO_GRAPHIC + " IS NULL"
|
||||
+ " AND " + table + "." + Cols.TV_BANNER + " IS NULL"
|
||||
+ " THEN 1 ELSE 0 END";
|
||||
}
|
||||
|
||||
// prevent SQL injection https://en.wikipedia.org/wiki/SQL_injection#Escaping
|
||||
final String[] terms = searchTerms.trim().replaceAll("[\\x1a\0\n\r\"';\\\\]+", " ").split("\\s+");
|
||||
if (terms.length == 0 || terms[0].equals("")) {
|
||||
return table + "." + Cols.NAME + " COLLATE LOCALIZED ";
|
||||
}
|
||||
|
||||
boolean potentialPackageName = false;
|
||||
StringBuilder packageNameFirstCase = new StringBuilder();
|
||||
if (terms[0].length() > 2 && terms[0].substring(1, terms[0].length() - 1).contains(".")) {
|
||||
potentialPackageName = true;
|
||||
packageNameFirstCase.append(String.format("%s LIKE '%%%s%%' ",
|
||||
packageCol, terms[0]));
|
||||
}
|
||||
StringBuilder titleCase = new StringBuilder(String.format("%s like '%%%s%%'", nameCol, terms[0]));
|
||||
StringBuilder summaryCase = new StringBuilder(String.format("%s like '%%%s%%'", summaryCol, terms[0]));
|
||||
StringBuilder packageNameCase = new StringBuilder(String.format("%s like '%%%s%%'", packageCol, terms[0]));
|
||||
for (int i = 1; i < terms.length; i++) {
|
||||
if (potentialPackageName) {
|
||||
packageNameCase.append(String.format(" and %s like '%%%s%%'", summaryCol, terms[i]));
|
||||
}
|
||||
titleCase.append(String.format(" and %s like '%%%s%%'", nameCol, terms[i]));
|
||||
summaryCase.append(String.format(" and %s like '%%%s%%'", summaryCol, terms[i]));
|
||||
}
|
||||
String sortOrder;
|
||||
if (packageNameCase.length() > 0) {
|
||||
sortOrder = String.format("CASE WHEN %s THEN 0 WHEN %s THEN 1 WHEN %s THEN 2 ELSE 3 END",
|
||||
packageNameCase.toString(), titleCase.toString(), summaryCase.toString());
|
||||
} else {
|
||||
sortOrder = String.format("CASE WHEN %s THEN 1 WHEN %s THEN 2 ELSE 3 END",
|
||||
titleCase.toString(), summaryCase.toString());
|
||||
}
|
||||
return sortOrder
|
||||
+ ", " + table + "." + Cols.IS_LOCALIZED + " DESC"
|
||||
+ ", " + table + "." + Cols.ADDED + " ASC"
|
||||
+ ", " + table + "." + Cols.NAME + " IS NULL ASC"
|
||||
+ ", CASE WHEN " + table + "." + Cols.ICON + " IS NULL"
|
||||
+ " AND " + table + "." + Cols.ICON_URL + " IS NULL"
|
||||
+ " THEN 1 ELSE 0 END"
|
||||
+ ", " + table + "." + Cols.SUMMARY + " IS NULL ASC"
|
||||
+ ", " + table + "." + Cols.DESCRIPTION + " IS NULL ASC"
|
||||
+ ", " + table + "." + Cols.WHATSNEW + " IS NULL ASC"
|
||||
+ ", CASE WHEN " + table + "." + Cols.PHONE_SCREENSHOTS + " IS NULL"
|
||||
+ " AND " + table + "." + Cols.SEVEN_INCH_SCREENSHOTS + " IS NULL"
|
||||
+ " AND " + table + "." + Cols.TEN_INCH_SCREENSHOTS + " IS NULL"
|
||||
+ " AND " + table + "." + Cols.TV_SCREENSHOTS + " IS NULL"
|
||||
+ " AND " + table + "." + Cols.WEAR_SCREENSHOTS + " IS NULL"
|
||||
+ " AND " + table + "." + Cols.FEATURE_GRAPHIC + " IS NULL"
|
||||
+ " AND " + table + "." + Cols.PROMO_GRAPHIC + " IS NULL"
|
||||
+ " AND " + table + "." + Cols.TV_BANNER + " IS NULL"
|
||||
+ " THEN 1 ELSE 0 END"
|
||||
+ ", " + table + "." + Cols.LAST_UPDATED + " DESC";
|
||||
}
|
||||
|
||||
public static void putSavedSearchSettings(Context context, String key, String searchTerms) {
|
||||
if (savedSearchSettings == null) {
|
||||
savedSearchSettings = getSavedSearchSettings(context);
|
||||
|
||||
@@ -1,34 +1,37 @@
|
||||
package org.fdroid.fdroid.views.apps;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.fdroid.fdroid.R;
|
||||
import org.fdroid.fdroid.data.App;
|
||||
import org.fdroid.fdroid.data.Schema;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.fdroid.database.AppListItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class AppListAdapter extends RecyclerView.Adapter<StandardAppListItemController> {
|
||||
|
||||
private Cursor cursor;
|
||||
private final List<AppListItem> items = new ArrayList<>();
|
||||
private Runnable hasHiddenAppsCallback;
|
||||
private final AppCompatActivity activity;
|
||||
|
||||
AppListAdapter(AppCompatActivity activity) {
|
||||
this.activity = activity;
|
||||
setHasStableIds(true);
|
||||
}
|
||||
|
||||
public void setAppCursor(Cursor cursor) {
|
||||
this.cursor = cursor;
|
||||
void setItems(List<AppListItem> items) {
|
||||
this.items.clear();
|
||||
this.items.addAll(items);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void setHasHiddenAppsCallback(Runnable callback) {
|
||||
void setHasHiddenAppsCallback(Runnable callback) {
|
||||
hasHiddenAppsCallback = callback;
|
||||
}
|
||||
|
||||
@@ -41,8 +44,8 @@ class AppListAdapter extends RecyclerView.Adapter<StandardAppListItemController>
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull StandardAppListItemController holder, int position) {
|
||||
cursor.moveToPosition(position);
|
||||
final App app = new App(cursor);
|
||||
AppListItem appItem = items.get(position);
|
||||
final App app = new App(appItem);
|
||||
holder.bindModel(app, null, null);
|
||||
|
||||
if (app.isDisabledByAntiFeatures(activity)) {
|
||||
@@ -68,14 +71,8 @@ class AppListAdapter extends RecyclerView.Adapter<StandardAppListItemController>
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
cursor.moveToPosition(position);
|
||||
return cursor.getLong(cursor.getColumnIndexOrThrow(Schema.AppMetadataTable.Cols.ROW_ID));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return cursor == null ? 0 : cursor.getCount();
|
||||
return items.size();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,18 @@ import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityOptionsCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.util.Pair;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.fdroid.database.AppVersion;
|
||||
import org.fdroid.database.DbUpdateChecker;
|
||||
import org.fdroid.database.FDroidDatabase;
|
||||
import org.fdroid.fdroid.AppUpdateStatusManager;
|
||||
import org.fdroid.fdroid.AppUpdateStatusManager.AppUpdateStatus;
|
||||
import org.fdroid.fdroid.Preferences;
|
||||
@@ -27,6 +39,7 @@ import org.fdroid.fdroid.R;
|
||||
import org.fdroid.fdroid.Utils;
|
||||
import org.fdroid.fdroid.data.Apk;
|
||||
import org.fdroid.fdroid.data.App;
|
||||
import org.fdroid.fdroid.data.DBHelper;
|
||||
import org.fdroid.fdroid.installer.ApkCache;
|
||||
import org.fdroid.fdroid.installer.InstallManagerService;
|
||||
import org.fdroid.fdroid.installer.Installer;
|
||||
@@ -36,16 +49,10 @@ import org.fdroid.fdroid.views.updates.UpdatesAdapter;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityOptionsCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.util.Pair;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
|
||||
/**
|
||||
* Supports the following layouts:
|
||||
@@ -110,6 +117,8 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
|
||||
|
||||
@Nullable
|
||||
private AppUpdateStatus currentStatus;
|
||||
@Nullable
|
||||
private Disposable disposable;
|
||||
|
||||
@TargetApi(21)
|
||||
public AppListItemController(final AppCompatActivity activity, View itemView) {
|
||||
@@ -185,9 +194,8 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
|
||||
return currentStatus;
|
||||
}
|
||||
|
||||
public void bindModel(@NonNull App app, Apk apk, @Nullable AppUpdateStatus s) {
|
||||
public void bindModel(@NonNull App app, @Nullable Apk apk, @Nullable AppUpdateStatus s) {
|
||||
currentApp = app;
|
||||
if (apk == null) throw new IllegalStateException(); // TODO remove at the end and make Apk @NonNull
|
||||
currentApk = apk;
|
||||
|
||||
if (actionButton != null) actionButton.setEnabled(true);
|
||||
@@ -490,8 +498,8 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
|
||||
}
|
||||
};
|
||||
|
||||
protected void onActionButtonPressed(App app, Apk apk) {
|
||||
if (app == null || apk == null) {
|
||||
protected void onActionButtonPressed(App app, @Nullable Apk apk) {
|
||||
if (app == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -537,7 +545,17 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
|
||||
Installer installer = InstallerFactory.create(activity, currentStatus.apk);
|
||||
installer.installPackage(Uri.parse(apkFilePath.toURI().toString()), canonicalUri);
|
||||
} else {
|
||||
InstallManagerService.queue(activity, app, apk);
|
||||
FDroidDatabase db = DBHelper.getDb(activity);
|
||||
DbUpdateChecker updateChecker = new DbUpdateChecker(db, activity.getPackageManager());
|
||||
List<String> releaseChannels = Preferences.get().getBackendReleaseChannels();
|
||||
if (disposable != null) disposable.dispose();
|
||||
disposable = Utils.runOffUiThread(() -> {
|
||||
AppVersion version = updateChecker.getSuggestedVersion(app.packageName,
|
||||
app.preferredSigner, releaseChannels);
|
||||
return version == null ? null : new Apk(version);
|
||||
}, receivedApk -> {
|
||||
if (receivedApk != null) InstallManagerService.queue(activity, app, receivedApk);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,21 +28,22 @@ public class StandardAppListItemController extends AppListItemController {
|
||||
@Override
|
||||
protected AppListItemState getCurrentViewState(
|
||||
@NonNull App app, @Nullable AppUpdateStatusManager.AppUpdateStatus appStatus) {
|
||||
|
||||
AppUpdateStatusManager updateStatusManager = AppUpdateStatusManager.getInstance(itemView.getContext());
|
||||
String versionName = updateStatusManager.getInstallableVersion(app.packageName);
|
||||
return super.getCurrentViewState(app, appStatus)
|
||||
.setStatusText(getStatusText(app))
|
||||
.setShowInstallButton(shouldShowInstall(app));
|
||||
.setStatusText(getStatusText(app, versionName))
|
||||
.setShowInstallButton(shouldShowInstall(app, versionName));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private CharSequence getStatusText(@NonNull App app) {
|
||||
private CharSequence getStatusText(@NonNull App app, @Nullable String versionName) {
|
||||
if (!app.compatible) {
|
||||
return activity.getString(R.string.app_incompatible);
|
||||
} else if (app.antiFeatures != null && app.antiFeatures.length > 0) {
|
||||
return activity.getString(R.string.antifeatures);
|
||||
} else if (app.isInstalled(activity.getApplicationContext())) {
|
||||
if (app.canAndWantToUpdate(activity)) {
|
||||
return activity.getString(R.string.app_version_x_available, app.getAutoInstallVersionName());
|
||||
} else if (app.installedVersionName != null) {
|
||||
if (versionName != null) {
|
||||
return activity.getString(R.string.app_version_x_available, versionName);
|
||||
} else {
|
||||
return activity.getString(R.string.app_version_x_installed, app.installedVersionName);
|
||||
}
|
||||
@@ -51,8 +52,8 @@ public class StandardAppListItemController extends AppListItemController {
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean shouldShowInstall(@NonNull App app) {
|
||||
boolean installable = app.canAndWantToUpdate(activity) || !app.isInstalled(activity.getApplicationContext());
|
||||
private boolean shouldShowInstall(@NonNull App app, @Nullable String versionName) {
|
||||
boolean installable = versionName != null || app.installedVersionName == null;
|
||||
boolean shouldAllow = app.compatible && (app.antiFeatures == null || app.antiFeatures.length == 0);
|
||||
|
||||
return installable && shouldAllow;
|
||||
|
||||
@@ -80,7 +80,8 @@
|
||||
app:layout_constraintTop_toTopOf="@id/search_card"
|
||||
app:layout_constraintBottom_toBottomOf="@id/search_card"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/search_card"/>
|
||||
app:layout_constraintStart_toEndOf="@+id/search_card"
|
||||
app:tint="?attr/colorControlNormal"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/hiddenAppNotice"
|
||||
|
||||
Reference in New Issue
Block a user