From 349f386d924f0b5a340901cfb4e39d9d33cc0be2 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Fri, 3 Nov 2023 14:50:54 -0300 Subject: [PATCH] [app] move archive repos to toggle of main repo archive repos can now be enabled/disabled in the details screen of each repo --- app/build.gradle | 1 + .../views/repos/ManageReposActivity.java | 4 +- .../fdroid/views/repos/RepoAdapter.java | 6 ++ .../views/repos/RepoDetailsActivity.java | 19 +++++ .../views/repos/RepoDetailsViewModel.kt | 73 +++++++++++++++++++ .../main/res/layout/activity_repo_details.xml | 6 ++ app/src/main/res/values/strings.xml | 2 + 7 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/org/fdroid/fdroid/views/repos/RepoDetailsViewModel.kt diff --git a/app/build.gradle b/app/build.gradle index 7133161b2..aaf0a718a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -167,6 +167,7 @@ dependencies { implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.vectordrawable:vectordrawable:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.2' implementation 'androidx.palette:palette:1.0.0' implementation 'androidx.work:work-runtime:2.8.1' implementation 'com.google.guava:guava:31.0-android' // somehow needed for work-runtime to function diff --git a/app/src/main/java/org/fdroid/fdroid/views/repos/ManageReposActivity.java b/app/src/main/java/org/fdroid/fdroid/views/repos/ManageReposActivity.java index 22b0581ac..34496ce04 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/repos/ManageReposActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/views/repos/ManageReposActivity.java @@ -47,6 +47,8 @@ import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.data.App; import org.fdroid.index.RepoManager; +import java.util.ArrayList; + import io.reactivex.rxjava3.disposables.CompositeDisposable; public class ManageReposActivity extends AppCompatActivity implements RepoAdapter.RepoItemListener { @@ -139,7 +141,7 @@ public class ManageReposActivity extends AppCompatActivity implements RepoAdapte touchHelper.attachToRecyclerView(repoList); repoList.setAdapter(repoAdapter); FDroidApp.getRepoManager(this).getLiveRepositories().observe(this, items -> { - repoAdapter.updateItems(items); + repoAdapter.updateItems(new ArrayList<>(items)); // copy list, so we don't modify original in adapter isItemReorderingEnabled = true; }); } diff --git a/app/src/main/java/org/fdroid/fdroid/views/repos/RepoAdapter.java b/app/src/main/java/org/fdroid/fdroid/views/repos/RepoAdapter.java index 56c76a309..c95dd2ab4 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/repos/RepoAdapter.java +++ b/app/src/main/java/org/fdroid/fdroid/views/repos/RepoAdapter.java @@ -23,6 +23,7 @@ import org.fdroid.index.v2.FileV2; import java.util.ArrayList; import java.util.List; +import java.util.ListIterator; public class RepoAdapter extends RecyclerView.Adapter { @@ -48,6 +49,11 @@ public class RepoAdapter extends RecyclerView.Adapter items) { this.items.clear(); + // filter out archive repos + ListIterator iterator = items.listIterator(); + while (iterator.hasNext()) { + if (iterator.next().isArchiveRepo()) iterator.remove(); + } this.items.addAll(items); notifyDataSetChanged(); } diff --git a/app/src/main/java/org/fdroid/fdroid/views/repos/RepoDetailsActivity.java b/app/src/main/java/org/fdroid/fdroid/views/repos/RepoDetailsActivity.java index 85670a9c2..79aecdda6 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/repos/RepoDetailsActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/views/repos/RepoDetailsActivity.java @@ -29,8 +29,10 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.SwitchCompat; import androidx.core.app.NavUtils; import androidx.core.content.ContextCompat; +import androidx.lifecycle.ViewModelProvider; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -105,7 +107,10 @@ public class RepoDetailsActivity extends AppCompatActivity { private MirrorAdapter adapterToNotify; + private RepoDetailsViewModel model; + // FIXME access to this could be moved into ViewModel private RepositoryDao repositoryDao; + // FIXME access to this could be moved into ViewModel private AppDao appDao; @Nullable private Disposable disposable; @@ -128,6 +133,7 @@ public class RepoDetailsActivity extends AppCompatActivity { fdroidApp.setSecureWindow(this); fdroidApp.applyPureBlackBackgroundInDarkTheme(this); + model = new ViewModelProvider(this).get(RepoDetailsViewModel.class); repositoryDao = DBHelper.getDb(this).getRepositoryDao(); appDao = DBHelper.getDb(this).getAppDao(); @@ -142,6 +148,7 @@ public class RepoDetailsActivity extends AppCompatActivity { repoView = findViewById(R.id.repo_view); repoId = getIntent().getLongExtra(ARG_REPO_ID, 0); + model.initRepo(repoId); repo = FDroidApp.getRepoManager(this).getRepository(repoId); TextView inputUrl = findViewById(R.id.input_repo_url); @@ -179,6 +186,18 @@ public class RepoDetailsActivity extends AppCompatActivity { qrCode.setImageBitmap(bitmap); } }); + + SwitchCompat switchCompat = findViewById(R.id.archiveRepo); + model.getLiveData().observe(this, s -> { + Boolean enabled = s.getArchiveEnabled(); + if (enabled == null) { + switchCompat.setEnabled(false); + } else { + switchCompat.setEnabled(true); + switchCompat.setChecked(enabled); + } + }); + switchCompat.setOnClickListener(v -> model.setArchiveRepoEnabled(repo, switchCompat.isChecked())); } @Override diff --git a/app/src/main/java/org/fdroid/fdroid/views/repos/RepoDetailsViewModel.kt b/app/src/main/java/org/fdroid/fdroid/views/repos/RepoDetailsViewModel.kt new file mode 100644 index 000000000..42381e393 --- /dev/null +++ b/app/src/main/java/org/fdroid/fdroid/views/repos/RepoDetailsViewModel.kt @@ -0,0 +1,73 @@ +package org.fdroid.fdroid.views.repos + +import android.app.Application +import android.util.Log +import android.widget.Toast +import android.widget.Toast.LENGTH_SHORT +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.asLiveData +import androidx.lifecycle.viewModelScope +import info.guardianproject.netcipher.NetCipher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.fdroid.database.Repository +import org.fdroid.fdroid.FDroidApp +import org.fdroid.fdroid.R +import org.fdroid.fdroid.UpdateService + +data class RepoDetailsState( + val repo: Repository?, + val archiveEnabled: Boolean? = null, +) + +class RepoDetailsViewModel(app: Application) : AndroidViewModel(app) { + + private val repoManager = FDroidApp.getRepoManager(app) + private val _state = MutableStateFlow(null) + val state = _state.asStateFlow() + val liveData = _state.asLiveData() + + fun initRepo(repoId: Long) { + val repo = repoManager.getRepository(repoId) + if (repo == null) { + _state.value = RepoDetailsState(null) + } else { + _state.value = RepoDetailsState( + repo = repo, + archiveEnabled = repo.isArchiveEnabled(), + ) + } + } + + fun setArchiveRepoEnabled(repo: Repository, enabled: Boolean) { + // archiveEnabled = null means we don't know current state, it's in progress + _state.value = _state.value?.copy(archiveEnabled = null) + viewModelScope.launch(Dispatchers.IO) { + try { + repoManager.setArchiveRepoEnabled(repo, enabled, NetCipher.getProxy()) + _state.value = _state.value?.copy(archiveEnabled = enabled) + if (enabled) withContext(Dispatchers.Main) { + val address = repo.address.replace(Regex("repo/?$"), "archive") + UpdateService.updateRepoNow(getApplication(), address) + } + } catch (e: Exception) { + Log.e(this.javaClass.simpleName, "Error toggling archive repo: ", e) + _state.value = _state.value?.copy(archiveEnabled = repo.isArchiveEnabled()) + withContext(Dispatchers.Main) { + Toast.makeText(getApplication(), R.string.repo_archive_failed, LENGTH_SHORT) + .show() + } + } + } + } + + private fun Repository.isArchiveEnabled(): Boolean { + return repoManager.getRepositories().find { r -> + r.isArchiveRepo && r.certificate == certificate + }?.enabled ?: false + } + +} diff --git a/app/src/main/res/layout/activity_repo_details.xml b/app/src/main/res/layout/activity_repo_details.xml index 9f7800ed7..894dfb613 100644 --- a/app/src/main/res/layout/activity_repo_details.xml +++ b/app/src/main/res/layout/activity_repo_details.xml @@ -161,6 +161,12 @@ android:layout_height="wrap_content" android:text="@string/repo_edit_credentials" /> + + Address Number of apps Show apps + Repository Archive Fingerprint of the signing key (SHA-256) Description Last update @@ -467,6 +468,7 @@ This often occurs with apps installed via Google Play or other sources, if they You need to enable it to view the apps it provides. Unknown + Archive repo currently not available Delete Repository? Deleting a repository means apps from it will no longer be available.\n\nNote: All