├── HelloWorld.apk ├── README ├── forms-1.3.0-src.zip ├── forms-1.3.0.jar ├── resources ├── LoadingAnim.gif └── icon.png └── src ├── APKEdit.java ├── EditLanguage.java └── XmlParsing.java /HelloWorld.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WouterSpaans/APK-Edit--Java-/9004bf03f596d348c3226894c990981147bacc4c/HelloWorld.apk -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WouterSpaans/APK-Edit--Java-/9004bf03f596d348c3226894c990981147bacc4c/README -------------------------------------------------------------------------------- /forms-1.3.0-src.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WouterSpaans/APK-Edit--Java-/9004bf03f596d348c3226894c990981147bacc4c/forms-1.3.0-src.zip -------------------------------------------------------------------------------- /forms-1.3.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WouterSpaans/APK-Edit--Java-/9004bf03f596d348c3226894c990981147bacc4c/forms-1.3.0.jar -------------------------------------------------------------------------------- /resources/LoadingAnim.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WouterSpaans/APK-Edit--Java-/9004bf03f596d348c3226894c990981147bacc4c/resources/LoadingAnim.gif -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WouterSpaans/APK-Edit--Java-/9004bf03f596d348c3226894c990981147bacc4c/resources/icon.png -------------------------------------------------------------------------------- /src/APKEdit.java: -------------------------------------------------------------------------------- 1 | import java.awt.EventQueue; 2 | 3 | import javax.swing.JFrame; 4 | import javax.swing.UIManager; 5 | 6 | import javax.swing.JTabbedPane; 7 | import javax.swing.JButton; 8 | import javax.swing.JPanel; 9 | import javax.swing.SwingWorker; 10 | 11 | import java.awt.BorderLayout; 12 | import java.awt.Color; 13 | import java.awt.Desktop; 14 | import java.awt.FileDialog; 15 | import java.awt.FlowLayout; 16 | import java.awt.Frame; 17 | import java.awt.Dimension; 18 | import java.awt.Image; 19 | import java.awt.Toolkit; 20 | 21 | import javax.swing.JLabel; 22 | import javax.swing.JTextField; 23 | 24 | import java.awt.event.KeyEvent; 25 | import java.awt.event.KeyListener; 26 | import java.awt.event.WindowAdapter; 27 | import java.awt.event.WindowEvent; 28 | import java.awt.Cursor; 29 | import brut.androlib.AndrolibException; 30 | 31 | import java.io.File; 32 | import java.io.FileInputStream; 33 | import java.io.FileNotFoundException; 34 | import java.io.FileOutputStream; 35 | import java.io.FilenameFilter; 36 | import java.io.IOException; 37 | import java.io.InputStream; 38 | import java.io.OutputStream; 39 | import java.util.Locale; 40 | import java.awt.Component; 41 | import com.jgoodies.forms.layout.FormLayout; 42 | import com.jgoodies.forms.layout.ColumnSpec; 43 | import com.jgoodies.forms.layout.RowSpec; 44 | import com.jgoodies.forms.factories.FormFactory; 45 | import javax.swing.ImageIcon; 46 | import java.awt.Insets; 47 | import javax.swing.SwingConstants; 48 | import java.awt.event.ActionListener; 49 | import java.awt.event.ActionEvent; 50 | import javax.swing.event.CaretListener; 51 | import javax.swing.event.CaretEvent; 52 | import javax.xml.parsers.ParserConfigurationException; 53 | import javax.xml.transform.TransformerException; 54 | import javax.xml.transform.TransformerFactoryConfigurationError; 55 | import javax.xml.xpath.XPathExpressionException; 56 | 57 | import org.xml.sax.SAXException; 58 | 59 | public class APKEdit { 60 | 61 | private JFrame frmApkEdit; 62 | private static String apkPath = null; 63 | private static String apkBackupPath = null; 64 | private static String tempPath = null; 65 | //private static String manifestXmlFile = null; 66 | 67 | XmlParsing.AndroidManifest AndroidManifest; 68 | 69 | static EditLanguage windowEditLanguages; 70 | 71 | private JTextField tfName = new JTextField(); 72 | private JButton pbLoading = new JButton(""); 73 | private JButton btnIconHDPI = new JButton(""); 74 | private JButton btnIconMDPI = new JButton(""); 75 | private JButton btnIconLDPI = new JButton(""); 76 | private JButton btnLanguages = new JButton("Languages"); 77 | private JButton btnBrowse = new JButton("Browse"); 78 | private JButton btnRevert = new JButton("Revert"); 79 | private JButton btnOK = new JButton("OK"); 80 | private JButton btnCancel = new JButton("Cancel"); 81 | private JButton btnApply = new JButton("Apply"); 82 | 83 | /** 84 | * Launch the application. 85 | */ 86 | public static void main(String[] args) { 87 | 88 | // Set the look and feel to that of the system 89 | try { UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() ); } catch ( Exception e ) { System.err.println( e ); } 90 | 91 | // Get tempPath 92 | tempPath = getTempPath(); 93 | 94 | // Get apkFilePath 95 | apkPath = getFileName(args); 96 | 97 | 98 | // Get AndroidManifset.xml Path 99 | //manifestXmlFile = getXmlManifestFile(tempPath); 100 | 101 | // Exit application if we don't have a valid file to work with 102 | if (apkPath == null) exitApplication(); 103 | 104 | apkBackupPath = apkPath + ".bck"; 105 | 106 | // Default generated code... 107 | EventQueue.invokeLater(new Runnable() { 108 | public void run() { 109 | try { 110 | APKEdit window = new APKEdit(); 111 | window.frmApkEdit.setVisible(true); 112 | 113 | windowEditLanguages = new EditLanguage(tempPath); 114 | windowEditLanguages.Languages.setVisible(false); 115 | } catch (Exception e) { 116 | e.printStackTrace(); 117 | } 118 | } 119 | }); 120 | } 121 | 122 | // private static String getXmlManifestFile(String TempPath) { 123 | // return TempPath + System.getProperty("file.separator") + "AndroidManifest.xml"; 124 | // } 125 | 126 | private static String getTempPath() { 127 | // Get random temp folder 128 | java.util.Random f = new java.util.Random(); 129 | return System.getProperty("java.io.tmpdir") + System.getProperty("file.separator") + "APKEdit" + f.nextInt(); 130 | } 131 | 132 | private static String getFileName(String[] args) { 133 | if (args.length == 0) 134 | { 135 | // Ask user - AWT Implementation 136 | Frame f = new Frame(); 137 | FileDialog fd1 = new FileDialog(f, "Select a Android Package (.apk) File to Open", FileDialog.LOAD); 138 | fd1.setFile("*.apk"); 139 | fd1.setFilenameFilter(new FilenameFilter(){ @Override public boolean accept(File dir, String name) { return (name.endsWith(".apk"));}}); 140 | fd1.setVisible(true); 141 | 142 | String apkDirectory = fd1.getDirectory(); 143 | String apkFile = fd1.getFile(); 144 | 145 | f.dispose(); 146 | 147 | return (apkDirectory != null && apkFile != null) ? 148 | apkDirectory + System.getProperty("file.separator") + apkFile : 149 | null; 150 | } 151 | else 152 | { 153 | return args[0]; 154 | } 155 | } 156 | 157 | private static String getIconName() { 158 | 159 | // Ask user - AWT Implementation 160 | Frame f = new Frame(); 161 | FileDialog fd1 = new FileDialog(f, "Select a icon file to Open", FileDialog.LOAD); 162 | fd1.setFile("*.png"); 163 | fd1.setFilenameFilter(new FilenameFilter(){ @Override public boolean accept(File dir, String name) { return (name.endsWith(".png"));}}); 164 | fd1.setVisible(true); 165 | 166 | String pngDirectory = fd1.getDirectory(); 167 | String pngFile = fd1.getFile(); 168 | 169 | f.dispose(); 170 | 171 | return (pngDirectory != null && pngFile != null) ? 172 | pngDirectory + System.getProperty("file.separator") + pngFile : 173 | null; 174 | 175 | } 176 | 177 | private static void decompileAPK(String ApkPath, String TempPath) { 178 | String[] argsApkTool = {"d", "-f", ApkPath, TempPath}; 179 | try { 180 | brut.apktool.Main.main(argsApkTool); 181 | } catch (AndrolibException e) { 182 | // TODO Auto-generated catch block 183 | e.printStackTrace(); 184 | } catch (IOException e) { 185 | // TODO Auto-generated catch block 186 | e.printStackTrace(); 187 | } catch (InterruptedException e) { 188 | // TODO Auto-generated catch block 189 | e.printStackTrace(); 190 | } 191 | } 192 | 193 | private static void exitApplication() { 194 | // remove temp path 195 | deleteDir(new File(tempPath)); 196 | System.exit(0); 197 | } 198 | 199 | // Deletes all files and subdirectories under dir. 200 | // Returns true if all deletions were successful. 201 | // If a deletion fails, the method stops attempting to delete and returns false. 202 | public static boolean deleteDir(File dir) { 203 | if (dir.isDirectory()) { 204 | String[] children = dir.list(); 205 | for (int i=0; i 0){ 861 | out.write(buf, 0, len); 862 | } 863 | in.close(); 864 | out.close(); 865 | System.out.println("File copied."); 866 | } 867 | catch(FileNotFoundException ex){ 868 | System.out.println(ex.getMessage() + " in the specified directory."); 869 | System.exit(0); 870 | } 871 | catch(IOException e){ 872 | System.out.println(e.getMessage()); 873 | } 874 | } 875 | 876 | } 877 | -------------------------------------------------------------------------------- /src/EditLanguage.java: -------------------------------------------------------------------------------- 1 | import java.awt.EventQueue; 2 | 3 | import javax.swing.JFrame; 4 | import javax.swing.JComboBox; 5 | import java.awt.BorderLayout; 6 | import java.io.File; 7 | import java.io.IOException; 8 | import java.util.ArrayList; 9 | import java.util.Iterator; 10 | import java.util.List; 11 | import java.util.Locale; 12 | import java.util.Map; 13 | 14 | import javax.swing.JTable; 15 | import javax.swing.event.ListSelectionEvent; 16 | import javax.swing.event.ListSelectionListener; 17 | import javax.swing.event.TableModelEvent; 18 | import javax.swing.event.TableModelListener; 19 | import javax.swing.table.DefaultTableModel; 20 | import javax.swing.table.TableColumn; 21 | import javax.swing.JScrollPane; 22 | import javax.swing.ListSelectionModel; 23 | import javax.xml.parsers.ParserConfigurationException; 24 | import javax.xml.transform.TransformerException; 25 | import javax.xml.transform.TransformerFactoryConfigurationError; 26 | import javax.xml.xpath.XPathExpressionException; 27 | 28 | import org.xml.sax.SAXException; 29 | import java.awt.event.ActionListener; 30 | import java.awt.event.ActionEvent; 31 | import java.awt.event.MouseAdapter; 32 | import java.awt.event.MouseEvent; 33 | import java.awt.Rectangle; 34 | import javax.swing.border.EmptyBorder; 35 | import javax.swing.JPanel; 36 | import java.awt.Toolkit; 37 | 38 | 39 | public class EditLanguage { 40 | 41 | JFrame Languages; 42 | private JTable table; 43 | private String tempPath = null; 44 | private static JComboBox comboBox; 45 | DefaultTableModel model = new DefaultTableModel( 46 | new Object[][] { 47 | }, 48 | new String[] { 49 | "Label", "Value" 50 | } 51 | ) { 52 | boolean[] columnEditables = new boolean[] { 53 | false, true 54 | }; 55 | public boolean isCellEditable(int row, int column) { 56 | return columnEditables[column]; 57 | } 58 | }; 59 | /** 60 | * Launch the application. 61 | */ 62 | // public static void main(String TempPath) { 63 | // EventQueue.invokeLater(new Runnable() { 64 | // public void run() { 65 | // try { 66 | // EditLanguage window = new EditLanguage(); 67 | // window.frame.setVisible(true); 68 | // } catch (Exception e) { 69 | // e.printStackTrace(); 70 | // } 71 | // } 72 | // }); 73 | // } 74 | 75 | /** 76 | * Create the application. 77 | */ 78 | public EditLanguage(String TempPath) { 79 | tempPath = TempPath; 80 | initialize(); 81 | 82 | // Add available languages... 83 | 84 | } 85 | 86 | /** 87 | * Convert a string based locale into a Locale Object. 88 | * Assumes the string has form "{language}_{country}_{variant}". 89 | * Examples: "en", "de_DE", "_GB", "en_US_WIN", "de__POSIX", "fr_MAC" 90 | * 91 | * @param localeString The String 92 | * @return the Locale 93 | */ 94 | public static Locale getLocaleFromString(String localeString) 95 | { 96 | if (localeString == null) 97 | { 98 | return null; 99 | } 100 | localeString = localeString.trim(); 101 | if (localeString.toLowerCase().equals("default")) 102 | { 103 | return Locale.getDefault(); 104 | } 105 | 106 | // Extract language 107 | int languageIndex = localeString.indexOf('-'); 108 | String language = null; 109 | if (languageIndex == -1) 110 | { 111 | // No further "_" so is "{language}" only 112 | return new Locale(localeString, ""); 113 | } 114 | else 115 | { 116 | language = localeString.substring(0, languageIndex); 117 | } 118 | 119 | // Extract country 120 | int countryIndex = localeString.indexOf('-', languageIndex + 1); 121 | String country = null; 122 | if (countryIndex == -1) 123 | { 124 | // No further "_" so is "{language}_{country}" 125 | country = localeString.substring(languageIndex+1); 126 | 127 | // remove r if it is there 128 | if (country.startsWith("r")) 129 | { 130 | country = country.substring(1); 131 | } 132 | return new Locale(language, country); 133 | } 134 | else 135 | { 136 | // Assume all remaining is the variant so is "{language}_{country}_{variant}" 137 | country = localeString.substring(languageIndex+1, countryIndex); 138 | String variant = localeString.substring(countryIndex+1); 139 | return new Locale(language, country, variant); 140 | } 141 | } 142 | 143 | private static List locales = new ArrayList(); 144 | private JPanel panel; 145 | public void ReadLanguages() throws XPathExpressionException, SAXException, IOException, ParserConfigurationException 146 | { 147 | 148 | // Read all directories in tempPath/res/values... 149 | String pathToScan = XmlParsing.ConvertSeperatorToOsSpecific(tempPath + "/res"); 150 | System.out.println("pathToScan = " + pathToScan); 151 | 152 | File myDirectory = new File(pathToScan); 153 | 154 | File[] allFiles = myDirectory.listFiles(); 155 | for (File file : allFiles) 156 | { 157 | if (file.isDirectory()) 158 | { 159 | String directoryName = file.getName(); 160 | // check if it starts with a values 161 | if (directoryName.startsWith("values")) 162 | { 163 | System.out.println("found a values folder: = " + directoryName); 164 | 165 | // skip default folder 166 | if (directoryName.length() != 6) 167 | { 168 | String locale = directoryName.substring(7); 169 | System.out.println("stipping values: " + locale); 170 | 171 | Locale.getISOCountries(); 172 | Locale l = getLocaleFromString(locale); 173 | 174 | if (new File(XmlParsing.ConvertSeperatorToOsSpecific(pathToScan + "/" + directoryName + "/strings.xml")).exists()) 175 | { 176 | if (l != null){ 177 | System.out.println("found locale: " + l.getISO3Language() + l.getISO3Language()); 178 | locales.add(l); 179 | } 180 | } 181 | // add to dropdown 182 | 183 | 184 | } 185 | else 186 | { 187 | // Add defualt language to localoes 188 | Locale l = getLocaleFromString("en-US"); 189 | 190 | locales.add(l); 191 | System.out.println("found locale: " + l.getLanguage() + l.getCountry()); 192 | 193 | } 194 | 195 | } 196 | } 197 | } 198 | 199 | 200 | // fill dropdown 201 | for (Locale l : locales) 202 | { 203 | comboBox.addItem(l.getDisplayName()); 204 | } 205 | 206 | Locale selectedLocale = locales.get(comboBox.getSelectedIndex()); 207 | 208 | 209 | // fill table with xml 210 | //Get Map in Set interface to get key and value 211 | 212 | Iterator it = XmlParsing.getValuesXml(tempPath, selectedLocale).entrySet().iterator(); 213 | while(it.hasNext()) 214 | { 215 | // key=value separator this by Map.Entry to get key and value 216 | Map.Entry m =(Map.Entry)it.next(); 217 | 218 | // getKey is used to get key of Map 219 | String key=(String)m.getKey(); 220 | 221 | // getValue is used to get value of key in Map 222 | String value=(String)m.getValue(); 223 | 224 | model.addRow(new Object[]{key, value}); 225 | 226 | System.out.println("Key :"+key+" Value :"+value); 227 | 228 | } 229 | } 230 | 231 | private void changeLanguage() { 232 | // TODO Auto-generated method stub 233 | Locale selectedLocale = locales.get(comboBox.getSelectedIndex()); 234 | 235 | 236 | // fill table with xml 237 | //Get Map in Set interface to get key and value 238 | 239 | Iterator it = null; 240 | try { 241 | it = XmlParsing.getValuesXml(tempPath, selectedLocale).entrySet().iterator(); 242 | } catch (XPathExpressionException e) { 243 | // TODO Auto-generated catch block 244 | e.printStackTrace(); 245 | } catch (SAXException e) { 246 | // TODO Auto-generated catch block 247 | e.printStackTrace(); 248 | } catch (IOException e) { 249 | // TODO Auto-generated catch block 250 | e.printStackTrace(); 251 | } catch (ParserConfigurationException e) { 252 | // TODO Auto-generated catch block 253 | e.printStackTrace(); 254 | } 255 | 256 | while (model.getRowCount()>0){ 257 | model.removeRow(0); 258 | } 259 | // for(int i=1; i getValuesXml(String TempPath, Locale locale) throws SAXException, IOException, ParserConfigurationException, XPathExpressionException 213 | { 214 | Map returnValue = new HashMap(); 215 | 216 | String inputFile = getStringsResPath(TempPath, locale); 217 | Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(inputFile)); 218 | 219 | // locate the node(s) 220 | XPath xpath = XPathFactory.newInstance().newXPath(); 221 | NodeList nodes = (NodeList)xpath.evaluate("/resources/string", doc, XPathConstants.NODESET); 222 | 223 | System.out.println("idx = " + nodes.getLength()); 224 | System.out.println("locale = " + locale.getDisplayLanguage()); 225 | 226 | // make the change 227 | for (int idx = 0; idx < nodes.getLength(); idx++) { 228 | returnValue.put(nodes.item(idx).getAttributes().item(0).getNodeValue(), nodes.item(idx).getTextContent()); 229 | } 230 | 231 | return returnValue; 232 | } 233 | 234 | public static boolean FileExists(String FileNameToCheck) 235 | { 236 | boolean exists = (new File(FileNameToCheck)).exists(); 237 | return exists; 238 | } 239 | 240 | public static String ConvertSeperatorToOsSpecific(String InputPath) 241 | { 242 | return InputPath.replace("/", System.getProperty("file.separator")); 243 | } 244 | } 245 | --------------------------------------------------------------------------------