commit 872c00b53c64a37add7cad1f61307aecf176e576 Author: Régis Behmo Date: Tue Apr 27 12:44:44 2021 +0200 :sunrise: diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f6a874f --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.*.swp +!.gitignore +TODO +__pycache__ +*.egg-info/ +/build/ +/dist/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..246f432 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,9 @@ +variables: + TUTOR_PLUGIN: android + TUTOR_IMAGES: android + TUTOR_PYPI_PACKAGE: tutor-android + OPENEDX_RELEASE: lilac + +include: + - project: 'community/tutor-ci' + file: 'plugin-gitlab-ci.yml' diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..a5ea76c --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,2 @@ +recursive-include tutorandroid/patches * +recursive-include tutorandroid/templates * diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..302c2f1 --- /dev/null +++ b/README.rst @@ -0,0 +1,22 @@ +android plugin for `Tutor `__ +=================================================================================== + +Installation +------------ + +:: + + pip install git+https://github.com/overhangio/tutor-android + +Usage +----- + +:: + + tutor plugins enable android + + +License +------- + +This software is licensed under the terms of the AGPLv3. \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..e7c8c2c --- /dev/null +++ b/setup.py @@ -0,0 +1,61 @@ +import io +import os +from setuptools import setup, find_packages + +HERE = os.path.abspath(os.path.dirname(__file__)) + + +def load_readme(): + with io.open(os.path.join(HERE, "README.rst"), "rt", encoding="utf8") as f: + return f.read() + + +def load_about(): + about = {} + with io.open( + os.path.join(HERE, "tutorandroid", "__about__.py"), + "rt", + encoding="utf-8", + ) as f: + exec(f.read(), about) # pylint: disable=exec-used + return about + + +ABOUT = load_about() + + +setup( + name="tutor-android", + version=ABOUT["__version__"], + url="https://github.com/overhangio/tutor-android", + project_urls={ + "Code": "https://github.com/overhangio/tutor-android", + "Issue tracker": "https://github.com/overhangio/tutor-android/issues", + }, + license="AGPLv3", + author="Overhang.IO", + description="android plugin for Tutor", + long_description=load_readme(), + packages=find_packages(exclude=["tests*"]), + include_package_data=True, + python_requires=">=3.5", + install_requires=["tutor-openedx>=12.0.0,<13.0.0"], + entry_points={ + "tutor.plugin.v0": [ + "android = tutorandroid.plugin" + ] + }, + classifiers=[ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: GNU Affero General Public License v3", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + ], +) diff --git a/tutorandroid/__about__.py b/tutorandroid/__about__.py new file mode 100644 index 0000000..0e21ac9 --- /dev/null +++ b/tutorandroid/__about__.py @@ -0,0 +1 @@ +__version__ = "12.0.0" diff --git a/tutorandroid/__init__.py b/tutorandroid/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tutorandroid/cli.py b/tutorandroid/cli.py new file mode 100644 index 0000000..4c6d95e --- /dev/null +++ b/tutorandroid/cli.py @@ -0,0 +1,52 @@ +import click + +from tutor.commands.compose import ComposeJobRunner +from tutor.commands.context import Context +from tutor.commands.local import docker_compose as local_docker_compose +from tutor import config as tutor_config +from tutor import env as tutor_env +from tutor import fmt +from tutor.types import Config + + +@click.group(help="Build an Android app for your Open edX platform") +def android() -> None: + pass + + +@click.command(help="Build the application") +@click.argument("mode", type=click.Choice(["debug", "release"])) +@click.pass_obj +def build(context: Context, mode: str) -> None: + config = tutor_config.load(context.root) + docker_run(context.root, build_command(config, mode)) + fmt.echo_info( + "The {} APK file is available in {}".format( + mode, tutor_env.data_path(context.root, "android") + ) + ) + + +def build_command(config: Config, target: str) -> str: + gradle_target = { + "debug": "assembleProdDebuggable", + "release": "assembleProdRelease", + }[target] + apk_folder = {"debug": "debuggable", "release": "release"}[target] + + command = """ +sed -i "s/APPLICATION_ID = .*/APPLICATION_ID = \\"{{ LMS_HOST|reverse_host|replace("-", "_") }}\\"/g" constants.gradle +./gradlew {gradle_target} +cp OpenEdXMobile/build/outputs/apk/prod/{apk_folder}/*.apk /openedx/data/""" + command = tutor_env.render_str(config, command) + command = command.format(gradle_target=gradle_target, apk_folder=apk_folder) + return command + + +def docker_run(root: str, command: str) -> None: + config = tutor_config.load(root) + runner = ComposeJobRunner(root, config, local_docker_compose) + runner.run_job("android", command) + + +android.add_command(build) diff --git a/tutorandroid/patches/.gitignore b/tutorandroid/patches/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/tutorandroid/patches/local-docker-compose-jobs-services b/tutorandroid/patches/local-docker-compose-jobs-services new file mode 100644 index 0000000..cc6669a --- /dev/null +++ b/tutorandroid/patches/local-docker-compose-jobs-services @@ -0,0 +1,5 @@ +android-job: + image: {{ ANDROID_DOCKER_IMAGE }} + volumes: + - "../android/:/openedx/config/" + - "../../data/android/:/openedx/data/" diff --git a/tutorandroid/plugin.py b/tutorandroid/plugin.py new file mode 100644 index 0000000..5308134 --- /dev/null +++ b/tutorandroid/plugin.py @@ -0,0 +1,37 @@ +from glob import glob +import os +import pkg_resources + +from .__about__ import __version__ +from .cli import android as android_command + + +templates = pkg_resources.resource_filename("tutorandroid", "templates") + + +config = { + "add": {"OAUTH2_SECRET": "{{ 24|random_string }}"}, + "defaults": { + "VERSION": __version__, + "DOCKER_IMAGE": "{{ DOCKER_REGISTRY }}overhangio/openedx-android:{{ ANDROID_VERSION }}", + "RELEASE_STORE_PASSWORD": "android store password", + "RELEASE_KEY_PASSWORD": "android release key password", + "RELEASE_KEY_ALIAS": "android release key alias", + }, +} + +hooks = {"build-image": {"android": "{{ ANDROID_DOCKER_IMAGE }}"}, "init": ["lms"]} + + +command = android_command + + +def patches(): + all_patches = {} + patches_dir = pkg_resources.resource_filename("tutorandroid", "patches") + for path in glob(os.path.join(patches_dir, "*")): + with open(path) as patch_file: + name = os.path.basename(path) + content = patch_file.read() + all_patches[name] = content + return all_patches diff --git a/tutorandroid/templates/android/apps/.gitignore b/tutorandroid/templates/android/apps/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/tutorandroid/templates/android/apps/edx.properties b/tutorandroid/templates/android/apps/edx.properties new file mode 100644 index 0000000..6913998 --- /dev/null +++ b/tutorandroid/templates/android/apps/edx.properties @@ -0,0 +1,3 @@ +edx.android { + configFiles = ['tutor.yaml'] +} diff --git a/tutorandroid/templates/android/apps/gradle.properties b/tutorandroid/templates/android/apps/gradle.properties new file mode 100644 index 0000000..486a13a --- /dev/null +++ b/tutorandroid/templates/android/apps/gradle.properties @@ -0,0 +1,4 @@ +RELEASE_STORE_FILE=/openedx/config/app.keystore +RELEASE_STORE_PASSWORD={{ ANDROID_RELEASE_STORE_PASSWORD }} +RELEASE_KEY_PASSWORD={{ ANDROID_RELEASE_KEY_PASSWORD }} +RELEASE_KEY_ALIAS={{ ANDROID_RELEASE_KEY_ALIAS }} diff --git a/tutorandroid/templates/android/apps/tutor.yaml b/tutorandroid/templates/android/apps/tutor.yaml new file mode 100644 index 0000000..d5ecc8d --- /dev/null +++ b/tutorandroid/templates/android/apps/tutor.yaml @@ -0,0 +1,18 @@ +# See docs: https://openedx.atlassian.net/wiki/spaces/LEARNER/pages/48792067/App+Configuration+Flags +API_HOST_URL: "{{ "https" if ENABLE_HTTPS else "http" }}://{{ LMS_HOST }}" +ENVIRONMENT_DISPLAY_NAME: "tutor" +PLATFORM_NAME: "{{ PLATFORM_NAME }}" +PLATFORM_DESTINATION_NAME: "{{ LMS_HOST }}" +FEEDBACK_EMAIL_ADDRESS: "{{ CONTACT_EMAIL }}" +OAUTH_CLIENT_ID: "android" + +COURSE_VIDEOS_ENABLED: true +CERTIFICATES_ENABLED: true +DISCUSSIONS_ENABLED: true +DISCOVERY: + COURSE: + TYPE: native +DOWNLOAD_TO_SD_CARD_ENABLED: true +NEW_LOGISTRATION_ENABLED: true +USER_PROFILES_ENABLED : true +VIDEO_TRANSCRIPT_ENABLED: true \ No newline at end of file diff --git a/tutorandroid/templates/android/build/.gitignore b/tutorandroid/templates/android/build/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/tutorandroid/templates/android/build/android/Dockerfile b/tutorandroid/templates/android/build/android/Dockerfile new file mode 100644 index 0000000..b586322 --- /dev/null +++ b/tutorandroid/templates/android/build/android/Dockerfile @@ -0,0 +1,40 @@ +FROM docker.io/ubuntu:20.04 +MAINTAINER Overhang.io + +ENV DEBIAN_FRONTEND=noninteractive +RUN apt update && \ + apt upgrade -y && \ + apt install -y wget unzip git openjdk-8-jre openjdk-8-jdk + +RUN mkdir /openedx + +# Install Android SDK +# Inspired from https://github.com/LiveXP/docker-android-sdk/blob/master/Dockerfile +ENV ANDROID_SDK_VERSION 6200805 +ENV ANDROID_SDK_PATH /openedx/android-sdk +ENV ANDROID_HOME /openedx/android-sdk +RUN mkdir ${ANDROID_HOME} +WORKDIR /openedx/android-sdk +RUN wget https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_SDK_VERSION}_latest.zip && \ + unzip commandlinetools-linux-${ANDROID_SDK_VERSION}_latest.zip && \ + rm commandlinetools-linux-${ANDROID_SDK_VERSION}_latest.zip + +# Accept licenses +# https://developer.android.com/studio/command-line/sdkmanager +ARG ANDROID_API_LEVEL=28 +RUN yes | /openedx/android-sdk/tools/bin/sdkmanager --sdk_root=${ANDROID_HOME} --install "platforms;android-$ANDROID_API_LEVEL" 1> /dev/null + +# Install android app repo +ARG ANDROID_APP_REPOSITORY=https://github.com/edx/edx-app-android +ARG ANDROID_APP_VERSION=release/2.23.2 +RUN git clone $ANDROID_APP_REPOSITORY --branch $ANDROID_APP_VERSION /openedx/edx-app-android +WORKDIR /openedx/edx-app-android + +# Install gradle and all dependencies +RUN ./gradlew -v +RUN ./gradlew tasks + +# User-customized config +COPY ./edx.properties ./OpenEdXMobile/edx.properties +RUN mkdir /openedx/config +RUN ln -s /openedx/config/gradle.properties ./OpenEdXMobile/gradle.properties diff --git a/tutorandroid/templates/android/build/android/edx.properties b/tutorandroid/templates/android/build/android/edx.properties new file mode 100644 index 0000000..f49574b --- /dev/null +++ b/tutorandroid/templates/android/build/android/edx.properties @@ -0,0 +1 @@ +edx.dir = '/openedx/config' diff --git a/tutorandroid/templates/android/hooks/.gitignore b/tutorandroid/templates/android/hooks/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/tutorandroid/templates/android/hooks/lms/init b/tutorandroid/templates/android/hooks/lms/init new file mode 100644 index 0000000..85c8e25 --- /dev/null +++ b/tutorandroid/templates/android/hooks/lms/init @@ -0,0 +1,12 @@ +# Delete obsolete credentials for Android application +./manage.py lms shell -c 'from oauth2_provider.models import get_application_model +get_application_model().objects.filter(name="android").exclude(user__username="login_service_user").delete()' +# Create oauth credentials for Android application +./manage.py lms create_dot_application \ + --client-id android \ + --client-secret {{ ANDROID_OAUTH2_SECRET }} \ + --grant-type password \ + --public \ + --update \ + android \ + login_service_user