From ae6dddf9f6931122261390de472d41b4c7ac5c26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=97=B1=20PixelyIon?= Date: Thu, 4 Jul 2019 00:14:28 +0530 Subject: [PATCH] UI update: Snackbars + NRO Metadata This UI updates adds Snackbar notifications for actions and extracts metadata in the form of an icon, name and author from NRO files to display in the game list. Co-Authored-By: Ryan Teal --- app/build.gradle | 2 +- .../gq/cyuubi/lightswitch/FileAdapter.java | 104 ++++++++++++++++++ .../gq/cyuubi/lightswitch/MainActivity.java | 91 ++------------- .../java/gq/cyuubi/lightswitch/NroMeta.java | 70 +++++------- app/src/main/res/drawable/ic_missing_icon.xml | 5 + app/src/main/res/layout/file_item.xml | 34 ++++++ app/src/main/res/values/strings.xml | 4 +- app/src/main/res/values/styles.xml | 2 +- 8 files changed, 186 insertions(+), 126 deletions(-) create mode 100644 app/src/main/java/gq/cyuubi/lightswitch/FileAdapter.java create mode 100644 app/src/main/res/drawable/ic_missing_icon.xml create mode 100644 app/src/main/res/layout/file_item.xml 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 @@ -