Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -154,15 +154,21 @@ dist/version.json: needs-version
mkdir -p dist
echo '{"versionCode": "$(VERSION_CODE)", "versionName": "$(VERSION_NAME)", "ekVersion": "$(EK_VERSION)"}' > $@

DIST_DEPS = \
p4a_android_distro \
BUILD_DEPS = \
src/kolibri \
src/apps-bundle \
src/collections \
assets/loadingScreen \
needs-version \
dist/version.json

DIST_DEPS = \
p4a_android_distro \
$(BUILD_DEPS)

check: $(BUILD_DEPS)
python3 -m unittest discover kolibri_android.tests -t src

.PHONY: kolibri.apk
# Build the signed version of the apk
kolibri.apk: $(DIST_DEPS)
Expand Down
21 changes: 21 additions & 0 deletions src/kolibri_android/android_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
Environment = autoclass("android.os.Environment")
File = autoclass("java.io.File")
FileProvider = autoclass("androidx.core.content.FileProvider")
FullScreen = autoclass("org.learningequality.FullScreen")
Intent = autoclass("android.content.Intent")
NotificationBuilder = autoclass("android.app.Notification$Builder")
NotificationManager = autoclass("android.app.NotificationManager")
Expand Down Expand Up @@ -816,6 +817,26 @@ def apply_android_workarounds():
_android11_ext_storage_workarounds()


@Runnable
def configure_webview(load_fn, load_with_usb_fn, loading_ready_fn):
FullScreen.configureWebview(
PythonActivity.mActivity,
Runnable(load_fn),
Runnable(load_with_usb_fn),
Runnable(loading_ready_fn),
)


@Runnable
def load_url_in_webview(url):
PythonActivity.mWebView.loadUrl(url)


@Runnable
def evaluate_javascript(js_code):
PythonActivity.mWebView.evaluateJavascript(js_code, None)


class StartupState(Enum):
FIRST_TIME = auto()
USB_USER = auto()
Expand Down
36 changes: 6 additions & 30 deletions src/kolibri_android/main_activity/activity.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import logging
import time

from jnius import autoclass

from ..android_utils import choose_endless_key_uris
from ..android_utils import configure_webview
from ..android_utils import evaluate_javascript
from ..android_utils import get_endless_key_uris
from ..android_utils import has_any_external_storage_device
from ..android_utils import load_url_in_webview
from ..android_utils import PermissionsCancelledError
from ..android_utils import PermissionsWrongFolderError
from ..android_utils import provision_endless_key_database
Expand All @@ -15,30 +16,6 @@
from ..android_utils import stat_file
from ..application import BaseActivity
from ..kolibri_utils import init_kolibri
from ..runnable import Runnable


PythonActivity = autoclass("org.kivy.android.PythonActivity")
FullScreen = autoclass("org.learningequality.FullScreen")


@Runnable
def configure_webview(
activity, load_runnable, load_with_usb_runnable, loading_ready_runnable
):
FullScreen.configureWebview(
activity, load_runnable, load_with_usb_runnable, loading_ready_runnable
)


@Runnable
def load_url_in_webview(url):
PythonActivity.mWebView.loadUrl(url)


@Runnable
def evaluate_javascript(js_code):
PythonActivity.mWebView.evaluateJavascript(js_code, None)


def is_endless_key_reachable():
Expand Down Expand Up @@ -88,10 +65,9 @@ def __init__(self):
super().__init__()

configure_webview(
PythonActivity.mActivity,
Runnable(self._on_load),
Runnable(self._on_load_with_usb),
Runnable(self._on_loading_ready),
self._on_load,
self._on_load_with_usb,
self._on_loading_ready,
)

def on_activity_stopped(self, activity):
Expand Down
9 changes: 9 additions & 0 deletions src/kolibri_android/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import importlib.util
import sys
from pathlib import Path

_kolibri_dist_spec = importlib.util.find_spec("kolibri.dist")

if _kolibri_dist_spec and _kolibri_dist_spec.has_location:
kolibri_dist_path = Path(_kolibri_dist_spec.origin).parent
sys.path.append(kolibri_dist_path.as_posix())
16 changes: 16 additions & 0 deletions src/kolibri_android/tests/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import unittest
from pathlib import Path


def main():
this_dir = Path(__file__).parent

selftest_loader = unittest.TestLoader()
selftests = selftest_loader.discover(this_dir)

selftest_runner = unittest.runner.TextTestRunner()
selftest_runner.run(selftests)


if __name__ == "__main__":
main()
68 changes: 68 additions & 0 deletions src/kolibri_android/tests/test_main_activity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import os
import sys
import tempfile
from unittest import TestCase
from unittest.mock import DEFAULT
from unittest.mock import MagicMock
from unittest.mock import patch

# TODO: Only mock these modules when Android is unavailable
android_mock = MagicMock()
jnius_mock = MagicMock()
runnable_mock = MagicMock()


@patch.dict(
sys.modules,
{
"android.activity": android_mock,
"jnius": jnius_mock,
"kolibri_android.runnable": runnable_mock,
},
)
class MainActivityTestCase(TestCase):
def setUp(self):
self.kolibri_home_tempdir = tempfile.TemporaryDirectory()
os.environ["KOLIBRI_HOME"] = self.kolibri_home_tempdir.name

def tearDown(self):
self.kolibri_home_tempdir.cleanup()
self.kolibri_home_tempdir = None

@patch.multiple(
"kolibri_android.android_utils",
configure_webview=DEFAULT,
get_signature_key_issuing_organization=DEFAULT,
get_timezone_name=DEFAULT,
get_version_name=DEFAULT,
get_endless_key_uris=DEFAULT,
get_home_folder=DEFAULT,
)
@patch.multiple(
"kolibri_android.main_activity.kolibri_bus.KolibriAppProcessBus",
run=DEFAULT,
)
def test_activity_run(self, **mocks):
mocks["get_signature_key_issuing_organization"].return_value = "test"
mocks["get_timezone_name"].return_value = "UTC"
mocks["get_version_name"].return_value = "Unknown"
mocks["get_endless_key_uris"].return_value = None
mocks["get_home_folder"].return_value = self.kolibri_home_tempdir.name

from kolibri_android.main_activity.activity import MainActivity

main_activity = MainActivity()

mocks["configure_webview"].assert_called_once()

(on_load_fn, on_load_with_usb_fn, on_loading_ready_fn) = mocks[
"configure_webview"
].call_args.args

on_load_fn()

# FIXME: We can't use main_activity.run() because it loops forever.

main_activity.start_kolibri()

mocks["run"].assert_called_once()