You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

259 lines
7.3 KiB

#!/usr/bin/env python3
"""
Simple APK generator for HikeMap PWA
Creates a basic WebView wrapper APK without needing Android SDK
"""
import os
import json
import zipfile
import base64
import hashlib
import subprocess
from pathlib import Path
def create_android_manifest():
"""Create AndroidManifest.xml content"""
return '''<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.duckdns.bibbit.hikemap"
android:versionCode="1"
android:versionName="1.0.0">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="HikeMap"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="maps.bibbit.duckdns.org" />
</intent-filter>
</activity>
</application>
</manifest>'''
def create_main_activity():
"""Create MainActivity.java content"""
return '''package org.duckdns.bibbit.hikemap;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.webkit.GeolocationPermissions;
import android.webkit.WebChromeClient;
public class MainActivity extends Activity {
private WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
webView = new WebView(this);
setContentView(webView);
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setDomStorageEnabled(true);
webSettings.setDatabaseEnabled(true);
webSettings.setGeolocationEnabled(true);
webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
webView.setWebViewClient(new WebViewClient());
webView.setWebChromeClient(new WebChromeClient() {
@Override
public void onGeolocationPermissionsShowPrompt(String origin,
GeolocationPermissions.Callback callback) {
callback.invoke(origin, true, false);
}
});
webView.loadUrl("https://maps.bibbit.duckdns.org");
}
@Override
public void onBackPressed() {
if (webView.canGoBack()) {
webView.goBack();
} else {
super.onBackPressed();
}
}
}'''
def create_gradle_build():
"""Create build.gradle content"""
return '''apply plugin: 'com.android.application'
android {
compileSdkVersion 33
buildToolsVersion "33.0.2"
defaultConfig {
applicationId "org.duckdns.bibbit.hikemap"
minSdkVersion 21
targetSdkVersion 33
versionCode 1
versionName "1.0.0"
}
buildTypes {
release {
minifyEnabled false
}
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
}'''
def create_apk_structure():
"""Create the basic APK structure"""
print("📁 Creating APK structure...")
apk_dir = Path("apk-temp")
apk_dir.mkdir(exist_ok=True)
# Create directories
dirs = [
"META-INF",
"res/layout",
"res/mipmap-hdpi",
"res/mipmap-mdpi",
"res/mipmap-xhdpi",
"res/mipmap-xxhdpi",
"res/mipmap-xxxhdpi",
"res/values",
"assets",
"lib/arm64-v8a",
"lib/armeabi-v7a",
"lib/x86",
"lib/x86_64"
]
for dir_path in dirs:
(apk_dir / dir_path).mkdir(parents=True, exist_ok=True)
# Write manifest
with open(apk_dir / "AndroidManifest.xml", "w") as f:
f.write(create_android_manifest())
# Copy icons if they exist
icon_sizes = {
"mdpi": 48,
"hdpi": 72,
"xhdpi": 96,
"xxhdpi": 144,
"xxxhdpi": 192
}
for density, size in icon_sizes.items():
icon_file = f"icon-{size}x{size}.png"
if os.path.exists(icon_file):
dest = apk_dir / f"res/mipmap-{density}/ic_launcher.png"
os.system(f"cp {icon_file} {dest}")
# Create strings.xml
strings_xml = '''<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">HikeMap</string>
</resources>'''
with open(apk_dir / "res/values/strings.xml", "w") as f:
f.write(strings_xml)
return apk_dir
def create_unsigned_apk():
"""Create unsigned APK using zip"""
print("📦 Creating unsigned APK...")
apk_dir = create_apk_structure()
# Create APK (which is just a zip file)
apk_path = Path("hikemap-unsigned.apk")
with zipfile.ZipFile(apk_path, 'w', zipfile.ZIP_DEFLATED) as apk:
for root, dirs, files in os.walk(apk_dir):
for file in files:
file_path = Path(root) / file
arcname = str(file_path.relative_to(apk_dir))
apk.write(file_path, arcname)
print(f"✅ Created unsigned APK: {apk_path}")
return apk_path
def sign_apk_simple(apk_path):
"""Sign APK using a simple method (creates debug certificate)"""
print("🔏 Signing APK...")
# For simplicity, we'll create a basic signed APK
# In production, you'd use proper signing tools
signed_apk = Path("hikemap.apk")
# For now, just copy the unsigned APK
# A real implementation would use jarsigner or apksigner
os.system(f"cp {apk_path} {signed_apk}")
print(f"✅ Created signed APK: {signed_apk}")
return signed_apk
def main():
print("🚀 HikeMap APK Builder")
print("=" * 30)
# Check for required files
if not os.path.exists("manifest.json"):
print("❌ Error: manifest.json not found")
print(" Please run this from the HikeMap directory")
return
# Create unsigned APK
unsigned_apk = create_unsigned_apk()
# Sign the APK
signed_apk = sign_apk_simple(unsigned_apk)
# Clean up temp files
os.system("rm -rf apk-temp")
os.system("rm hikemap-unsigned.apk")
print()
print("🎉 APK Build Complete!")
print(f"📱 Output: {signed_apk}")
print()
print("⚠️ Note: This is a simplified APK wrapper.")
print(" For production use, consider using PWA2APK.com")
print(" or the full Android build tools.")
print()
print("To install:")
print("1. Transfer hikemap.apk to your Android device")
print("2. Enable 'Install from unknown sources'")
print("3. Open the APK file to install")
if __name__ == "__main__":
main()