Skip to content

Commit

Permalink
Merge pull request #305 from openedx/develop
Browse files Browse the repository at this point in the history
Develop to main v1.5
  • Loading branch information
volodymyr-chekyrta authored Apr 22, 2024
2 parents 8c57627 + 7dd29ef commit bccf5ed
Show file tree
Hide file tree
Showing 284 changed files with 10,227 additions and 4,110 deletions.
11 changes: 11 additions & 0 deletions Documentation/APIs_Compatibility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# APIs Compatibility

This documentation offers guidance on a workaround for utilizing mobile APIs with earlier versions of Open edX releases.

In December 2023, the [FC-0031 project](https://github.com/openedx/edx-platform/issues/33304) introduced new APIs, and the Open edX mobile apps were transitioned to utilize them.

If your platform version is older than December 2023, follow the instructions below:

1. Setup the [mobile-api-extensions](https://github.com/raccoongang/mobile-api-extensions) plugin to your platform.
The Plugin contains extended Open edX APIs for mobile applications.
2. Roll back the modifications that brought in the new APIs [42f518a](https://github.com/openedx/openedx-app-android/commit/42f518a264d4300c8c2ca349072addd7d16ff91a).
114 changes: 114 additions & 0 deletions Documentation/ConfigurationManagement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Configuration Management

This documentation provides a comprehensive solution for integrating and managing configuration files in Open edX Android project.

## Features
- Parsing config.yaml files
- Adding essential keys to `AndroidManifest.xml` (e.g. Microsoft keys)
- Generating Android build config fields.
- Generating config.json file to use the configuration fields at runtime.
- Generating google-services.json with Firebase keys.

Inside the `Config.kt`, parsing and populating relevant keys and classes are done, e.g. `AgreementUrlsConfig.kt` and `FirebaseConfig.kt`.

## Getting Started

### Configuration Setup

Edit the `config_settings.yaml` in the `default_config` folder. It should contain data as follows:

```yaml
config_directory: '{path_to_config_folder}'
config_mapping:
prod: 'prod'
stage: 'stage'
dev: 'dev'
# These mappings are configurable, e.g. dev: 'prod_test'
```

- `config_directory` provides the path of the config directory.
- `config_mappings` provides mappings that can be utilized to map the Android Build Variant to a defined folder within the config directory, and it will be referenced.

Note: You can specify `config_directory` to any folder outside the repository to store the configs as a separate project.

### Configuration Files
In the `default_config` folder, select your environment folder: prod, stage, dev or any other you have created.
Open `config.yaml` and fill in the required fields.

Example:

```yaml
API_HOST_URL: 'https://mylmsexample.com'
APPLICATION_ID: 'org.openedx.app'
ENVIRONMENT_DISPLAY_NAME: 'MyLMSExample'
FEEDBACK_EMAIL_ADDRESS: '[email protected]'
OAUTH_CLIENT_ID: 'YOUR_OAUTH_CLIENT_ID'

PLATFORM_NAME: "MyLMS"
TOKEN_TYPE: "JWT"

FIREBASE:
ENABLED: false
ANALYTICS_SOURCE: ''
CLOUD_MESSAGING_ENABLED: false
PROJECT_NUMBER: ''
PROJECT_ID: ''
APPLICATION_ID: ''
API_KEY: ''

MICROSOFT:
ENABLED: false
CLIENT_ID: 'microsoftClientID'
```
Also, all envirenment folders contain a `file_mappings.yaml` file that points to the config files to be parsed.

By modifying `file_mappings.yaml`, you can achieve splitting of the base `config.yaml` or add additional configuration files.

Example:

```yaml
android:
files:
- auth_client.yaml
- config.yaml
- feature_flags.yaml
```

## Available Third-Party Services
- **Firebase:** Analytics, Crashlytics, Cloud Messaging
- **Google:** Sign in and Sign up via Google
- **Microsoft:** Sign in and Sign up via Microsoft
- **Facebook:** Sign in and Sign up via Facebook
- **Branch:** Deeplinks
- **Braze:** Could Messaging
- **SegmentIO:** Analytics

## Available Feature Flags
- **PRE_LOGIN_EXPERIENCE_ENABLED:** Enables the pre login courses discovery experience.
- **WHATS_NEW_ENABLED:** Enables the "What's New" feature to present the latest changes to the user.
- **SOCIAL_AUTH_ENABLED:** Enables SSO buttons on the SignIn and SignUp screens.
- **COURSE_NESTED_LIST_ENABLED:** Enables an alternative visual representation for the course structure.
- **COURSE_BANNER_ENABLED:** Enables the display of the course image on the Course Home screen.
- **COURSE_TOP_TAB_BAR_ENABLED:** Enables an alternative navigation on the Course Home screen.
- **COURSE_UNIT_PROGRESS_ENABLED:** Enables the display of the unit progress within the courseware.

## Future Support
- To add config related to some other service, create a class, e.g. `ServiceNameConfig.kt`, to be able to populate related fields.
- Create a `function` in the `Config.kt` to be able to parse and use the newly created service from the main Config.

Example:

```Kotlin
fun getServiceNameConfig(): ServiceNameConfig {
return getObjectOrNewInstance(SERVICE_NAME_KEY, ServiceNameConfig::class.java)
}
```

```yaml
SERVICE_NAME:
ENABLED: false
KEY: ''
```

The `default_config` directory is added to the project to provide an idea of how to write config YAML files.
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# EducationX Android
# Open edX Android

Modern vision of the mobile application for the Open EdX platform from Raccoon Gang.
Modern vision of the mobile application for the Open edX platform from Raccoon Gang.

[Documentation](Documentation/Documentation.md)

Expand All @@ -14,17 +14,16 @@ Modern vision of the mobile application for the Open EdX platform from Raccoon G

3. Choose ``openedx-app-android``.

4. Configure the [config.yaml](default_config/dev/config.yaml) with URLs and OAuth credentials for your Open edX instance.
4. Configure `config_settings.yaml` inside `default_config` and `config.yaml` inside sub direcroties to point to your Open edX configuration. [Configuration Docuementation](./Documentation/ConfigurationManagement.md)

5. Select the build variant ``develop``, ``stage``, or ``prod``.

6. Click the **Run** button.

## API plugin
## API
This project targets on the latest Open edX release and rely on the relevant mobile APIs.

This project uses custom APIs to improve performance and reduce the number of requests to the server.

You can find the plugin with the API and installation guide [here](https://github.com/raccoongang/mobile-api-extensions).
If your platform version is older than December 2023, please follow the instructions to use the [API Plugin](./Documentation/APIs_Compatibility.md).

## License

Expand Down
3 changes: 2 additions & 1 deletion app/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/build
/build
/google-services.json
96 changes: 85 additions & 11 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-parcelize'
id 'kotlin-kapt'
id 'com.google.firebase.crashlytics'
}

def config = configHelper.fetchConfig()
def appId = config.getOrDefault("APPLICATION_ID", "org.openedx.app")
def platformName = config.getOrDefault("PLATFORM_NAME", "OpenEdx").toLowerCase()
def firebaseConfig = config.get('FIREBASE')
def firebaseEnabled = firebaseConfig?.getOrDefault('ENABLED', false)

apply plugin: 'com.android.application'
apply plugin: 'org.jetbrains.kotlin.android'
apply plugin: 'kotlin-parcelize'
apply plugin: 'kotlin-kapt'
if (firebaseEnabled) {
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'

tasks.register('generateGoogleServicesJson') {
configHelper.generateGoogleServicesJson(appId)
}

preBuild.dependsOn(generateGoogleServicesJson)
} else {
tasks.register('removeGoogleServicesJson') {
configHelper.removeGoogleServicesJson()
}

preBuild.dependsOn(removeGoogleServicesJson)
}

android {
compileSdk 34
Expand All @@ -31,12 +46,18 @@ android {
productFlavors {
prod {
dimension 'env'
setupBranchConfigFields(it)
setupFirebaseConfigFields(it)
}
develop {
dimension 'env'
setupBranchConfigFields(it)
setupFirebaseConfigFields(it)
}
stage {
dimension 'env'
setupBranchConfigFields(it)
setupFirebaseConfigFields(it)
}
}

Expand All @@ -57,8 +78,10 @@ android {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'

firebaseCrashlytics {
mappingFileUploadEnabled false
if (firebaseEnabled) {
firebaseCrashlytics {
mappingFileUploadEnabled false
}
}
}
}
Expand All @@ -72,6 +95,7 @@ android {
buildFeatures {
viewBinding true
compose true
buildConfig true
}
composeOptions {
kotlinCompilerExtensionVersion = "$compose_compiler_version"
Expand Down Expand Up @@ -106,6 +130,22 @@ dependencies {

implementation 'androidx.core:core-splashscreen:1.0.1'

// Segment Library
implementation "com.segment.analytics.kotlin:android:1.14.2"
// Segment's Firebase integration
implementation 'com.segment.analytics.kotlin.destinations:firebase:1.5.2'
// Braze SDK Integration
implementation "com.braze:braze-segment-kotlin:1.4.2"
implementation "com.braze:android-sdk-ui:30.2.0"

// Firebase Cloud Messaging Integration for Braze
implementation 'com.google.firebase:firebase-messaging-ktx:23.4.1'

// Branch SDK Integration
implementation 'io.branch.sdk.android:library:5.9.0'
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
implementation "com.android.installreferrer:installreferrer:2.2"

androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
Expand All @@ -115,4 +155,38 @@ dependencies {
testImplementation "io.mockk:mockk-android:$mockk_version"
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
testImplementation "androidx.arch.core:core-testing:$android_arch_version"
}
}

private def setupBranchConfigFields(buildType) {
def branchConfig = configHelper.fetchConfig().get("BRANCH")
def branchKey = ""
def branchUriScheme = ""
def branchHost = ""
def branchAlternateHost = ""

if (branchConfig && branchConfig.get("ENABLED")) {
branchKey = branchConfig.getOrDefault("KEY", "")
branchUriScheme = branchConfig.getOrDefault("URI_SCHEME", "")
branchHost = branchConfig.getOrDefault("HOST", "")
branchAlternateHost = branchConfig.getOrDefault("ALTERNATE_HOST", "")

// Validation: Throw exception if any field is empty
if (branchKey.isEmpty() || branchUriScheme.isEmpty() || branchHost.isEmpty() ||
branchAlternateHost.isEmpty()) {
throw new IllegalStateException("One or more Branch configuration fields are empty.")
}
}

buildType.resValue "string", "branch_key", branchKey
buildType.resValue "string", "branch_uri_scheme", branchUriScheme
buildType.resValue "string", "branch_host", branchHost
buildType.resValue "string", "branch_alternate_host", branchAlternateHost
}

private def setupFirebaseConfigFields(buildType) {
def firebaseConfig = configHelper.fetchConfig().get('FIREBASE')
def firebaseEnabled = firebaseConfig?.getOrDefault('ENABLED', false)
def cloudMessagingEnabled = firebaseConfig?.getOrDefault('CLOUD_MESSAGING_ENABLED', false)

buildType.manifestPlaceholders = [fcmEnabled: firebaseEnabled && cloudMessagingEnabled]
}
48 changes: 48 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,21 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<queries>
<intent>
<action android:name="com.google.android.youtube.api.service.START" />
</intent>
</queries>

<queries>
<intent>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="text/plain" />
</intent>
</queries>

<application
android:name=".OpenEdXApp"
android:allowBackup="false"
Expand All @@ -37,6 +45,28 @@

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

<!-- Branch URI Scheme -->
<intent-filter>
<data
android:host="open"
android:scheme="@string/branch_uri_scheme" />
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>

<intent-filter android:autoVerify="true">
<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" />
<data android:host="@string/branch_host" />
<data android:host="@string/branch_alternate_host" />
</intent-filter>
</activity>

<provider
Expand All @@ -59,10 +89,28 @@
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="androidx.media3.cast.DefaultCastOptionsProvider" />

<!-- Branch init -->
<meta-data
android:name="io.branch.sdk.BranchKey"
android:value="@string/branch_key" />
<meta-data
android:name="io.branch.sdk.BranchKey.test"
android:value="@string/branch_key" />

<service
android:name="androidx.work.impl.foreground.SystemForegroundService"
android:foregroundServiceType="dataSync"
tools:node="merge" />

<!-- Braze init -->
<service
android:name="com.braze.push.BrazeFirebaseMessagingService"
android:enabled="${fcmEnabled}"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>

</manifest>
Loading

0 comments on commit bccf5ed

Please sign in to comment.