Skip to content

Commit bccf5ed

Browse files
Merge pull request #305 from openedx/develop
Develop to main v1.5
2 parents 8c57627 + 7dd29ef commit bccf5ed

File tree

284 files changed

+10227
-4110
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

284 files changed

+10227
-4110
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# APIs Compatibility
2+
3+
This documentation offers guidance on a workaround for utilizing mobile APIs with earlier versions of Open edX releases.
4+
5+
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.
6+
7+
If your platform version is older than December 2023, follow the instructions below:
8+
9+
1. Setup the [mobile-api-extensions](https://github.com/raccoongang/mobile-api-extensions) plugin to your platform.
10+
The Plugin contains extended Open edX APIs for mobile applications.
11+
2. Roll back the modifications that brought in the new APIs [42f518a](https://github.com/openedx/openedx-app-android/commit/42f518a264d4300c8c2ca349072addd7d16ff91a).
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Configuration Management
2+
3+
This documentation provides a comprehensive solution for integrating and managing configuration files in Open edX Android project.
4+
5+
## Features
6+
- Parsing config.yaml files
7+
- Adding essential keys to `AndroidManifest.xml` (e.g. Microsoft keys)
8+
- Generating Android build config fields.
9+
- Generating config.json file to use the configuration fields at runtime.
10+
- Generating google-services.json with Firebase keys.
11+
12+
Inside the `Config.kt`, parsing and populating relevant keys and classes are done, e.g. `AgreementUrlsConfig.kt` and `FirebaseConfig.kt`.
13+
14+
## Getting Started
15+
16+
### Configuration Setup
17+
18+
Edit the `config_settings.yaml` in the `default_config` folder. It should contain data as follows:
19+
20+
```yaml
21+
config_directory: '{path_to_config_folder}'
22+
config_mapping:
23+
prod: 'prod'
24+
stage: 'stage'
25+
dev: 'dev'
26+
# These mappings are configurable, e.g. dev: 'prod_test'
27+
```
28+
29+
- `config_directory` provides the path of the config directory.
30+
- `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.
31+
32+
Note: You can specify `config_directory` to any folder outside the repository to store the configs as a separate project.
33+
34+
### Configuration Files
35+
In the `default_config` folder, select your environment folder: prod, stage, dev or any other you have created.
36+
Open `config.yaml` and fill in the required fields.
37+
38+
Example:
39+
40+
```yaml
41+
API_HOST_URL: 'https://mylmsexample.com'
42+
APPLICATION_ID: 'org.openedx.app'
43+
ENVIRONMENT_DISPLAY_NAME: 'MyLMSExample'
44+
FEEDBACK_EMAIL_ADDRESS: '[email protected]'
45+
OAUTH_CLIENT_ID: 'YOUR_OAUTH_CLIENT_ID'
46+
47+
PLATFORM_NAME: "MyLMS"
48+
TOKEN_TYPE: "JWT"
49+
50+
FIREBASE:
51+
ENABLED: false
52+
ANALYTICS_SOURCE: ''
53+
CLOUD_MESSAGING_ENABLED: false
54+
PROJECT_NUMBER: ''
55+
PROJECT_ID: ''
56+
APPLICATION_ID: ''
57+
API_KEY: ''
58+
59+
MICROSOFT:
60+
ENABLED: false
61+
CLIENT_ID: 'microsoftClientID'
62+
```
63+
64+
Also, all envirenment folders contain a `file_mappings.yaml` file that points to the config files to be parsed.
65+
66+
By modifying `file_mappings.yaml`, you can achieve splitting of the base `config.yaml` or add additional configuration files.
67+
68+
Example:
69+
70+
```yaml
71+
android:
72+
files:
73+
- auth_client.yaml
74+
- config.yaml
75+
- feature_flags.yaml
76+
```
77+
78+
## Available Third-Party Services
79+
- **Firebase:** Analytics, Crashlytics, Cloud Messaging
80+
- **Google:** Sign in and Sign up via Google
81+
- **Microsoft:** Sign in and Sign up via Microsoft
82+
- **Facebook:** Sign in and Sign up via Facebook
83+
- **Branch:** Deeplinks
84+
- **Braze:** Could Messaging
85+
- **SegmentIO:** Analytics
86+
87+
## Available Feature Flags
88+
- **PRE_LOGIN_EXPERIENCE_ENABLED:** Enables the pre login courses discovery experience.
89+
- **WHATS_NEW_ENABLED:** Enables the "What's New" feature to present the latest changes to the user.
90+
- **SOCIAL_AUTH_ENABLED:** Enables SSO buttons on the SignIn and SignUp screens.
91+
- **COURSE_NESTED_LIST_ENABLED:** Enables an alternative visual representation for the course structure.
92+
- **COURSE_BANNER_ENABLED:** Enables the display of the course image on the Course Home screen.
93+
- **COURSE_TOP_TAB_BAR_ENABLED:** Enables an alternative navigation on the Course Home screen.
94+
- **COURSE_UNIT_PROGRESS_ENABLED:** Enables the display of the unit progress within the courseware.
95+
96+
## Future Support
97+
- To add config related to some other service, create a class, e.g. `ServiceNameConfig.kt`, to be able to populate related fields.
98+
- Create a `function` in the `Config.kt` to be able to parse and use the newly created service from the main Config.
99+
100+
Example:
101+
102+
```Kotlin
103+
fun getServiceNameConfig(): ServiceNameConfig {
104+
return getObjectOrNewInstance(SERVICE_NAME_KEY, ServiceNameConfig::class.java)
105+
}
106+
```
107+
108+
```yaml
109+
SERVICE_NAME:
110+
ENABLED: false
111+
KEY: ''
112+
```
113+
114+
The `default_config` directory is added to the project to provide an idea of how to write config YAML files.

README.md

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# EducationX Android
1+
# Open edX Android
22

3-
Modern vision of the mobile application for the Open EdX platform from Raccoon Gang.
3+
Modern vision of the mobile application for the Open edX platform from Raccoon Gang.
44

55
[Documentation](Documentation/Documentation.md)
66

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

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

17-
4. Configure the [config.yaml](default_config/dev/config.yaml) with URLs and OAuth credentials for your Open edX instance.
17+
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)
1818

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

2121
6. Click the **Run** button.
2222

23-
## API plugin
23+
## API
24+
This project targets on the latest Open edX release and rely on the relevant mobile APIs.
2425

25-
This project uses custom APIs to improve performance and reduce the number of requests to the server.
26-
27-
You can find the plugin with the API and installation guide [here](https://github.com/raccoongang/mobile-api-extensions).
26+
If your platform version is older than December 2023, please follow the instructions to use the [API Plugin](./Documentation/APIs_Compatibility.md).
2827

2928
## License
3029

app/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
/build
1+
/build
2+
/google-services.json

app/build.gradle

Lines changed: 85 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,29 @@
1-
plugins {
2-
id 'com.android.application'
3-
id 'org.jetbrains.kotlin.android'
4-
id 'kotlin-parcelize'
5-
id 'kotlin-kapt'
6-
id 'com.google.firebase.crashlytics'
7-
}
8-
91
def config = configHelper.fetchConfig()
102
def appId = config.getOrDefault("APPLICATION_ID", "org.openedx.app")
113
def platformName = config.getOrDefault("PLATFORM_NAME", "OpenEdx").toLowerCase()
4+
def firebaseConfig = config.get('FIREBASE')
5+
def firebaseEnabled = firebaseConfig?.getOrDefault('ENABLED', false)
6+
7+
apply plugin: 'com.android.application'
8+
apply plugin: 'org.jetbrains.kotlin.android'
9+
apply plugin: 'kotlin-parcelize'
10+
apply plugin: 'kotlin-kapt'
11+
if (firebaseEnabled) {
12+
apply plugin: 'com.google.gms.google-services'
13+
apply plugin: 'com.google.firebase.crashlytics'
14+
15+
tasks.register('generateGoogleServicesJson') {
16+
configHelper.generateGoogleServicesJson(appId)
17+
}
18+
19+
preBuild.dependsOn(generateGoogleServicesJson)
20+
} else {
21+
tasks.register('removeGoogleServicesJson') {
22+
configHelper.removeGoogleServicesJson()
23+
}
24+
25+
preBuild.dependsOn(removeGoogleServicesJson)
26+
}
1227

1328
android {
1429
compileSdk 34
@@ -31,12 +46,18 @@ android {
3146
productFlavors {
3247
prod {
3348
dimension 'env'
49+
setupBranchConfigFields(it)
50+
setupFirebaseConfigFields(it)
3451
}
3552
develop {
3653
dimension 'env'
54+
setupBranchConfigFields(it)
55+
setupFirebaseConfigFields(it)
3756
}
3857
stage {
3958
dimension 'env'
59+
setupBranchConfigFields(it)
60+
setupFirebaseConfigFields(it)
4061
}
4162
}
4263

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

60-
firebaseCrashlytics {
61-
mappingFileUploadEnabled false
81+
if (firebaseEnabled) {
82+
firebaseCrashlytics {
83+
mappingFileUploadEnabled false
84+
}
6285
}
6386
}
6487
}
@@ -72,6 +95,7 @@ android {
7295
buildFeatures {
7396
viewBinding true
7497
compose true
98+
buildConfig true
7599
}
76100
composeOptions {
77101
kotlinCompilerExtensionVersion = "$compose_compiler_version"
@@ -106,6 +130,22 @@ dependencies {
106130

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

133+
// Segment Library
134+
implementation "com.segment.analytics.kotlin:android:1.14.2"
135+
// Segment's Firebase integration
136+
implementation 'com.segment.analytics.kotlin.destinations:firebase:1.5.2'
137+
// Braze SDK Integration
138+
implementation "com.braze:braze-segment-kotlin:1.4.2"
139+
implementation "com.braze:android-sdk-ui:30.2.0"
140+
141+
// Firebase Cloud Messaging Integration for Braze
142+
implementation 'com.google.firebase:firebase-messaging-ktx:23.4.1'
143+
144+
// Branch SDK Integration
145+
implementation 'io.branch.sdk.android:library:5.9.0'
146+
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
147+
implementation "com.android.installreferrer:installreferrer:2.2"
148+
109149
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
110150
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
111151
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
@@ -115,4 +155,38 @@ dependencies {
115155
testImplementation "io.mockk:mockk-android:$mockk_version"
116156
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
117157
testImplementation "androidx.arch.core:core-testing:$android_arch_version"
118-
}
158+
}
159+
160+
private def setupBranchConfigFields(buildType) {
161+
def branchConfig = configHelper.fetchConfig().get("BRANCH")
162+
def branchKey = ""
163+
def branchUriScheme = ""
164+
def branchHost = ""
165+
def branchAlternateHost = ""
166+
167+
if (branchConfig && branchConfig.get("ENABLED")) {
168+
branchKey = branchConfig.getOrDefault("KEY", "")
169+
branchUriScheme = branchConfig.getOrDefault("URI_SCHEME", "")
170+
branchHost = branchConfig.getOrDefault("HOST", "")
171+
branchAlternateHost = branchConfig.getOrDefault("ALTERNATE_HOST", "")
172+
173+
// Validation: Throw exception if any field is empty
174+
if (branchKey.isEmpty() || branchUriScheme.isEmpty() || branchHost.isEmpty() ||
175+
branchAlternateHost.isEmpty()) {
176+
throw new IllegalStateException("One or more Branch configuration fields are empty.")
177+
}
178+
}
179+
180+
buildType.resValue "string", "branch_key", branchKey
181+
buildType.resValue "string", "branch_uri_scheme", branchUriScheme
182+
buildType.resValue "string", "branch_host", branchHost
183+
buildType.resValue "string", "branch_alternate_host", branchAlternateHost
184+
}
185+
186+
private def setupFirebaseConfigFields(buildType) {
187+
def firebaseConfig = configHelper.fetchConfig().get('FIREBASE')
188+
def firebaseEnabled = firebaseConfig?.getOrDefault('ENABLED', false)
189+
def cloudMessagingEnabled = firebaseConfig?.getOrDefault('CLOUD_MESSAGING_ENABLED', false)
190+
191+
buildType.manifestPlaceholders = [fcmEnabled: firebaseEnabled && cloudMessagingEnabled]
192+
}

app/src/main/AndroidManifest.xml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,21 @@
55
<uses-permission android:name="android.permission.INTERNET" />
66
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
77
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
8+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
89

910
<queries>
1011
<intent>
1112
<action android:name="com.google.android.youtube.api.service.START" />
1213
</intent>
1314
</queries>
1415

16+
<queries>
17+
<intent>
18+
<action android:name="android.intent.action.SEND" />
19+
<data android:mimeType="text/plain" />
20+
</intent>
21+
</queries>
22+
1523
<application
1624
android:name=".OpenEdXApp"
1725
android:allowBackup="false"
@@ -37,6 +45,28 @@
3745

3846
<category android:name="android.intent.category.LAUNCHER" />
3947
</intent-filter>
48+
49+
<!-- Branch URI Scheme -->
50+
<intent-filter>
51+
<data
52+
android:host="open"
53+
android:scheme="@string/branch_uri_scheme" />
54+
<action android:name="android.intent.action.VIEW" />
55+
56+
<category android:name="android.intent.category.DEFAULT" />
57+
<category android:name="android.intent.category.BROWSABLE" />
58+
</intent-filter>
59+
60+
<intent-filter android:autoVerify="true">
61+
<action android:name="android.intent.action.VIEW" />
62+
63+
<category android:name="android.intent.category.DEFAULT" />
64+
<category android:name="android.intent.category.BROWSABLE" />
65+
66+
<data android:scheme="https" />
67+
<data android:host="@string/branch_host" />
68+
<data android:host="@string/branch_alternate_host" />
69+
</intent-filter>
4070
</activity>
4171

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

92+
<!-- Branch init -->
93+
<meta-data
94+
android:name="io.branch.sdk.BranchKey"
95+
android:value="@string/branch_key" />
96+
<meta-data
97+
android:name="io.branch.sdk.BranchKey.test"
98+
android:value="@string/branch_key" />
99+
62100
<service
63101
android:name="androidx.work.impl.foreground.SystemForegroundService"
64102
android:foregroundServiceType="dataSync"
65103
tools:node="merge" />
104+
105+
<!-- Braze init -->
106+
<service
107+
android:name="com.braze.push.BrazeFirebaseMessagingService"
108+
android:enabled="${fcmEnabled}"
109+
android:exported="false">
110+
<intent-filter>
111+
<action android:name="com.google.firebase.MESSAGING_EVENT" />
112+
</intent-filter>
113+
</service>
66114
</application>
67115

68116
</manifest>

0 commit comments

Comments
 (0)