Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
870e1b6
[MBL-19875][All] Add mobile_consent flag to auth session
hermannakos Mar 27, 2026
bec6f0d
fix tests
hermannakos Mar 27, 2026
e055f34
[MBL-19874][All] Add cookie consent dialog
hermannakos Mar 31, 2026
8f4c3de
Merge branch 'master' into MBL-19874-privacy-consent-dialog
hermannakos Mar 31, 2026
2d5f5b7
CRs
hermannakos Apr 7, 2026
cc614fe
fix tests
hermannakos Apr 7, 2026
41a0c92
Merge branch 'master' into MBL-19874-privacy-consent-dialog
tamaskozmer Apr 22, 2026
f6a266d
Conflict resolution.
tamaskozmer Apr 22, 2026
bda5b34
Fixed horizon build.
tamaskozmer Apr 22, 2026
a49a57e
Removed consent storage on the web.
tamaskozmer Apr 22, 2026
4fd0b99
Fixed oauth api call.
tamaskozmer Apr 22, 2026
b4b7b27
Restart Pendo tracking when consent is accepted in the settings.
tamaskozmer Apr 22, 2026
7f39dec
Fixed UI issues.
tamaskozmer Apr 23, 2026
2b3d6cf
Merge branch 'master' into MBL-19874-privacy-consent-dialog
tamaskozmer Apr 23, 2026
030b281
Fixed tests.
tamaskozmer Apr 23, 2026
389b546
Merge branch 'master' into MBL-19874-privacy-consent-dialog
tamaskozmer Apr 23, 2026
e04475b
Removed feature flag dependency for pandata.
tamaskozmer Apr 23, 2026
bf4230c
Correctly handle change user consent.
tamaskozmer Apr 23, 2026
025a7fa
Fixed routing to NGC.
tamaskozmer Apr 24, 2026
08166fd
Fixed QR login.
tamaskozmer Apr 24, 2026
d8271fa
New UI for changing the privacy settings.
tamaskozmer Apr 24, 2026
8305085
Fixed review items
tamaskozmer Apr 24, 2026
b604990
Changed description and title order.
tamaskozmer Apr 27, 2026
4d406d5
Fixed tests
tamaskozmer Apr 27, 2026
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2026 - present Instructure, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.instructure.parentapp.di.feature

import com.instructure.pandautils.features.cookieconsent.AnalyticsConsentHandler
import com.instructure.pandautils.features.cookieconsent.CookieConsentNamespace
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import sdk.pendo.io.Pendo

@Module
@InstallIn(SingletonComponent::class)
class CookieConsentModule {

@Provides
fun provideCookieConsentNamespace(): CookieConsentNamespace {
return CookieConsentNamespace.PARENT
}

@Provides
fun provideAnalyticsConsentHandler(): AnalyticsConsentHandler {
return object : AnalyticsConsentHandler {
override fun onConsentGranted() {
// Parent app does not schedule pandata upload
// Pendo session will be started on next app launch via SplashViewModel
}

override fun onConsentRevoked() {
Pendo.endSession()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ import com.instructure.canvasapi2.utils.Analytics
import com.instructure.canvasapi2.utils.ApiPrefs
import com.instructure.loginapi.login.LoginNavigation
import com.instructure.loginapi.login.features.acceptableusepolicy.AcceptableUsePolicyRouter
import com.instructure.loginapi.login.features.cookieconsent.CookieConsentRouter
import com.instructure.loginapi.login.util.LoginPrefs
import com.instructure.pandautils.features.reminder.AlarmScheduler
import com.instructure.parentapp.features.login.ParentAcceptableUsePolicyRouter
import com.instructure.parentapp.features.login.ParentCookieConsentRouter
import com.instructure.parentapp.features.login.ParentLoginNavigation
import com.instructure.parentapp.features.login.SignInActivity
import dagger.Module
Expand Down Expand Up @@ -59,6 +61,11 @@ class LoginModule {
): LoginNavigation {
return ParentLoginNavigation(activity, alarmScheduler)
}

@Provides
fun provideCookieConsentRouter(activity: FragmentActivity): CookieConsentRouter {
return ParentCookieConsentRouter(activity)
}
}

@Module
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ import androidx.fragment.app.FragmentActivity
import com.instructure.canvasapi2.utils.Analytics
import com.instructure.canvasapi2.utils.AnalyticsEventConstants
import com.instructure.loginapi.login.features.acceptableusepolicy.AcceptableUsePolicyRouter
import com.instructure.loginapi.login.features.cookieconsent.CookieConsentActivity
import com.instructure.loginapi.login.tasks.LogoutTask
import com.instructure.pandautils.features.reminder.AlarmScheduler
import com.instructure.parentapp.R
import com.instructure.parentapp.features.main.MainActivity
import com.instructure.parentapp.features.webview.HtmlContentActivity
import com.instructure.parentapp.util.ParentLogoutTask

Expand All @@ -42,8 +42,9 @@ class ParentAcceptableUsePolicyRouter(
override fun startApp() {
CookieManager.getInstance().flush()

val intent = Intent(activity, MainActivity::class.java)
val intent = Intent(activity, CookieConsentActivity::class.java)
activity.intent?.extras?.let { intent.putExtras(it) }
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
activity.startActivity(intent)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (C) 2026 - present Instructure, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.instructure.parentapp.features.login

import android.content.Intent
import androidx.fragment.app.FragmentActivity
import com.instructure.loginapi.login.features.cookieconsent.CookieConsentRouter
import com.instructure.parentapp.features.main.MainActivity

class ParentCookieConsentRouter(
private val activity: FragmentActivity
) : CookieConsentRouter {

override fun startApp() {
val intent = Intent(activity, MainActivity::class.java)
activity.intent?.extras?.let { intent.putExtras(it) }
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
activity.startActivity(intent)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,18 +83,20 @@ class SplashViewModel @Inject constructor(
}
}

val sendUsageMetrics = repository.getSendUsageMetrics()
if (sendUsageMetrics) {
val userWithIds = repository.getSelfWithUuid()
val visitorData = mapOf(
"locale" to apiPrefs.effectiveLocale,
)
val accountData = mapOf(
"surveyOptOut" to featureFlagProvider.checkAccountSurveyNotificationsFlag()
)
Pendo.startSession(userWithIds?.uuid?.SHA256().orEmpty(), userWithIds?.accountUuid.orEmpty(), visitorData, accountData)
} else {
Pendo.endSession()
if (apiPrefs.mobileConsent) {
val sendUsageMetrics = repository.getSendUsageMetrics()
if (sendUsageMetrics) {
val userWithIds = repository.getSelfWithUuid()
val visitorData = mapOf(
"locale" to apiPrefs.effectiveLocale,
)
val accountData = mapOf(
"surveyOptOut" to featureFlagProvider.checkAccountSurveyNotificationsFlag()
)
Pendo.startSession(userWithIds?.uuid?.SHA256().orEmpty(), userWithIds?.accountUuid.orEmpty(), visitorData, accountData)
} else {
Pendo.endSession()
}
}

val students = repository.getStudents()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ abstract class CallbackActivity : ParentActivity(), OnUnreadCountInvalidated, No
}

private suspend fun setupPendoTracking() {
if (!ApiPrefs.mobileConsent) return

val user = userApi.getSelfWithUUID(RestParams(isForceReadFromNetwork = true)).dataOrNull
val featureFlagsResult = FeaturesManager.getEnvironmentFeatureFlagsAsync(true).await().dataOrNull
val sendUsageMetrics = featureFlagsResult?.get(FeaturesManager.SEND_USAGE_METRICS) ?: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ import com.instructure.canvasapi2.models.AccountDomain
import com.instructure.canvasapi2.utils.ApiPrefs
import com.instructure.loginapi.login.LoginNavigation
import com.instructure.loginapi.login.features.acceptableusepolicy.AcceptableUsePolicyRouter
import com.instructure.loginapi.login.features.cookieconsent.CookieConsentRouter
import com.instructure.loginapi.login.util.LoginPrefs
import com.instructure.pandautils.features.reminder.AlarmScheduler
import com.instructure.pandautils.room.offline.DatabaseProvider
import com.instructure.student.activity.SignInActivity
import com.instructure.student.features.login.StudentAcceptableUsePolicyRouter
import com.instructure.student.features.login.StudentCookieConsentRouter
import com.instructure.student.features.login.StudentLoginNavigation
import dagger.Module
import dagger.Provides
Expand Down Expand Up @@ -59,6 +61,13 @@ class LoginModule {
): LoginNavigation {
return StudentLoginNavigation(activity, databaseProvider, alarmScheduler)
}

@Provides
fun provideCookieConsentRouter(
activity: FragmentActivity
): CookieConsentRouter {
return StudentCookieConsentRouter(activity)
}
}

@Module
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright (C) 2026 - present Instructure, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.instructure.student.di.feature

import android.content.Context
import androidx.work.WorkManager
import com.instructure.pandautils.features.cookieconsent.AnalyticsConsentHandler
import com.instructure.pandautils.features.cookieconsent.CookieConsentNamespace
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import sdk.pendo.io.Pendo

@Module
@InstallIn(SingletonComponent::class)
class CookieConsentModule {

@Provides
fun provideCookieConsentNamespace(): CookieConsentNamespace {
return CookieConsentNamespace.STUDENT
}

@Provides
fun provideAnalyticsConsentHandler(@ApplicationContext context: Context): AnalyticsConsentHandler {
return object : AnalyticsConsentHandler {
override fun onConsentGranted() {
// Pendo session will be started on next app launch via CallbackActivity
// Re-schedule pandata upload
val workManager = WorkManager.getInstance(context)
val workRequest = androidx.work.PeriodicWorkRequestBuilder<com.instructure.pandautils.analytics.pageview.PageViewUploadWorker>(
Comment thread
hermannakos marked this conversation as resolved.
Outdated
15, java.util.concurrent.TimeUnit.MINUTES
).setConstraints(
androidx.work.Constraints.Builder()
.setRequiredNetworkType(androidx.work.NetworkType.CONNECTED)
.build()
).build()
workManager.enqueueUniquePeriodicWork(
"pageView-student",
androidx.work.ExistingPeriodicWorkPolicy.KEEP,
workRequest
)
}

override fun onConsentRevoked() {
Pendo.endSession()
WorkManager.getInstance(context).cancelUniqueWork("pageView-student")
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,14 @@ import android.content.Intent
import android.webkit.CookieManager
import androidx.fragment.app.FragmentActivity
import com.instructure.canvasapi2.utils.ApiPrefs
import com.instructure.horizon.HorizonActivity
import com.instructure.loginapi.login.CANVAS_CAREER
import com.instructure.loginapi.login.features.acceptableusepolicy.AcceptableUsePolicyRouter
import com.instructure.loginapi.login.features.cookieconsent.CookieConsentActivity
import com.instructure.loginapi.login.tasks.LogoutTask
import com.instructure.pandautils.features.reminder.AlarmScheduler
import com.instructure.pandautils.room.offline.DatabaseProvider
import com.instructure.pandautils.services.PushNotificationRegistrationWorker
import com.instructure.student.R
import com.instructure.student.activity.InternalWebViewActivity
import com.instructure.student.activity.NavigationActivity
import com.instructure.student.tasks.StudentLogoutTask

class StudentAcceptableUsePolicyRouter(
Expand All @@ -48,17 +46,10 @@ class StudentAcceptableUsePolicyRouter(

CookieManager.getInstance().flush()

val isCanvasCareer = activity.intent?.getBooleanExtra(CANVAS_CAREER, false) ?: false
val intent = if (isCanvasCareer) {
Intent(activity, HorizonActivity::class.java)
} else {
Intent(activity, NavigationActivity.startActivityClass)
}

val intent = Intent(activity, CookieConsentActivity::class.java)
activity.intent?.extras?.let { extras ->
intent.putExtras(extras)
}

intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
activity.startActivity(intent)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2026 - present Instructure, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.instructure.student.features.login

import android.content.Intent
import androidx.fragment.app.FragmentActivity
import com.instructure.horizon.HorizonActivity
import com.instructure.loginapi.login.CANVAS_CAREER
import com.instructure.loginapi.login.features.cookieconsent.CookieConsentRouter
import com.instructure.student.activity.NavigationActivity

class StudentCookieConsentRouter(
private val activity: FragmentActivity
) : CookieConsentRouter {

override fun startApp() {
val isCanvasCareer = activity.intent?.getBooleanExtra(CANVAS_CAREER, false) ?: false
val intent = if (isCanvasCareer) {
Intent(activity, HorizonActivity::class.java)
} else {
Intent(activity, NavigationActivity.startActivityClass)
}

activity.intent?.extras?.let { extras ->
intent.putExtras(extras)
}

intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
activity.startActivity(intent)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import androidx.work.NetworkType
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.WorkerFactory
import com.instructure.canvasapi2.utils.ApiPrefs
import com.instructure.canvasapi2.utils.MasqueradeHelper
import com.instructure.canvasapi2.utils.PendoInitCallbackHandler
import com.instructure.loginapi.login.tasks.LogoutTask
Expand Down Expand Up @@ -67,7 +68,9 @@ class AppManager : BaseAppManager() {
).execute()
}

schedulePandataUpload()
if (ApiPrefs.mobileConsent) {
schedulePandataUpload()
}
initPendo()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,8 @@ class SplashActivity : BaseCanvasActivity() {
}

private suspend fun setupPendoTracking(user: User) {
if (!ApiPrefs.mobileConsent) return

val featureFlagsResult = FeaturesManager.getEnvironmentFeatureFlagsAsync(true).await().dataOrNull
val sendUsageMetrics = featureFlagsResult?.get(FeaturesManager.SEND_USAGE_METRICS) ?: false
if (sendUsageMetrics) {
Expand Down
Loading
Loading