diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/SearchModFragment.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/SearchModFragment.java index 26f8f7400..128fb1a6a 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/SearchModFragment.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/SearchModFragment.java @@ -2,9 +2,7 @@ package net.kdt.pojavlaunch.fragments; import android.content.res.ColorStateList; import android.graphics.Color; -import android.os.Build; import android.os.Bundle; -import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; @@ -18,23 +16,14 @@ import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import net.kdt.pojavlaunch.PojavApplication; import net.kdt.pojavlaunch.R; -import net.kdt.pojavlaunch.Tools; import net.kdt.pojavlaunch.modloaders.modpacks.ModItemAdapter; -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.ModpackApi; -import net.kdt.pojavlaunch.modloaders.modpacks.api.ModrinthApi; -import net.kdt.pojavlaunch.modloaders.modpacks.models.ModItem; import net.kdt.pojavlaunch.modloaders.modpacks.models.SearchFilters; import net.kdt.pojavlaunch.profiles.VersionSelectorDialog; -import java.util.Arrays; -import java.util.concurrent.Future; - -public class SearchModFragment extends Fragment { +public class SearchModFragment extends Fragment implements ModItemAdapter.SearchResultCallback { public static final String TAG = "SearchModFragment"; private View mOverlay; @@ -53,13 +42,12 @@ public class SearchModFragment extends Fragment { private RecyclerView mRecyclerview; private ModItemAdapter mModItemAdapter; private ProgressBar mSearchProgressBar; - private Future mSearchFuture; private TextView mStatusTextView; private ColorStateList mDefaultTextColor; private ModpackApi modpackApi; - private SearchFilters mSearchFilters; + private final SearchFilters mSearchFilters; public SearchModFragment(){ super(R.layout.fragment_mod_search); @@ -71,7 +59,7 @@ public class SearchModFragment extends Fragment { @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { // You can only access resources after attaching to current context - mModItemAdapter = new ModItemAdapter(getResources(), modpackApi); + mModItemAdapter = new ModItemAdapter(getResources(), modpackApi, this); mOverlayTopCache = getResources().getDimension(R.dimen.fragment_padding_medium); mOverlay = view.findViewById(R.id.search_mod_overlay); @@ -96,15 +84,19 @@ public class SearchModFragment extends Fragment { })); mSearchEditText.setOnEditorActionListener((v, actionId, event) -> { - if(mSearchFuture != null && !mSearchFuture.isCancelled()) { - mSearchFuture.cancel(true); - } mSearchProgressBar.setVisibility(View.VISIBLE); mSearchFilters.name = mSearchEditText.getText().toString(); - mSearchFuture = new SelfReferencingFuture(new SearchModTask(mSearchFilters)) - .startOnExecutor(PojavApplication.sExecutorService); + mModItemAdapter.performSearchQuery(mSearchFilters); return true; }); + + mOverlay.post(()->{ + int overlayHeight = mOverlay.getHeight(); + mRecyclerview.setPadding(mRecyclerview.getPaddingLeft(), + mRecyclerview.getPaddingTop() + overlayHeight, + mRecyclerview.getPaddingRight(), + mRecyclerview.getPaddingBottom()); + }); } @Override @@ -113,35 +105,25 @@ public class SearchModFragment extends Fragment { mRecyclerview.removeOnScrollListener(mOverlayPositionListener); } - class SearchModTask implements SelfReferencingFuture.FutureInterface { + @Override + public void onSearchFinished() { + mSearchProgressBar.setVisibility(View.GONE); + mStatusTextView.setVisibility(View.GONE); + } - private final SearchFilters mTaskFilters; - SearchModTask(SearchFilters mSearchFilters) { - this.mTaskFilters = mSearchFilters; - } - - @Override - public void run(Future myFuture) { - ModItem[] items = modpackApi.searchMod(mTaskFilters); - Log.d(SearchModFragment.class.toString(), Arrays.toString(items)); - Tools.runOnUiThread(() -> { - ModItem[] localItems = items; - if(myFuture.isCancelled()) return; - mSearchProgressBar.setVisibility(View.GONE); - if(localItems == null) { - mStatusTextView.setVisibility(View.VISIBLE); - mStatusTextView.setTextColor(Color.RED); - mStatusTextView.setText(R.string.search_modpack_error); - }else if(localItems.length == 0) { - mStatusTextView.setVisibility(View.VISIBLE); - mStatusTextView.setTextColor(mDefaultTextColor); - mStatusTextView.setText(R.string.search_modpack_no_result); - localItems = null; - }else{ - mStatusTextView.setVisibility(View.GONE); - } - mModItemAdapter.setModItems(localItems, mSelectedVersion.getText().toString()); - }); + @Override + public void onSearchError(int error) { + mSearchProgressBar.setVisibility(View.GONE); + mStatusTextView.setVisibility(View.VISIBLE); + switch(error) { + case ERROR_INTERNAL: + mStatusTextView.setTextColor(Color.RED); + mStatusTextView.setText(R.string.search_modpack_error); + break; + case ERROR_NO_RESULTS: + mStatusTextView.setTextColor(mDefaultTextColor); + mStatusTextView.setText(R.string.search_modpack_no_result); + break; } } } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/ModItemAdapter.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/ModItemAdapter.java index 5c78d8780..8e140ea1d 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/ModItemAdapter.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/ModItemAdapter.java @@ -29,23 +29,103 @@ 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.ModItem; +import net.kdt.pojavlaunch.modloaders.modpacks.models.SearchFilters; +import net.kdt.pojavlaunch.modloaders.modpacks.models.SearchResult; import java.util.Arrays; import java.util.Collections; import java.util.concurrent.Future; -public class ModItemAdapter extends RecyclerView.Adapter { +public class ModItemAdapter extends RecyclerView.Adapter { private static final ModItem[] MOD_ITEMS_EMPTY = new ModItem[0]; + private static final int VIEW_TYPE_MOD_ITEM = 0; + private static final int VIEW_TYPE_LOADING = 1; /* Used when versions haven't loaded yet, default text to reduce layout shifting */ private final SimpleArrayAdapter mLoadingAdapter = new SimpleArrayAdapter<>(Collections.singletonList("Loading")); private final ModIconCache mIconCache = new ModIconCache(); + private final SearchResultCallback mSearchResultCallback; private ModItem[] mModItems; private final ModpackApi mModpackApi; /* Cache for ever so slightly rounding the image for the corner not to stick out of the layout */ private final float mCornerDimensionCache; + private Future mTaskInProgress; + private SearchFilters mSearchFilters; + private SearchResult mCurrentResult; + + + public ModItemAdapter(Resources resources, ModpackApi api, SearchResultCallback callback) { + mCornerDimensionCache = resources.getDimension(R.dimen._1sdp) / 250; + mModpackApi = api; + mModItems = new ModItem[]{}; + mSearchResultCallback = callback; + } + + public void performSearchQuery(SearchFilters searchFilters) { + if(mTaskInProgress != null) { + mTaskInProgress.cancel(true); + mTaskInProgress = null; + } + this.mSearchFilters = searchFilters; + mTaskInProgress = new SelfReferencingFuture(new SearchApiTask(mSearchFilters, null)) + .startOnExecutor(PojavApplication.sExecutorService); + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { + LayoutInflater layoutInflater = LayoutInflater.from(viewGroup.getContext()); + View view; + switch (viewType) { + case VIEW_TYPE_MOD_ITEM: + // Create a new view, which defines the UI of the list item + view = layoutInflater.inflate(R.layout.view_mod, viewGroup, false); + return new ViewHolder(view); + case VIEW_TYPE_LOADING: + // Create a new view, which is actually just the progress bar + view = layoutInflater.inflate(R.layout.view_loading, viewGroup, false); + return new LoadingViewHolder(view); + default: + throw new RuntimeException("Unimplemented view type!"); + } + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + switch (getItemViewType(position)) { + case VIEW_TYPE_MOD_ITEM: + ((ModItemAdapter.ViewHolder)holder).setStateLimited(mModItems[position]); + break; + case VIEW_TYPE_LOADING: + loadMoreResults(); + break; + default: + throw new RuntimeException("Unimplemented view type!"); + } + } + + @Override + public int getItemCount() { + if(mModItems.length == 0) return 0; + return mModItems.length+1; + } + + private void loadMoreResults() { + if(mTaskInProgress != null) return; + mTaskInProgress = new SelfReferencingFuture(new SearchApiTask(mSearchFilters, mCurrentResult)) + .startOnExecutor(PojavApplication.sExecutorService); + } + + @Override + public int getItemViewType(int position) { + if(position < mModItems.length) return VIEW_TYPE_MOD_ITEM; + return VIEW_TYPE_LOADING; + } + + + /** * Basic viewholder with expension capabilities */ @@ -228,38 +308,67 @@ public class ModItemAdapter extends RecyclerView.Adapter myFuture) { + SearchResult result = mModpackApi.searchMod(mSearchFilters, mPreviousResult); + ModItem[] resultModItems = result != null ? result.results : null; + Tools.runOnUiThread(() -> { + if(myFuture.isCancelled()) return; + + if(resultModItems == null) { + mSearchResultCallback.onSearchError(SearchResultCallback.ERROR_INTERNAL); + }else if(resultModItems.length == 0) { + mSearchResultCallback.onSearchError(SearchResultCallback.ERROR_NO_RESULTS); + }else{ + mSearchResultCallback.onSearchFinished(); + } + mCurrentResult = result; + if(resultModItems == null) { + mModItems = MOD_ITEMS_EMPTY; + mTaskInProgress = null; + notifyDataSetChanged(); + return; + } + if(mPreviousResult != null) { + ModItem[] newModItems = new ModItem[resultModItems.length + mModItems.length]; + System.arraycopy(mModItems, 0, newModItems, 0, mModItems.length); + System.arraycopy(resultModItems, 0, newModItems, mModItems.length, resultModItems.length); + mModItems = newModItems; + mTaskInProgress = null; + notifyItemChanged(mModItems.length); + notifyItemRangeInserted(mModItems.length+1, newModItems.length); + return; + } + mModItems = resultModItems; + mTaskInProgress = null; + notifyDataSetChanged(); + }); + } } - @NonNull - @Override - public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { - // Create a new view, which defines the UI of the list item - View view = LayoutInflater.from(viewGroup.getContext()) - .inflate(R.layout.view_mod, viewGroup, false); - - return new ViewHolder(view); - } - - @Override - public void onBindViewHolder(ViewHolder viewHolder, final int position) { - viewHolder.setStateLimited(mModItems[position]); - } - - @Override - public int getItemCount() { - return mModItems.length; + public interface SearchResultCallback { + int ERROR_INTERNAL = 0; + int ERROR_NO_RESULTS = 1; + void onSearchFinished(); + void onSearchError(int error); } } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CommonApi.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CommonApi.java index e527219b9..aad45ad3c 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CommonApi.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CommonApi.java @@ -7,6 +7,7 @@ import net.kdt.pojavlaunch.modloaders.modpacks.models.Constants; import net.kdt.pojavlaunch.modloaders.modpacks.models.ModDetail; import net.kdt.pojavlaunch.modloaders.modpacks.models.ModItem; import net.kdt.pojavlaunch.modloaders.modpacks.models.SearchFilters; +import net.kdt.pojavlaunch.modloaders.modpacks.models.SearchResult; import java.util.Arrays; import java.util.concurrent.Callable; @@ -21,22 +22,36 @@ public class CommonApi implements ModpackApi { private final ModpackApi mModrinthApi = new ModrinthApi(); private final ModpackApi[] mModpackApis = new ModpackApi[]{mModrinthApi, mCurseforgeApi}; @Override - public ModItem[] searchMod(SearchFilters searchFilters) { - ModItem[][] items = new ModItem[mModpackApis.length][]; + public SearchResult searchMod(SearchFilters searchFilters, SearchResult previousPageResult) { + CommonApiSearchResult commonApiSearchResult = (CommonApiSearchResult) previousPageResult; + // If there are no previous page results, create a new array. Otherwise, use the one from the previous page + SearchResult[] results = commonApiSearchResult == null ? + new SearchResult[mModpackApis.length] : commonApiSearchResult.searchResults; + int totalSize = 0; + int totalTotalSize = 0; Future[] futures = new Future[mModpackApis.length]; for(int i = 0; i < mModpackApis.length; i++) { - futures[i] = PojavApplication.sExecutorService.submit(new ApiDownloadTask(i, searchFilters)); + // If there is an array and its length is zero, this means that we've exhausted the results for this + // search query and we don't need to actually do the search + if(results[i] != null && results[i].results.length == 0) continue; + futures[i] = PojavApplication.sExecutorService.submit(new ApiDownloadTask(i, searchFilters, + results[i])); } + if(Thread.interrupted()) { cancelAllFutures(futures); return null; } + // Count up all the results for(int i = 0; i < mModpackApis.length; i++) { + Future future = futures[i]; + if(future == null) continue; try { - items[i] = (ModItem[]) futures[i].get(); - totalSize += items[i].length; + SearchResult searchResult = results[i] = (SearchResult) future.get(); + totalSize += searchResult.results.length; + totalTotalSize += searchResult.totalResultCount; }catch (Exception e) { cancelAllFutures(futures); e.printStackTrace(); @@ -46,14 +61,22 @@ public class CommonApi implements ModpackApi { // Then build an array with all the mods ModItem[] concatenatedItems = new ModItem[totalSize]; int copyOffset = 0; - for(ModItem[] apiItems : items) { - System.arraycopy(apiItems, 0, concatenatedItems, copyOffset, apiItems.length); - copyOffset += apiItems.length; + for(SearchResult result : results) { + ModItem[] searchResults = result.results; + // If the length is zero, we don't need to perform needless copies + if(searchResults.length == 0) continue; + System.arraycopy(searchResults, 0, concatenatedItems, copyOffset, searchResults.length); + copyOffset += searchResults.length; } if(Thread.interrupted()) return null; Arrays.sort(concatenatedItems, (modItem, t1) -> modItem.title.compareToIgnoreCase(t1.title)); if(Thread.interrupted()) return null; - return concatenatedItems; + // Recycle or create new search result + if(commonApiSearchResult == null) commonApiSearchResult = new CommonApiSearchResult(); + commonApiSearchResult.searchResults = results; + commonApiSearchResult.totalResultCount = totalTotalSize; + commonApiSearchResult.results = concatenatedItems; + return commonApiSearchResult; } @Override @@ -84,18 +107,24 @@ public class CommonApi implements ModpackApi { } } - private class ApiDownloadTask implements Callable { + private class ApiDownloadTask implements Callable { private final int mModApi; private final SearchFilters mSearchFilters; + private final SearchResult mPreviousPageResult; - private ApiDownloadTask(int modApi, SearchFilters searchFilters) { + private ApiDownloadTask(int modApi, SearchFilters searchFilters, SearchResult previousPageResult) { this.mModApi = modApi; this.mSearchFilters = searchFilters; + this.mPreviousPageResult = previousPageResult; } @Override - public ModItem[] call() { - return mModpackApis[mModApi].searchMod(mSearchFilters); + public SearchResult call() { + return mModpackApis[mModApi].searchMod(mSearchFilters, mPreviousPageResult); } } + + class CommonApiSearchResult extends SearchResult { + SearchResult[] searchResults = new SearchResult[mModpackApis.length]; + } } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CurseforgeApi.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CurseforgeApi.java index 48a203caf..f6118aef6 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CurseforgeApi.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CurseforgeApi.java @@ -14,6 +14,7 @@ import net.kdt.pojavlaunch.modloaders.modpacks.models.CurseManifest; import net.kdt.pojavlaunch.modloaders.modpacks.models.ModDetail; import net.kdt.pojavlaunch.modloaders.modpacks.models.ModItem; import net.kdt.pojavlaunch.modloaders.modpacks.models.SearchFilters; +import net.kdt.pojavlaunch.modloaders.modpacks.models.SearchResult; import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper; import net.kdt.pojavlaunch.utils.FileUtils; import net.kdt.pojavlaunch.utils.ZipUtils; @@ -40,17 +41,21 @@ public class CurseforgeApi implements ModpackApi{ private final ApiHandler mApiHandler = new ApiHandler("https://api.curseforge.com/v1", "$2a$10$Vxkj4kH1Ekf8EsS4Mx8b2eVTHsht107Lk2erVEUtnbqvojsLy.jYq"); @Override - public ModItem[] searchMod(SearchFilters searchFilters) { + public SearchResult searchMod(SearchFilters searchFilters, SearchResult previousPageResult) { + CurseforgeSearchResult curseforgeSearchResult = (CurseforgeSearchResult) previousPageResult; HashMap params = new HashMap<>(); params.put("gameId", CURSEFORGE_MINECRAFT_GAME_ID); params.put("classId", searchFilters.isModpack ? CURSEFORGE_MODPACK_CLASS_ID : CURSEFORGE_MOD_CLASS_ID); params.put("searchFilter", searchFilters.name); if(searchFilters.mcVersion != null && !searchFilters.mcVersion.isEmpty()) params.put("gameVersion", searchFilters.mcVersion); + if(previousPageResult != null) + params.put("index", curseforgeSearchResult.previousOffset); JsonObject response = mApiHandler.get("mods/search", params, JsonObject.class); if(response == null) return null; JsonArray dataArray = response.getAsJsonArray("data"); if(dataArray == null) return null; + JsonObject paginationInfo = response.getAsJsonObject("pagination"); ArrayList modItemList = new ArrayList<>(dataArray.size()); for(int i = 0; i < dataArray.size(); i++) { JsonObject dataElement = dataArray.get(i).getAsJsonObject(); @@ -69,7 +74,12 @@ public class CurseforgeApi implements ModpackApi{ dataElement.getAsJsonObject("logo").get("thumbnailUrl").getAsString()); modItemList.add(modItem); } - return modItemList.toArray(new ModItem[0]); + if(curseforgeSearchResult == null) curseforgeSearchResult = new CurseforgeSearchResult(); + curseforgeSearchResult.results = modItemList.toArray(new ModItem[0]); + curseforgeSearchResult.totalResultCount = paginationInfo.get("totalCount").getAsInt(); + curseforgeSearchResult.previousOffset += dataArray.size(); + return curseforgeSearchResult; + } @Override @@ -215,4 +225,8 @@ public class CurseforgeApi implements ModpackApi{ if(manifest.minecraft.modLoaders.length < 1) return false; return true; } + + class CurseforgeSearchResult extends SearchResult { + int previousOffset; + } } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModpackApi.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModpackApi.java index ff9f9c67e..55b9b05bd 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModpackApi.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModpackApi.java @@ -10,6 +10,7 @@ import net.kdt.pojavlaunch.modloaders.ModloaderDownloadListener; import net.kdt.pojavlaunch.modloaders.modpacks.models.ModDetail; import net.kdt.pojavlaunch.modloaders.modpacks.models.ModItem; import net.kdt.pojavlaunch.modloaders.modpacks.models.SearchFilters; +import net.kdt.pojavlaunch.modloaders.modpacks.models.SearchResult; import java.io.File; @@ -18,11 +19,20 @@ import java.io.File; */ public interface ModpackApi { + /** + * @param searchFilters Filters + * @param offset the offset into the list of search results + * @return the list of mod items from specified offset + */ + SearchResult searchMod(SearchFilters searchFilters, SearchResult previousPageResult); + /** * @param searchFilters Filters * @return A list of mod items */ - ModItem[] searchMod(SearchFilters searchFilters); + default SearchResult searchMod(SearchFilters searchFilters) { + return searchMod(searchFilters, null); + } /** * Fetch the mod details diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModrinthApi.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModrinthApi.java index 5e88940f0..479fcd351 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModrinthApi.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModrinthApi.java @@ -11,6 +11,7 @@ import net.kdt.pojavlaunch.modloaders.modpacks.models.ModDetail; import net.kdt.pojavlaunch.modloaders.modpacks.models.ModItem; import net.kdt.pojavlaunch.modloaders.modpacks.models.ModrinthIndex; import net.kdt.pojavlaunch.modloaders.modpacks.models.SearchFilters; +import net.kdt.pojavlaunch.modloaders.modpacks.models.SearchResult; import net.kdt.pojavlaunch.progresskeeper.DownloaderProgressWrapper; import net.kdt.pojavlaunch.utils.ZipUtils; @@ -27,7 +28,8 @@ public class ModrinthApi implements ModpackApi{ } @Override - public ModItem[] searchMod(SearchFilters searchFilters) { + public SearchResult searchMod(SearchFilters searchFilters, SearchResult previousPageResult) { + ModrinthSearchResult modrinthSearchResult = (ModrinthSearchResult) previousPageResult; HashMap params = new HashMap<>(); // Build the facets filters @@ -39,7 +41,9 @@ public class ModrinthApi implements ModpackApi{ facetString.append("]"); params.put("facets", facetString.toString()); params.put("query", searchFilters.name); - params.put("limit", 100); + params.put("limit", 50); + if(modrinthSearchResult != null) + params.put("offset", modrinthSearchResult.previousOffset); JsonObject response = mApiHandler.get("search", params, JsonObject.class); if(response == null) return null; @@ -58,8 +62,11 @@ public class ModrinthApi implements ModpackApi{ hit.get("icon_url").getAsString() ); } - - return items; + if(modrinthSearchResult == null) modrinthSearchResult = new ModrinthSearchResult(); + modrinthSearchResult.previousOffset += responseHits.size(); + modrinthSearchResult.results = items; + modrinthSearchResult.totalResultCount = response.get("total_hits").getAsInt(); + return modrinthSearchResult; } @Override @@ -124,4 +131,8 @@ public class ModrinthApi implements ModpackApi{ return createInfo(modrinthIndex); } } + + class ModrinthSearchResult extends SearchResult { + int previousOffset; + } } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/models/SearchResult.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/models/SearchResult.java new file mode 100644 index 000000000..94638435c --- /dev/null +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/models/SearchResult.java @@ -0,0 +1,6 @@ +package net.kdt.pojavlaunch.modloaders.modpacks.models; + +public class SearchResult { + public int totalResultCount; + public ModItem[] results; +} diff --git a/app_pojavlauncher/src/main/res/layout/fragment_mod_search.xml b/app_pojavlauncher/src/main/res/layout/fragment_mod_search.xml index c2a75cbb4..6455fa1d9 100644 --- a/app_pojavlauncher/src/main/res/layout/fragment_mod_search.xml +++ b/app_pojavlauncher/src/main/res/layout/fragment_mod_search.xml @@ -101,7 +101,7 @@ android:id="@+id/search_mod_list" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingTop="@dimen/_65sdp" + android:paddingTop="@dimen/_15sdp" android:clipToPadding="false" app:layout_constraintBottom_toBottomOf="parent" diff --git a/app_pojavlauncher/src/main/res/layout/view_loading.xml b/app_pojavlauncher/src/main/res/layout/view_loading.xml new file mode 100644 index 000000000..385e1eed8 --- /dev/null +++ b/app_pojavlauncher/src/main/res/layout/view_loading.xml @@ -0,0 +1,12 @@ + + + + \ No newline at end of file