/** Copyright (C) 2013 Louis Teboul (a.k.a Androguide) * * admin@pimpmyrom.org || louisteboul@gmail.com * http://pimpmyrom.org || http://androguide.fr * 71 quai Clémenceau, 69300 Caluire-et-Cuire, FRANCE. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. **/ package com.androguide.apkreator.fragments; import android.content.SharedPreferences; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v7.app.ActionBarActivity; import android.support.v7.view.ActionMode; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.LinearLayout; import com.androguide.apkreator.R; import com.androguide.apkreator.cards.CardButton; import com.androguide.apkreator.cards.CardButtonDouble; import com.androguide.apkreator.cards.CardSeekBarCombo; import com.androguide.apkreator.helpers.CMDProcessor.CMDProcessor; import com.androguide.apkreator.helpers.CMDProcessor.Shell; import com.androguide.apkreator.helpers.Helpers; import com.androguide.apkreator.helpers.SystemPropertiesReflection; import com.androguide.apkreator.pluggable.objects.CardPlugin; import com.androguide.apkreator.pluggable.parsers.ParserInterface; import com.androguide.apkreator.pluggable.parsers.PluginParser; import com.fima.cardsui.objects.CardStack; import com.fima.cardsui.views.CardDownload; import com.fima.cardsui.views.CardEditText; import com.fima.cardsui.views.CardImage; import com.fima.cardsui.views.CardPresentation; import com.fima.cardsui.views.CardSeekBar; import com.fima.cardsui.views.CardSpinner; import com.fima.cardsui.views.CardSwitch; import com.fima.cardsui.views.CardText; import com.fima.cardsui.views.CardTextStripe; import com.fima.cardsui.views.CardUI; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import static com.androguide.apkreator.helpers.CMDProcessor.CMDProcessor.runSuCommand; /** * Every tab in the application contains an instance of this unique Fragment Object. * I send & retrieve the desired position of the tab which each PluginFragment instance * should belong to via the Activity Bundle. * This 0-based index also determines which XML plugin file the PluginFragment instance * will load (tab0.xml, tab1.xml, tab2.xml etc...) * * @see com.androguide.apkreator.MainActivity */ public class PluginFragment extends Fragment implements ParserInterface { private static final String ARG_POSITION = "position"; private int position; public static LinearLayout ll; private ActionBarActivity fa; private ActionMode mActionMode; /** * PluginFragment constructor * * @param position : The 0-based index of the tab each instance of * PluginFragment belongs to, passed-in via the parent Activity's Bundle. * Determines which tab-?.xml file to load for each instance of PluginFragment. */ public static PluginFragment newInstance(int position) { PluginFragment f = new PluginFragment(); Bundle b = new Bundle(); b.putInt(ARG_POSITION, position); f.setArguments(b); return f; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); position = getArguments().getInt(ARG_POSITION); } @Override public void onResume() { super.onResume(); } @Override public void onPause() { super.onPause(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { fa = (ActionBarActivity) super.getActivity(); ll = (LinearLayout) inflater.inflate(com.androguide.apkreator.R.layout.cardsui, container, false); assert ll != null; CardUI mCardsView = (CardUI) ll.findViewById(com.androguide.apkreator.R.id.cardsui); mCardsView.addStack(new CardStack("")); List<CardPlugin> cardPlugins = null; try { PluginParser parser = new PluginParser(); File file = new File(fa.getFilesDir() + "/.APKreator/tabs/tab" + position + ".xml"); FileInputStream fis = new FileInputStream(file); cardPlugins = parser.parse(fis); } catch (IOException e) { /*Helpers.sendMsg(fa, "Couldn't load plugin file: tab" + position + ".xml");*/ e.printStackTrace(); } try { /* Retrieve the right attributes based on the position parameter passed to this instance of PluginFragment, * and store them in the separate ArrayLists<?> we declared above the constructor */ for (int i = 0; i < (cardPlugins != null ? cardPlugins.size() : 0); i++) { final int posHolder = i; title.add(i, cardPlugins.get(i).getTitle()); desc.add(i, cardPlugins.get(i).getDesc()); type.add(i, cardPlugins.get(i).getType()); unit.add(i, cardPlugins.get(i).getUnit()); control.add(i, cardPlugins.get(i).getControl()); min.add(i, cardPlugins.get(i).getMin()); max.add(i, cardPlugins.get(i).getMax()); def.add(i, cardPlugins.get(i).getDef()); prop.add(i, cardPlugins.get(i).getProp()); props.add(i, cardPlugins.get(i).getProps()); shellCmds.add(i, cardPlugins.get(i).getShellCmd()); shellCmds2.add(i, cardPlugins.get(i).getShellCmd2()); on.add(i, cardPlugins.get(i).getBooleanOn()); off.add(i, cardPlugins.get(i).getBooleanOff()); buttons.add(i, cardPlugins.get(i).getButtonText()); buttons2.add(i, cardPlugins.get(i).getButtonText2()); spinners.add(i, cardPlugins.get(i).getSpinnerEntries()); spinnerCmds.add(i, cardPlugins.get(i).getSpinnerCommands()); urls.add(i, cardPlugins.get(i).getUrl()); paths.add(i, cardPlugins.get(i).getFilePath()); stripeColor.add(i, cardPlugins.get(i).getStripeColor()); /************************************************ * Build.prop Cards * ************************************************/ if (type.get(i).equalsIgnoreCase("build.prop")) { /** SeekBar + EditText Combo Card **** @see com.androguide.apkreator.cards.CardSeekBarCombo */ if (control.get(i).equalsIgnoreCase("seekbar-combo")) { CardSeekBarCombo card = new CardSeekBarCombo(title.get(i), desc.get(i), unit.get(i), prop.get(i), max.get(i), def.get(i), fa); mCardsView.addCard(card, true); /** SeekBar Card **** @see com.androguide.apkreator.cards.CardSeekBar */ } else if (control.get(i).equalsIgnoreCase("seekbar")) { CardSeekBar card = new CardSeekBar(title.get(i), desc.get(i), unit.get(i), prop.get(i), max.get(i), def.get(i), fa, mActionModeCallback); mCardsView.addCard(card, true); /** EditText Card **** @see com.androguide.apkreator.cards.CardEditText */ } else if (control.get(i).equalsIgnoreCase("edit-text")) { CardEditText card = new CardEditText(title.get(i), desc.get(i), unit.get(i), prop.get(i), max.get(i), def.get(i), fa, mActionModeCallback); mCardsView.addCard(card, true); /** Switch Card **** @see com.androguide.apkreator.cards.CardSwitchPlugin */ } else if (control.get(i).equalsIgnoreCase("switch")) { CardSwitch card = new CardSwitch(title.get(i), desc.get(i), prop.get(i), fa, new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { Helpers.applyBuildPropTweak(prop.get(posHolder), on.get(posHolder)); SharedPreferences prefs = fa.getSharedPreferences(prop.get(posHolder), 0); prefs.edit().putBoolean("isChecked", true).commit(); } else { Helpers.applyBuildPropTweak(prop.get(posHolder), off.get(posHolder)); SharedPreferences prefs = fa.getSharedPreferences(prop.get(posHolder), 0); prefs.edit().putBoolean("isChecked", false).commit(); } } }); mCardsView.addCard(card, true); /** Spinner Card **** @see com.androguide.apkreator.cards.CardSpinnerPlugin */ } else if (control.get(i).equalsIgnoreCase("spinner")) { CardSpinner card = new CardSpinner(title.get(i), desc.get(i), prop.get(i), spinners.get(i), fa, new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { String bp = ""; if (posHolder <= prop.size()) bp = prop.get(posHolder); final String bProp = bp; /* In order to avoid re-applying the current value in onCreate(), I compare the saved spinner position with the current one and only apply the value if they differ. This way root access isn't requested upon launch. */ SharedPreferences p = fa.getSharedPreferences(title.get(posHolder), 0); int curr = p.getInt("CURRENT", 0); final int pos = position; if (pos != curr) { final String item = spinners.get(posHolder).get(pos); new Thread(new Runnable() { @Override public void run() { CMDProcessor.runSuCommand(Shell.MOUNT_SYSTEM_RW); runSuCommand(Shell.SED + bProp + "/d\" " + Shell.BUILD_PROP); runSuCommand(Shell.ECHO + "\"" + bProp + "=" + item + "\" >> " + Shell.BUILD_PROP); runSuCommand("setprop " + bProp + " " + item); SystemPropertiesReflection.set(fa, bProp, item + ""); CMDProcessor.runSuCommand(Shell.MOUNT_SYSTEM_RO); SharedPreferences prefs = fa.getSharedPreferences(bProp, 0); prefs.edit().putInt("CURRENT", pos).commit(); } }).start(); } } @Override public void onNothingSelected(AdapterView<?> parent) { } }); mCardsView.addCard(card, true); } /************************************************ * Shell Commands Cards * ************************************************/ } else if (type.get(i).equalsIgnoreCase("shell")) { if (control.get(i).equalsIgnoreCase("button")) { /** Card with Button, which executes a shell command on click **** @see com.androguide.apkreator.cards.CardButton */ CardButton card = new CardButton(title.get(i), desc.get(i), buttons.get(i), shellCmds.get(i), fa); mCardsView.addCard(card, true); } else if (control.get(i).equalsIgnoreCase("double-button")) { /** Card with 2 Buttons, which executes a shell command on click **** @see com.androguide.apkreator.cards.CardButtonDouble */ CardButtonDouble card = new CardButtonDouble(title.get(i), desc.get(i), buttons.get(i), buttons2.get(i), shellCmds.get(i), shellCmds2.get(i), fa); mCardsView.addCard(card, true); } else if (control.get(i).equalsIgnoreCase("spinner")) { /** Card with a Spinner, which executes a different shell command for each entry **** @see com.androguide.apkreator.cards.CardSpinner */ CardSpinner card = new CardSpinner(title.get(i), desc.get(i), prop.get(i), spinners.get(i), fa, new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { /* In order to avoid re-applying the current value in onCreate(), I compare the saved spinner position with the current one and only apply the value if they differ. This way root access isn't requested upon launch. */ final int pos = position; final ArrayList<String> item = spinnerCmds.get(posHolder); new Thread(new Runnable() { @Override public void run() { SharedPreferences prefs = fa.getSharedPreferences(title.get(posHolder), 0); prefs.edit().putInt("CURRENT", pos).commit(); for (String anItem : item) CMDProcessor.runSuCommand(anItem); } }).start(); } @Override public void onNothingSelected(AdapterView<?> parent) { } }); mCardsView.addCard(card, true); } /************************************************ * Miscellaneous Cards * ************************************************/ } else if (type.get(i).equalsIgnoreCase("download")) { /** Card for downloading from a URL **** @see com.androguide.apkreator.cards.CardDownload */ CardDownload card = new CardDownload(title.get(i), desc.get(i), urls.get(i), paths.get(i), buttons.get(i), fa); mCardsView.addCard(card, true); } else if (type.get(i).equalsIgnoreCase("special")) { if (control.get(i).equalsIgnoreCase("presentation")) { /** Card with a button which launches an Activity with a webview, intended for Reveal-JS presentations **** @see com.androguide.apkreator.cards.CardPresentation */ CardPresentation card = new CardPresentation(title.get(i), desc.get(i), buttons.get(i), null, fa); mCardsView.addCard(card); } /************************************************ * Plain Text Cards * ************************************************/ } else if (type.get(i).equalsIgnoreCase("text")) { /** Plain Text Card **** @see com.androguide.apkreator.cards.CardText */ SharedPreferences p = fa.getSharedPreferences("CONFIG", 0); CardText card = new CardText(title.get(i), desc.get(i), p.getString("APP_COLOR", "#96AA39"), false, false); mCardsView.addCard(card, true); } else if (type.get(i).equalsIgnoreCase("text-stripe")) { /** Plain Text Card with colored stripe **** @see com.androguide.apkreator.cards.CardTextStripe */ CardTextStripe card = new CardTextStripe(title.get(i), desc.get(i), stripeColor.get(i), false, false); mCardsView.addCard(card, true); } else if (type.get(i).equalsIgnoreCase("image")) { SharedPreferences p = fa.getSharedPreferences("CONFIG", 0); CardImage card = new CardImage(title.get(i), desc.get(i), p.getString("APP_COLOR", "#96AA39"), urls.get(i)); mCardsView.addCard(card, true); } } } catch (Exception e) { Log.e("PluginFragment", e.getMessage() + ""); } return ll; } @Override public void onDestroy() { super.onDestroy(); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); } @Override public boolean onOptionsItemSelected(MenuItem item) { return super.onOptionsItemSelected(item); } /** * Contextual ActionBar triggered by SeekBar-enabled cards * *** @see com.androguide.apkreator.cards.CardSeekBar * *** @see com.androguide.apkreator.cards.CardSeekBarCombo */ private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() { @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { MenuInflater inflater = mode.getMenuInflater(); assert inflater != null; inflater.inflate(R.menu.contextual_menu, menu); return true; } @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return false; } @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { switch (item.getItemId()) { case R.id.apply: mActionMode = mode; SharedPreferences p = fa.getSharedPreferences("TO_APPLY", 0); final String prop = p.getString("PROP", ""); final int value = p.getInt("VALUE", 0); final String title = p.getString("TO_SAVE", ""); new Thread(new Runnable() { public void run() { try { runSuCommand(Shell.MOUNT_SYSTEM_RW); runSuCommand(Shell.MOUNT_SYSTEM_RW); runSuCommand(Shell.SED + prop + "/d\" " + Shell.BUILD_PROP); runSuCommand(Shell.ECHO + "\"" + prop + "=" + value + "\" >> " + Shell.BUILD_PROP); runSuCommand("setprop " + prop + " " + value); SystemPropertiesReflection.set(fa, prop, value + ""); runSuCommand(Shell.MOUNT_SYSTEM_RO); SharedPreferences pref = fa.getSharedPreferences(title, 0); pref.edit().putInt(title, value).commit(); } catch (NullPointerException e) { Log.e("WIFI_SCAN", "NullPointerException: " + e); } } }).start(); mActionMode.finish(); return true; default: return false; } } @Override public void onDestroyActionMode(ActionMode mode) { mActionMode = null; } }; }