File indexing completed on 2024-04-21 03:48:35

0001 #!/usr/bin/env python3
0002 # -*- coding: utf-8 -*-
0003 
0004 # SPDX-License-Identifier: LGPL-2.1-or-later
0005 #
0006 # SPDX-FileCopyrightText: 2015 Dennis Nienhüser <nienhueser@kde.org>
0007 #
0008 
0009 import os
0010 import sys
0011 import argparse
0012 from subprocess import call
0013 import tempfile
0014 import shutil
0015 
0016 class Filter(object):
0017     '''
0018     Decides which files to include in the APK
0019     '''
0020 
0021     def __init__(self, base):
0022         self.base = base.rstrip(os.sep)
0023 
0024     def shouldPackage(self, dir, files):
0025         # Change absolute directory name to one relative to the installation directory
0026         dir = dir.replace(self.base, '', 1)
0027 
0028         if dir == '':
0029             # Would not end up in the package, but is slightly faster
0030             return ['include']
0031         if dir == '/assets/data':
0032             # Currently not used
0033             return ['mwdbii', 'weather', 'naturalearth', 'flags']
0034         elif dir == '/assets/data/maps':
0035             # Other planets are not used
0036             return ['moon']
0037         elif dir == '/assets/data/maps/earth':
0038             # Unused map themes
0039             return ['srtm', 'srtm2', 'bluemarble', 'temp-july', 'precip-july', 'temp-dec', 'precip-dec', 'citylights', 'plain', 'schagen1689', 'political', 'behaim1492', 'openstreetmap', 'clouds', 'sentinel2']
0040         elif dir == '/assets/data/maps/earth/openstreetmap':
0041             # Large images from example KML tour
0042             return [item for item in files if item.endswith('.png') or item.endswith('.jpg')]
0043         elif dir == '/assets/data/placemarks':
0044             # Only include very basic placemarks
0045             return ['moonlandingsites.cache', 'moonterrain.cache', 'elevplacemarks.cache', 'otherplacemarks.cache', 'cityplacemarks.cache']
0046         elif dir == '/assets/data/svg':
0047             # Large images. worldmap.svg is used by the overviewmap, bring back if that plugin should be enabled
0048             return ['application-x-marble.svg', 'marsmap.svg', 'marble-logo.svg', 'lunarmap.svg', 'worldmap.svg']
0049         elif dir == '/assets/plugins':
0050             # Whitelisted plugins, all others are ignored
0051             search = ['LatLonPlugin', 'NominatimSearchPlugin', 'LocalDatabasePlugin', 'LocalOsmSearchPlugin']
0052             routing = ['CycleStreetsPlugin', 'OpenRouteServicePlugin', 'NominatimReverseGeocodingPlugin']
0053             fileFormats = ['CachePlugin', 'GpxPlugin', 'KmlPlugin', 'OsmPlugin']
0054             floatItems = ['License', 'MapScaleFloatItem']
0055             positioning = ['QtPositioningPositionProviderPlugin']
0056             render = ['StarsPlugin', 'GraticulePlugin']
0057             plugins = search + routing + fileFormats + floatItems + positioning + render
0058             whitelist = set(['lib{}.so'.format(plugin) for plugin in plugins])
0059             masked = [item for item in files if item not in whitelist]
0060             if len(files) - len(masked) != len(whitelist):
0061                 print ('Warning: At least one white-listed plugin is not installed')
0062             return masked
0063         elif dir.startswith('/libs/'):
0064             # other android app binary
0065             return ['libMarbleBehaim.so']
0066 
0067         return []
0068 
0069 qtDir = os.environ.get('Qt5_android')
0070 if qtDir is None:
0071     print ('Please setup the Qt5_android environment variable point to your Qt installation')
0072     sys.exit(1)
0073 
0074 parser = argparse.ArgumentParser(description='Create an Android application package (APK) for Marble Maps')
0075 parser.add_argument('--target', help='Target filename (or directory) for the .apk package')
0076 parser.add_argument('--keystore', help='Keystore file for signing the package')
0077 parser.add_argument('--storepass', help='Keystore password for signing the package')
0078 parser.add_argument('--release', action='store_true', help='Build a release package')
0079 parser.add_argument('--install', action='store_true', help='Install the package to a connected device (uninstalling it before, if needed)')
0080 parser.add_argument('--reinstall', action='store_true', help='Install the package to a connected device (keeping previous data intact, if any)')
0081 parser.add_argument('directory', help='The directory where the Android build is installed to')
0082 args = parser.parse_args()
0083 
0084 # Sanity check for given options
0085 if not args.install and not args.reinstall and args.target is None:
0086     print('Please pass one of --install, --reinstall or --target to either install the package directly or store it locally.')
0087     sys.exit(1)
0088 jsonFile = os.path.join(args.directory, 'share', 'deploy-marble-maps.json')
0089 if not os.path.isfile(jsonFile):
0090     print('Cannot find {}. Is {} really a Marble Android installation?'.format(jsonFile, args.directory))
0091     sys.exit(1)
0092 
0093 # Gather needed tools
0094 deployExe = os.path.join(qtDir, 'bin', 'androiddeployqt')
0095 antExe = os.environ.get('ANT', '/usr/bin/ant')
0096 
0097 with tempfile.TemporaryDirectory() as tempDir:
0098     os.rmdir(tempDir) #  shutil.copytree does not like directories to exist
0099     filter = Filter(args.directory)
0100     shutil.copytree(args.directory, tempDir, ignore=filter.shouldPackage)
0101     deployOptions = ['--verbose', '--output', tempDir, '--input', jsonFile, '--ant', antExe]
0102 
0103     # Debug vs. release type packages, signing options
0104     if args.release or args.keystore is not None:
0105         deployOptions.append('--release')
0106     if args.keystore is not None:
0107         deployOptions.append('--sign')
0108         deployOptions.append(args.keystore)
0109         deployOptions.append('Marble')
0110         deployOptions.append('--tsa')
0111         deployOptions.append('http://timestamp.digicert.com')
0112     if args.storepass is not None:
0113         deployOptions.append('--storepass')
0114         deployOptions.append(args.storepass)
0115 
0116     # Options for installing the created package to a connected device
0117     if args.install:
0118         deployOptions.append('--install')
0119     if args.reinstall:
0120         deployOptions.append('--reinstall')
0121     call([deployExe] + deployOptions)
0122 
0123     if args.target is not None:
0124         packageType = 'debug'
0125         if args.keystore is not None:
0126             packageType = 'release-signed'
0127         elif args.release:
0128             packageType = 'release-unsigned'
0129 
0130         targetFile = os.path.join(tempDir, 'bin', 'QtApp-{}.apk'.format(packageType))
0131         if os.path.isfile(targetFile):
0132             shutil.copy(targetFile, args.target)
0133             print ('Created APK at ' + args.target)
0134         else:
0135             sys.exit(1)