├── README ├── HelloWorld.apk ├── forms-1.3.0.jar ├── forms-1.3.0-src.zip ├── resources ├── icon.png └── LoadingAnim.gif └── src ├── XmlParsing.java ├── EditLanguage.java └── APKEdit.java /README: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /HelloWorld.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WouterSpaans/APK-Edit--Java-/HEAD/HelloWorld.apk -------------------------------------------------------------------------------- /forms-1.3.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WouterSpaans/APK-Edit--Java-/HEAD/forms-1.3.0.jar -------------------------------------------------------------------------------- /forms-1.3.0-src.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WouterSpaans/APK-Edit--Java-/HEAD/forms-1.3.0-src.zip -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WouterSpaans/APK-Edit--Java-/HEAD/resources/icon.png -------------------------------------------------------------------------------- /resources/LoadingAnim.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WouterSpaans/APK-Edit--Java-/HEAD/resources/LoadingAnim.gif -------------------------------------------------------------------------------- /src/XmlParsing.java: -------------------------------------------------------------------------------- 1 | import java.io.File; 2 | import java.io.IOException; 3 | import java.io.StringWriter; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Locale; 7 | import java.util.Map; 8 | 9 | import javax.xml.parsers.DocumentBuilder; 10 | import javax.xml.parsers.DocumentBuilderFactory; 11 | import javax.xml.parsers.ParserConfigurationException; 12 | import javax.xml.transform.OutputKeys; 13 | import javax.xml.transform.Transformer; 14 | import javax.xml.transform.TransformerConfigurationException; 15 | import javax.xml.transform.TransformerException; 16 | import javax.xml.transform.TransformerFactory; 17 | import javax.xml.transform.TransformerFactoryConfigurationError; 18 | import javax.xml.transform.dom.DOMSource; 19 | import javax.xml.transform.stream.StreamResult; 20 | import javax.xml.xpath.XPath; 21 | import javax.xml.xpath.XPathConstants; 22 | import javax.xml.xpath.XPathExpression; 23 | import javax.xml.xpath.XPathExpressionException; 24 | import javax.xml.xpath.XPathFactory; 25 | 26 | import org.w3c.dom.Document; 27 | import org.w3c.dom.NamedNodeMap; 28 | import org.w3c.dom.Node; 29 | import org.w3c.dom.NodeList; 30 | import org.xml.sax.InputSource; 31 | import org.xml.sax.SAXException; 32 | 33 | 34 | public class XmlParsing { 35 | 36 | final public static class AndroidManifest 37 | { 38 | public boolean ContainsApplicationName; 39 | public String ApplicationName; 40 | public String ApplicationNameAttribute; 41 | public String PackageName; 42 | public String PathIconHDPI; 43 | public String PathIconMDPI; 44 | public String PathIconLDPI; 45 | } 46 | 47 | public static void saveManifestXml(String TempPath, AndroidManifest androidManifest) throws SAXException, IOException, ParserConfigurationException, XPathExpressionException, TransformerFactoryConfigurationError, TransformerException 48 | { 49 | String inputFile = ConvertSeperatorToOsSpecific(TempPath + "/AndroidManifest.xml"); 50 | 51 | Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(inputFile)); 52 | 53 | // locate the node(s) 54 | XPath xpath = XPathFactory.newInstance().newXPath(); 55 | NodeList nodes = (NodeList)xpath.evaluate("/manifest/application", doc, XPathConstants.NODESET); 56 | 57 | System.out.println("idx = " + nodes.getLength()); 58 | 59 | // make the change 60 | for (int idx = 0; idx < nodes.getLength(); idx++) { 61 | nodes.item(idx).getAttributes().getNamedItem("android:label").setNodeValue(androidManifest.ApplicationName); 62 | } 63 | 64 | // save the result 65 | Transformer xformer = TransformerFactory.newInstance().newTransformer(); 66 | xformer.transform 67 | (new DOMSource(doc), new StreamResult(new File(inputFile))); 68 | } 69 | 70 | public static void saveSettingsXml(String TempPath, Locale locale, String Name, String Value) throws SAXException, IOException, ParserConfigurationException, XPathExpressionException, TransformerFactoryConfigurationError, TransformerException 71 | { 72 | // String inputFile = ConvertSeperatorToOsSpecific(TempPath + "/res/values/strings.xml"); 73 | String inputFile = getStringsResPath(TempPath, locale); 74 | 75 | Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(inputFile)); 76 | 77 | // locate the node(s) 78 | XPath xpath = XPathFactory.newInstance().newXPath(); 79 | NodeList nodes = (NodeList)xpath.evaluate("/resources/string[@name=\"" + Name + "\"]", doc, XPathConstants.NODESET); 80 | 81 | // make the change 82 | for (int idx = 0; idx < nodes.getLength(); idx++) { 83 | nodes.item(idx).setTextContent(Value); 84 | } 85 | 86 | // save the result 87 | Transformer xformer = TransformerFactory.newInstance().newTransformer(); 88 | xformer.transform 89 | (new DOMSource(doc), new StreamResult(new File(inputFile))); 90 | 91 | } 92 | 93 | public static AndroidManifest parseManifestXml(String TempPath) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException 94 | { 95 | AndroidManifest returnValue = new AndroidManifest(); 96 | returnValue.ContainsApplicationName = false; 97 | returnValue.ApplicationName = null; 98 | returnValue.ApplicationNameAttribute = null; 99 | returnValue.PackageName = null; 100 | returnValue.PathIconHDPI = null; 101 | returnValue.PathIconMDPI = null; 102 | returnValue.PathIconLDPI = null; 103 | 104 | 105 | String iconValue = null; 106 | String labelValue = null; 107 | String pathPreFix = "/res/"; 108 | String iconname = "icon.png"; // Default value 109 | 110 | 111 | File file = new File(ConvertSeperatorToOsSpecific(TempPath + "/AndroidManifest.xml")); 112 | DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 113 | DocumentBuilder db = dbf.newDocumentBuilder(); 114 | Document doc = db.parse(file); 115 | doc.getDocumentElement().normalize(); 116 | NodeList nodeLst = doc.getElementsByTagName("manifest"); 117 | NamedNodeMap attributes = nodeLst.item(0).getAttributes(); 118 | 119 | // Set returnValue.PackageName 120 | returnValue.PackageName = attributes.getNamedItem("package").getNodeValue(); 121 | 122 | nodeLst = doc.getElementsByTagName("application"); 123 | attributes = nodeLst.item(0).getAttributes(); 124 | iconValue = attributes.getNamedItem("android:icon").getNodeValue(); 125 | labelValue = attributes.getNamedItem("android:label").getNodeValue(); 126 | if (iconValue.startsWith("@")) 127 | { 128 | int i = iconValue.lastIndexOf("/"); 129 | pathPreFix = pathPreFix + iconValue.substring(1, i ).replace("/", System.getProperty("file.separator") ) + System.getProperty("file.separator") ; 130 | String iconNamePreFix = iconValue.substring(i + 1, iconValue.length()); 131 | iconname = iconNamePreFix + ".png"; 132 | } 133 | 134 | // HD 135 | String path = TempPath + pathPreFix + iconname; 136 | if (!FileExists(ConvertSeperatorToOsSpecific(path))) 137 | { 138 | path = TempPath + "/res/drawable-hdpi/" + iconname; 139 | if (!FileExists(ConvertSeperatorToOsSpecific(path))) 140 | { 141 | path = TempPath + "/res/drawable/" + iconname; 142 | if (!FileExists(ConvertSeperatorToOsSpecific(path))) 143 | { 144 | path = TempPath + "/res/" + iconname; 145 | if (!FileExists(ConvertSeperatorToOsSpecific(path))) 146 | { 147 | path = null; 148 | } 149 | } 150 | } 151 | } 152 | returnValue.PathIconHDPI = path; 153 | 154 | // MD 155 | path = ConvertSeperatorToOsSpecific(TempPath + "/res/drawable-mdpi/" + iconname); 156 | returnValue.PathIconMDPI = (FileExists(path)) ? path : null; 157 | 158 | // LD 159 | path = ConvertSeperatorToOsSpecific(TempPath + "/res/drawable-ldpi/" + iconname); 160 | returnValue.PathIconLDPI = (FileExists(path)) ? path : null; 161 | 162 | 163 | // Get attribute name in stings or use this as name 164 | if (labelValue.startsWith("@string")) 165 | { 166 | returnValue.ContainsApplicationName = false; 167 | returnValue.ApplicationNameAttribute = labelValue.replace("@string/", ""); 168 | 169 | // Retrieve application name from strings.xml 170 | file = new File(ConvertSeperatorToOsSpecific(TempPath + "/res/values/strings.xml")); 171 | dbf = DocumentBuilderFactory.newInstance(); 172 | db = dbf.newDocumentBuilder(); 173 | doc = db.parse(file); 174 | doc.getDocumentElement().normalize(); 175 | 176 | XPathFactory xPathFactory = XPathFactory.newInstance(); 177 | XPath xPath = xPathFactory.newXPath(); 178 | String expression = "/resources/string[@name=\"" + returnValue.ApplicationNameAttribute + "\"]"; 179 | XPathExpression xPathExpression = xPath.compile(expression); 180 | returnValue.ApplicationName = (String) xPathExpression.evaluate(doc, XPathConstants.STRING); 181 | } 182 | else 183 | { 184 | returnValue.ContainsApplicationName = true; 185 | returnValue.ApplicationName = labelValue; 186 | returnValue.ApplicationNameAttribute = null; 187 | } 188 | 189 | System.out.println("returnValue.ApplicationName = " + returnValue.ApplicationName); 190 | System.out.println("returnValue.ApplicationNameAttribute = " + returnValue.ApplicationNameAttribute); 191 | System.out.println("returnValue.PackageName = " + returnValue.PackageName); 192 | System.out.println("returnValue.PathIconHDPI = " + returnValue.PathIconHDPI); 193 | System.out.println("returnValue.PathIconLDPI = " + returnValue.PathIconLDPI); 194 | System.out.println("returnValue.PathIconMDPI = " + returnValue.PathIconMDPI); 195 | System.out.println("returnValue.ContainsApplicationName = " + returnValue.ContainsApplicationName); 196 | 197 | return returnValue; 198 | } 199 | 200 | public static String getStringsResPath(String TempPath, Locale locale) 201 | { 202 | String inputFile = ConvertSeperatorToOsSpecific(TempPath + "/res/values/strings.xml"); 203 | 204 | if (locale.getLanguage() != "en" && locale.getCountry() != "US") 205 | { 206 | String coutryStr = (locale.getCountry() != "") ? "-r" + locale.getCountry() : ""; 207 | System.out.println("coutryStr = " + coutryStr); 208 | inputFile = ConvertSeperatorToOsSpecific(TempPath + "/res/values-" + locale.getLanguage() + coutryStr + "/strings.xml"); 209 | } 210 | return inputFile; 211 | } 212 | public static Map 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 | -------------------------------------------------------------------------------- /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 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 | --------------------------------------------------------------------------------