Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into feat/offline
Browse files Browse the repository at this point in the history
# Conflicts:
#	app/src/main/java/org/openedx/app/AppViewModel.kt
#	app/src/main/java/org/openedx/app/di/ScreenModule.kt
#	app/src/test/java/org/openedx/AppViewModelTest.kt
  • Loading branch information
PavloNetrebchuk committed Jun 26, 2024
2 parents a8c9e27 + 948277a commit 18b5fc5
Show file tree
Hide file tree
Showing 55 changed files with 896 additions and 424 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/validate-english-strings.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Validate English strings.xml

on:
pull_request: { }
push:
branches: [ main, develop ]

jobs:
translation_strings:
name: Validate strings.xml
runs-on: ubuntu-latest

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

- name: Use Python
uses: actions/setup-python@v5
with:
python-version: 3.11

- name: Install translations requirements
run: make translation_requirements

- name: Validate English plurals in strings.xml
run: make validate_english_plurals

- name: Test extract strings
run: |
make extract_translations
# Ensure the file is extracted
test -f i18n/src/main/res/values/strings.xml
13 changes: 13 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,16 @@ pull_translations: clean_translations_temp_directory

extract_translations: clean_translations_temp_directory
python3 i18n_scripts/translation.py --combine

validate_english_plurals:
@if git grep 'quantity' -- '**/res/values/strings.xml' | grep -E 'quantity=.(zero|two|few|many)'; then \
echo ""; \
echo ""; \
echo "Error: Found invalid plurals in the files listed above."; \
echo " Please only use 'one' and 'other' in English strings.xml files,"; \
echo " otherwise Transifex fails to parse them."; \
echo ""; \
exit 1; \
else \
echo "strings.xml files are valid."; \
fi
6 changes: 3 additions & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ dependencies {

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

api platform("com.google.firebase:firebase-bom:$firebase_version")
api "com.google.firebase:firebase-messaging"

// Segment Library
implementation "com.segment.analytics.kotlin:android:1.14.2"
// Segment's Firebase integration
Expand All @@ -138,9 +141,6 @@ dependencies {
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'

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 Down
4 changes: 2 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@
android:foregroundServiceType="dataSync"
tools:node="merge" />

<!-- Braze init -->
<!-- FirebaseMessaging init -->
<service
android:name="com.braze.push.BrazeFirebaseMessagingService"
android:name=".system.push.OpenEdXFirebaseMessagingService"
android:enabled="${fcmEnabled}"
android:exported="false">
<intent-filter>
Expand Down
16 changes: 16 additions & 0 deletions app/src/main/java/org/openedx/app/AppActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.core.view.WindowInsetsControllerCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.window.layout.WindowMetricsCalculator
import com.braze.support.toStringMap
import io.branch.referral.Branch
import io.branch.referral.Branch.BranchUniversalReferralInitListener
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -139,6 +140,11 @@ class AppActivity : AppCompatActivity(), InsetHolder, WindowSizeHolder {
addFragment(MainFragment.newInstance())
}
}

val extras = intent.extras
if (extras?.containsKey(DeepLink.Keys.NOTIFICATION_TYPE.value) == true) {
handlePushNotification(extras)
}
}

viewModel.logoutUser.observe(this) {
Expand Down Expand Up @@ -183,6 +189,11 @@ class AppActivity : AppCompatActivity(), InsetHolder, WindowSizeHolder {
super.onNewIntent(intent)
this.intent = intent

val extras = intent?.extras
if (extras?.containsKey(DeepLink.Keys.NOTIFICATION_TYPE.value) == true) {
handlePushNotification(extras)
}

if (viewModel.isBranchEnabled) {
if (intent?.getBooleanExtra(BRANCH_FORCE_NEW_SESSION, false) == true) {
Branch.sessionBuilder(this).withCallback { referringParams, error ->
Expand Down Expand Up @@ -231,6 +242,11 @@ class AppActivity : AppCompatActivity(), InsetHolder, WindowSizeHolder {
}
}

private fun handlePushNotification(data: Bundle) {
val deepLink = DeepLink(data.toStringMap())
viewModel.makeExternalRoute(supportFragmentManager, deepLink)
}

companion object {
const val TOP_INSET = "topInset"
const val BOTTOM_INSET = "bottomInset"
Expand Down
62 changes: 49 additions & 13 deletions app/src/main/java/org/openedx/app/AppViewModel.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.openedx.app

import android.annotation.SuppressLint
import android.app.NotificationManager
import android.content.Context
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
Expand All @@ -13,16 +16,22 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.openedx.app.deeplink.DeepLink
import org.openedx.app.deeplink.DeepLinkRouter
import org.openedx.app.system.notifier.AppNotifier
import org.openedx.app.system.notifier.LogoutEvent
import org.openedx.app.system.push.RefreshFirebaseTokenWorker
import org.openedx.app.system.push.SyncFirebaseTokenWorker
import org.openedx.core.BaseViewModel
import org.openedx.core.SingleEventLiveData
import org.openedx.core.config.Config
import org.openedx.core.data.model.User
import org.openedx.core.data.storage.CorePreferences
import org.openedx.core.system.notifier.DownloadFailed
import org.openedx.core.system.notifier.DownloadNotifier
import org.openedx.core.system.notifier.app.AppNotifier
import org.openedx.core.system.notifier.app.LogoutEvent
import org.openedx.core.system.notifier.app.SignInEvent
import org.openedx.core.utils.FileUtil


@SuppressLint("StaticFieldLeak")
class AppViewModel(
private val config: Config,
private val appNotifier: AppNotifier,
Expand All @@ -33,6 +42,7 @@ class AppViewModel(
private val deepLinkRouter: DeepLinkRouter,
private val fileUtil: FileUtil,
private val downloadNotifier: DownloadNotifier,
private val context: Context
) : BaseViewModel() {

private val _logoutUser = SingleEventLiveData<Unit>()
Expand All @@ -53,20 +63,25 @@ class AppViewModel(

override fun onCreate(owner: LifecycleOwner) {
super.onCreate(owner)
setUserId()

val user = preferencesManager.user

setUserId(user)

if (user != null && preferencesManager.pushToken.isNotEmpty()) {
SyncFirebaseTokenWorker.schedule(context)
}

if (canResetAppDirectory) {
resetAppDirectory()
}

viewModelScope.launch {
appNotifier.notifier.collect { event ->
if (event is LogoutEvent && System.currentTimeMillis() - logoutHandledAt > 5000) {
logoutHandledAt = System.currentTimeMillis()
preferencesManager.clear()
withContext(dispatcher) {
room.clearAllTables()
}
analytics.logoutEvent(true)
_logoutUser.value = Unit
if (event is SignInEvent && config.getFirebaseConfig().isCloudMessagingEnabled) {
SyncFirebaseTokenWorker.schedule(context)
} else if (event is LogoutEvent) {
handleLogoutEvent(event)
}
}
}
Expand Down Expand Up @@ -97,9 +112,30 @@ class AppViewModel(
deepLinkRouter.makeRoute(fm, deepLink)
}

private fun setUserId() {
preferencesManager.user?.let {
private fun setUserId(user: User?) {
user?.let {
analytics.setUserIdForSession(it.id)
}
}

private suspend fun handleLogoutEvent(event: LogoutEvent) {
if (System.currentTimeMillis() - logoutHandledAt > 5000) {
if (event.isForced) {
logoutHandledAt = System.currentTimeMillis()
preferencesManager.clear()
withContext(dispatcher) {
room.clearAllTables()
}
analytics.logoutEvent(true)
_logoutUser.value = Unit
}

if (config.getFirebaseConfig().isCloudMessagingEnabled) {
RefreshFirebaseTokenWorker.schedule(context)
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.cancelAll()
}
}
}
}
14 changes: 14 additions & 0 deletions app/src/main/java/org/openedx/app/data/api/NotificationsApi.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.openedx.app.data.api

import retrofit2.http.Field
import retrofit2.http.FormUrlEncoded
import retrofit2.http.POST

interface NotificationsApi {
@POST("/api/mobile/v4/notifications/create-token/")
@FormUrlEncoded
suspend fun syncFirebaseToken(
@Field("registration_id") token: String,
@Field("active") active: Boolean = true
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import kotlinx.coroutines.runBlocking
import okhttp3.Interceptor
import okhttp3.Response
import org.openedx.app.BuildConfig
import org.openedx.core.system.notifier.AppUpgradeEvent
import org.openedx.core.system.notifier.AppUpgradeNotifier
import org.openedx.core.system.notifier.app.AppNotifier
import org.openedx.core.system.notifier.app.AppUpgradeEvent
import org.openedx.core.utils.TimeUtils
import java.util.Date

class AppUpgradeInterceptor(
private val appUpgradeNotifier: AppUpgradeNotifier
private val appNotifier: AppNotifier
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
Expand All @@ -21,15 +21,15 @@ class AppUpgradeInterceptor(
runBlocking {
when {
responseCode == 426 -> {
appUpgradeNotifier.send(AppUpgradeEvent.UpgradeRequiredEvent)
appNotifier.send(AppUpgradeEvent.UpgradeRequiredEvent)
}

BuildConfig.VERSION_NAME != latestAppVersion && lastSupportedDateTime > Date().time -> {
appUpgradeNotifier.send(AppUpgradeEvent.UpgradeRecommendedEvent(latestAppVersion))
appNotifier.send(AppUpgradeEvent.UpgradeRecommendedEvent(latestAppVersion))
}

latestAppVersion.isNotEmpty() && BuildConfig.VERSION_NAME != latestAppVersion && lastSupportedDateTime < Date().time -> {
appUpgradeNotifier.send(AppUpgradeEvent.UpgradeRequiredEvent)
appNotifier.send(AppUpgradeEvent.UpgradeRequiredEvent)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import okhttp3.ResponseBody.Companion.toResponseBody
import okhttp3.logging.HttpLoggingInterceptor
import org.json.JSONException
import org.json.JSONObject
import org.openedx.app.system.notifier.AppNotifier
import org.openedx.app.system.notifier.LogoutEvent
import org.openedx.core.system.notifier.app.LogoutEvent
import org.openedx.auth.data.api.AuthApi
import org.openedx.auth.domain.model.AuthResponse
import org.openedx.core.ApiConstants
import org.openedx.core.ApiConstants.TOKEN_TYPE_JWT
import org.openedx.core.BuildConfig
import org.openedx.core.config.Config
import org.openedx.core.data.storage.CorePreferences
import org.openedx.core.system.notifier.app.AppNotifier
import org.openedx.core.utils.TimeUtils
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
Expand Down Expand Up @@ -119,7 +119,7 @@ class OauthRefreshTokenAuthenticator(
}

runBlocking {
appNotifier.send(LogoutEvent())
appNotifier.send(LogoutEvent(true))
}
}

Expand All @@ -128,7 +128,7 @@ class OauthRefreshTokenAuthenticator(
JWT_USER_EMAIL_MISMATCH,
-> {
runBlocking {
appNotifier.send(LogoutEvent())
appNotifier.send(LogoutEvent(true))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class PreferencesManager(context: Context) : CorePreferences, ProfilePreferences
remove(ACCESS_TOKEN)
remove(REFRESH_TOKEN)
remove(USER)
remove(ACCOUNT)
remove(EXPIRES_IN)
}.apply()
}
Expand All @@ -70,6 +71,12 @@ class PreferencesManager(context: Context) : CorePreferences, ProfilePreferences
}
get() = getString(REFRESH_TOKEN)

override var pushToken: String
set(value) {
saveString(PUSH_TOKEN, value)
}
get() = getString(PUSH_TOKEN)

override var accessTokenExpiresAt: Long
set(value) {
saveLong(EXPIRES_IN, value)
Expand Down Expand Up @@ -168,6 +175,7 @@ class PreferencesManager(context: Context) : CorePreferences, ProfilePreferences
companion object {
private const val ACCESS_TOKEN = "access_token"
private const val REFRESH_TOKEN = "refresh_token"
private const val PUSH_TOKEN = "push_token"
private const val EXPIRES_IN = "expires_in"
private const val USER = "user"
private const val ACCOUNT = "account"
Expand Down
Loading

0 comments on commit 18b5fc5

Please sign in to comment.