diff --git a/app/src/main/java/InstallFragment.java b/app/src/main/java/InstallFragment.java deleted file mode 100644 index 057d290..0000000 --- a/app/src/main/java/InstallFragment.java +++ /dev/null @@ -1,48 +0,0 @@ -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.view.View; -import android.widget.Button; - -import com.MartyrPher.smapiandroidinstaller.ApkExtractor; -import com.MartyrPher.smapiandroidinstaller.BackgroundTask; -import com.MartyrPher.smapiandroidinstaller.DialogFrag; -import com.MartyrPher.smapiandroidinstaller.MainActivity; -import com.MartyrPher.smapiandroidinstaller.R; - -//import androidx.annotation.NonNull; -//import androidx.annotation.Nullable; - -public class InstallFragment extends Fragment { - -// @Override -// public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) -// { -// final Button start_button = view.findViewById(R.id.start_button); -// start_button.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// start_button.setBackgroundColor(getResources().getColor(R.color.colorAccent)); -// if (true) -// { -// boolean[] foundGame; -// ApkExtractor apkExtractor = new ApkExtractor(null); -// -// foundGame = apkExtractor.checkForInstallOrUpgrade(); -// -// if((foundGame[0] || foundGame[1])) -// { -// BackgroundTask backgroundTask = new BackgroundTask(null, apkExtractor, foundGame[0]); -// backgroundTask.execute(); -// } -// else -// { -// DialogFrag.showDialog(null, R.string.cant_find, 1); -// start_button.setBackgroundColor(getResources().getColor(R.color.colorPrimary)); -// } -// -// } -// } -// }); -// -// } -} diff --git a/app/src/main/java/com/MartyrPher/smapiandroidinstaller/ConfigAdapter.java b/app/src/main/java/com/MartyrPher/smapiandroidinstaller/ConfigAdapter.java new file mode 100644 index 0000000..98c33df --- /dev/null +++ b/app/src/main/java/com/MartyrPher/smapiandroidinstaller/ConfigAdapter.java @@ -0,0 +1,216 @@ +package com.MartyrPher.smapiandroidinstaller; + +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.List; +import java.util.Scanner; + +/** + * Config Adapter used to show the different configs and their views + */ +public class ConfigAdapter extends RecyclerView.Adapter +{ + //TAG used for debugging purposes + private final static String TAG = "ConfigAdapter"; + + //List of the mods + private List mModFiles; + + //ViewHolder class that extends RecyclerView.ViewHolder + public static class ConfigViewHolder extends RecyclerView.ViewHolder + { + //The view that needs to be inflated + public View mConstraintLayout; + + /** + * Constructor that sets the layout + * @param layout = The views layout + */ + public ConfigViewHolder(View layout) + { + super(layout); + mConstraintLayout = layout; + } + } + + /** + * Constructor that sets the Mod File list + * @param modFiles = The list of Mod Files + */ + public ConfigAdapter(List modFiles) + { + mModFiles = modFiles; + } + + /** + * Override that gets called when a view pops into the RecyclerView + * @param parent = The parent viewgroup + * @param viewType = The view type + * @return The configViewHolder + */ + @Override + public ConfigAdapter.ConfigViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) + { + //Inflate the view + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.text_view, parent, false); + + //Create a new ConfigViewHolder + final ConfigViewHolder configViewHolder = new ConfigViewHolder(view); + + //Set an onClickListner for each of the config options + view.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v){ + //The position in the RecyclerView + final int position = ConfigEditorFragment.mRecyclerView.getChildLayoutPosition(v); + + //Save button + final Button saveButton = configViewHolder.mConstraintLayout.findViewById(R.id.save_button); + + //Cancel Button + final Button cancelButton = configViewHolder.mConstraintLayout.findViewById(R.id.cancel_button); + + //Edit Text + final EditText editText = configViewHolder.mConstraintLayout.findViewById(R.id.editText); + try + { + //When clicked read the json and make the buttons VISIBLE + editText.setText(readStringFromJson(position)); + editText.setVisibility(View.VISIBLE); + saveButton.setVisibility(View.VISIBLE); + saveButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + try + { + //Try to save the json when the save button is clicked + writeStringToJson(editText.getText().toString(), position); + + //Set the edit text to an empty string and set everything to INVISIBLE + editText.setText(""); + editText.setVisibility(View.INVISIBLE); + saveButton.setVisibility(View.INVISIBLE); + cancelButton.setVisibility(View.INVISIBLE); + } + catch(IOException io) + { + + } + } + }); + + cancelButton.setVisibility(View.VISIBLE); + + //When cancel is clicked, set everything to INVISIBLE again + cancelButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + editText.setText(""); + editText.setVisibility(View.INVISIBLE); + saveButton.setVisibility(View.INVISIBLE); + cancelButton.setVisibility(View.INVISIBLE); + } + }); + } + catch (IOException io) + { + } + + } + }); + + return configViewHolder; + } + + /** + * Override that shows the initial mod name when the View is binded to the RecyclerView + * @param holder = The ConfigViewHolder + * @param position = The position of the item in the RecyclerView + */ + @Override + public void onBindViewHolder(ConfigViewHolder holder, int position) + { + //Grab the textView and set the string to the mods name + TextView textView = holder.mConstraintLayout.findViewById(R.id.textView3); + textView.setText(mModFiles.get(position).getParentFile().getName()); + } + + /** + * Override that gets called when a view is recycled from the RecyclerView + * This is needed or a UI bug shows up when opening a config file (Thanks BluPenDragon!) + * @param holder = The ConfigViewHolder + */ + @Override + public void onViewRecycled(ConfigViewHolder holder) + { + //Get the save button, cancel button and edit text from the holder + Button saveButton = holder.mConstraintLayout.findViewById(R.id.save_button); + Button cancelButton = holder.mConstraintLayout.findViewById(R.id.cancel_button); + EditText text = holder.mConstraintLayout.findViewById(R.id.editText); + + //Make them all INVISIBLE and set the edit text to an empty string + saveButton.setVisibility(View.INVISIBLE); + cancelButton.setVisibility(View.INVISIBLE); + text.setText(""); + text.setVisibility(View.INVISIBLE); + } + + /** + * Override that gets the amount of items in the Mod File list + * @return The size of the Mod File list + */ + @Override + public int getItemCount() + { + return mModFiles.size(); + } + + /** + * Tries to write the string in the edit text to json + * @param text = The text that tries to be saved + * @param position = The position of the item that is being saved + * @throws IOException if the output fails + */ + private void writeStringToJson(String text, int position) throws IOException + { + //BufferedWriter to write the string to the json file + BufferedWriter outConfig = new BufferedWriter(new FileWriter(mModFiles.get(position).getAbsolutePath())); + + try + { + //write the text + outConfig.write(text); + } + finally + { + //close the file + outConfig.close(); + } + } + + /** + * Reads the json file from the mod folder into a string for editing + * @param position = The position of the string to edit + * @return The json content in String form + * @throws IOException if the read fails + */ + //Reads the json file from the mod folder into a string for editing + private String readStringFromJson(int position) throws IOException + { + //Use a Scanner with a delimiter to parse into string + String content = new Scanner(new File(mModFiles.get(position).getAbsolutePath())).useDelimiter("\\Z").next(); + + return content; + } +} diff --git a/app/src/main/java/com/MartyrPher/smapiandroidinstaller/ConfigEditorFragment.java b/app/src/main/java/com/MartyrPher/smapiandroidinstaller/ConfigEditorFragment.java new file mode 100644 index 0000000..0e4eb34 --- /dev/null +++ b/app/src/main/java/com/MartyrPher/smapiandroidinstaller/ConfigEditorFragment.java @@ -0,0 +1,117 @@ +package com.MartyrPher.smapiandroidinstaller; + +import android.os.Bundle; +import android.os.Environment; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Fragment used to handle config editing + */ +public class ConfigEditorFragment extends Fragment { + + //Tag used for debug purposes + private final static String TAG = "ConfigEditorFragment"; + + //Constant string used the mod directory + private final static String MOD_DIR = Environment.getExternalStorageDirectory() + "/StardewValley/Mods/"; + + //The Recycler View + public static RecyclerView mRecyclerView; + + //The Recycler Views adapter + private RecyclerView.Adapter mAdapter; + + //The Recycler Views layout manager + private RecyclerView.LayoutManager mLayoutManager; + + //The list of mod files + private final static List mModFiles = new ArrayList<>(); + + /** + * Override that gets called when the view needs to be created + * @param inflater = The layout inflator + * @param container = The container to inflate the view + * @param savedInstanceState = The savedInstanceState + * @return the inflated view/layout + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) + { + //Inflate the fragment layout + return inflater.inflate(R.layout.config_editing, container, false); + } + + /** + * Override that gets called when the view is created, used to setup the Recycler View + * @param view = The view that was created + * @param savedInstanceState = The savedInstanceState + */ + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) + { + //Find the Recycler View and set the hasFixedSize + mRecyclerView = view.findViewById(R.id.recycler_view); + mRecyclerView.setHasFixedSize(false); + + //Create a new Layout Manager and set Recyclers View layout manager + mLayoutManager = new LinearLayoutManager(getActivity()); + mRecyclerView.setLayoutManager(mLayoutManager); + + //Only grab the mod file list once + //Not having this allows duplicate mods to show (Thanks Minerva!!!) + if (!MainActivity.mHasFoundMods) + getModFiles(); + + //Create a new Config Adapter and set Recycle Views adapter to the new adapter + mAdapter = new ConfigAdapter(mModFiles); + mRecyclerView.setAdapter(mAdapter); + } + + /** + * Get the mod files using a recursive file check + */ + private void getModFiles() + { + File modFolder = new File(MOD_DIR); + for (File file : modFolder.listFiles()) + { + recursiveFileCheck(file); + } + + //Set that the mods were found and sort the list + MainActivity.mHasFoundMods = true; + Collections.sort(mModFiles); + } + + /** + * Recursive file check that searches for config files within the mod folder + * @param file = The file that is being checked + */ + private void recursiveFileCheck(File file) + { + if (file.isDirectory()) + { + for (File configFile : file.listFiles()) { + recursiveFileCheck(configFile); + } + } + else + { + //If it's a config then add it to the list! + if (file.getName().equals("config.json")) + mModFiles.add(file); + } + } +} diff --git a/app/src/main/java/com/MartyrPher/smapiandroidinstaller/InstallFragment.java b/app/src/main/java/com/MartyrPher/smapiandroidinstaller/InstallFragment.java new file mode 100644 index 0000000..9c1aff9 --- /dev/null +++ b/app/src/main/java/com/MartyrPher/smapiandroidinstaller/InstallFragment.java @@ -0,0 +1,73 @@ +package com.MartyrPher.smapiandroidinstaller; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; + +/** + * Fragment that shows the Install Button + */ +public class InstallFragment extends Fragment { + + /** + * Override that gets called when the view needs to be created + * @param inflater = The layout inflator + * @param container = The container to inflate the view in + * @param savedInstanceState = The savedInstanceState + * @return the inflated view + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) + { + //Inflate the fragment layout + return inflater.inflate(R.layout.activity_main, container, false); + } + + /** + * Override that gets called when the view is created, used to setup the Recycler View + * @param view = The view that was created + * @param savedInstanceState = The savedInstanceState + */ + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) + { + //Start button, one click install. Can't beat it. + final Button start_button = view.findViewById(R.id.start_button); + start_button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + start_button.setBackgroundColor(getResources().getColor(R.color.colorAccent)); + + //Did the user give permission to storage + if (MainActivity.mHasPermissions) + { + boolean[] foundGame; + ApkExtractor apkExtractor = new ApkExtractor(getActivity()); + + //Did it find the game. + foundGame = apkExtractor.checkForInstallOrUpgrade(); + + if((foundGame[0] || foundGame[1])) + { + //Start the task + BackgroundTask backgroundTask = new BackgroundTask(getActivity(), apkExtractor, foundGame[0]); + backgroundTask.execute(); + } + else + { + //Show the dialog saying that the game can't be found + DialogFrag.showDialog(getActivity(), R.string.cant_find, 1); + start_button.setBackgroundColor(getResources().getColor(R.color.colorPrimary)); + } + + } + } + }); + + } +} diff --git a/app/src/main/java/com/MartyrPher/smapiandroidinstaller/TabAdapter.java b/app/src/main/java/com/MartyrPher/smapiandroidinstaller/TabAdapter.java new file mode 100644 index 0000000..ac07cb8 --- /dev/null +++ b/app/src/main/java/com/MartyrPher/smapiandroidinstaller/TabAdapter.java @@ -0,0 +1,74 @@ +package com.MartyrPher.smapiandroidinstaller; + +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentStatePagerAdapter; + +import java.util.ArrayList; +import java.util.List; + +/** + * Allows the tabs at the top of the screen + */ +public class TabAdapter extends FragmentStatePagerAdapter { + + //List of the fragments + private final static List mFragmentList = new ArrayList<>(); + + //List of the fragment titles + private final static List mTitleList = new ArrayList<>(); + + /** + * Constructor that sets the fragment manager + * @param fragmentManager = The fragment manager + */ + public TabAdapter(FragmentManager fragmentManager) + { + super(fragmentManager); + } + + /** + * Override that gets the item at a specific position + * @param position = The position of the item + * @return the position in the fragment list + */ + @Override + public Fragment getItem(int position) + { + return mFragmentList.get(position); + } + + /** + * Add a fragment to the list and add the title + * @param fragment = The fragment to add + * @param title = The title to add + */ + public void addFragment(Fragment fragment, String title) + { + mFragmentList.add(fragment); + mTitleList.add(title); + } + + /** + * Get the page title + * @param position = The position to get + * @return the position of the title in the list + */ + @Nullable + @Override + public CharSequence getPageTitle(int position) + { + return mTitleList.get(position); + } + + /** + * Get the count of fragments in the list + * @return the size of the fragment list + */ + @Override + public int getCount() + { + return mFragmentList.size(); + } +}