Asynchronous CommonApi implementation + modpack source icons

This commit is contained in:
BuildTools 2023-08-16 11:17:04 +03:00 committed by ArtDev
parent 1542ea0204
commit 17cce29c00
6 changed files with 78 additions and 29 deletions

View File

@ -21,6 +21,7 @@ import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.Tools; import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.modloaders.modpacks.ModItemAdapter; import net.kdt.pojavlaunch.modloaders.modpacks.ModItemAdapter;
import net.kdt.pojavlaunch.modloaders.modpacks.SelfReferencingFuture; import net.kdt.pojavlaunch.modloaders.modpacks.SelfReferencingFuture;
import net.kdt.pojavlaunch.modloaders.modpacks.api.CommonApi;
import net.kdt.pojavlaunch.modloaders.modpacks.api.CurseforgeApi; import net.kdt.pojavlaunch.modloaders.modpacks.api.CurseforgeApi;
import net.kdt.pojavlaunch.modloaders.modpacks.api.ModpackApi; import net.kdt.pojavlaunch.modloaders.modpacks.api.ModpackApi;
import net.kdt.pojavlaunch.modloaders.modpacks.api.ModrinthApi; import net.kdt.pojavlaunch.modloaders.modpacks.api.ModrinthApi;
@ -52,7 +53,7 @@ public class SearchModFragment extends Fragment {
public SearchModFragment(){ public SearchModFragment(){
super(R.layout.fragment_mod_search); super(R.layout.fragment_mod_search);
modpackApi = new CurseforgeApi(); modpackApi = new CommonApi();
mSearchFilters = new SearchFilters(); mSearchFilters = new SearchFilters();
mSearchFilters.isModpack = true; mSearchFilters.isModpack = true;
} }

View File

@ -26,6 +26,7 @@ import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.modloaders.modpacks.api.ModpackApi; import net.kdt.pojavlaunch.modloaders.modpacks.api.ModpackApi;
import net.kdt.pojavlaunch.modloaders.modpacks.imagecache.ImageReceiver; import net.kdt.pojavlaunch.modloaders.modpacks.imagecache.ImageReceiver;
import net.kdt.pojavlaunch.modloaders.modpacks.imagecache.ModIconCache; import net.kdt.pojavlaunch.modloaders.modpacks.imagecache.ModIconCache;
import net.kdt.pojavlaunch.modloaders.modpacks.models.Constants;
import net.kdt.pojavlaunch.modloaders.modpacks.models.ModDetail; import net.kdt.pojavlaunch.modloaders.modpacks.models.ModDetail;
import net.kdt.pojavlaunch.modloaders.modpacks.models.ModItem; import net.kdt.pojavlaunch.modloaders.modpacks.models.ModItem;
@ -49,7 +50,7 @@ public class ModItemAdapter extends RecyclerView.Adapter<ModItemAdapter.ViewHold
private ModDetail mModDetail = null; private ModDetail mModDetail = null;
private ModItem mModItem = null; private ModItem mModItem = null;
private final TextView mTitle, mDescription; private final TextView mTitle, mDescription;
private final ImageView mIconView; private final ImageView mIconView, mSourceView;
private View mExtendedLayout; private View mExtendedLayout;
private Spinner mExtendedSpinner; private Spinner mExtendedSpinner;
private Button mExtendedButton; private Button mExtendedButton;
@ -122,6 +123,7 @@ public class ModItemAdapter extends RecyclerView.Adapter<ModItemAdapter.ViewHold
mTitle = view.findViewById(R.id.mod_title_textview); mTitle = view.findViewById(R.id.mod_title_textview);
mDescription = view.findViewById(R.id.mod_body_textview); mDescription = view.findViewById(R.id.mod_body_textview);
mIconView = view.findViewById(R.id.mod_thumbnail_imageview); mIconView = view.findViewById(R.id.mod_thumbnail_imageview);
mSourceView = view.findViewById(R.id.mod_source_imageview);
} }
/** Display basic info about the moditem */ /** Display basic info about the moditem */
@ -153,6 +155,7 @@ public class ModItemAdapter extends RecyclerView.Adapter<ModItemAdapter.ViewHold
mIconView.setImageDrawable(drawable); mIconView.setImageDrawable(drawable);
}; };
mIconCache.getImage(mImageReceiver, mModItem.getIconCacheTag(), mModItem.imageUrl); mIconCache.getImage(mImageReceiver, mModItem.getIconCacheTag(), mModItem.imageUrl);
mSourceView.setImageResource(getSourceDrawable(item.apiSource));
mTitle.setText(item.title); mTitle.setText(item.title);
mDescription.setText(item.description); mDescription.setText(item.description);
@ -208,6 +211,17 @@ public class ModItemAdapter extends RecyclerView.Adapter<ModItemAdapter.ViewHold
private boolean isExtended(){ private boolean isExtended(){
return hasExtended() && mExtendedLayout.getVisibility() == View.VISIBLE; return hasExtended() && mExtendedLayout.getVisibility() == View.VISIBLE;
} }
private int getSourceDrawable(int apiSource) {
switch (apiSource) {
case Constants.SOURCE_CURSEFORGE:
return R.drawable.ic_curseforge;
case Constants.SOURCE_MODRINTH:
return R.drawable.ic_modrinth;
default:
throw new RuntimeException("Unknown API source");
}
}
} }

View File

@ -1,17 +1,16 @@
package net.kdt.pojavlaunch.modloaders.modpacks.api; package net.kdt.pojavlaunch.modloaders.modpacks.api;
import androidx.annotation.NonNull;
import net.kdt.pojavlaunch.PojavApplication;
import net.kdt.pojavlaunch.modloaders.modpacks.models.Constants; import net.kdt.pojavlaunch.modloaders.modpacks.models.Constants;
import net.kdt.pojavlaunch.modloaders.modpacks.models.ModDetail; import net.kdt.pojavlaunch.modloaders.modpacks.models.ModDetail;
import net.kdt.pojavlaunch.modloaders.modpacks.models.ModItem; import net.kdt.pojavlaunch.modloaders.modpacks.models.ModItem;
import net.kdt.pojavlaunch.modloaders.modpacks.models.SearchFilters; import net.kdt.pojavlaunch.modloaders.modpacks.models.SearchFilters;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.concurrent.Callable;
import java.util.List; import java.util.concurrent.Future;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/** /**
* Group all apis under the same umbrella, as another layer of abstraction * Group all apis under the same umbrella, as another layer of abstraction
@ -25,13 +24,25 @@ public class CommonApi implements ModpackApi {
public ModItem[] searchMod(SearchFilters searchFilters) { public ModItem[] searchMod(SearchFilters searchFilters) {
ModItem[][] items = new ModItem[mModpackApis.length][]; ModItem[][] items = new ModItem[mModpackApis.length][];
int totalSize = 0; int totalSize = 0;
// TODO unoptimized as hell
for(int i=0; i<mModpackApis.length; i++) { Future<?>[] futures = new Future<?>[mModpackApis.length];
items[i] = mModpackApis[i].searchMod(searchFilters); for(int i = 0; i < mModpackApis.length; i++) {
totalSize += items[i].length; futures[i] = PojavApplication.sExecutorService.submit(new ApiDownloadTask(i, searchFilters));
}
if(Thread.interrupted()) {
cancelAllFutures(futures);
return null;
}
for(int i = 0; i < mModpackApis.length; i++) {
try {
items[i] = (ModItem[]) futures[i].get();
totalSize += items[i].length;
}catch (Exception e) {
cancelAllFutures(futures);
e.printStackTrace();
return null;
}
} }
// Then build an array with all the mods // Then build an array with all the mods
ModItem[] concatenatedItems = new ModItem[totalSize]; ModItem[] concatenatedItems = new ModItem[totalSize];
int copyOffset = 0; int copyOffset = 0;
@ -39,31 +50,52 @@ public class CommonApi implements ModpackApi {
System.arraycopy(apiItems, 0, concatenatedItems, copyOffset, apiItems.length); System.arraycopy(apiItems, 0, concatenatedItems, copyOffset, apiItems.length);
copyOffset += apiItems.length; copyOffset += apiItems.length;
} }
if(Thread.interrupted()) return null;
Arrays.sort(concatenatedItems, (modItem, t1) -> modItem.title.compareToIgnoreCase(t1.title));
if(Thread.interrupted()) return null;
return concatenatedItems; return concatenatedItems;
} }
@Override @Override
public ModDetail getModDetails(ModItem item) { public ModDetail getModDetails(ModItem item) {
switch (item.apiSource) { return getModpackApi(item.apiSource).getModDetails(item);
case Constants.SOURCE_MODRINTH:
return mModrinthApi.getModDetails(item);
case Constants.SOURCE_CURSEFORGE:
return mCurseforgeApi.getModDetails(item);
default:
throw new UnsupportedOperationException("Unknown API source: " + item.apiSource);
}
} }
@Override @Override
public ModLoader installMod(ModDetail modDetail, int selectedVersion) { public ModLoader installMod(ModDetail modDetail, int selectedVersion) {
switch (modDetail.apiSource) { return getModpackApi(modDetail.apiSource).installMod(modDetail, selectedVersion);
}
private @NonNull ModpackApi getModpackApi(int apiSource) {
switch (apiSource) {
case Constants.SOURCE_MODRINTH: case Constants.SOURCE_MODRINTH:
return mModrinthApi.installMod(modDetail, selectedVersion); return mModrinthApi;
case Constants.SOURCE_CURSEFORGE: case Constants.SOURCE_CURSEFORGE:
return mCurseforgeApi.installMod(modDetail, selectedVersion); return mCurseforgeApi;
default: default:
throw new UnsupportedOperationException("Unknown API source: " + modDetail.apiSource); throw new UnsupportedOperationException("Unknown API source: " + apiSource);
}
}
private void cancelAllFutures(Future<?>[] futures) {
for(Future<?> future : futures) {
if(future == null) continue;
future.cancel(true);
}
}
private class ApiDownloadTask implements Callable<ModItem[]> {
private final int mModApi;
private final SearchFilters mSearchFilters;
private ApiDownloadTask(int modApi, SearchFilters searchFilters) {
this.mModApi = modApi;
this.mSearchFilters = searchFilters;
}
@Override
public ModItem[] call() {
return mModpackApis[mModApi].searchMod(mSearchFilters);
} }
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -24,10 +24,12 @@
<ImageView <ImageView
android:id="@+id/mod_source_imageview" android:id="@+id/mod_source_imageview"
android:layout_width="@dimen/_20sdp" android:layout_width="@dimen/_13sdp"
android:layout_height="@dimen/_20sdp" android:layout_height="@dimen/_13sdp"
android:scaleType="centerCrop" android:layout_marginTop="3dp"
android:layout_marginEnd="3dp"
android:scaleType="centerCrop"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:src="@mipmap/ic_launcher_foreground" /> tools:src="@mipmap/ic_launcher_foreground" />