Implement game selector.

Item selection is currently not implemented.
This commit is contained in:
Miku AuahDark
2022-05-22 13:18:40 +08:00
parent b1f1062854
commit cee0ca9661
3 changed files with 187 additions and 2 deletions
@@ -0,0 +1,70 @@
package org.love2d.android;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.io.File;
public class GameListAdapter extends RecyclerView.Adapter<GameListAdapter.ViewHolder> {
private Data[] data = null;
@NonNull
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_game, parent, false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Data data = this.data[position];
holder.setName(data.path.getName());
holder.setIsDirectory(data.directory);
}
@Override
public int getItemCount() {
return data != null ? data.length : 0;
}
public void setData(Data[] data) {
this.data = data;
}
static class ViewHolder extends RecyclerView.ViewHolder {
private final TextView name;
private final ImageView image;
public ViewHolder(View itemView) {
super(itemView);
name = itemView.findViewById(R.id.textView);
image = itemView.findViewById(R.id.imageView);
}
public void setName(String name) {
this.name.setText(name);
}
public void setIsDirectory(boolean directory) {
this.image.setImageResource(directory ? R.drawable.ic_baseline_folder_32 : R.drawable.ic_baseline_insert_drive_file_32);
}
}
static class Data {
// Absolute path of the file.
public File path;
// Denote if this game is a directory.
public boolean directory;
}
}
@@ -2,6 +2,9 @@ package org.love2d.android;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import android.content.Intent;
@@ -10,19 +13,40 @@ import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.zip.ZipFile;
public class MainActivity extends AppCompatActivity {
private Executor executor = Executors.newSingleThreadExecutor();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = findViewById(R.id.recyclerView);
SwipeRefreshLayout swipeLayout = findViewById(R.id.swipeRefreshLayout);
ConstraintLayout noGameText = findViewById(R.id.constraintLayout);
GameListAdapter adapter = new GameListAdapter();
// Set refresh listener
swipeLayout.setOnRefreshListener(() -> {
// TODO: Actually update list of games
swipeLayout.setRefreshing(false);
scanGames(adapter, noGameText, swipeLayout);
});
// Set layout manager and adapter
recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
recyclerView.setAdapter(adapter);
scanGames(adapter, noGameText, null);
}
@Override
@@ -69,4 +93,93 @@ public class MainActivity extends AppCompatActivity {
return builder.setMessage(message.toString()).create();
}
private void scanGames(GameListAdapter adapter, ConstraintLayout noGameText, SwipeRefreshLayout swipeRefreshLayout) {
final MainActivity current = this;
executor.execute(() -> {
File extDir = getExternalFilesDir("games");
if (!extDir.isDirectory()) {
if (!extDir.mkdir()) {
// Scan failure, abort
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(false);
}
new AlertDialog.Builder(current)
.setTitle("Scan error")
.setMessage("Scan failure")
.setPositiveButton(R.string.ok, (dialog1, which) -> { })
.create()
.show();
return;
}
}
ArrayList<GameListAdapter.Data> validGames = new ArrayList<>();
File[] files = extDir.listFiles();
if (files != null) {
for (File file: files) {
GameListAdapter.Data gameData = null;
if (file.isDirectory()) {
if (isValidGamedir(file)) {
gameData = new GameListAdapter.Data();
gameData.path = file;
gameData.directory = true;
}
} else {
if (isValidLovegame(file)) {
gameData = new GameListAdapter.Data();
gameData.path = file;
gameData.directory = false;
}
}
if (gameData != null) {
validGames.add(gameData);
}
}
}
boolean empty = validGames.isEmpty();
runOnUiThread(() -> {
if (empty) {
adapter.setData(null);
} else {
GameListAdapter.Data[] gameDatas = new GameListAdapter.Data[validGames.size()];
validGames.toArray(gameDatas);
adapter.setData(gameDatas);
}
adapter.notifyDataSetChanged();
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(false);
}
noGameText.setVisibility(empty ? View.VISIBLE : View.INVISIBLE);
});
});
}
public static boolean isValidLovegame(File file) {
boolean valid = false;
try {
ZipFile zip = new ZipFile(file, ZipFile.OPEN_READ);
valid = zip.getEntry("main.lua") != null;
zip.close();
} catch (IOException ignored) { }
return valid;
}
public static boolean isValidGamedir(File file) {
File mainLua = new File(file, "main.lua");
return mainLua.isFile();
}
}
@@ -12,6 +12,7 @@
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/row_game">
@@ -20,6 +21,7 @@
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"