gt:                 uri = AppProvider.getInstalledWithKnownVulnsUri();
context: package org.fdroid.fdroid.views.updates;
  
  import android.content.BroadcastReceiver;
  import android.content.Context;
  import android.content.Intent;
  import android.content.IntentFilter;
  import android.database.Cursor;
  import android.net.Uri;
  import android.os.Bundle;
  import android.view.ViewGroup;
  
  import com.hannesdorfmann.adapterdelegates3.AdapterDelegatesManager;
  
  import org.fdroid.fdroid.AppUpdateStatusManager;
  import org.fdroid.fdroid.data.App;
  import org.fdroid.fdroid.data.AppProvider;
  import org.fdroid.fdroid.data.Schema;
  import org.fdroid.fdroid.views.updates.items.AppStatus;
  import org.fdroid.fdroid.views.updates.items.AppUpdateData;
  import org.fdroid.fdroid.views.updates.items.KnownVulnApp;
  import org.fdroid.fdroid.views.updates.items.UpdateableApp;
  import org.fdroid.fdroid.views.updates.items.UpdateableAppsHeader;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Set;
  
  import androidx.annotation.NonNull;
  import androidx.appcompat.app.AppCompatActivity;
  import androidx.loader.app.LoaderManager;
  import androidx.loader.content.CursorLoader;
  import androidx.loader.content.Loader;
  import androidx.localbroadcastmanager.content.LocalBroadcastManager;
  import androidx.recyclerview.widget.RecyclerView;
  
  /**
   * Manages the following types of information:
   * <ul>
   * <li>Apps marked for downloading (while the user is offline)</li>
   * <li>Currently downloading apps</li>
   * <li>Apps which have been downloaded (and need further action to install)</li>
   * </ul>
   * This includes new installs and updates.
   * <ul>
   * <li>Reminders to users that they can donate to apps (only shown infrequently after several updates)</li>
   * <li>A list of apps which are eligible to be updated (for when the "Automatic Updates" option is disabled),
   * including:
   * + A summary of all apps to update including an "Update all" button and a "Show apps" button.
   * + Once "Show apps" is expanded then each app is shown along with its own download button.</li>
   * </ul>
   * It does this by maintaining several different lists of interesting apps. Each list contains wrappers
   * around the piece of data it wants to render ({@link AppStatus}, {@link UpdateableApp}).
   * Instead of juggling the various viewTypes
   * to find out which position in the adapter corresponds to which view type, this is handled by
   * the {@link UpdatesAdapter#delegatesManager}.
   * <p>
   * There are a series of type-safe lists which hold the specific data this adapter is interested in.
   * This data is then collated into a single list (see {@link UpdatesAdapter#populateItems()}) which
   * is the actual thing the adapter binds too. At any point it is safe to clear the single list and
   * repopulate it from the original source lists of data. When this is done, the adapter will notify
   * the recycler view that its data has changed. Sometimes it will also ask the recycler view to
   * scroll to the newly added item (if attached to the recycler view).
   * <p>
   * TODO: If a user downloads an old version of an app (resulting in a new update being available
   * instantly), then we need to refresh the list of apps to update.
   */
  public class UpdatesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
          implements LoaderManager.LoaderCallbacks<Cursor> {
  
      private static final int LOADER_CAN_UPDATE = 289753982;
      private static final int LOADER_KNOWN_VULN = 520389740;
  
      private final AdapterDelegatesManager<List<AppUpdateData>> delegatesManager = new AdapterDelegatesManager<>();
      private final List<AppUpdateData> items = new ArrayList<>();
  
      private final AppCompatActivity activity;
  
      private final List<AppStatus> appsToShowStatus = new ArrayList<>();
      private final List<UpdateableApp> updateableApps = new ArrayList<>();
      private final List<KnownVulnApp> knownVulnApps = new ArrayList<>();
  
      private boolean showAllUpdateableApps = false;
  
      public UpdatesAdapter(AppCompatActivity activity) {
          this.activity = activity;
  
          delegatesManager.addDelegate(new AppStatus.Delegate(activity))
                  .addDelegate(new UpdateableApp.Delegate(activity))
                  .addDelegate(new UpdateableAppsHeader.Delegate(activity))
                  .addDelegate(new KnownVulnApp.Delegate(activity));
  
          initLoaders();
      }
  
      /**
       * There are some statuses managed by {@link AppUpdateStatusManager} which we don't care about
       * for the "Updates" view. For example Also, although this
       * adapter does know about apps with updates available, it does so by querying the database not
       * by querying the app update status manager. As such, apps with the status
       * {@link org.fdroid.fdroid.AppUpdateStatusManager.Status#UpdateAvailable} are not interesting here.
       */
      private boolean shouldShowStatus(AppUpdateStatusManager.AppUpdateStatus status) {
          return status.status == AppUpdateStatusManager.Status.PendingInstall ||
                  status.status == AppUpdateStatusManager.Status.Downloading ||
                  status.status == AppUpdateStatusManager.Status.Installing ||
                  status.status == AppUpdateStatusManager.Status.Installed ||
                  status.status == AppUpdateStatusManager.Status.ReadyToInstall;
      }
  
      /**
       * Adds items from the {@link AppUpdateStatusManager} to {@link UpdatesAdapter#appsToShowStatus}.
       * Note that this will then subsequently rebuild the underlying adapter data structure by
       * invoking {@link UpdatesAdapter#populateItems}. However as per the populateItems method, it
       * does not know how best to notify the recycler view of any changes. That is up to the caller
       * of this method.
       */
      private void populateAppStatuses() {
          for (AppUpdateStatusManager.AppUpdateStatus status : AppUpdateStatusManager.getInstance(activity).getAll()) {
              if (shouldShowStatus(status)) {
                  appsToShowStatus.add(new AppStatus(activity, status));
              }
          }
  
          Collections.sort(appsToShowStatus, new Comparator<AppStatus>() {
              @Override
              public int compare(AppStatus o1, AppStatus o2) {
                  return o1.status.app.name.compareTo(o2.status.app.name);
              }
          });
  
          populateItems();
      }
  
      public boolean canViewAllUpdateableApps() {
          return showAllUpdateableApps;
      }
  
      public void toggleAllUpdateableApps() {
          showAllUpdateableApps = !showAllUpdateableApps;
          populateItems();
      }
  
      /**
       * Completely rebuilds the underlying data structure used by this adapter. Note however, that
       * this does not notify the recycler view of any changes. Thus, it is up to other methods which
       * initiate a call to this method to make sure they appropriately notify the recyler view.
       */
      private void populateItems() {
          items.clear();
  
          Set<String> toShowStatusPackageNames = new HashSet<>(appsToShowStatus.size());
          for (AppStatus app : appsToShowStatus) {
              toShowStatusPackageNames.add(app.status.app.packageName);
              items.add(app);
          }
  
          if (updateableApps != null) {
              // Only count/show apps which are not shown above in the "Apps to show status" list.
              List<UpdateableApp> updateableAppsToShow = new ArrayList<>(updateableApps.size());
              for (UpdateableApp app : updateableApps) {
                  if (!toShowStatusPackageNames.contains(app.app.packageName)) {
                      updateableAppsToShow.add(app);
                  }
              }
  
              if (updateableAppsToShow.size() > 0) {
                  items.add(new UpdateableAppsHeader(activity, this, updateableAppsToShow));
  
                  if (showAllUpdateableApps) {
                      items.addAll(updateableAppsToShow);
                  }
              }
          }
  
          items.addAll(knownVulnApps);
      }
  
      @Override
      public int getItemCount() {
          return items.size();
      }
  
      @Override
      public int getItemViewType(int position) {
          return delegatesManager.getItemViewType(items, position);
      }
  
      @NonNull
      @Override
      public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
          return delegatesManager.onCreateViewHolder(parent, viewType);
      }
  
      @Override
      public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
          delegatesManager.onBindViewHolder(items, position, holder);
      }
  
      @NonNull
      @Override
      public Loader<Cursor> onCreateLoader(int id, Bundle args) {
          Uri uri;
          switch (id) {
              case LOADER_CAN_UPDATE:
                  uri = AppProvider.getCanUpdateUri();
                  break;
  
              case LOADER_KNOWN_VULN:
  