Skip to content

Commit

Permalink
fix: Release an apk for the mobile log viewer as well, fixes #19
Browse files Browse the repository at this point in the history
  • Loading branch information
Sobottasgithub committed Apr 25, 2024
1 parent ea5d240 commit 8eeb22c
Show file tree
Hide file tree
Showing 5 changed files with 252 additions and 3 deletions.
85 changes: 82 additions & 3 deletions .github/workflows/autorelease.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
jobs:
# see https://data-dive.com/multi-os-deployment-in-cloud-using-pyinstaller-and-github-actions/
# but here we build first and run our createrelease job afterwards, accessing the artifacts of our build job
build-android:
build-android-MMCA:
name: Build MobileCrashAnalyzer for android
runs-on: ubuntu-latest

Expand All @@ -21,7 +21,8 @@ jobs:
- name: Install dependencies
run: sudo apt -y install apksigner

- name: Prepare app for buildozer
# Build Mobile Crash Analyzer
- name: Prepare MMCA for buildozer
run: |
REFNAME="${{ github.ref_name }}"
echo "VERSION='${REFNAME}'" >src/shared/utils/version.py
Expand Down Expand Up @@ -85,6 +86,83 @@ jobs:
with:
name: MobileCrashAnalyzer
path: ${{ steps.signer.outputs.signed_apk }}

build-android-MMLV:
name: Build MobileLogViewer for android
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install dependencies
run: sudo apt -y install apksigner

# Build Monal Mobile Log Viewer
- name: Prepare MMLV for buildozer
run: |
REFNAME="${{ github.ref_name }}"
echo "VERSION='${REFNAME}'" >src/shared/utils/version.py
mkdir MobileLogViewer_Deploy
cp -av src/MobileLogViewer.py MobileLogViewer_Deploy/
cp -av src/MobileLogViewer MobileLogViewer_Deploy/
cp -av src/shared MobileLogViewer_Deploy/
cp -av src/MobileLogViewer/data/conf/intent_filters.xml MobileLogViewer_Deploy/
cp -av src/MobileLogViewer/data/conf/buildozer.spec MobileLogViewer_Deploy/
echo "" >>MobileLogViewer_Deploy/buildozer.spec
# buildozer does not like non-numeric version numbers
echo "version = ${REFNAME#v}" >>MobileLogViewer_Deploy/buildozer.spec
# dynamically build main.py proxy to load
echo "import os" >MobileLogViewer_Deploy/main.py
echo "os.environ['KIVY_LOG_MODE'] = 'MIXED'" >>MobileLogViewer_Deploy/main.py
echo "os.environ['KIVY_NO_FILELOG'] = '1'" >>MobileLogViewer_Deploy/main.py
echo "from importlib import util" >>MobileLogViewer_Deploy/main.py
echo "real_file = os.path.join(os.path.dirname(__file__), 'MobileLogViewer.pyc')" >>MobileLogViewer_Deploy/main.py
echo "print('Proxy loading real file: %s' % real_file)" >>MobileLogViewer_Deploy/main.py
echo "" >>MobileLogViewer_Deploy/main.py
echo "spec = util.spec_from_file_location('MobileLogViewer', real_file)" >>MobileLogViewer_Deploy/main.py
echo "print(f'{spec = }')" >>MobileLogViewer_Deploy/main.py
echo "mod = util.module_from_spec(spec)" >>MobileLogViewer_Deploy/main.py
echo "# we don't want to add our module to sys.modules to not interfere with the package path having the same name" >>MobileLogViewer_Deploy/main.py
echo "#(just like if our real_file was the real entry point)" >>MobileLogViewer_Deploy/main.py
echo "print(f'{mod = }')" >>MobileLogViewer_Deploy/main.py
echo "out = spec.loader.exec_module(mod)" >>MobileLogViewer_Deploy/main.py
echo "print(f'{out = }')" >>MobileLogViewer_Deploy/main.py
echo "print('Proxied file returned, terminating...')" >>MobileLogViewer_Deploy/main.py
echo ""
echo "buildozer.spec:"
cat MobileLogViewer_Deploy/buildozer.spec
echo ""
echo "main.py:"
cat MobileLogViewer_Deploy/main.py
- name: Build with Buildozer
uses: Sobottasgithub/buildozer-action@v1.1.3
id: buildozer
with:
workdir: MobileLogViewer_Deploy
buildozer_version: stable

- name: Sign Apk
id: signer
run: |
apkinfile="${{ steps.buildozer.outputs.filename }}"
apkoutfile="$HOME/signed-$(basename "${apkinfile}")"
echo -n "${{ secrets.APK_SIGNING_KEY }}" >"$HOME/keystore.key"
openssl enc -d -chacha20 -pbkdf2 -in src/MobileLogViewer/data/conf/release.keystore.enc -out "$HOME/release.keystore" -kfile "$HOME/keystore.key"
cat "$HOME/keystore.key" | apksigner sign -ks "$HOME/release.keystore" -in "${apkinfile}" -out "${apkoutfile}"
echo "unsigned_apk=${apkinfile}" >> "$GITHUB_OUTPUT"
echo "signed_apk=${apkoutfile}" >> "$GITHUB_OUTPUT"
- name: Upload signed apk
uses: actions/upload-artifact@v4
with:
name: MobileLogViewer
path: ${{ steps.signer.outputs.signed_apk }}

build-desktop:
name: Build ${{ matrix.OUT_FILE_NAME }} for ${{ matrix.TARGET }}
Expand Down Expand Up @@ -184,7 +262,7 @@ jobs:
createrelease:
name: Create Release
runs-on: [ubuntu-latest]
needs: [build-desktop, build-android]
needs: [build-desktop, build-android-MMCA, build-android-MMLV]
steps:
- name: Load build artifacts
uses: actions/download-artifact@v4
Expand All @@ -209,6 +287,7 @@ jobs:
LogViewer*/*
CrashAnalyzer*/*
MobileCrashAnalyzer*/*
MobileLogViewer*/*
fail_on_unmatched_files: true
token: ${{ secrets.GITHUB_TOKEN }}
draft: false
30 changes: 30 additions & 0 deletions src/MobileLogViewer/data/conf/buildozer.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[buildozer]
log_level = 2

[app]
title = Mobile Log Viewer
package.name = MobileLogViewer
package.domain = im.monal

source.dir = .
source.include_exts = py,png,jpg,kv,atlas,json
source.include_patterns = shared/*,MobileLogViewer/*
icon.filename = %(source.dir)s/MobileLogViewer/data/art/icon.png
presplash.filename = %(source.dir)s/MobileLogViewer/data/art/icon.png

requirements = python3,kivy,platformdirs,logging,jnius

orientation = portrait
fullscreen = 0

# android specific
android.minapi = 29
android.permissions = WRITE_EXTERNAL_STORAGE,READ_EXTERNAL_STORAGE
android.arch = armeabi-v7a
android.manifest.intent_filters = intent_filters.xml

# iOS specific
ios.kivy_ios_url = https://github.com/kivy/kivy-ios
ios.kivy_ios_branch = master
ios.ios_deploy_url = https://github.com/phonegap/ios-deploy
ios.ios_deploy_branch = 1.7.0
61 changes: 61 additions & 0 deletions src/MobileLogViewer/data/conf/intent_filters.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<!-- see https://stackoverflow.com/a/31028507/3528174 -->
<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="file" />
<data android:scheme="content" />
<data android:host="*" />
<data android:mimeType="*/*" />
<data android:pathPattern=".*\\.rawlog\\.gz" />
<data android:pathPattern=".*\\..*\\.rawlog\\.gz" />
<data android:pathPattern=".*\\..*\\..*\\.rawlog\\.gz" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.rawlog\\.gz" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.rawlog\\.gz" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.rawlog\\.gz" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.rawlog\\.gz" />
<data android:pathPattern=".*\\.rawlog" />
<data android:pathPattern=".*\\..*\\.rawlog" />
<data android:pathPattern=".*\\..*\\..*\\.rawlog" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.rawlog" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.rawlog" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.rawlog" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.rawlog" />
</intent-filter>

<!-- handle intents with null mimeType -->
<!-- see https://stackoverflow.com/a/31028507/3528174 -->
<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="file" />
<data android:scheme="content" />
<data android:host="*" />
<data android:pathPattern=".*\\.rawlog\\.gz" />
<data android:pathPattern=".*\\..*\\.rawlog\\.gz" />
<data android:pathPattern=".*\\..*\\..*\\.rawlog\\.gz" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.rawlog\\.gz" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.rawlog\\.gz" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.rawlog\\.gz" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.rawlog\\.gz" />
<data android:pathPattern=".*\\.rawlog" />
<data android:pathPattern=".*\\..*\\.rawlog" />
<data android:pathPattern=".*\\..*\\..*\\.rawlog" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.rawlog" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.rawlog" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.rawlog" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.rawlog" />
</intent-filter>

<!-- some file providers (mail etc.) don't provide meaningful names we could check against, just use the gzip mimetype instead -->
<!-- yes, that's unfortunately more broad than we need, but there seems to be no known fix -->
<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="file"/>
<data android:scheme="content"/>
<data android:mimeType="application/gzip"/>
<data android:mimeType="application/x-gzip"/>
</intent-filter>
Binary file not shown.
79 changes: 79 additions & 0 deletions src/MobileLogViewer/ui/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,27 @@

from kivy.uix.filechooser import FileChooserListView
from kivy.uix.scrollview import ScrollView
from kivy.clock import mainthread
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from kivy.factory import Factory
from kivy.utils import platform

import functools
import os
from collections import defaultdict
from jnius import autoclass, cast

# Just import if the os is Android to avoid Android peculiarities
if platform == "android":
from android import activity, mActivity, permissions
J_FileOutputStream = autoclass("java.io.FileOutputStream")
J_FileUtils = autoclass("android.os.FileUtils")
#J_Intent = autoclass("android.content.Intent")
#J_PythonActivity = autoclass('org.kivy.android.PythonActivity')
permissions.request_permissions([permissions.Permission.READ_EXTERNAL_STORAGE, permissions.Permission.WRITE_EXTERNAL_STORAGE])

from shared.utils.constants import LOGLEVELS
from shared.storage import Rawlog
Expand Down Expand Up @@ -72,10 +85,76 @@ def build(self):
self.layout.add_widget(gridLayout_menueBar)
self.layout.add_widget(self.uiScrollWidget_logs)

# Just import if the os is Android to avoid Android peculiarities
if platform == "android":
activity.bind(on_new_intent=self.on_new_intent)

return self.layout

def quit(self, *args):
self.stop()

def on_start(self, *args):
# Just import if the os is Android to avoid Android peculiarities
if platform == "android":
context = cast('android.content.Context', mActivity.getApplicationContext())
logger.info(f"Startup application context: {context}")
intent = mActivity.getIntent()
logger.info(f"Got startup intent: {intent}")
if intent:
self.on_new_intent(intent)

#see https://github.com/termux/termux-app/blob/74b23cb2096652601050d0f4951f9fb92577743c/app/src/main/java/com/termux/filepicker/TermuxFileReceiverActivity.java#L70
@mainthread
def on_new_intent(self, intent):
logger.info("Got new intent with action: %s" % str(intent.getAction()))
logger.debug("Raw intent: %s" % str(intent))
if intent.getAction() == "android.intent.action.VIEW":
logger.info("Intent scheme: %s" % intent.getScheme())
logger.info("Intent type: %s" % intent.getType())
logger.info("Intent data: %s" % intent.getData())
logger.info("Intent path: %s" % intent.getData().getPath())

uri = intent.getData()
context = mActivity.getApplicationContext()
contentResolver = context.getContentResolver()

cacheFile = Paths.get_cache_filepath("intent.file")
if os.path.exists(cacheFile):
os.remove(cacheFile)
logger.debug(f"Writing file at '{uri.getPath()}' to '{cacheFile}'...")
bytecount = J_FileUtils.copy(contentResolver.openInputStream(uri), J_FileOutputStream(cacheFile))
logger.debug(f"{bytecount} bytes copied...")
self.openFile(cacheFile)
os.remove(cacheFile)

"""
logger.info("Intent uri: %s" % intent.getParcelableExtra(J_Intent.EXTRA_STREAM))
logger.info("Intent text: %s" % intent.getStringExtra(J_Intent.EXTRA_TEXT))
uri = intent.getParcelableExtra(J_Intent.EXTRA_STREAM)
context = mActivity.getApplicationContext()
contentResolver = context.getContentResolver()
if uri != None and type(uri) != str:
logger.info("Real android.net.Uri found...")
uri = cast("android.net.Uri", uri)
if uri.getScheme().lower() != 'content':
logger.error("Uri scheme not supported: '%s'" % uri.getScheme())
return
cacheFile = Paths.get_cache_filepath("intent.file")
if os.path.exists(cacheFile):
os.remove(cacheFile)
J_FileUtils.copy(contentResolver.openInputStream(uri), J_FileOutputStream(cacheFile))
self.openFile(cacheFile)
os.remove(cacheFile)
else:
logger.info("Str based uri found...")
cacheFile = Paths.get_cache_filepath("intent.file")
if os.path.exists(cacheFile):
os.remove(cacheFile)
J_FileUtils.copy(contentResolver.openInputStream(intent.getData()), J_FileOutputStream(cacheFile))
self.openFile(cacheFile)
os.remove(cacheFile)
"""

def selectFile(self, *args):
# Create popup window to select file
Expand Down

0 comments on commit 8eeb22c

Please sign in to comment.