diff --git a/app/build.gradle b/app/build.gradle index 572a23aa..5e35c98b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -41,5 +41,5 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.0.2' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.preference:preference:1.1.0-beta01' - implementation 'com.squareup.picasso:picasso:2.4.0' + implementation 'com.google.android.material:material:1.0.0' } diff --git a/app/src/main/java/gq/cyuubi/lightswitch/FileAdapter.java b/app/src/main/java/gq/cyuubi/lightswitch/FileAdapter.java new file mode 100644 index 00000000..e8d87067 --- /dev/null +++ b/app/src/main/java/gq/cyuubi/lightswitch/FileAdapter.java @@ -0,0 +1,104 @@ +package gq.cyuubi.lightswitch; + +import android.content.Context; +import android.graphics.Bitmap; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; + +import java.io.File; +import java.util.ArrayList; + +class DataModel { + File file; + TitleEntry meta; + + int index; + + public DataModel(File file) { + this.file = file; + index = file.getName().lastIndexOf("."); + meta = NroMeta.GetNroTitle(getPath()); + } + + public Bitmap getIcon() { + return meta.getIcon(); + } + + public String getTitle() { + return meta.getName() + " (" + getType() + ")"; + } + + public String getFileName() { + return file.getName(); + } + + public String getAuthor() { + return meta.getAuthor(); + } + + public String getType() { + return file.getName().substring(index + 1).toUpperCase(); + } + + public String getPath() { + return file.getAbsolutePath(); + } +} + +public class FileAdapter extends ArrayAdapter implements View.OnClickListener { + + Context mContext; + private ArrayList dataSet; + + public FileAdapter(Context context, @NonNull ArrayList data) { + super(context, R.layout.file_item, data); + this.dataSet = new ArrayList<>(); + this.mContext = context; + } + + @Override + public void onClick(View v) { + + int position = (Integer) v.getTag(); + Object object = getItem(position); + DataModel dataModel = (DataModel) object; + switch (v.getId()) { + case R.id.icon: + + break; + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + DataModel dataModel = getItem(position); + ViewHolder viewHolder; + if (convertView == null) { + viewHolder = new ViewHolder(); + LayoutInflater inflater = LayoutInflater.from(getContext()); + convertView = inflater.inflate(R.layout.file_item, parent, false); + viewHolder.icon = convertView.findViewById(R.id.icon); + viewHolder.txtTitle = convertView.findViewById(R.id.text_title); + viewHolder.txtSub = convertView.findViewById(R.id.text_subtitle); + convertView.setTag(viewHolder); + } else { + viewHolder = (ViewHolder) convertView.getTag(); + } + viewHolder.txtTitle.setText(dataModel.getTitle()); + viewHolder.txtSub.setText(dataModel.getAuthor()); + viewHolder.icon.setImageBitmap(dataModel.getIcon()); + return convertView; + } + + private static class ViewHolder { + ImageView icon; + TextView txtTitle; + TextView txtSub; + } +} diff --git a/app/src/main/java/gq/cyuubi/lightswitch/MainActivity.java b/app/src/main/java/gq/cyuubi/lightswitch/MainActivity.java index 38d90cb3..10bd872d 100644 --- a/app/src/main/java/gq/cyuubi/lightswitch/MainActivity.java +++ b/app/src/main/java/gq/cyuubi/lightswitch/MainActivity.java @@ -1,22 +1,17 @@ package gq.cyuubi.lightswitch; import android.Manifest; -import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Bundle; -import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.ArrayAdapter; import android.widget.ListView; -import android.widget.TextView; +import android.widget.Toast; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; @@ -24,82 +19,12 @@ import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.preference.PreferenceManager; +import com.google.android.material.snackbar.Snackbar; + import java.io.File; import java.util.ArrayList; import java.util.List; -class DataModel { - File file; - int index; - - public DataModel(File file) { - this.file = file; - index = file.getName().lastIndexOf("."); - } - - public String getTitle() { - return getName() + "(" + getType() + ")"; - } - - public String getName() { - String name = ""; - for (String str_i : file.getName().substring(0, index).split("_")) { - name += str_i.substring(0, 1).toUpperCase() + str_i.substring(1) + " "; - } - return name; - } - - public String getType() { - return file.getName().substring(index + 1).toUpperCase(); - } - - public String getPath() { - return file.getAbsolutePath(); - } -} - -class FileAdapter extends ArrayAdapter { - - Context mContext; - private ArrayList dataSet; - - public FileAdapter(Context context, @NonNull ArrayList data) { - super(context, android.R.layout.simple_list_item_2, data); - this.dataSet = new ArrayList<>(); - this.mContext = context; - } - - @Override - public void add(DataModel object) { - super.add(object); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - DataModel dataModel = getItem(position); - ViewHolder viewHolder; - if (convertView == null) { - viewHolder = new ViewHolder(); - LayoutInflater inflater = LayoutInflater.from(getContext()); - convertView = inflater.inflate(android.R.layout.simple_list_item_2, parent, false); - viewHolder.txtTitle = convertView.findViewById(android.R.id.text1); - viewHolder.txtPath = convertView.findViewById(android.R.id.text2); - convertView.setTag(viewHolder); - } else { - viewHolder = (ViewHolder) convertView.getTag(); - } - viewHolder.txtTitle.setText(dataModel.getTitle()); - viewHolder.txtPath.setText(dataModel.getPath()); - - return convertView; - } - - private static class ViewHolder { - TextView txtTitle; - TextView txtPath; - } -} - public class MainActivity extends AppCompatActivity { static { @@ -109,6 +34,11 @@ public class MainActivity extends AppCompatActivity { SharedPreferences sharedPreferences; FileAdapter adapter; + private void notifyUser(String text) { + Snackbar.make(findViewById(android.R.id.content), text, Snackbar.LENGTH_SHORT).show(); + // Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show(); + } + private List findFile(String ext, File file, @Nullable List files) { if (files == null) { files = new ArrayList<>(); @@ -159,7 +89,9 @@ public class MainActivity extends AppCompatActivity { game_list.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { - loadFile(((DataModel) parent.getItemAtPosition(position)).getPath()); + String path = ((DataModel) parent.getItemAtPosition(position)).getPath(); + notifyUser(getString(R.string.launch_string) + " " + path); + loadFile(path); } }); refresh_files(); @@ -178,6 +110,7 @@ public class MainActivity extends AppCompatActivity { startActivity(new Intent(this, SettingsActivity.class)); return true; case R.id.action_refresh: + notifyUser(getString(R.string.refresh_string)); refresh_files(); return true; default: diff --git a/app/src/main/java/gq/cyuubi/lightswitch/NroMeta.java b/app/src/main/java/gq/cyuubi/lightswitch/NroMeta.java index 19c65da4..b21aecfc 100644 --- a/app/src/main/java/gq/cyuubi/lightswitch/NroMeta.java +++ b/app/src/main/java/gq/cyuubi/lightswitch/NroMeta.java @@ -1,30 +1,34 @@ package gq.cyuubi.lightswitch; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.util.Log; -import android.widget.ImageView; -import com.squareup.picasso.Picasso; - -import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; final class TitleEntry { private final String name; private final String author; + private final Bitmap icon; - public TitleEntry(String name, String author) { + public TitleEntry(String name, String author, Bitmap icon) { this.name = name; this.author = author; + this.icon = icon; } - public String Name() { + public String getName() { return name; } - public String Author() { + public String getAuthor() { return author; } + + public Bitmap getIcon() { + return icon; + } } public class NroMeta { @@ -36,56 +40,34 @@ public class NroMeta { f.seek(asetOffset); // Skip to the offset specified by NroHeader.size byte[] buffer = new byte[4]; f.read(buffer); - if(!(new String(buffer).equals("ASET"))) + if (!(new String(buffer).equals("ASET"))) return null; - f.skipBytes(0x14); + f.skipBytes(0x4); + long iconOffset = Long.reverseBytes(f.readLong()); + int iconSize = Integer.reverseBytes(f.readInt()); + if (iconOffset == 0 || iconSize == 0) + throw new IOException(); + f.seek(asetOffset + iconOffset); + byte[] iconData = new byte[iconSize]; + f.read(iconData); + Bitmap icon = BitmapFactory.decodeByteArray(iconData, 0, iconSize); + + f.seek(asetOffset + 0x18); long nacpOffset = Long.reverseBytes(f.readLong()); long nacpSize = Long.reverseBytes(f.readLong()); - if(nacpOffset == 0 || nacpSize == 0) + if (nacpOffset == 0 || nacpSize == 0) return null; - f.seek(asetOffset + nacpOffset); byte[] name = new byte[0x200]; f.read(name); byte[] author = new byte[0x100]; f.read(author); - return new TitleEntry(new String(name).trim(), new String(author).trim()); - } - catch(IOException e) { + return new TitleEntry(new String(name).trim(), new String(author).trim(), icon); + } catch (IOException e) { Log.e("app_process64", "Error while loading ASET: " + e.getMessage()); return null; } } - - public static void LoadImage(String file, ImageView target, MainActivity context) { - try { - RandomAccessFile f = new RandomAccessFile(file, "r"); - f.seek(0x18); // Skip to NroHeader.size - int asetOffset = Integer.reverseBytes(f.readInt()); - f.seek(asetOffset); // Skip to the offset specified by NroHeader.size - byte[] buffer = new byte[4]; - f.read(buffer); - if(!(new String(buffer).equals("ASET"))) - return; - - f.skipBytes(0x4); - long iconOffset = Long.reverseBytes(f.readLong()); - long iconSize = Long.reverseBytes(f.readLong()); - if(iconOffset == 0 || iconSize == 0) - return; - f.seek(asetOffset + iconOffset); - - byte[] iconData = new byte[(int)iconSize]; - f.read(iconData); - - new FileOutputStream(context.getFilesDir() + "/tmp.jpg").write(iconData); - Picasso.with(context).load(context.getFilesDir() + "/tmp.jpg").into(target); - } - catch(IOException e) { - Log.e("app_process64", "Error while loading ASET: " + e.getMessage()); - return; - } - } } diff --git a/app/src/main/res/drawable/ic_missing_icon.xml b/app/src/main/res/drawable/ic_missing_icon.xml new file mode 100644 index 00000000..25e8f8f0 --- /dev/null +++ b/app/src/main/res/drawable/ic_missing_icon.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/file_item.xml b/app/src/main/res/layout/file_item.xml new file mode 100644 index 00000000..e42e5bbb --- /dev/null +++ b/app/src/main/res/layout/file_item.xml @@ -0,0 +1,34 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7b6d24b6..08f2e00b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4,7 +4,9 @@ Settings Refresh - The following permission + The list of ROMs has been refreshed. + Launching + Icon Search Search Location diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 70461b9c..17a1fcd7 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,6 +1,6 @@ -