Skip to content

Commit 6509606

Browse files
committed
Add creator script
1 parent c21b87e commit 6509606

File tree

1 file changed

+160
-0
lines changed

1 file changed

+160
-0
lines changed

create-android-kotlin-app.py

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
#!/usr/bin/env python
2+
# coding:utf-8
3+
4+
import sys
5+
import os
6+
import io
7+
import zipfile
8+
import requests
9+
import re
10+
11+
12+
REPO_URL = 'https://github.com/nekocode/create-android-kotlin-app'
13+
ORIGINAL_PACKAGE_NAME = 'cn.nekocode.gank'
14+
15+
16+
class ProjectCreator:
17+
def __init__(self, archive_data: bytes, project_name: str, package_name: str):
18+
self.zip_file = zipfile.ZipFile(io.BytesIO(archive_data), 'r')
19+
self.project_name = project_name
20+
self.package_name = package_name
21+
self.root_dir_path = None
22+
23+
def create(self):
24+
entry_names = self.zip_file.namelist()
25+
self.root_dir_path = entry_names[0]
26+
27+
for entry_name in entry_names:
28+
if entry_name.endswith('/'):
29+
# skip directory
30+
continue
31+
32+
last_semgnet = entry_name.split('/')[-1]
33+
if last_semgnet.endswith('.kt'):
34+
self.kt_file(entry_name)
35+
36+
elif last_semgnet.endswith('.gradle'):
37+
self.gradle_file(entry_name)
38+
39+
elif last_semgnet == 'AndroidManifest.xml':
40+
self.manifest_file(entry_name)
41+
42+
elif last_semgnet.endswith('.py') or last_semgnet == 'README.md':
43+
continue
44+
45+
else:
46+
self.common_file(entry_name)
47+
48+
def kt_file(self, entry_name: str):
49+
file_name = self.replace_root_path(entry_name)
50+
file_name = file_name.replace(
51+
ORIGINAL_PACKAGE_NAME.replace('.', '/'),
52+
self.package_name.replace('.', '/'))
53+
content = TextProcesser(self.zip_file.read(entry_name).decode('utf-8'))\
54+
.replace_all_text(ORIGINAL_PACKAGE_NAME, self.package_name)\
55+
.remove_unwanted_comments().commit().encode()
56+
57+
self.write_to_file(file_name, content)
58+
59+
def gradle_file(self, entry_name: str):
60+
self.manifest_file(entry_name)
61+
62+
def manifest_file(self, entry_name: str):
63+
file_name = self.replace_root_path(entry_name)
64+
content = TextProcesser(self.zip_file.read(entry_name).decode('utf-8'))\
65+
.replace_all_text(ORIGINAL_PACKAGE_NAME, self.package_name)\
66+
.commit().encode()
67+
68+
self.write_to_file(file_name, content)
69+
70+
def common_file(self, entry_name: str):
71+
file_name = self.replace_root_path(entry_name)
72+
content = self.zip_file.read(entry_name)
73+
74+
self.write_to_file(file_name, content)
75+
76+
def replace_root_path(self, entry_name: str) -> str:
77+
return entry_name.replace(
78+
self.root_dir_path,
79+
self.project_name + '/')
80+
81+
@staticmethod
82+
def write_to_file(file_name: str, content: bytes):
83+
os.makedirs(os.path.dirname(file_name), exist_ok=True)
84+
open(file_name, 'wb').write(content)
85+
86+
87+
class TextProcesser:
88+
def __init__(self, txt: str):
89+
self.txt = txt
90+
self.commands = []
91+
92+
def replace_all_text(self, src: str, dst: str) -> 'TextProcesser':
93+
self.commands.append(('replace_all_text', src, dst))
94+
return self
95+
96+
def remove_unwanted_comments(self) -> 'TextProcesser':
97+
self.commands.append(('remove_unwanted_comments', None))
98+
return self
99+
100+
def commit(self) -> str:
101+
rlt = ''
102+
for line in self.txt.splitlines():
103+
skip_line = False
104+
105+
for cmd in self.commands:
106+
if cmd[0] == 'replace_all_text':
107+
line = line.replace(cmd[1], cmd[2])
108+
109+
elif cmd[0] == 'remove_unwanted_comments' and (
110+
line.startswith('/*') or
111+
line.startswith(' *') or
112+
line.startswith(' */')):
113+
skip_line = True
114+
115+
if not skip_line:
116+
if not (len(rlt) == 0 and len(line) == 0):
117+
rlt += line + '\n'
118+
119+
return rlt
120+
121+
122+
def fetch_lastest_archive() -> bytes:
123+
r = requests.get(REPO_URL + '/releases/latest', allow_redirects=False)
124+
lastest_tag = r.headers['location'].split('/')[-1]
125+
archive_url = REPO_URL + '/archive/%s.zip' % lastest_tag
126+
return requests.get(archive_url).content
127+
128+
129+
def main():
130+
if not (len(sys.argv) == 3):
131+
print("Usage: python3 create-android-kotlin-app.py PROJECT_NAME APP_PACKAGE_NAME")
132+
return
133+
134+
project_name = sys.argv[1]
135+
if os.path.exists(project_name):
136+
print('Can not create project. There is already a directory named "%s" in the current path.' % project_name)
137+
return
138+
try:
139+
os.mkdir(project_name)
140+
except:
141+
print('Can not create directory "%s".' % project_name)
142+
return
143+
144+
package_name = sys.argv[2]
145+
if not re.match('^[a-z][a-z0-9_]*(\.[a-z0-9_]+)+[0-9a-z_]$', package_name):
146+
print('Invaild java package name "%s".' % package_name)
147+
return
148+
149+
print('Creating a new android kotlin app in "%s".\n' % project_name)
150+
print('Fetching the lastest source code archive from %s\nThis might take a couple minutes.' % REPO_URL)
151+
archive_data = fetch_lastest_archive()
152+
153+
print('Unziping template files...\n')
154+
ProjectCreator(archive_data, project_name, package_name).create()
155+
print('Done. Happy hacking!')
156+
157+
158+
if __name__ == '__main__':
159+
main()
160+

0 commit comments

Comments
 (0)