Skip to content
This repository was archived by the owner on Mar 12, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ android {
applicationId "org.qp.android"
minSdk 26
targetSdk 34
versionCode 202502
versionName "3.25.2"
versionCode 202503
versionName "3.25.3"
resourceConfigurations += ['en', 'ru']
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
Expand Down
18 changes: 17 additions & 1 deletion app/src/main/java/org/qp/android/helpers/utils/DirUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import com.anggrayudi.storage.file.DocumentFileCompat;
import com.anggrayudi.storage.file.DocumentFileType;
import com.anggrayudi.storage.file.DocumentFileUtils;
import com.anggrayudi.storage.file.MimeType;

import java.util.Collections;
import java.util.List;
Expand All @@ -28,12 +29,27 @@ public static boolean isDirContainsGameFile(@NonNull Context context,
@NonNull Uri dirUri) {
var targetDir = DocumentFileCompat.fromUri(context, dirUri);
if (!isWritableDir(context, targetDir)) return false;
var files = DocumentFileUtils.search(targetDir, true, DocumentFileType.FILE);
var files = targetDir.listFiles();
if (files == null || files.length == 0) return false;

for (var file : files) {
var dirExtension = documentWrap(file).getExtension();
var lcName = dirExtension.toLowerCase(Locale.ROOT);
if (lcName.contains("qsp") || lcName.contains("gam")) return true;
}

var allFiles = DocumentFileUtils.search(
targetDir,
true,
DocumentFileType.FILE,
new String[]{MimeType.BINARY_FILE}
);
for (var file : allFiles) {
var dirExtension = documentWrap(file).getExtension();
var lcName = dirExtension.toLowerCase(Locale.ROOT);
if (lcName.contains("qsp") || lcName.contains("gam")) return true;
}

return false;
}

Expand Down
64 changes: 46 additions & 18 deletions app/src/main/java/org/qp/android/model/repository/LocalGame.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
Expand Down Expand Up @@ -136,17 +137,34 @@ public boolean searchAndWriteFileInfo(DocumentFile rootDir, GameData data) {
return false;
}

var gameFiles = Collections.synchronizedList(new ArrayList<Uri>());
var files = DocumentFileUtils.search(rootDir, true, DocumentFileType.FILE);
var files = rootDir.listFiles();
if (files == null || files.length == 0) return false;

files.parallelStream().forEach(d -> {
var gameFiles = Collections.synchronizedList(new ArrayList<Uri>());
Arrays.stream(files).forEach(d -> {
var dirExtension = documentWrap(d).getExtension();
var lcName = dirExtension.toLowerCase(Locale.ROOT);
if (lcName.endsWith("qsp") || lcName.endsWith("gam")) {
gameFiles.add(d.getUri());
}
});

if (gameFiles.isEmpty()) {
var allFiles = DocumentFileUtils.search(
rootDir,
true,
DocumentFileType.FILE,
new String[]{MimeType.BINARY_FILE}
);
allFiles.forEach(d -> {
var dirExtension = documentWrap(d).getExtension();
var lcName = dirExtension.toLowerCase(Locale.ROOT);
if (lcName.endsWith("qsp") || lcName.endsWith("gam")) {
gameFiles.add(d.getUri());
}
});
}

if (!Objects.equals(data.gameDirUri.getPath(), rootDir.getUri().getPath())) {
data.gameDirUri = rootDir.getUri();
}
Expand All @@ -166,7 +184,6 @@ public List<GameData> lightExtractDataFromDir(File generalGamesDir) throws IOExc
return Collections.emptyList();
}

var itemsGamesDirs = Collections.synchronizedList(new ArrayList<GameData>());
var subRootDir = Collections.synchronizedList(new ArrayList<File>());
synchronized (subRootDir) {
try (var walk = Files.walk(generalGamesDir.toPath(), 1)) {
Expand All @@ -178,26 +195,37 @@ public List<GameData> lightExtractDataFromDir(File generalGamesDir) throws IOExc
}
}

synchronized (itemsGamesDirs) {
var subInfosFiles = Collections.synchronizedList(new ArrayList<File>());
synchronized (subInfosFiles) {
for (var dir : subRootDir) {
if (!isWritableDir(context, dir)) {
try (var walk = Files.walk(dir.toPath())) {
walk.map(Path::toFile)
.filter(f -> f.isFile() && f.getPath().contains(GAME_INFO_FILENAME))
.forEach(subInfosFiles::add);
} catch (IOException e) {
return Collections.emptyList();
}
}
}

var itemsGamesDirs = Collections.synchronizedList(new ArrayList<GameData>());
synchronized (itemsGamesDirs) {
for (var infos : subInfosFiles) {
if (!isWritableFile(context, infos)) {
continue;
}
var item = (GameData) null;
var infoFile = fromRelPath(GAME_INFO_FILENAME, dir);
if (isWritableFile(context, infoFile)) {
var infoFileCont = readFileAsString(infoFile);
if (isNotEmptyOrBlank(infoFileCont)) {
try {
item = parseGameInfo(infoFileCont);
} catch (IOException e) {
continue;
}
}
if (item != null) {
itemsGamesDirs.add(item);
var infoFileCont = readFileAsString(infos);
if (isNotEmptyOrBlank(infoFileCont)) {
try {
item = parseGameInfo(infoFileCont);
} catch (IOException e) {
continue;
}
}
if (item != null) {
itemsGamesDirs.add(item);
}
}
}

Expand Down
8 changes: 4 additions & 4 deletions app/src/main/java/org/qp/android/ui/stock/StockActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static org.qp.android.helpers.utils.FileUtil.findOrCreateFile;
import static org.qp.android.helpers.utils.JsonUtil.jsonToObject;
import static org.qp.android.helpers.utils.JsonUtil.objectToJson;
import static org.qp.android.helpers.utils.StringUtil.isNotEmptyOrBlank;
import static org.qp.android.helpers.utils.XmlUtil.objectToXml;
import static org.qp.android.helpers.utils.XmlUtil.xmlToObject;
import static org.qp.android.ui.stock.StockViewModel.CODE_PICK_IMAGE_FILE;
Expand Down Expand Up @@ -80,7 +81,6 @@
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import info.hannes.changelog.ChangeLog;
import okhttp3.ResponseBody;
Expand Down Expand Up @@ -578,11 +578,11 @@ public boolean onQueryTextSubmit(String query) {

@Override
public boolean onQueryTextChange(String newText) {
var gameDataList = stockViewModel.getSortedGames();
var gameDataList = stockViewModel.getGamesMap().values();
stockViewModel.setDataList(
gameDataList.stream()
.filter(d -> d.title.toLowerCase().contains(newText.toLowerCase()))
.collect(Collectors.toList())
.filter(r -> isNotEmptyOrBlank(r.title) && r.title.toLowerCase().contains(newText.toLowerCase()))
.toList()
);
return true;
}
Expand Down
14 changes: 3 additions & 11 deletions app/src/main/java/org/qp/android/ui/stock/StockViewModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -180,16 +180,6 @@ private SettingsController getController() {
return SettingsController.newInstance(getApplication());
}

@NonNull
public List<GameData> getSortedGames() {
var unsortedGameData = gamesMap.values();
var gameData = new ArrayList<>(unsortedGameData);
if (gameData.size() < 2) return gameData;
gameData.sort(Comparator.comparing(game -> game.title.toLowerCase()));
gameData.sort(Comparator.comparing(game -> game.listId));
return gameData;
}

public Optional<GameData> getCurrGameData() {
return Optional.ofNullable(currGameData);
}
Expand Down Expand Up @@ -475,7 +465,7 @@ public boolean onCreateActionMode(ActionMode mode, Menu menu) {

@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
tempList.addAll(getSortedGames());
tempList.addAll(gamesMap.values());
isEnableDeleteMode = true;
return true;
}
Expand Down Expand Up @@ -838,6 +828,7 @@ private void syncFromDisk() {
if (syncDataList.size() < 2) return syncDataList;
synchronized (syncDataList) {
return syncDataList.stream()
.filter(game -> isNotEmptyOrBlank(game.title))
.sorted(Comparator.comparing(game -> game.title.toLowerCase()))
.sorted(Comparator.comparing(game -> game.listId))
.filter(this::isGameInstalled)
Expand Down Expand Up @@ -871,6 +862,7 @@ private void syncRemote() {
if (syncDataList.size() < 2) return syncDataList;
synchronized (syncDataList) {
return syncDataList.stream()
.filter(game -> isNotEmptyOrBlank(game.title))
.sorted(Comparator.comparing(game -> game.title.toLowerCase()))
.filter(d -> !isGameInstalled(d))
.toList();
Expand Down
Loading