├── README.md ├── android-text-localizer.py ├── cacert.sh ├── constantizer.py ├── drawablefixer.py ├── genca.sh ├── github_pr.sh ├── iphoneassetconvert.py ├── itemizer.py ├── layoutparser.py ├── loggrep.py ├── parcelgen.py ├── parseplist.py ├── prefmaker.py ├── pullapk.sh ├── signapk.sh ├── spusagefixer.py ├── stringfixer.py ├── stringizer.py ├── stringparser.py ├── stripper.py ├── swiper.sh ├── tappablemaker.py ├── tinder.sh └── xmlparser.py /README.md: -------------------------------------------------------------------------------- 1 | android-tools 2 | ============= 3 | 4 | A collection of scripts to speed up Android development 5 | -------------------------------------------------------------------------------- /android-text-localizer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Usage: ./android-text-localizer.py 5 | 6 | Recommended you install python-lxml, but this script should still work with 7 | nearly any Python installation. 8 | 9 | IMO, one of the most annoying things about Android is its insistence that 10 | developers localize strings by referencing the strings in layouts using @string 11 | and placing the actual string in res/values/strings.xml. 12 | 13 | This script does that for you automatically. It will parse a layout file for 14 | any element with an android:text attribute. If that attribute has not already 15 | been localized, it generates the correct element and writes it to the 16 | strings XML file. It will also re-write the layout file to use the new 17 | element. 18 | 19 | The name of the string is the android:id of the element it came from. If the 20 | element doesn't have an android:id, a random string is used. If the name is 21 | duplicated in the strings.xml, a random string is appended. 22 | 23 | Errors and warnings are printed at the end of processing. 24 | 25 | Author: Joman Chu 26 | License: Public domain / Apache License 2.0 27 | """ 28 | 29 | import string 30 | import random 31 | import sys 32 | 33 | # Compatibility code stolen from http://lxml.de/tutorial.html 34 | try: 35 | from lxml import etree as ElementTree 36 | #print >> sys.stderr, "running with lxml.etree" 37 | except ImportError: 38 | try: 39 | # Python 2.5 40 | import xml.etree.cElementTree as ElementTree 41 | #print >> sys.stderr, "running with cElementTree on Python 2.5+" 42 | except ImportError: 43 | try: 44 | # Python 2.5 45 | import xml.etree.ElementTree as ElementTree 46 | #print >> sys.stderr, "running with ElementTree on Python 2.5+" 47 | except ImportError: 48 | try: 49 | # normal cElementTree install 50 | import cElementTree as ElementTree 51 | #print >> sys.stderr, "running with cElementTree" 52 | except ImportError: 53 | try: 54 | # normal ElementTree install 55 | import elementtree.ElementTree as ElementTree 56 | #print >> sys.stderr, "running with ElementTree" 57 | except ImportError: 58 | print >> sys.stderr, "Failed to import ElementTree from any known place" 59 | sys.exit(2) 60 | 61 | def gen_rand_str(size=10, chars=string.ascii_uppercase + string.digits + string.ascii_lowercase): 62 | """ 63 | Generates a random string, defaulting to length 10. 64 | Stolen from http://stackoverflow.com/a/2257449 65 | """ 66 | return ''.join(random.choice(chars) for x in range(size)) 67 | 68 | # Check and set arguments 69 | if (len(sys.argv) < 3): 70 | print >> sys.stderr, 'Usage: %s ' % (sys.argv[0]) 71 | sys.exit(1) 72 | 73 | LAYOUTFILE = sys.argv[1] 74 | STRINGSFILE = sys.argv[2] 75 | 76 | errors = [] 77 | 78 | # Parse xml files 79 | layout_tree = ElementTree.parse(LAYOUTFILE) 80 | strings_tree = ElementTree.parse(STRINGSFILE) 81 | nsmap = layout_tree.getroot().nsmap 82 | prefix = '{%s}' % nsmap['android'] 83 | 84 | #print >> sys.stderr, ElementTree.tostring(layout_tree) 85 | #print >> sys.stderr, layout_tree.getroot().nsmap 86 | 87 | r = layout_tree.xpath('//*[@android:text]', namespaces=nsmap) 88 | #print >> sys.stderr, r 89 | for elem in r: 90 | #print >> sys.stderr, elem.attrib 91 | 92 | android_text = elem.get(prefix+'text') 93 | #print >> sys.stderr, android_text 94 | 95 | # Skip if already localized 96 | if '@string' == android_text[0:7]: 97 | continue; 98 | 99 | # Get an id 100 | android_id = elem.get(prefix+'id') 101 | if android_id != None and '@+id/' == android_id[0:5]: 102 | android_id = android_id.split('/',1)[1] 103 | else: 104 | android_id = gen_rand_str(size=4) 105 | errors.append('WARN: Unable to find id for element %s at %s. ' \ 106 | 'Random string \"%s\" used for name' 107 | % (elem.tag, layout_tree.getpath(elem), android_id)) 108 | #continue 109 | 110 | # De-duplicate name in strings.xml 111 | original_android_id = android_id 112 | while True: 113 | localized = strings_tree.xpath('//string[@name=\"%s\"]' % (android_id)) 114 | if len(localized) <= 0: 115 | if android_id != original_android_id: 116 | errors.append('WARN: Duplicated string name \"%s\" at %s. ' \ 117 | 'Used name \"%s\" instead.' 118 | % (original_android_id, layout_tree.getpath(elem), android_id)) 119 | break 120 | android_id = android_id + gen_rand_str(size=1, chars=string.digits) 121 | 122 | # Generate 123 | stringElem = ElementTree.Element("string") 124 | stringElem.set('name', android_id) 125 | stringElem.text = android_text 126 | #print ElementTree.tostring(stringElem) 127 | strings_tree.getroot().insert(len(strings_tree.getroot()), stringElem) 128 | 129 | # Set element to refer to 130 | elem.set(prefix+'text', '@string/'+android_id) 131 | 132 | # Write files 133 | file = open(STRINGSFILE, 'w') 134 | strings_tree.write(file, pretty_print=True) 135 | file.close() 136 | 137 | file = open(LAYOUTFILE, 'w') 138 | layout_tree.write(file, pretty_print=True) 139 | file.close() 140 | 141 | # Print errors 142 | if len(errors) > 0: 143 | print >> sys.stderr, '' 144 | for e in errors: 145 | print >> sys.stderr, e 146 | -------------------------------------------------------------------------------- /cacert.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | echo "$0 inputcert.pem" 5 | exit 6 | } 7 | 8 | if [ -z "$1" ] 9 | then 10 | usage 11 | fi 12 | 13 | SUBJECT_HASH=`openssl x509 -inform PEM -in $1 -subject_hash -noout` 14 | OUTFILE=$SUBJECT_HASH.0 15 | 16 | echo "Creating $OUTFILE..." 17 | 18 | openssl x509 -inform PEM -in $1 > $OUTFILE 19 | openssl x509 -inform PEM -in $1 -text -noout >> $OUTFILE 20 | openssl x509 -inform PEM -in $1 -fingerprint -noout >> $OUTFILE 21 | 22 | -------------------------------------------------------------------------------- /constantizer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import string 5 | 6 | outdata = '' 7 | 8 | def printitem(inputstring): 9 | global outdata 10 | name = inputstring.replace(' ', '_').upper() 11 | outdata += 'public static final String ' + name + ' = "' + inputstring + '"\n' 12 | 13 | for line in iter(sys.stdin.readline, ""): 14 | if (len(line) < 2): 15 | print outdata 16 | outdata = '' 17 | else: 18 | printitem(line.rstrip('\n')) 19 | 20 | -------------------------------------------------------------------------------- /drawablefixer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # parse an xml layout and produce android code to access the elements with ids 4 | 5 | import sys 6 | import os 7 | import math 8 | import string 9 | import shutil 10 | import xml.parsers.expat 11 | 12 | def testdrawable(infile): 13 | testdrawable.retvalue = False 14 | 15 | def start_element(name, attrs): 16 | if name != 'corners': 17 | return 18 | 19 | rcorner = str(attrs.get('android:bottomRightRadius')) 20 | lcorner = str(attrs.get('android:bottomLeftRadius')) 21 | 22 | if rcorner != lcorner: 23 | #print 'Start element:', name, "attrs:", attrs 24 | testdrawable.retvalue = True 25 | 26 | p = xml.parsers.expat.ParserCreate() 27 | p.StartElementHandler = start_element 28 | 29 | f = open(infile,"r"); 30 | try: 31 | p.ParseFile(f) 32 | except: 33 | return False 34 | 35 | if testdrawable.retvalue: 36 | infile 37 | 38 | return testdrawable.retvalue 39 | 40 | if __name__ == "__main__": 41 | filelist = [] 42 | for root, subFolders, files in os.walk('res/drawable'): 43 | for ffile in files: 44 | testfile = os.path.join(root,ffile) 45 | extension = os.path.splitext(testfile)[1] 46 | if extension and extension == '.xml': 47 | if testdrawable(testfile): 48 | filelist.append(testfile) 49 | 50 | #print filelist 51 | 52 | if len(filelist) > 0: 53 | if not os.path.exists('res/drawable-v12'): 54 | os.makedirs('res/drawable-v12') 55 | 56 | for ffile in filelist: 57 | nfile = ffile.replace('/drawable/', '/drawable-v12/') 58 | print 'Generating: ' + nfile 59 | newfile = open(nfile, 'w') 60 | oldfile = open(ffile) 61 | for line in oldfile: 62 | templine = line.replace('bottomRightRadius', 'bottomTempRadius') 63 | templine = templine.replace('bottomLeftRadius', 'bottomRightRadius') 64 | templine = templine.replace('bottomTempRadius', 'bottomLeftRadius') 65 | newfile.write(templine) 66 | newfile.close() 67 | oldfile.close() 68 | -------------------------------------------------------------------------------- /genca.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | openssl genrsa -des3 -out ca.key 2048 4 | openssl req -x509 -new -nodes -key ca.key -sha256 -days 1825 -out ca.pem 5 | 6 | -------------------------------------------------------------------------------- /github_pr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "$1" ] 4 | then 5 | DESTINATION_BRANCH="master" 6 | else 7 | DESTINATION_BRANCH=$1 8 | fi 9 | 10 | BRANCH=$(git rev-parse --abbrev-ref HEAD) 11 | REPO_URL=$(git remote get-url origin | sed -e 's/git@//' -e 's/.git//' -e 's/:/\//') 12 | PR_URL="https://$REPO_URL/compare/$DESTINATION_BRANCH...$BRANCH" 13 | open $PR_URL 14 | 15 | -------------------------------------------------------------------------------- /iphoneassetconvert.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import string 6 | import shutil 7 | import errno 8 | 9 | def copy(fromfile, tofile): 10 | print 'cp ' + fromfile + ' ' + tofile 11 | try: 12 | shutil.copyfile(fromfile, tofile) 13 | except shutil.Error: 14 | pass 15 | 16 | def createdir(dirname): 17 | try: 18 | os.makedirs(dirname) 19 | except OSError,err: 20 | if err.errno!=17: 21 | raise 22 | 23 | def copyfile(filename, fullpath): 24 | global assetpath, outputpath 25 | 26 | if filename.endswith('@2x.png'): 27 | filename = filename[:-5] + '.png' 28 | rname = 'ic' 29 | for i, c in enumerate(filename): 30 | if c.isupper(): 31 | rname += '_' + string.lower(c) 32 | else: 33 | rname += c 34 | filename = filter(lambda x: x in (string.lowercase + '._'), rname) 35 | #if filename.endswith('active.png'): 36 | #filename = filename[:-10] + '_enabled.png' 37 | 38 | dirname = os.path.join(outputpath, 'drawable-xhdpi') 39 | createdir(dirname) 40 | outfile = os.path.join(dirname, filename) 41 | copy(fullpath, outfile) 42 | 43 | if __name__ == "__main__": 44 | global assetpath, outputpath 45 | assetpath = os.path.abspath('.') 46 | outputpath = os.path.abspath('.') 47 | if len(sys.argv) > 1: 48 | assetpath = sys.argv[1] 49 | if len(sys.argv) > 2: 50 | outputpath = sys.argv[2] 51 | for root, subFolders, files in os.walk(assetpath): 52 | for ffile in files: 53 | testfile = os.path.join(root,ffile) 54 | extension = os.path.splitext(testfile)[1] 55 | if extension and extension == '.png': 56 | copyfile(ffile, testfile) 57 | 58 | -------------------------------------------------------------------------------- /itemizer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | 5 | outdata = '' 6 | 7 | def printitem(inputstring): 8 | global outdata 9 | inputstring = inputstring.strip() 10 | name = inputstring[:inputstring.find("=")] 11 | item = inputstring[inputstring.find('"')+1:-1] 12 | outdata += '' + item + '\n' 13 | 14 | for line in iter(sys.stdin.readline, ""): 15 | if (len(line) < 2): 16 | print outdata 17 | outdata = '' 18 | else: 19 | printitem(line) 20 | 21 | -------------------------------------------------------------------------------- /layoutparser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # parse an xml layout and produce android code to access the elements with ids 4 | 5 | import sys 6 | import xml.parsers.expat 7 | 8 | privates = str() 9 | lookups = str() 10 | 11 | if len(sys.argv) < 2: 12 | print "Usage " , sys.argv[0] , " layout.xml"; 13 | else: 14 | adapter = False 15 | if len(sys.argv) == 3: 16 | adapter = True 17 | 18 | def start_element(name, attrs): 19 | global privates 20 | global lookups 21 | 22 | if 'android:id' in attrs: 23 | classname = str(name) 24 | if classname == 'include' or classname == 'merge': 25 | classname = 'View' 26 | dotindex = classname.rfind('.') 27 | if dotindex != -1: 28 | classname = classname[dotindex+1:] 29 | rid = str(attrs.get('android:id')) 30 | rid = rid[rid.find('/')+1:] 31 | rname = '' 32 | foundunderscore = False 33 | for i, c in enumerate(rid): 34 | if c == '_': 35 | if i == 0: 36 | return 37 | foundunderscore = True 38 | i += 1 39 | elif foundunderscore: 40 | rname += c.upper() 41 | foundunderscore = False 42 | else: 43 | rname += c 44 | 45 | privates += '' if adapter else 'private ' 46 | privates += classname + ' ' + rname + ';\n' 47 | lookups += 'holder.' if adapter else '' 48 | lookups += rname + ' = (' + classname + ') ' 49 | lookups += 'convertView.' if adapter else '' 50 | lookups += 'findViewById(R.id.' + rid + ');\n' 51 | if classname == 'Button': 52 | lookups += 'holder.' if adapter else '' 53 | lookups += rname + '.setOnClickListener(new View.OnClickListener() {\n' 54 | lookups += '\t@Override\n' 55 | lookups += '\tpublic void onClick(View v) {\n' 56 | lookups += '\t\t' + rname + 'Clicked();\n' 57 | lookups += '\t}\n' 58 | lookups += '});\n' 59 | 60 | #print 'Start element:', name, "attrs:", attrs 61 | #print 'rid', rid 62 | #print 'rname', rname 63 | 64 | 65 | p = xml.parsers.expat.ParserCreate() 66 | p.StartElementHandler = start_element 67 | 68 | f = open(sys.argv[1],"r"); 69 | p.ParseFile(f) 70 | 71 | print privates 72 | print lookups 73 | -------------------------------------------------------------------------------- /loggrep.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import re 5 | import subprocess 6 | 7 | class bcolors: 8 | HEADER = '\033[95m' 9 | OKBLUE = '\033[94m' 10 | OKGREEN = '\033[92m' 11 | WARNING = '\033[93m' 12 | FAIL = '\033[91m' 13 | ENDC = '\033[0m' 14 | 15 | logbuf = None 16 | 17 | def striptag(line): 18 | tag = line.find(": ") 19 | if tag != None: 20 | print line[tag+2] 21 | return line[tag+2:] 22 | return line 23 | 24 | def printline(color, line): 25 | global logbuf 26 | if len(line) > 3800: 27 | if logbuf == None: 28 | logbuf = line 29 | else: 30 | logbuf += striptag(line) 31 | else: 32 | if logbuf != None: 33 | printstring(color, logbuf + striptag(line)) 34 | logbuf = None 35 | else: 36 | printstring(color, line) 37 | 38 | 39 | def printstring(color, line): 40 | if color is None: 41 | print line 42 | else: 43 | print color + line + bcolors.ENDC 44 | #print str(len(line)) + ']' + color + line + bcolors.ENDC 45 | 46 | def outputline(line): 47 | color = None 48 | if line[0] == 'E': 49 | color = bcolors.FAIL 50 | elif line[0] == 'I': 51 | color = bcolors.OKGREEN 52 | elif line[0] == 'W': 53 | color = bcolors.WARNING 54 | elif line[0] == 'D': 55 | color = bcolors.OKBLUE 56 | printline(color, line) 57 | 58 | if __name__ == "__main__": 59 | grepstring = 'hailo' 60 | 61 | if len(sys.argv) > 1: 62 | grepstring = sys.argv[1] 63 | 64 | regexes = [] 65 | 66 | if grepstring != '-a': 67 | p = subprocess.Popen(["adb", "shell", "ps"], stdout=subprocess.PIPE) 68 | stdout = p.communicate()[0] 69 | for line in stdout.splitlines(): 70 | pstable = line.split() 71 | if grepstring in pstable[-1]: 72 | matchstring = '\((\s)*' + pstable[1] + '\):' 73 | regexes += [re.compile(matchstring)] 74 | print matchstring 75 | 76 | p = subprocess.Popen(["adb", "logcat"], stdout=subprocess.PIPE) 77 | 78 | while True: 79 | try: 80 | line = p.stdout.readline().rstrip() 81 | except KeyboardInterrupt: 82 | break 83 | 84 | if len(regexes) > 0: 85 | for reg in regexes: 86 | if reg.search(line) != None: 87 | outputline(line) 88 | else: 89 | outputline(line) 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /parcelgen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys, re, os.path, json 4 | 5 | # Parcelgen generates parcelable Java classes based 6 | # on a json dictionary of types and properties. It generates 7 | # a class with the appropriate members and working 8 | # writeToParcel and readFromParcel methods. 9 | 10 | # Primary Author: Alex Pretzlav 11 | 12 | 13 | class ParcelGen: 14 | BASE_IMPORTS = ("android.os.Parcel", "android.os.Parcelable") 15 | CLASS_STR = "public class %s implements %s {" 16 | CHILD_CLASS_STR = "public class {0} extends _{0} {{" 17 | NATIVE_TYPES = ["string", "byte", "double", "float", "int", "long"] 18 | JSON_IMPORTS = ["org.json.JSONException", "org.json.JSONObject"] 19 | 20 | tablevel = 0 21 | outfile = None 22 | 23 | def tabify(self, string): 24 | return (" " * self.tablevel) + string 25 | 26 | def printtab(self, string): 27 | self.output(self.tabify(string)) 28 | 29 | def newline(self, count=1): 30 | self.output("\n" * (count-1)) 31 | 32 | def output(self, string=""): 33 | if self.outfile: 34 | self.outfile.write(string + "\n") 35 | else: 36 | print string 37 | 38 | def uptab(self): 39 | self.tablevel += 1 40 | 41 | def downtab(self): 42 | self.tablevel -= 1 43 | 44 | def memberize(self, name): 45 | return name 46 | 47 | def member_map(self): 48 | for typ in self.get_types(): 49 | for member in self.props[typ]: 50 | yield (typ, member) 51 | 52 | def gen_getter(self, typ, member): 53 | method_name = "" 54 | if typ == "boolean" and member.startswith("is"): 55 | method_name = member 56 | else: 57 | method_name = "get%s%s" % (member[0].capitalize(), member[1:]) 58 | return " public %s %s() {\n return %s;\n }" % (typ, method_name, self.memberize(member)) 59 | 60 | def list_type(self, typ): 61 | match = re.match(r"(List|ArrayList)<(.*)>", typ) 62 | if match: 63 | return match.group(2) 64 | return None 65 | 66 | def gen_list_parcelable(self, typ, memberized): 67 | classname = self.list_type(typ) 68 | if not classname: 69 | return None 70 | elif classname == "String": 71 | return self.tabify("parcel.writeStringList(%s);" % memberized) 72 | else: 73 | return self.tabify("parcel.writeTypedList(%s);" % memberized) 74 | 75 | def gen_list_unparcel(self, typ, memberized): 76 | classname = self.list_type(typ) 77 | if not classname: 78 | return None 79 | if (classname == "String"): 80 | return self.tabify("%s = source.createStringArrayList();" % memberized) 81 | else: 82 | return self.tabify("%s = source.createTypedArrayList(%s.CREATOR);" % (memberized, classname)) 83 | 84 | def gen_parcelable_line(self, typ, member): 85 | memberized = self.memberize(member) 86 | if typ.lower() in self.NATIVE_TYPES: 87 | return self.tabify("parcel.write%s(%s);" % (typ.capitalize(), memberized)) 88 | elif typ == "Date": 89 | return self.tabify("parcel.writeLong(%s == null ? Integer.MIN_VALUE : %s.getTime());" % ( 90 | memberized, memberized)) 91 | elif self.list_type(typ): 92 | return self.gen_list_parcelable(typ, memberized) 93 | elif typ in self.serializables: 94 | return self.tabify("parcel.writeSerializable(%s);" % memberized) 95 | else: 96 | return self.tabify("parcel.writeParcelable(%s, 0);" % memberized) 97 | 98 | def get_types(self): 99 | types = self.props.keys() 100 | types.sort() 101 | return types 102 | 103 | def gen_parcelable(self): 104 | result = "" 105 | for typ in self.get_types(): 106 | if typ == "boolean": 107 | joined = ", ".join(map(self.memberize, self.props[typ])) 108 | result += self.tabify("parcel.writeBooleanArray(new boolean[] {%s});\n" % joined) 109 | else: 110 | for member in self.props[typ]: 111 | result += self.gen_parcelable_line(typ, member) + "\n" 112 | return result[:-1] # Strip last newline because I'm too lazy to do this right 113 | 114 | def print_creator(self, class_name, parcel_class, close=True): 115 | # Simple parcelable creator that uses readFromParcel 116 | self.printtab("public static final {0}<{1}> CREATOR = new {0}<{1}>() {{".format( 117 | parcel_class, class_name)) 118 | self.uptab() 119 | self.printtab("public {0}[] newArray(int size) {{\n{1}return new {0}[size];\n }}".format( 120 | class_name, " " * (self.tablevel + 1))) 121 | self.newline() 122 | self.printtab("public %s createFromParcel(Parcel source) {" % class_name) 123 | self.uptab() 124 | self.printtab("{0} object = new {0}();".format(class_name)) 125 | self.printtab("object.readFromParcel(source);") 126 | self.printtab("return object;") 127 | self.downtab() 128 | self.printtab("}") 129 | if close: 130 | self.downtab() 131 | self.printtab("};\n") 132 | self.downtab() 133 | 134 | def print_child(self, child_name, package): 135 | self.tablevel = 0 136 | self.printtab("package %s;\n" % package) 137 | imports = ["android.os.Parcel"] 138 | if self.do_json: 139 | imports.extend(self.JSON_IMPORTS) 140 | imports.append("com.yelp.parcelgen.JsonParser.DualCreator") 141 | else: 142 | imports.append("android.os.Parcelable") 143 | for import_string in imports: 144 | self.printtab("import %s;" % import_string) 145 | self.newline(2) 146 | self.printtab(self.CHILD_CLASS_STR.format(child_name)) 147 | self.newline() 148 | self.uptab() 149 | if self.do_json: 150 | self.print_creator(child_name, "DualCreator", False) 151 | self.newline() 152 | self.printtab("@Override") 153 | self.printtab("public %s parse(JSONObject obj) throws JSONException {" % child_name) 154 | self.uptab() 155 | self.printtab("{0} newInstance = new {0}();".format(child_name)) 156 | self.printtab("newInstance.readFromJson(obj);") 157 | self.printtab("return newInstance;") 158 | self.downtab() 159 | self.printtab("}\n };\n") 160 | self.downtab() 161 | else: 162 | self.print_creator(child_name, "Parcelable.Creator") 163 | self.downtab() 164 | self.printtab("}") 165 | 166 | def needs_jsonutil(self): 167 | if "Date" in self.props: 168 | return True 169 | for key in self.props.keys(): 170 | if "List" in key: 171 | return True 172 | return False 173 | 174 | def print_gen(self, props, class_name, package, imports, transient): 175 | self.props = props 176 | self.tablevel = 0 177 | # Imports and open class definition 178 | self.printtab("package %s;\n" % package) 179 | imports = set(tuple(imports) + self.BASE_IMPORTS) 180 | for prop in props.keys(): 181 | if prop.startswith("List"): 182 | imports.add("java.util.List") 183 | elif prop.startswith("ArrayList"): 184 | imports.add("java.util.ArrayList") 185 | elif prop == "Date": 186 | imports.add("java.util.Date") 187 | elif prop == "Uri": 188 | imports.add("android.net.Uri") 189 | 190 | if self.do_json: 191 | imports.update(self.JSON_IMPORTS) 192 | if self.needs_jsonutil(): 193 | imports.add("com.yelp.parcelgen.JsonUtil") 194 | if self.make_serializable: 195 | imports.add("java.io.Serializable") 196 | imports = list(imports) 197 | imports.sort() 198 | 199 | for imp in imports: 200 | self.printtab("import %s;" % imp) 201 | 202 | self.output("") 203 | self.printtab("// Automatically generated Parcelable implementation for %s." % class_name) 204 | 205 | implements = "Parcelable" 206 | if self.make_serializable: 207 | implements += ", Serializable" 208 | self.printtab((self.CLASS_STR % (class_name, implements)) + "\n") 209 | 210 | # Protected member variables 211 | self.uptab() 212 | for typ, member in self.member_map(): 213 | if member in transient: 214 | typ = "transient " + typ 215 | self.printtab("private %s %s;" % (typ, self.memberize(member))) 216 | self.output("") 217 | 218 | # Parameterized Constructor 219 | constructor = "public %s(" % class_name 220 | params = [] 221 | for typ, member in self.member_map(): 222 | params.append("%s %s" % (typ, member)) 223 | constructor += "%s) {" % ", ".join(params) 224 | self.printtab(constructor) 225 | self.uptab() 226 | for typ, member in self.member_map(): 227 | self.printtab("this.%s = %s;" % (self.memberize(member), member)) 228 | self.tablevel -= 1 229 | self.printtab("}\n") 230 | 231 | # Empty constructor for Parcelable 232 | self.printtab("public %s(Parcel p) {" % class_name) 233 | self.uptab() 234 | self.printtab("readFromParcel(p);") 235 | self.downtab() 236 | self.printtab("}\n") 237 | 238 | # Getters for member variables 239 | for typ, member in self.member_map(): 240 | self.output(self.gen_getter(typ, member)) 241 | self.output("\n") 242 | 243 | # Parcelable writeToParcel 244 | self.printtab("public int describeContents() {\n return 0;\n }") 245 | self.output("") 246 | self.printtab("public void writeToParcel(Parcel parcel, int flags) {") 247 | self.uptab() 248 | self.output(self.gen_parcelable()) 249 | self.downtab() 250 | self.printtab("}\n") 251 | 252 | # readFromParcel that allows subclasses to use parcelable-ness of their superclass 253 | self.printtab("public void readFromParcel(Parcel source) {") 254 | self.tablevel += 1 255 | i = 0 256 | all_members = [] 257 | for typ in self.get_types(): 258 | if typ == "boolean": 259 | self.printtab("boolean[] bools = source.createBooleanArray();") 260 | for j in xrange(len(props[typ])): 261 | self.printtab("%s = bools[%d];" % (self.memberize(props[typ][j]), j)) 262 | else: 263 | for member in props[typ]: 264 | memberized = self.memberize(member) 265 | list_gen = self.gen_list_unparcel(typ, memberized) 266 | if list_gen: 267 | self.output(list_gen) 268 | elif typ == "Date": 269 | self.printtab("long date%d = source.readLong();" % i) 270 | self.printtab("if (date%d != Integer.MIN_VALUE) {" % i) 271 | self.uptab() 272 | self.printtab("%s = new Date(date%d);" % (memberized, i)) 273 | self.downtab() 274 | self.printtab("}") 275 | i += 1 276 | elif typ.lower() in self.NATIVE_TYPES: 277 | self.printtab("%s = source.read%s();" % (memberized, typ.capitalize())) 278 | elif typ in self.serializables: 279 | self.printtab("%s = (%s)source.readSerializable();" % (memberized, typ)) 280 | else: 281 | self.printtab("%s = source.readParcelable(%s.class.getClassLoader());" % (memberized, typ)) 282 | self.tablevel -= 1 283 | self.printtab("}\n") 284 | self.print_creator(class_name, "Parcelable.Creator") 285 | self.uptab() 286 | 287 | if self.do_json: 288 | self.output(self.generate_json_reader(props)) 289 | if self.do_json_writer: 290 | self.output(self.generate_json_writer(props)) 291 | self.downtab() 292 | self.printtab("}") 293 | 294 | def generate_json_reader(self, props): 295 | self.props = props 296 | fun = self.tabify("public void readFromJson(JSONObject json) throws JSONException {\n") 297 | self.uptab() 298 | # Parcelable doesn't support boolean without help, JSON does 299 | NATIVES = self.NATIVE_TYPES + ["boolean"] 300 | for typ in self.get_types(): 301 | list_type = self.list_type(typ) 302 | # Always protect strings with isNull check because JSONObject.optString() 303 | # returns the string "null" for null strings. AWESOME. 304 | protect = typ not in [native for native in NATIVES if native != "String"] 305 | for member in props[typ]: 306 | # Some object members are derived and not stored in JSON 307 | if member in self.json_blacklist: 308 | continue 309 | # Some members have different names in JSON 310 | if member in self.json_map: 311 | key = self.json_map[member] 312 | else: 313 | key = camel_to_under(member) 314 | # Need to check if key is defined if we have a default value too 315 | if member in self.default_values: 316 | protect = True 317 | if protect: 318 | fun += self.tabify("if (!json.isNull(\"%s\")) {\n" % key) 319 | self.uptab() 320 | fun += self.tabify("%s = " % self.memberize(member)) 321 | if typ.lower() == "float": 322 | fun += "(float)json.optDouble(\"%s\")" % key 323 | elif typ.lower() in NATIVES: 324 | fun += "json.opt%s(\"%s\")" % (typ.capitalize(), key) 325 | elif typ == "List": 326 | fun += "JsonUtil.getStringList(json.optJSONArray(\"%s\"))" % key 327 | elif typ == "Date": 328 | fun += "JsonUtil.parseTimestamp(json, \"%s\")" % key 329 | elif typ == "Uri": 330 | fun += "Uri.parse(json.getString(\"%s\"))" % key 331 | elif list_type: 332 | fun += "JsonUtil.parseJsonList(json.optJSONArray(\"%s\"), %s.CREATOR)" % (key, list_type) 333 | else: 334 | fun += "%s.CREATOR.parse(json.getJSONObject(\"%s\"))" % (typ, key) 335 | fun += ";\n" 336 | if protect: 337 | self.downtab() 338 | listmatcher = re.match(r"(?PArray)?List(?P[<>a-zA-Z0-9_]*)", typ) 339 | if listmatcher is not None: 340 | match_dict = listmatcher.groupdict() 341 | fun += self.tabify("} else {\n") 342 | self.uptab() 343 | fun += self.tabify(("%s = " % self.memberize(member))) 344 | if match_dict['list_type'] is not None and match_dict['content_type'] is not None: 345 | fun += ("new %sList%s()" % (match_dict['list_type'], match_dict['content_type'])) 346 | else: 347 | fun += "java.util.Collections.emptyList()" 348 | fun += ";\n" 349 | self.downtab() 350 | elif member in self.default_values: 351 | fun += self.tabify("} else {\n") 352 | self.uptab() 353 | fun += self.tabify(("%s = %s;\n" % (self.memberize(member), self.default_values[member]))) 354 | self.downtab() 355 | fun += self.tabify("}\n") 356 | self.downtab() 357 | fun += self.tabify("}\n") 358 | return fun 359 | 360 | def generate_json_writer(self, foo): 361 | fun = self.tabify("public JSONObject writeJSON() throws JSONException {\n") 362 | self.uptab() 363 | fun += self.tabify("JSONObject json = new JSONObject();\n") 364 | # Parcelable doesn't support boolean without help, JSON does 365 | NATIVES = self.NATIVE_TYPES + ["boolean", "String"] 366 | for typ in self.get_types(): 367 | list_type = self.list_type(typ) 368 | # Always protect strings with isNull check because JSONObject.optString() 369 | # returns the string "null" for null strings. AWESOME. 370 | protect = typ not in [native for native in NATIVES if native != "String"] 371 | for member in self.props[typ]: 372 | # Some object members are derived and not stored in JSON 373 | if member in self.json_blacklist: 374 | continue 375 | # Some members have different names in JSON 376 | if member in self.json_map: 377 | key = self.json_map[member] 378 | else: 379 | key = camel_to_under(member) 380 | if protect: 381 | fun += self.tabify("if (%s != null) {\n" % self.memberize(member)) 382 | self.uptab() 383 | if typ == "List": 384 | fun += self.tabify("// TODO list writing %s\n" % self.memberize(member)) 385 | elif typ == "Date": 386 | fun += self.tabify("json.put(\"%s\", %s.getTime() / 1000);\n" % (key, self.memberize(member))) 387 | elif typ == "Uri": 388 | fun += self.tabify("json.put(\"%s\", String.valueOf(%s));\n" % (key, self.memberize(member))) 389 | elif list_type: 390 | fun += self.tabify("// TODO LIST writing %s \n" % self.memberize(member)) 391 | elif typ in NATIVES: 392 | fun += self.tabify("json.put(\"%s\", %s);\n" % (key, self.memberize(member))) 393 | else: 394 | fun += self.tabify("json.put(\"%s\", %s.writeJSON());\n" % (key, self.memberize(member))) 395 | if protect: 396 | self.downtab() 397 | fun += self.tabify("}\n") 398 | fun += self.tabify("return json;\n") 399 | self.downtab() 400 | fun += self.tabify("}\n") 401 | return fun 402 | 403 | def camel_to_under(member): 404 | """ Convert NamesInCamelCase to jsonic_underscore_names""" 405 | s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', member) 406 | return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower() 407 | 408 | def generate_class(filePath, output): 409 | # Read parcelable description json 410 | description = json.load(open(filePath, 'r')) 411 | props = description.get("props") or {} 412 | package = description.get("package") or None 413 | imports = description.get("imports") or () 414 | json_map = description.get("json_map") or {} 415 | default_values = description.get("default_values") or {} 416 | transient = description.get("transient") or [] 417 | make_serializable = description.get("make_serializable") 418 | do_json_writer = description.get("do_json_writer") 419 | json_blacklist = description.get("json_blacklist") or [] 420 | serializables = description.get("serializables") or () 421 | if 'do_json' in description: 422 | do_json = description.get("do_json") 423 | else: 424 | do_json = False 425 | class_name = os.path.basename(filePath).split(".")[0] 426 | 427 | generator = ParcelGen() 428 | generator.json_map = json_map 429 | generator.json_blacklist = json_blacklist 430 | generator.serializables = serializables 431 | generator.do_json = do_json 432 | generator.do_json_writer = do_json_writer 433 | generator.make_serializable = make_serializable 434 | 435 | generator.default_values = default_values 436 | if output: 437 | if (os.path.isdir(output)): # Resolve file location based on package + path 438 | dirs = package.split(".") 439 | dirs.append(class_name + ".java") 440 | targetFile = os.path.join(output, *dirs) 441 | # Generate child subclass if it doesn't exist 442 | if class_name.startswith("_"): 443 | child = class_name[1:] 444 | new_dirs = package.split(".") 445 | new_dirs.append(child + ".java") 446 | child_file = os.path.join(output, *new_dirs) 447 | if not os.path.exists(child_file): 448 | generator.outfile = open(child_file, 'w') 449 | generator.print_child(child, package) 450 | generator.outfile = open(targetFile, 'w') 451 | generator.print_gen(props, class_name, package, imports, transient) 452 | 453 | 454 | if __name__ == "__main__": 455 | usage = """USAGE: %s parcelfile [destination] 456 | 457 | Generates a parcelable Java implementation for provided description file. 458 | Writes to stdout unless destination is specified. 459 | 460 | If destination is a directory, it is assumed to be the top level 461 | directory of your Java source. Your class file will be written in the 462 | appropriate folder based on its Java package. 463 | If destination is a file, your class will be written to that file.""" 464 | if len(sys.argv) < 2: 465 | print(usage % sys.argv[0]) 466 | exit(0) 467 | destination = None 468 | if len(sys.argv) > 2: 469 | destination = sys.argv[2] 470 | source = sys.argv[1] 471 | # If both source and destination are directories, run in 472 | # fake make mode 473 | if (os.path.isdir(source) and os.path.isdir(destination)): 474 | for sourcefile in [sourcefile for sourcefile in os.listdir(source) if sourcefile.endswith(".json")]: 475 | print "decoding ", sourcefile 476 | generate_class(os.path.join(source, sourcefile), destination) 477 | else: 478 | generate_class(sys.argv[1], destination) 479 | 480 | -------------------------------------------------------------------------------- /parseplist.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | import json 6 | import plistlib 7 | import xml.parsers.expat 8 | 9 | DESIRED_PADDING = 30 10 | 11 | config = {} 12 | 13 | def printconfig_xml(): 14 | for c in config: 15 | spaceavailable = DESIRED_PADDING - len(testxml.keyname) 16 | if spaceavailable > 0: 17 | tabs = '\t' * (spaceavailable / 4) 18 | else: 19 | tabs = ' ' 20 | print '\t<' + c + tabs + 'value="' + config[c] + '"/>' 21 | 22 | def printconfig(): 23 | print config 24 | #print json.dumps(config) 25 | 26 | def testxml(): 27 | config = plistlib.readPlist(sys.argv[1]) 28 | printconfig() 29 | 30 | if __name__ == "__main__": 31 | if len(sys.argv) < 2: 32 | print "Usage " , sys.argv[0] , " input.plist"; 33 | else: 34 | testxml() 35 | 36 | 37 | -------------------------------------------------------------------------------- /prefmaker.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # parse an xml layout and produce android code to access the elements with ids 4 | 5 | import sys 6 | 7 | inputdata = sys.argv 8 | 9 | if len(inputdata) < 3: 10 | inputdata = sys.stdin.readline().split() 11 | else: 12 | inputdata = inputdata[1:] 13 | 14 | if len(inputdata) < 2: 15 | print "Usage " , sys.argv[0] , " type name (e.g long pref)" 16 | 17 | datatype = inputdata[0] 18 | dataftype = datatype[:1].upper() + datatype[1:] 19 | dataname = inputdata[1].lower() 20 | if len(inputdata) > 2: 21 | for word in inputdata[2:]: 22 | dataname += '_' + word.lower() 23 | dataconst = inputdata[1].upper() 24 | if len(inputdata) > 2: 25 | for word in inputdata[2:]: 26 | dataconst += '_' + word.upper() 27 | datafname = '' 28 | for word in inputdata[1:]: 29 | datafname += word[:1].upper() + word[1:] 30 | 31 | if datatype == 'boolean': 32 | datadefault = 'false' 33 | elif datatype == 'String': 34 | datadefault = '""' 35 | else: 36 | datadefault = '0' 37 | 38 | print '\tprivate final static String ' + dataconst + ' = "' + dataname + '_key";' 39 | print '' 40 | print '\tpublic ' + datatype + ' get' + datafname + '() {' 41 | print '\t\treturn preferences.get' + dataftype + '(' + dataconst + ', ' + datadefault + ');' 42 | print '\t}' 43 | print '' 44 | print '\tpublic void set' + datafname + '(' + datatype + ' ' + dataname + ') {' 45 | print '\t\tpreferencesEditor.put' + dataftype + '(' + dataconst + ', ' + dataname + ').commit();' 46 | print '\t}' 47 | 48 | 49 | -------------------------------------------------------------------------------- /pullapk.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ $# -eq 0 ]] ; then 4 | echo "Usage $0 com.package" 5 | exit 1 6 | fi 7 | 8 | PACKAGES=`adb shell pm list packages | grep "$@" | cut -d ':' -f2- ` 9 | NUMPACKAGES=`echo $PACKAGES | wc -l` 10 | if [[ $NUMPACKAGES -le 0 ]]; then 11 | echo "No matching packages" 12 | exit 1 13 | fi 14 | if [[ $NUMPACKAGES -ge 2 ]]; then 15 | echo -e "Multiple packages:\n$PACKAGES" 16 | exit 1 17 | fi 18 | 19 | PACKAGE="${PACKAGES/$'\r'/}" 20 | APKPATH=`adb shell pm path $PACKAGE | cut -d ':' -f2-` 21 | APKPATH="${APKPATH/$'\r'/}" 22 | COMMAND="adb pull $APKPATH $PACKAGE.apk" 23 | echo $COMMAND 24 | $COMMAND 25 | 26 | -------------------------------------------------------------------------------- /signapk.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ $# -eq 0 ]] ; then 4 | echo "Usage $0 app.apk" 5 | exit 1 6 | fi 7 | 8 | jarsigner -verbose -keystore ~/.android/debug.keystore -storepass android -keypass android -digestalg SHA1 -sigalg MD5withRSA $1 androiddebugkey 9 | 10 | -------------------------------------------------------------------------------- /spusagefixer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # parse an xml layout and produce android code to access the elements with ids 4 | 5 | import sys 6 | import os 7 | import math 8 | import string 9 | import shutil 10 | import xml.parsers.expat 11 | 12 | def testdrawable(infile): 13 | testdrawable.retvalue = False 14 | 15 | def start_element(name, attrs): 16 | rcorner = str(attrs.get('android:textSize')) 17 | if rcorner and rcorner.find('dp') != -1: 18 | testdrawable.retvalue = True 19 | 20 | p = xml.parsers.expat.ParserCreate() 21 | p.StartElementHandler = start_element 22 | 23 | f = open(infile,"r"); 24 | try: 25 | p.ParseFile(f) 26 | except: 27 | return False 28 | 29 | return testdrawable.retvalue 30 | 31 | if __name__ == "__main__": 32 | filelist = [] 33 | for root, subFolders, files in os.walk('res'): 34 | for ffile in files: 35 | testfile = os.path.join(root,ffile) 36 | extension = os.path.splitext(testfile)[1] 37 | if extension and extension == '.xml': 38 | filelist.append(testfile) 39 | 40 | #print filelist 41 | 42 | if len(filelist) > 0: 43 | for ffile in filelist: 44 | nfile = ffile.replace('.xml', '.tmp') 45 | print 'Generating: ' + nfile 46 | newfile = open(nfile, 'w') 47 | oldfile = open(ffile) 48 | for line in oldfile: 49 | templine = line 50 | if line.find('textSize') != -1: 51 | templine = line.replace('sp', 'dp') 52 | newfile.write(templine) 53 | newfile.close() 54 | oldfile.close() 55 | os.rename(nfile, ffile) 56 | -------------------------------------------------------------------------------- /stringfixer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # parse an xml layout and produce android code to access the elements with ids 4 | 5 | import sys 6 | import os 7 | import math 8 | import string 9 | import shutil 10 | import xml.parsers.expat 11 | 12 | def get_var_name(instring): 13 | var_name = '' 14 | words = instring.split(' ') 15 | for word in words: 16 | var_name += word.lower() + '_' 17 | return var_name[:-1] 18 | 19 | def testlayout(infile): 20 | def start_element(name, attrs): 21 | attrib = attrs.get('android:text') 22 | if attrib == None: 23 | return 24 | textvalue = str(attrib) 25 | if '@string' == textvalue[0:7]: 26 | return 27 | var_name = get_var_name(textvalue) 28 | print '' + textvalue + '' 29 | 30 | p = xml.parsers.expat.ParserCreate() 31 | p.StartElementHandler = start_element 32 | 33 | f = open(infile,"r"); 34 | try: 35 | p.ParseFile(f) 36 | except: 37 | return 38 | 39 | 40 | if __name__ == "__main__": 41 | for root, subFolders, files in os.walk('res/layout'): 42 | for ffile in files: 43 | testfile = os.path.join(root,ffile) 44 | extension = os.path.splitext(testfile)[1] 45 | if extension and extension == '.xml': 46 | testlayout(testfile) 47 | 48 | -------------------------------------------------------------------------------- /stringizer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import string 5 | 6 | outdata = '' 7 | 8 | def printitem(inputstring): 9 | global outdata 10 | name = inputstring.replace(' ', '_').lower() 11 | name = filter(lambda x: x in (string.lowercase + '_'), name) 12 | if inputstring[0] == ' ': 13 | inputstring = '"' + inputstring + '"' 14 | outdata += '' + inputstring + '\n' 15 | 16 | for line in iter(sys.stdin.readline, ""): 17 | if (len(line) < 2): 18 | print outdata 19 | outdata = '' 20 | else: 21 | printitem(line.rstrip('\n')) 22 | 23 | -------------------------------------------------------------------------------- /stringparser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import os 5 | import math 6 | import string 7 | import shutil 8 | import xml.parsers.expat 9 | 10 | #import codecs, locale 11 | 12 | #if sys.stdout.encoding is None: 13 | #(lang, enc) = locale.getdefaultlocale() 14 | #if enc is not None: 15 | #(e, d, sr, sw) = codecs.lookup(enc) 16 | #sys.stdout = sw(sys.stdout) 17 | 18 | def testxml(infile): 19 | testxml.name = None 20 | 21 | def start_element(name, attrs): 22 | #print 'element:', name, "attrs:", attrs 23 | if name == 'string': 24 | testxml.name = str(attrs.get('name')) 25 | sys.stdout.write(testxml.name + '=') 26 | 27 | def end_element(name): 28 | if testxml.name != None: 29 | sys.stdout.write('\n') 30 | testxml.name = None 31 | #print 'end element:', name 32 | 33 | def string_data(text): 34 | if testxml.name != None: 35 | sys.stdout.write(text.encode('utf8')) 36 | 37 | p = xml.parsers.expat.ParserCreate() 38 | p.StartElementHandler = start_element 39 | p.EndElementHandler = end_element 40 | p.CharacterDataHandler = string_data 41 | 42 | f = open(infile,"r"); 43 | try: 44 | p.ParseFile(f) 45 | except: 46 | raise 47 | return False 48 | 49 | 50 | return True 51 | 52 | if __name__ == "__main__": 53 | filelist = [] 54 | for root, subFolders, files in os.walk('res/values'): 55 | for ffile in files: 56 | testfile = os.path.join(root,ffile) 57 | extension = os.path.splitext(testfile)[1] 58 | if extension and extension == '.xml': 59 | testxml(testfile) 60 | 61 | -------------------------------------------------------------------------------- /stripper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # quick simple script to detect unused resources 4 | # install python (brew install python) 5 | # delete the drawable-mdpi/ldpi folders to avoid outputting duplicated resources 6 | 7 | import os 8 | import sys 9 | import math 10 | import string 11 | 12 | from subprocess import call 13 | 14 | print "The build succeeds without:" 15 | 16 | for root, subFolders, files in os.walk('res'): 17 | for file in files: 18 | testfile = os.path.join(root,file) 19 | 20 | call("rm " + testfile, stdout=open(os.devnull, 'wb'), shell=True) 21 | 22 | call("ant clean", stdout=open(os.devnull, 'wb'), stderr=open(os.devnull, 'wb'), shell=True) 23 | return_code = call("ant debug", stdout=open(os.devnull, 'wb'), stderr=open(os.devnull, 'wb'), shell=True) 24 | if return_code == 0: 25 | print testfile 26 | 27 | call("git checkout -- " + testfile, stdout=open(os.devnull, 'wb'), shell=True) 28 | -------------------------------------------------------------------------------- /swiper.sh: -------------------------------------------------------------------------------- 1 | 2 | while true; do 3 | adb shell input swipe 500 500 1000 500 70 4 | done 5 | 6 | -------------------------------------------------------------------------------- /tappablemaker.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | 5 | inputdata = sys.argv 6 | 7 | if len(inputdata) < 2: 8 | inputdata = sys.stdin.readline().split() 9 | else: 10 | inputdata = inputdata[1:] 11 | 12 | if len(inputdata) < 1: 13 | print "Usage " , sys.argv[0] , " drawable (e.g icon_blah[.png])" 14 | 15 | drawable = inputdata[0] 16 | newfile = open(drawable + '.xml', 'w') 17 | newfile.write( '\n') 18 | newfile.write( '\n') 19 | newfile.write( '\n') 20 | newfile.write( '\n') 21 | newfile.write( '\n') 22 | newfile.close() 23 | 24 | -------------------------------------------------------------------------------- /tinder.sh: -------------------------------------------------------------------------------- 1 | 2 | while true; do 3 | adb shell input tap 879 2153 4 | done 5 | 6 | -------------------------------------------------------------------------------- /xmlparser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # parse an xml layout and produce android code to access the elements with ids 4 | 5 | import sys 6 | import os 7 | import math 8 | import string 9 | import shutil 10 | import xml.parsers.expat 11 | 12 | def testxml(infile): 13 | testxml.printvalue = False 14 | 15 | def start_element(name, attrs): 16 | print 'element:', name, "attrs:", attrs 17 | if name == 'string': 18 | testxml.printvalue = True 19 | 20 | def end_element(name): 21 | testxml.printvalue = False 22 | print 'end element:', name 23 | 24 | def string_data(text): 25 | if testxml.printvalue: 26 | print "DATA", text 27 | 28 | p = xml.parsers.expat.ParserCreate() 29 | p.StartElementHandler = start_element 30 | p.EndElementHandler = end_element 31 | p.CharacterDataHandler = string_data 32 | 33 | f = open(infile,"r"); 34 | try: 35 | p.ParseFile(f) 36 | except: 37 | raise 38 | return False 39 | 40 | 41 | return True 42 | 43 | if __name__ == "__main__": 44 | filelist = [] 45 | for root, subFolders, files in os.walk('res/values'): 46 | for ffile in files: 47 | testfile = os.path.join(root,ffile) 48 | extension = os.path.splitext(testfile)[1] 49 | if extension and extension == '.xml': 50 | testxml(testfile) 51 | 52 | --------------------------------------------------------------------------------