diff --git a/apps/build.gradle b/apps/build.gradle
index a0b3f6aaa8..5d6de59820 100644
--- a/apps/build.gradle
+++ b/apps/build.gradle
@@ -34,6 +34,7 @@ buildscript {
classpath Plugins.FIREBASE_CRASHLYTICS
if (project.coverageEnabled) { classpath Plugins.JACOCO_ANDROID }
classpath Plugins.HILT
+ classpath Plugins.KSP
}
}
@@ -50,10 +51,40 @@ allprojects {
username pspdfMavenUser
password pspdfMavenPass
}
- url 'https://customers.pspdfkit.com/maven/'
+ url 'https://my.nutrient.io/maven'
}
maven { url "https://maven.google.com/" }
}
+
+ plugins.withType(com.android.build.gradle.BasePlugin) {
+ android {
+ packaging {
+ resources {
+ pickFirsts += [
+ 'META-INF/INDEX.LIST',
+ 'META-INF/io.netty.versions.properties'
+ ]
+ merges += [
+ 'META-INF/LICENSE*',
+ 'META-INF/NOTICE*',
+ 'META-INF/DEPENDENCIES*'
+ ]
+ excludes += [
+ 'META-INF/DEPENDENCIES',
+ 'META-INF/LICENSE',
+ 'META-INF/LICENSE.txt',
+ 'META-INF/LICENSE.md',
+ 'META-INF/NOTICE',
+ 'META-INF/NOTICE.txt',
+ 'META-INF/NOTICE.md',
+ 'META-INF/maven/**',
+ 'META-INF/*.kotlin_module',
+ 'META-INF/services/javax.annotation.processing.Processor'
+ ]
+ }
+ }
+ }
+ }
}
task assembleAllApps() {
@@ -72,4 +103,3 @@ configurations.all{
}
}
}
-
diff --git a/apps/buildSrc/src/main/java/GlobalDependencies.kt b/apps/buildSrc/src/main/java/GlobalDependencies.kt
index a10251eb0d..d922ebaa3c 100644
--- a/apps/buildSrc/src/main/java/GlobalDependencies.kt
+++ b/apps/buildSrc/src/main/java/GlobalDependencies.kt
@@ -8,7 +8,7 @@ object Versions {
/* Build/tooling */
const val ANDROID_GRADLE_TOOLS = "8.6.1"
- const val BUILD_TOOLS = "34.0.0"
+ const val BUILD_TOOLS = "35.0.0"
/* Testing */
const val JUNIT = "4.13.2"
@@ -18,31 +18,31 @@ object Versions {
/* Kotlin */
const val KOTLIN = "2.0.21"
const val KOTLIN_COROUTINES = "1.9.0"
+ const val KSP = "2.0.21-1.0.27"
/* Google, Play Services */
- const val GOOGLE_SERVICES = "4.4.2"
+ const val GOOGLE_SERVICES = "4.4.3"
/* Others */
- const val APOLLO = "4.1.1"
- const val PSPDFKIT = "2024.3.1"
+ const val APOLLO = "4.3.3"
+ const val NUTRIENT = "10.7.0"
const val PHOTO_VIEW = "2.3.0"
const val MOBIUS = "1.2.1"
- const val HILT = "2.52"
- const val HILT_ANDROIDX = "1.2.0"
- const val LIFECYCLE = "2.8.6"
- const val FRAGMENT = "1.8.4"
- const val WORK_MANAGER = "2.9.1"
- const val WORK_TEST = "2.9.1"
- const val GLIDE_VERSION = "4.16.0"
+ const val HILT = "2.57.2"
+ const val HILT_ANDROIDX = "1.3.0"
+ const val LIFECYCLE = "2.9.4"
+ const val FRAGMENT = "1.8.9"
+ const val WORK_MANAGER = "2.10.5"
+ const val GLIDE_VERSION = "5.0.5"
const val RETROFIT = "2.11.0"
const val OKHTTP = "4.12.0"
- const val ROOM = "2.6.1"
- const val HAMCREST = "2.2"
- const val NAVIGATION = "2.8.3"
- const val MEDIA3 = "1.6.1"
- const val DATASTORE = "1.1.1"
- const val LOTTIE = "6.5.2"
- const val ENCRYPTED_SHARED_PREFERENCES = "1.0.0"
+ const val ROOM = "2.7.0"
+ const val HAMCREST = "3.0"
+ const val NAVIGATION = "2.9.5"
+ const val MEDIA3 = "1.8.0"
+ const val DATASTORE = "1.1.7"
+ const val LOTTIE = "6.6.6"
+ const val ENCRYPTED_SHARED_PREFERENCES = "1.1.0"
const val JAVA_JWT = "4.5.0"
const val GLANCE = "1.1.1"
const val LIVEDATA = "1.9.0"
@@ -65,28 +65,27 @@ object Libs {
const val ANDROIDX_APPCOMPAT = "androidx.appcompat:appcompat:1.7.0"
const val ANDROIDX_BROWSER = "androidx.browser:browser:1.8.0"
const val ANDROIDX_CARDVIEW = "androidx.cardview:cardview:1.0.0"
- const val ANDROIDX_CONSTRAINT_LAYOUT = "androidx.constraintlayout:constraintlayout:2.1.4"
+ const val ANDROIDX_CONSTRAINT_LAYOUT = "androidx.constraintlayout:constraintlayout:2.2.0"
const val ANDROIDX_EXIF = "androidx.exifinterface:exifinterface:1.3.7"
const val ANDROIDX_FRAGMENT = "androidx.fragment:fragment:${Versions.FRAGMENT}"
const val ANDROIDX_FRAGMENT_KTX = "androidx.fragment:fragment-ktx:${Versions.FRAGMENT}"
const val ANDROIDX_PALETTE = "androidx.palette:palette:1.0.0"
const val ANDROIDX_PERCENT = "androidx.percentlayout:percentlayout:1.0.0"
- const val ANDROIDX_RECYCLERVIEW = "androidx.recyclerview:recyclerview:1.3.2"
+ const val ANDROIDX_RECYCLERVIEW = "androidx.recyclerview:recyclerview:1.4.0"
const val ANDROIDX_VECTOR = "androidx.vectordrawable:vectordrawable:1.2.0"
const val ANDROIDX_SWIPE_REFRESH_LAYOUT = "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
const val ANDROIDX_CORE_TESTING = "androidx.arch.core:core-testing:2.2.0"
const val ANDROIDX_WORK_MANAGER = "androidx.work:work-runtime:${Versions.WORK_MANAGER}"
const val ANDROIDX_WORK_MANAGER_KTX = "androidx.work:work-runtime-ktx:${Versions.WORK_MANAGER}"
- const val ANDROIDX_WORK_TEST = "androidx.work:work-testing:${Versions.WORK_TEST}"
- const val ANDROIDX_WEBKIT = "androidx.webkit:webkit:1.9.0"
- const val ANDROIDX_DATABINDING_COMPILER = "androidx.databinding:databinding-compiler:${Versions.ANDROID_GRADLE_TOOLS}" // This is bundled with the gradle plugin so we use the same version
- const val ANDROIDX_COMPOSE_ACTIVITY = "androidx.activity:activity-compose:1.9.0"
+ const val ANDROIDX_WORK_TEST = "androidx.work:work-testing:${Versions.WORK_MANAGER}"
+ const val ANDROIDX_WEBKIT = "androidx.webkit:webkit:1.12.0"
+ const val ANDROIDX_COMPOSE_ACTIVITY = "androidx.activity:activity-compose:1.10.0"
const val DATASTORE = "androidx.datastore:datastore-preferences:${Versions.DATASTORE}"
const val ENCRYPTED_SHARED_PREFERENCES = "androidx.security:security-crypto:${Versions.ENCRYPTED_SHARED_PREFERENCES}"
const val JAVA_JWT = "com.auth0:java-jwt:${Versions.JAVA_JWT}"
/* Firebase */
- const val FIREBASE_BOM = "com.google.firebase:firebase-bom:33.4.0"
+ const val FIREBASE_BOM = "com.google.firebase:firebase-bom:34.3.0"
const val FIREBASE_CRASHLYTICS = "com.google.firebase:firebase-crashlytics"
const val FIREBASE_MESSAGING = "com.google.firebase:firebase-messaging"
const val FIREBASE_CONFIG = "com.google.firebase:firebase-config"
@@ -95,7 +94,7 @@ object Libs {
/* Google Dependencies */
const val PLAY_IN_APP_UPDATES = "com.google.android.play:app-update:2.1.0"
const val FLEXBOX_LAYOUT = "com.google.android.flexbox:flexbox:3.0.0"
- const val MATERIAL_DESIGN = "com.google.android.material:material:1.12.0"
+ const val MATERIAL_DESIGN = "com.google.android.material:material:1.13.0"
/* Mobius */
const val MOBIUS_CORE = "com.spotify.mobius:mobius-core:${Versions.MOBIUS}"
@@ -133,7 +132,7 @@ object Libs {
const val COMPOSE_VIEW_MODEL = "androidx.lifecycle:lifecycle-viewmodel-compose:${Versions.LIFECYCLE}"
const val COMPOSE_NAVIGATION = "androidx.navigation:navigation-compose:2.8.9"
/* Media and content handling */
- const val PSPDFKIT = "com.pspdfkit:pspdfkit:${Versions.PSPDFKIT}"
+ const val NUTRIENT = "io.nutrient:nutrient:${Versions.NUTRIENT}"
const val MEDIA3 = "androidx.media3:media3-exoplayer:${Versions.MEDIA3}"
const val MEDIA3_UI = "androidx.media3:media3-ui:${Versions.MEDIA3}"
const val MEDIA3_HLS = "androidx.media3:media3-exoplayer-hls:${Versions.MEDIA3}"
@@ -171,7 +170,7 @@ object Libs {
const val APACHE_COMMONS_TEXT = "org.apache.commons:commons-text:1.12.0"
const val CAMERA_VIEW = "com.otaliastudios:cameraview:2.7.2"
- const val PENDO = "sdk.pendo.io:pendoIO:3.6+"
+ const val PENDO = "sdk.pendo.io:pendoIO:3.7.+"
const val ROOM = "androidx.room:room-runtime:${Versions.ROOM}"
const val ROOM_COMPILER = "androidx.room:room-compiler:${Versions.ROOM}"
@@ -183,7 +182,7 @@ object Libs {
const val RRULE = "org.scala-saddle:google-rfc-2445:20110304"
// Compose
- const val COMPOSE_BOM = "androidx.compose:compose-bom:2024.09.02"
+ const val COMPOSE_BOM = "androidx.compose:compose-bom:2025.09.01"
const val COMPOSE_MATERIAL = "androidx.compose.material:material"
const val COMPOSE_MATERIAL_ICONS = "androidx.compose.material:material-icons-core"
const val COMPOSE_PREVIEW = "androidx.compose.ui:ui-tooling-preview"
@@ -191,11 +190,11 @@ object Libs {
const val COMPOSE_UI = "androidx.compose.ui:ui-android"
const val COMPOSE_UI_TEST = "androidx.compose.ui:ui-test-junit4"
const val COMPOSE_UI_TEST_MANIFEST = "androidx.compose.ui:ui-test-manifest"
- const val COMPOSE_MATERIAL_3 = "androidx.compose.material3:material3:1.4.0-alpha12"
+ const val COMPOSE_MATERIAL_3 = "androidx.compose.material3:material3:1.4.0"
const val COMPOSE_ADAPTIVE = "androidx.compose.material3.adaptive:adaptive"
const val COMPOSE_MATERIAL3_WINDOW_SIZE = "androidx.compose.material3:material3-window-size-class"
- const val COMPOSE_NAVIGATION_HILT = "androidx.hilt:hilt-navigation-compose:1.2.0"
- const val COMPOSE_FRAGMENT = "androidx.fragment:fragment-compose:1.8.6"
+ const val COMPOSE_NAVIGATION_HILT = "androidx.hilt:hilt-navigation-compose:1.3.0"
+ const val COMPOSE_FRAGMENT = "androidx.fragment:fragment-compose:1.8.9"
// Glance
const val GLANCE = "androidx.glance:glance:${Versions.GLANCE}"
@@ -220,4 +219,5 @@ object Plugins {
const val GOOGLE_SERVICES = "com.google.gms:google-services:${Versions.GOOGLE_SERVICES}"
const val JACOCO_ANDROID = "com.dicedmelon.gradle:jacoco-android:${Versions.JACOCO_ANDROID}"
const val HILT = "com.google.dagger:hilt-android-gradle-plugin:${Versions.HILT}"
+ const val KSP = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:${Versions.KSP}"
}
diff --git a/apps/gradle.properties b/apps/gradle.properties
index 61364d13a3..01ce297dfc 100644
--- a/apps/gradle.properties
+++ b/apps/gradle.properties
@@ -3,4 +3,4 @@ android.enableJetifier=true
android.nonFinalResIds=false
android.nonTransitiveRClass=false
android.useAndroidX=true
-org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
\ No newline at end of file
+org.gradle.jvmargs=-Xmx6g -XX:MaxMetaspaceSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
\ No newline at end of file
diff --git a/apps/parent/build.gradle b/apps/parent/build.gradle
index d8c7168f16..1736c5549c 100644
--- a/apps/parent/build.gradle
+++ b/apps/parent/build.gradle
@@ -19,7 +19,8 @@ plugins {
id 'com.android.application'
id 'com.google.gms.google-services'
id 'org.jetbrains.kotlin.android'
- id 'kotlin-kapt'
+ id 'kotlin-kapt' // Keep kapt for Data Binding
+ id 'com.google.devtools.ksp'
id 'com.google.firebase.crashlytics'
id 'dagger.hilt.android.plugin'
id 'org.jetbrains.kotlin.plugin.compose'
@@ -51,15 +52,23 @@ android {
PrivateData.merge(project, "parent")
}
- packagingOptions {
- exclude 'META-INF/maven/com.google.guava/guava/pom.xml'
- exclude 'META-INF/maven/com.google.guava/guava/pom.properties'
- exclude 'META-INF/DEPENDENCIES'
- exclude 'META-INF/LICENSE'
- exclude 'META-INF/LICENSE.txt'
- exclude 'META-INF/NOTICE'
- exclude 'META-INF/rxjava.properties'
- exclude 'LICENSE.txt'
+ packaging {
+ resources {
+ pickFirsts += [
+ 'META-INF/INDEX.LIST',
+ 'META-INF/io.netty.versions.properties'
+ ]
+ excludes += [
+ 'META-INF/DEPENDENCIES',
+ 'META-INF/LICENSE',
+ 'META-INF/LICENSE.txt',
+ 'META-INF/NOTICE',
+ 'META-INF/NOTICE.txt',
+ 'META-INF/maven/com.google.guava/guava/pom.properties',
+ 'META-INF/maven/com.google.guava/guava/pom.xml',
+ 'META-INF/rxjava.properties'
+ ]
+ }
}
@@ -194,14 +203,14 @@ dependencies {
/* DI */
implementation Libs.HILT
- kapt Libs.HILT_COMPILER
+ ksp Libs.HILT_COMPILER
implementation Libs.HILT_ANDROIDX_WORK
- kapt Libs.HILT_ANDROIDX_COMPILER
+ ksp Libs.HILT_ANDROIDX_COMPILER
androidTestImplementation Libs.HILT_TESTING
/* ROOM */
implementation Libs.ROOM
- kapt Libs.ROOM_COMPILER
+ ksp Libs.ROOM_COMPILER
implementation Libs.ROOM_COROUTINES
/* Navigation */
diff --git a/apps/parent/proguard-rules.txt b/apps/parent/proguard-rules.txt
index bde42b7512..ab46a57d84 100644
--- a/apps/parent/proguard-rules.txt
+++ b/apps/parent/proguard-rules.txt
@@ -254,4 +254,19 @@
-dontwarn java.beans.SimpleBeanInfo
-keep class androidx.navigation.** { *; }
- -keep interface androidx.navigation.** { *; }
\ No newline at end of file
+ -keep interface androidx.navigation.** { *; }
+
+# Netty and BlockHound integration
+-dontwarn reactor.blockhound.integration.BlockHoundIntegration
+-dontwarn io.netty.util.internal.Hidden$NettyBlockHoundIntegration
+-keep class reactor.blockhound.integration.** { *; }
+-keep class io.netty.util.internal.Hidden$NettyBlockHoundIntegration { *; }
+
+# Additional Netty keep rules for R8
+-dontwarn io.netty.**
+-keep class io.netty.** { *; }
+-keepclassmembers class io.netty.** { *; }
+
+# BlockHound related classes
+-dontwarn reactor.blockhound.**
+-keep class reactor.blockhound.** { *; }
diff --git a/apps/parent/src/androidTest/java/com/instructure/parentapp/ui/espresso/TestAppManager.kt b/apps/parent/src/androidTest/java/com/instructure/parentapp/ui/espresso/TestAppManager.kt
index 5677669b65..7f637c07c3 100644
--- a/apps/parent/src/androidTest/java/com/instructure/parentapp/ui/espresso/TestAppManager.kt
+++ b/apps/parent/src/androidTest/java/com/instructure/parentapp/ui/espresso/TestAppManager.kt
@@ -17,6 +17,7 @@
package com.instructure.parentapp.ui.espresso
+import androidx.work.DefaultWorkerFactory
import androidx.work.WorkerFactory
import com.instructure.pandautils.features.reminder.AlarmScheduler
import com.instructure.parentapp.util.BaseAppManager
@@ -26,7 +27,7 @@ open class TestAppManager : BaseAppManager() {
private var workerFactory: WorkerFactory? = null
override fun getWorkManagerFactory(): WorkerFactory {
- return workerFactory ?: WorkerFactory.getDefaultWorkerFactory()
+ return workerFactory ?: DefaultWorkerFactory
}
override fun getScheduler(): AlarmScheduler? {
diff --git a/apps/settings.gradle b/apps/settings.gradle
index 6be9862e0f..99bd1d48a7 100644
--- a/apps/settings.gradle
+++ b/apps/settings.gradle
@@ -7,7 +7,7 @@ pluginManagement {
}
}
dependencies {
- classpath("com.android.tools:r8:8.2.47")
+ classpath("com.android.tools:r8:8.13.6")
}
}
}
@@ -29,7 +29,6 @@ include ':pandautils'
include ':rceditor'
include ':recyclerview'
include ':pandares'
-include ':DocumentScanner'
include ':horizon'
project(':annotations').projectDir = new File(rootProject.projectDir, '/../libs/annotations')
@@ -43,5 +42,4 @@ project(':pandautils').projectDir = new File(rootProject.projectDir, '/../libs/p
project(':rceditor').projectDir = new File(rootProject.projectDir, '/../libs/rceditor')
project(':recyclerview').projectDir = new File(rootProject.projectDir, '/../libs/recyclerview')
project(':pandares').projectDir = new File(rootProject.projectDir, '/../libs/pandares')
-project(':DocumentScanner').projectDir = new File(rootProject.projectDir, '/../libs/DocumentScanner')
project(':horizon').projectDir = new File(rootProject.projectDir, '/../libs/horizon')
diff --git a/apps/student/build.gradle b/apps/student/build.gradle
index 9c288dbd49..0f87999743 100644
--- a/apps/student/build.gradle
+++ b/apps/student/build.gradle
@@ -16,7 +16,8 @@
*/
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-kapt'
+apply plugin: 'kotlin-kapt' // Keep kapt for Data Binding
+apply plugin: 'com.google.devtools.ksp'
apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'dagger.hilt.android.plugin'
apply plugin: 'org.jetbrains.kotlin.plugin.compose'
@@ -61,15 +62,23 @@ android {
}
}
- packagingOptions {
- exclude 'META-INF/maven/com.google.guava/guava/pom.xml'
- exclude 'META-INF/maven/com.google.guava/guava/pom.properties'
- exclude 'META-INF/DEPENDENCIES'
- exclude 'META-INF/LICENSE'
- exclude 'META-INF/LICENSE.txt'
- exclude 'META-INF/NOTICE'
- exclude 'META-INF/rxjava.properties'
- exclude 'LICENSE.txt'
+ packaging {
+ resources {
+ pickFirsts += [
+ 'META-INF/INDEX.LIST',
+ 'META-INF/io.netty.versions.properties'
+ ]
+ excludes += [
+ 'META-INF/DEPENDENCIES',
+ 'META-INF/LICENSE',
+ 'META-INF/LICENSE.txt',
+ 'META-INF/NOTICE',
+ 'META-INF/NOTICE.txt',
+ 'META-INF/maven/com.google.guava/guava/pom.properties',
+ 'META-INF/maven/com.google.guava/guava/pom.xml',
+ 'META-INF/rxjava.properties'
+ ]
+ }
}
lintOptions {
@@ -227,7 +236,6 @@ dependencies {
implementation project(path: ':annotations')
implementation project(path: ':rceditor')
implementation project(path: ':interactions')
- implementation project(path: ':DocumentScanner')
implementation project(path: ':horizon')
/* Android Test Dependencies */
@@ -294,15 +302,15 @@ dependencies {
implementation Libs.LIVE_DATA
implementation Libs.VIEW_MODE_SAVED_STATE
implementation Libs.ANDROIDX_FRAGMENT_KTX
- kapt Libs.LIFECYCLE_COMPILER
+ kapt Libs.LIFECYCLE_COMPILER // Keep kapt for lifecycle if it includes Data Binding
/* DI */
implementation Libs.HILT
- kapt Libs.HILT_COMPILER
+ ksp Libs.HILT_COMPILER
androidTestImplementation Libs.HILT_TESTING
- kaptAndroidTestQa Libs.HILT_TESTING_COMPILER
+ kspAndroidTest Libs.HILT_TESTING_COMPILER
implementation Libs.HILT_ANDROIDX_WORK
- kapt Libs.HILT_ANDROIDX_COMPILER
+ ksp Libs.HILT_ANDROIDX_COMPILER
androidTestImplementation Libs.UI_AUTOMATOR
@@ -314,7 +322,7 @@ dependencies {
/* ROOM */
implementation Libs.ROOM
- kapt Libs.ROOM_COMPILER
+ ksp Libs.ROOM_COMPILER
implementation Libs.ROOM_COROUTINES
testImplementation Libs.HAMCREST
diff --git a/apps/student/proguard-rules.txt b/apps/student/proguard-rules.txt
index aec6327015..90370642f2 100644
--- a/apps/student/proguard-rules.txt
+++ b/apps/student/proguard-rules.txt
@@ -269,3 +269,18 @@
-keep class androidx.navigation.** { *; }
-keep interface androidx.navigation.** { *; }
+
+# Netty and BlockHound integration
+-dontwarn reactor.blockhound.integration.BlockHoundIntegration
+-dontwarn io.netty.util.internal.Hidden$NettyBlockHoundIntegration
+-keep class reactor.blockhound.integration.** { *; }
+-keep class io.netty.util.internal.Hidden$NettyBlockHoundIntegration { *; }
+
+# Additional Netty keep rules for R8
+-dontwarn io.netty.**
+-keep class io.netty.** { *; }
+-keepclassmembers class io.netty.** { *; }
+
+# BlockHound related classes
+-dontwarn reactor.blockhound.**
+-keep class reactor.blockhound.** { *; }
diff --git a/apps/student/src/androidTest/java/com/instructure/student/espresso/TestAppManager.kt b/apps/student/src/androidTest/java/com/instructure/student/espresso/TestAppManager.kt
index fc00cc637e..3b21a05d1d 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/espresso/TestAppManager.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/espresso/TestAppManager.kt
@@ -16,6 +16,7 @@
*/
package com.instructure.student.espresso
+import androidx.work.DefaultWorkerFactory
import androidx.work.WorkerFactory
import com.instructure.student.util.BaseAppManager
@@ -24,6 +25,6 @@ open class TestAppManager : BaseAppManager() {
var workerFactory: WorkerFactory? = null
override fun getWorkManagerFactory(): WorkerFactory {
- return workerFactory ?: WorkerFactory.getDefaultWorkerFactory()
+ return workerFactory ?: DefaultWorkerFactory
}
}
\ No newline at end of file
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/classic/GradesE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/classic/GradesE2ETest.kt
index 2669a7eb0e..8114133b74 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/classic/GradesE2ETest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/classic/GradesE2ETest.kt
@@ -81,7 +81,8 @@ class GradesE2ETest: StudentComposeTest() {
courseGradesPage.assertItemDisplayed(quizMatcher)
courseGradesPage.assertGradeNotDisplayed(quizMatcher)
- val dueDateInCanvasFormat = getDateInCanvasCalendarFormat(1.days.fromNow.iso8601)
+ var dueDateInCanvasFormat = getDateInCanvasCalendarFormat(1.days.fromNow.iso8601)
+ dueDateInCanvasFormat = dueDateInCanvasFormat.replace(" 0", " ")
Log.d(ASSERTION_TAG, "Assert that the '${assignment.name}' assignment's due date is tomorrow ('$dueDateInCanvasFormat').")
courseGradesPage.assertAssignmentDueDate(assignment.name, dueDateInCanvasFormat)
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/PickerSubmissionUploadInteractionTest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/PickerSubmissionUploadInteractionTest.kt
index 86ac820578..0620ff5dfa 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/PickerSubmissionUploadInteractionTest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/PickerSubmissionUploadInteractionTest.kt
@@ -190,37 +190,6 @@ class PickerSubmissionUploadInteractionTest : StudentComposeTest() {
pickerSubmissionUploadPage.assertFileDisplayed(mockedFileName)
}
- @Test
- @TestMetaData(Priority.COMMON, FeatureCategory.SUBMISSIONS, TestCategory.INTERACTION)
- fun testFab_scanner(){
- val scannerComponent = "com.instructure.student.features.documentscanning.DocumentScanningActivity"
-
- goToSubmissionPicker()
-
- Intents.init()
- try {
- val context = getInstrumentation().targetContext
- val dir = context.externalCacheDir
- val sampleFile = File(dir, mockedFileName)
- val uri = Uri.fromFile(sampleFile)
- val resultData = Intent().apply { data = uri }
- val scannerResult = Instrumentation.ActivityResult(Activity.RESULT_OK, resultData)
-
- intending(
- IntentMatchers.hasComponent(scannerComponent)
- ).respondWith(scannerResult)
-
- pickerSubmissionUploadPage.chooseScanner()
- } finally {
- release()
- }
-
- pickerSubmissionUploadPage.waitForSubmitButtonToAppear()
-
- pickerSubmissionUploadPage.assertFileDisplayed(mockedFileName)
-
- }
-
@Test
@TestMetaData(Priority.COMMON, FeatureCategory.SUBMISSIONS, TestCategory.INTERACTION)
fun testDeleteFile() {
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/classic/PickerSubmissionUploadPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/classic/PickerSubmissionUploadPage.kt
index 97d3136adf..3c96301b25 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/classic/PickerSubmissionUploadPage.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/classic/PickerSubmissionUploadPage.kt
@@ -33,7 +33,6 @@ class PickerSubmissionUploadPage : BasePage(R.id.pickerSubmissionUploadPage) {
private val deviceIcon by OnViewWithId(R.id.sourceDeviceIcon)
private val cameraIcon by OnViewWithId(R.id.sourceCameraIcon)
private val galleryIcon by OnViewWithId(R.id.sourceGalleryIcon)
- private val scannerIcon by OnViewWithId(R.id.sourceDocumentScanningIcon)
private val deleteButton by OnViewWithId(R.id.deleteButton)
fun chooseDevice() {
@@ -48,10 +47,6 @@ class PickerSubmissionUploadPage : BasePage(R.id.pickerSubmissionUploadPage) {
galleryIcon.click()
}
- fun chooseScanner() {
- scannerIcon.click()
- }
-
fun waitForSubmitButtonToAppear() {
waitForViewWithText(R.string.submit)
}
diff --git a/apps/student/src/main/AndroidManifest.xml b/apps/student/src/main/AndroidManifest.xml
index 6cc7ffea16..af986c7a9d 100644
--- a/apps/student/src/main/AndroidManifest.xml
+++ b/apps/student/src/main/AndroidManifest.xml
@@ -286,12 +286,6 @@
-
-
diff --git a/apps/student/src/main/java/com/instructure/student/features/documentscanning/BitmapExtensions.kt b/apps/student/src/main/java/com/instructure/student/features/documentscanning/BitmapExtensions.kt
deleted file mode 100644
index a4a8cd2a98..0000000000
--- a/apps/student/src/main/java/com/instructure/student/features/documentscanning/BitmapExtensions.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2022 - 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 .
- */
-
-package com.instructure.student.features.documentscanning
-
-import android.graphics.*
-
-fun Bitmap.toGrayscale(): Bitmap {
- val width = this.width
- val height = this.height
-
- val grayscaleBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
- val canvas = Canvas(grayscaleBitmap)
- val paint = Paint()
- val colorMatrix = ColorMatrix()
- colorMatrix.setSaturation(0f)
- val colorMatrixFilter = ColorMatrixColorFilter(colorMatrix)
- paint.colorFilter = colorMatrixFilter
- canvas.drawBitmap(this, 0f, 0f, paint)
- return grayscaleBitmap
-}
-
-fun Bitmap.toMonochrome(): Bitmap {
- val width = this.width
- val height = this.height
-
- val monochromeBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
-
- val hsv = FloatArray(3)
- for (column in 0 until width) {
- for (row in 0 until height) {
- Color.colorToHSV(this.getPixel(column, row), hsv)
- if (hsv[2] > 0.5f) {
- monochromeBitmap.setPixel(column, row, Color.WHITE)
- } else {
- monochromeBitmap.setPixel(column, row, Color.BLACK)
- }
- }
- }
-
- return monochromeBitmap
-}
\ No newline at end of file
diff --git a/apps/student/src/main/java/com/instructure/student/features/documentscanning/DocumentScanningActivity.kt b/apps/student/src/main/java/com/instructure/student/features/documentscanning/DocumentScanningActivity.kt
deleted file mode 100644
index 73badd284b..0000000000
--- a/apps/student/src/main/java/com/instructure/student/features/documentscanning/DocumentScanningActivity.kt
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2022 - 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 .
- */
-
-package com.instructure.student.features.documentscanning
-
-import android.app.Activity
-import android.content.Intent
-import android.graphics.Bitmap
-import android.os.Bundle
-import androidx.activity.viewModels
-import androidx.core.content.ContextCompat
-import androidx.core.net.toUri
-import androidx.databinding.DataBindingUtil
-import com.instructure.pandautils.binding.viewBinding
-import com.instructure.pandautils.utils.ViewStyler
-import com.instructure.student.R
-import com.instructure.student.databinding.ActivityDocumentScanningBinding
-import com.zynksoftware.documentscanner.ScanActivity
-import com.zynksoftware.documentscanner.model.DocumentScannerErrorModel
-import com.zynksoftware.documentscanner.model.ScannerResults
-import dagger.hilt.android.AndroidEntryPoint
-import java.io.File
-import java.io.FileOutputStream
-import java.text.SimpleDateFormat
-import java.util.*
-
-@AndroidEntryPoint
-class DocumentScanningActivity : ScanActivity() {
-
- private lateinit var binding: ActivityDocumentScanningBinding
-
- private val viewModel: DocumentScanningViewModel by viewModels()
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- binding = DataBindingUtil.setContentView(this, R.layout.activity_document_scanning)
- binding.lifecycleOwner = this
- binding.viewModel = viewModel
-
- addFragmentContentLayout()
-
- setupToolbar()
-
- viewModel.events.observe(this) { event ->
- event.getContentIfNotHandled()?.let {
- handleAction(it)
- }
- }
- }
-
- private fun handleAction(action: DocumentScanningAction) {
- when (action) {
- is DocumentScanningAction.SaveBitmapAction -> {
- val file = File(filesDir, "scanned_${SimpleDateFormat("yyyyMMddkkmmss", Locale.getDefault()).format(Date())}.jpg")
- var fileOutputStream: FileOutputStream? = null
- try {
- fileOutputStream = FileOutputStream(file.absolutePath)
- action.bitmap.compress(Bitmap.CompressFormat.JPEG, action.quality, fileOutputStream)
- val intent = Intent()
- intent.data = file.toUri()
- setResult(Activity.RESULT_OK, intent)
- finish()
- } finally {
- fileOutputStream?.run {
- flush()
- close()
- }
- }
- }
- }
- }
-
- override fun onClose() {
- setResult(RESULT_CANCELED)
- finish()
- }
-
- override fun onError(error: DocumentScannerErrorModel) {
-
- }
-
- override fun onSuccess(scannerResults: ScannerResults) {
- viewModel.setScannerResults(scannerResults)
- }
-
- private fun setupToolbar() {
- binding.toolbar.apply {
- setTitle(R.string.documentScanningTitle)
- navigationIcon = ContextCompat.getDrawable(this@DocumentScanningActivity, R.drawable.ic_back_arrow)
- navigationIcon?.isAutoMirrored = true
- ViewStyler.themeToolbarLight(this@DocumentScanningActivity, this)
- setNavigationContentDescription(R.string.close)
- setNavigationOnClickListener { onClose() }
- }
- }
-}
\ No newline at end of file
diff --git a/apps/student/src/main/java/com/instructure/student/features/documentscanning/DocumentScanningViewData.kt b/apps/student/src/main/java/com/instructure/student/features/documentscanning/DocumentScanningViewData.kt
deleted file mode 100644
index 7db75cde27..0000000000
--- a/apps/student/src/main/java/com/instructure/student/features/documentscanning/DocumentScanningViewData.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2022 - 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 .
- */
-
-package com.instructure.student.features.documentscanning
-
-import android.graphics.Bitmap
-import androidx.databinding.BaseObservable
-import androidx.databinding.Bindable
-import com.instructure.student.features.documentscanning.itemviewmodels.FilterItemViewModel
-
-data class DocumentScanningViewData(
- @get:Bindable var selectedBitmap: Bitmap,
- val filterItemViewModels: List
-) : BaseObservable()
-
-data class FilterItemViewData(
- val bitmap: Bitmap,
- val name: String
-)
-
-sealed class DocumentScanningAction {
- data class SaveBitmapAction(val bitmap: Bitmap, val quality: Int): DocumentScanningAction()
-}
\ No newline at end of file
diff --git a/apps/student/src/main/java/com/instructure/student/features/documentscanning/DocumentScanningViewModel.kt b/apps/student/src/main/java/com/instructure/student/features/documentscanning/DocumentScanningViewModel.kt
deleted file mode 100644
index 7a99039541..0000000000
--- a/apps/student/src/main/java/com/instructure/student/features/documentscanning/DocumentScanningViewModel.kt
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2022 - 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 .
- */
-
-package com.instructure.student.features.documentscanning
-
-import android.content.res.Resources
-import android.graphics.Bitmap
-import android.graphics.BitmapFactory
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.ViewModel
-import com.instructure.pandautils.R
-import com.instructure.pandautils.BR
-import com.instructure.student.features.documentscanning.itemviewmodels.FilterItemViewModel
-import com.instructure.pandautils.mvvm.Event
-import com.instructure.pandautils.mvvm.ViewState
-import com.zynksoftware.documentscanner.model.ScannerResults
-import dagger.hilt.android.lifecycle.HiltViewModel
-import javax.inject.Inject
-
-@HiltViewModel
-class DocumentScanningViewModel @Inject constructor(
- private val resources: Resources
-) : ViewModel() {
-
- val state: LiveData
- get() = _state
- private val _state = MutableLiveData()
-
- val data: LiveData
- get() = _data
- private val _data = MutableLiveData()
-
- val events: LiveData>
- get() = _events
- private val _events = MutableLiveData>()
-
- private lateinit var selectedItem: FilterItemViewModel
-
- fun setScannerResults(results: ScannerResults) {
- _state.postValue(ViewState.Loading)
- createViewData(results)
- }
-
- private fun createViewData(results: ScannerResults) {
- if (results.croppedImageFile != null && results.originalImageFile != null) {
- val croppedBitmap = BitmapFactory.decodeFile(results.croppedImageFile!!.path)
- val originalBitmap = BitmapFactory.decodeFile(results.originalImageFile!!.path)
- val grayscaleBitmap = croppedBitmap.toGrayscale()
- val monochromeBitmap = croppedBitmap.toMonochrome()
-
- //We no longer need these files
- results.croppedImageFile?.delete()
- results.croppedImageFile?.delete()
- results.transformedImageFile?.delete()
-
- val filters = listOf(
- createFilterViewModel(croppedBitmap, true, resources.getString(R.string.filter_name_color)),
- createFilterViewModel(grayscaleBitmap, false, resources.getString(R.string.filter_name_grayscale)),
- createFilterViewModel(monochromeBitmap, false, resources.getString(R.string.filter_name_monochrome)),
- createFilterViewModel(originalBitmap, false, resources.getString(R.string.filter_name_original))
- )
- selectedItem = filters[0]
-
- val viewData = DocumentScanningViewData(
- croppedBitmap,
- filters
- )
- _data.postValue(viewData)
- _state.postValue(ViewState.Success)
- } else {
- _state.postValue(ViewState.Error())
- }
- }
-
- private fun createFilterViewModel(bitmap: Bitmap, selected: Boolean, name: String): FilterItemViewModel {
- return FilterItemViewModel(
- FilterItemViewData(bitmap, name),
- selected,
- this::onFilterSelected
- )
- }
-
- fun onFilterSelected(itemViewModel: FilterItemViewModel) {
- selectedItem.apply {
- selected = false
- notifyPropertyChanged(BR.selected)
- }
- _data.value?.apply {
- selectedBitmap = itemViewModel.data.bitmap
- notifyPropertyChanged(BR.selectedBitmap)
- }
- selectedItem = itemViewModel
- }
-
- fun onSaveClicked() {
- _events.postValue(Event(DocumentScanningAction.SaveBitmapAction(selectedItem.data.bitmap, 100)))
- }
-}
\ No newline at end of file
diff --git a/apps/student/src/main/java/com/instructure/student/features/documentscanning/itemviewmodels/FilterItemViewModel.kt b/apps/student/src/main/java/com/instructure/student/features/documentscanning/itemviewmodels/FilterItemViewModel.kt
deleted file mode 100644
index c84895a93e..0000000000
--- a/apps/student/src/main/java/com/instructure/student/features/documentscanning/itemviewmodels/FilterItemViewModel.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2022 - 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 .
- */
-
-package com.instructure.student.features.documentscanning.itemviewmodels
-
-import androidx.databinding.BaseObservable
-import androidx.databinding.Bindable
-import com.instructure.pandautils.BR
-import com.instructure.student.features.documentscanning.FilterItemViewData
-import com.instructure.pandautils.mvvm.ItemViewModel
-import com.instructure.student.R
-
-class FilterItemViewModel(
- val data: FilterItemViewData,
- @get:Bindable var selected: Boolean,
- val onSelect: (FilterItemViewModel) -> Unit
-) : ItemViewModel, BaseObservable() {
- override val layoutId: Int = R.layout.item_document_scanning_filter
-
- fun select() {
- if (!selected) {
- selected = true
- notifyPropertyChanged(BR.selected)
- onSelect(this)
- }
- }
-}
\ No newline at end of file
diff --git a/apps/student/src/main/java/com/instructure/student/features/files/list/FileListFragment.kt b/apps/student/src/main/java/com/instructure/student/features/files/list/FileListFragment.kt
index df6e22f475..c6fd5a2a59 100644
--- a/apps/student/src/main/java/com/instructure/student/features/files/list/FileListFragment.kt
+++ b/apps/student/src/main/java/com/instructure/student/features/files/list/FileListFragment.kt
@@ -551,8 +551,9 @@ class FileListFragment : ParentFragment(), Bookmarkable, FileUploadDialogParent
}
}
- override fun workInfoLiveDataCallback(uuid: UUID?, workInfoLiveData: LiveData) {
+ override fun workInfoLiveDataCallback(uuid: UUID?, workInfoLiveData: LiveData) {
workInfoLiveData.observe(viewLifecycleOwner) {
+ if (it == null) return@observe
if (it.state == WorkInfo.State.SUCCEEDED) {
updateFileList(true)
folder?.let { fileFolder ->
diff --git a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/annnotation/AnnotationSubmissionViewModel.kt b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/annnotation/AnnotationSubmissionViewModel.kt
index 57cb35e018..9d09c607ff 100644
--- a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/annnotation/AnnotationSubmissionViewModel.kt
+++ b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/annnotation/AnnotationSubmissionViewModel.kt
@@ -59,6 +59,7 @@ class AnnotationSubmissionViewModel @Inject constructor(
ViewState.Error(resources.getString(R.string.failedToLoadSubmission))
}
} catch (e: Exception) {
+ e.printStackTrace()
_state.value = ViewState.Error(resources.getString(R.string.failedToLoadSubmission))
}
}
diff --git a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/picker/PickerSubmissionUploadEffectHandler.kt b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/picker/PickerSubmissionUploadEffectHandler.kt
index f99647fdac..726b08dbce 100644
--- a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/picker/PickerSubmissionUploadEffectHandler.kt
+++ b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/picker/PickerSubmissionUploadEffectHandler.kt
@@ -18,7 +18,6 @@ package com.instructure.student.mobius.assignmentDetails.submission.picker
import android.app.Activity
import android.content.Context
-import android.content.Intent
import android.net.Uri
import androidx.core.content.FileProvider
import com.instructure.canvasapi2.models.postmodels.FileSubmitObject
@@ -32,7 +31,6 @@ import com.instructure.pandautils.utils.getFragmentActivity
import com.instructure.pandautils.utils.remove
import com.instructure.pandautils.utils.requestPermissions
import com.instructure.student.R
-import com.instructure.student.features.documentscanning.DocumentScanningActivity
import com.instructure.student.mobius.assignmentDetails.isIntentAvailable
import com.instructure.student.mobius.assignmentDetails.submission.picker.PickerSubmissionMode.CommentAttachment
import com.instructure.student.mobius.assignmentDetails.submission.picker.PickerSubmissionMode.FileSubmission
@@ -117,9 +115,6 @@ class PickerSubmissionUploadEffectHandler(
PickerSubmissionUploadEffect.LaunchSelectFile -> {
launchSelectFile()
}
- PickerSubmissionUploadEffect.LaunchDocumentScanning -> {
- launchDocumentScanning()
- }
is PickerSubmissionUploadEffect.LoadFileContents -> {
loadFile(effect.allowedExtensions, effect.uri, context)
}
@@ -210,17 +205,6 @@ class PickerSubmissionUploadEffectHandler(
}
}
- private fun launchDocumentScanning() {
- // Get camera permission if we need it
- if (needsPermissions(
- PickerSubmissionUploadEvent.DocumentScanningClicked,
- PermissionUtils.CAMERA
- )
- ) return
- val intent = Intent(context, DocumentScanningActivity::class.java)
- (context.getFragmentActivity()).startActivityForResult(intent, REQUEST_DOCUMENT_SCANNING)
- }
-
private fun launchCamera() {
// Get camera permission if we need it
if (needsPermissions(
diff --git a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/picker/PickerSubmissionUploadModels.kt b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/picker/PickerSubmissionUploadModels.kt
index 0a2ca09bfa..20c7544991 100644
--- a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/picker/PickerSubmissionUploadModels.kt
+++ b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/picker/PickerSubmissionUploadModels.kt
@@ -25,7 +25,6 @@ sealed class PickerSubmissionUploadEvent {
object CameraClicked : PickerSubmissionUploadEvent()
object GalleryClicked : PickerSubmissionUploadEvent()
object SelectFileClicked : PickerSubmissionUploadEvent()
- object DocumentScanningClicked : PickerSubmissionUploadEvent()
data class OnFileSelected(val uri: Uri) : PickerSubmissionUploadEvent()
data class OnFileRemoved(val fileIndex: Int) : PickerSubmissionUploadEvent()
data class OnFileAdded(val file: FileSubmitObject?) : PickerSubmissionUploadEvent()
@@ -35,7 +34,6 @@ sealed class PickerSubmissionUploadEffect {
object LaunchCamera : PickerSubmissionUploadEffect()
object LaunchGallery : PickerSubmissionUploadEffect()
object LaunchSelectFile : PickerSubmissionUploadEffect()
- object LaunchDocumentScanning : PickerSubmissionUploadEffect()
data class HandleSubmit(val model: PickerSubmissionUploadModel) : PickerSubmissionUploadEffect()
data class LoadFileContents(val uri: Uri, val allowedExtensions: List) :
PickerSubmissionUploadEffect()
diff --git a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/picker/PickerSubmissionUploadUpdate.kt b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/picker/PickerSubmissionUploadUpdate.kt
index 00b68bcedb..c16a600954 100644
--- a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/picker/PickerSubmissionUploadUpdate.kt
+++ b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/picker/PickerSubmissionUploadUpdate.kt
@@ -38,7 +38,6 @@ class PickerSubmissionUploadUpdate :
PickerSubmissionUploadEvent.CameraClicked -> Next.dispatch(setOf(PickerSubmissionUploadEffect.LaunchCamera))
PickerSubmissionUploadEvent.GalleryClicked -> Next.dispatch(setOf(PickerSubmissionUploadEffect.LaunchGallery))
PickerSubmissionUploadEvent.SelectFileClicked -> Next.dispatch(setOf(PickerSubmissionUploadEffect.LaunchSelectFile))
- PickerSubmissionUploadEvent.DocumentScanningClicked -> Next.dispatch(setOf(PickerSubmissionUploadEffect.LaunchDocumentScanning))
is PickerSubmissionUploadEvent.OnFileSelected -> {
Next.next(
model.copy(isLoadingFile = true),
diff --git a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/picker/ui/PickerSubmissionUploadView.kt b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/picker/ui/PickerSubmissionUploadView.kt
index 7a6d08e6ce..8a5f4cfe74 100644
--- a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/picker/ui/PickerSubmissionUploadView.kt
+++ b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/picker/ui/PickerSubmissionUploadView.kt
@@ -60,7 +60,6 @@ class PickerSubmissionUploadView(inflater: LayoutInflater, parent: ViewGroup, va
binding.sourceDevice.setOnClickListener { consumer?.accept(PickerSubmissionUploadEvent.SelectFileClicked) }
binding.sourceGallery.setOnClickListener { consumer?.accept(PickerSubmissionUploadEvent.GalleryClicked) }
- binding.sourceDocumentScanning.setOnClickListener { consumer?.accept(PickerSubmissionUploadEvent.DocumentScanningClicked) }
}
override fun onConnect(output: Consumer) {
diff --git a/apps/student/src/main/java/com/instructure/student/util/BaseAppManager.kt b/apps/student/src/main/java/com/instructure/student/util/BaseAppManager.kt
index 8010db1157..ab26515598 100644
--- a/apps/student/src/main/java/com/instructure/student/util/BaseAppManager.kt
+++ b/apps/student/src/main/java/com/instructure/student/util/BaseAppManager.kt
@@ -21,7 +21,6 @@ import android.webkit.WebView
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.ContextCompat
import com.google.firebase.crashlytics.FirebaseCrashlytics
-import com.instructure.pandautils.utils.filecache.FileCache
import com.instructure.canvasapi2.utils.Analytics
import com.instructure.canvasapi2.utils.AnalyticsEventConstants
import com.instructure.canvasapi2.utils.Logger
@@ -32,14 +31,13 @@ import com.instructure.pandautils.utils.AppTheme
import com.instructure.pandautils.utils.AppType
import com.instructure.pandautils.utils.ColorKeeper
import com.instructure.pandautils.utils.ThemePrefs
+import com.instructure.pandautils.utils.filecache.FileCache
import com.instructure.student.BuildConfig
import com.instructure.student.R
import com.instructure.student.activity.NavigationActivity
-import com.pspdfkit.PSPDFKit
-import com.pspdfkit.exceptions.InvalidPSPDFKitLicenseException
-import com.pspdfkit.exceptions.PSPDFKitInitializationFailedException
-import com.pspdfkit.initialization.InitializationOptions
-import com.zynksoftware.documentscanner.ui.DocumentScanner
+import com.pspdfkit.Nutrient
+import com.pspdfkit.exceptions.InvalidNutrientLicenseException
+import com.pspdfkit.exceptions.NutrientInitializationFailedException
abstract class BaseAppManager : com.instructure.canvasapi2.AppManager(), AnalyticsEventHandling {
@@ -61,9 +59,7 @@ abstract class BaseAppManager : com.instructure.canvasapi2.AppManager(), Analyti
// Hold off on initializing this until we emit the user properties.
RemoteConfigUtils.initialize()
- initPSPDFKit()
-
- initDocumentScanning()
+ initNutrient()
if (BuildConfig.DEBUG) {
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(false)
@@ -110,19 +106,15 @@ abstract class BaseAppManager : com.instructure.canvasapi2.AppManager(), Analyti
}
- private fun initPSPDFKit() {
+ private fun initNutrient() {
try {
- PSPDFKit.initialize(this, InitializationOptions(licenseKey = BuildConfig.PSPDFKIT_LICENSE_KEY))
- } catch (e: PSPDFKitInitializationFailedException) {
- Logger.e("Current device is not compatible with PSPDFKIT!")
- } catch (e: InvalidPSPDFKitLicenseException) {
- Logger.e("Invalid or Trial PSPDFKIT License!")
+ Nutrient.initialize(this, BuildConfig.PSPDFKIT_LICENSE_KEY)
+ } catch (e: NutrientInitializationFailedException) {
+ Logger.e("Current device is not compatible with Nutrient!")
+ } catch (e: InvalidNutrientLicenseException) {
+ Logger.e("Invalid or Trial Nutrient License!")
}
}
- private fun initDocumentScanning() {
- DocumentScanner.init(this)
- }
-
override fun performLogoutOnAuthError() = Unit
}
\ No newline at end of file
diff --git a/apps/student/src/main/java/com/instructure/student/util/FileUtils.kt b/apps/student/src/main/java/com/instructure/student/util/FileUtils.kt
index 73fb0b1066..1e34d0599f 100644
--- a/apps/student/src/main/java/com/instructure/student/util/FileUtils.kt
+++ b/apps/student/src/main/java/com/instructure/student/util/FileUtils.kt
@@ -72,20 +72,14 @@ object FileUtils {
// We don't want to allow users to edit for submission viewing
pspdfActivityConfiguration = PdfActivityConfiguration.Builder(context)
.scrollDirection(PageScrollDirection.HORIZONTAL)
- .showThumbnailGrid()
.setThumbnailBarMode(ThumbnailBarMode.THUMBNAIL_BAR_MODE_PINNED)
- .disableAnnotationEditing()
- .disableAnnotationList()
- .disableDocumentEditor()
.fitMode(PageFitMode.FIT_TO_WIDTH)
.build()
} else {
// Standard behavior
pspdfActivityConfiguration = PdfActivityConfiguration.Builder(context)
.scrollDirection(PageScrollDirection.HORIZONTAL)
- .showThumbnailGrid()
.setDocumentInfoViewSeparated(false)
- .enableDocumentEditor()
.enabledAnnotationTools(annotationCreationList)
.editableAnnotationTypes(annotationEditList)
.fitMode(PageFitMode.FIT_TO_WIDTH)
diff --git a/apps/student/src/main/res/layout/activity_document_scanning.xml b/apps/student/src/main/res/layout/activity_document_scanning.xml
deleted file mode 100644
index a8b7347a77..0000000000
--- a/apps/student/src/main/res/layout/activity_document_scanning.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/apps/student/src/main/res/layout/activity_navigation.xml b/apps/student/src/main/res/layout/activity_navigation.xml
index d508f631e4..cdbdefd661 100644
--- a/apps/student/src/main/res/layout/activity_navigation.xml
+++ b/apps/student/src/main/res/layout/activity_navigation.xml
@@ -61,6 +61,7 @@
android:id="@+id/bottomBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:minHeight="48dp"
android:background="@color/backgroundLightestElevated"
app:elevation="0dp"
app:itemIconTint="@color/textDarkest"
diff --git a/apps/student/src/main/res/layout/fragment_picker_submission_upload.xml b/apps/student/src/main/res/layout/fragment_picker_submission_upload.xml
index 96be4e68e1..73b14a4553 100644
--- a/apps/student/src/main/res/layout/fragment_picker_submission_upload.xml
+++ b/apps/student/src/main/res/layout/fragment_picker_submission_upload.xml
@@ -160,36 +160,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/apps/teacher/build.gradle b/apps/teacher/build.gradle
index 94405a5a01..29113262da 100644
--- a/apps/teacher/build.gradle
+++ b/apps/teacher/build.gradle
@@ -16,7 +16,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-parcelize'
-apply plugin: 'kotlin-kapt'
+apply plugin: 'kotlin-kapt' // Keep kapt for Data Binding
+apply plugin: 'com.google.devtools.ksp'
apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'dagger.hilt.android.plugin'
apply plugin: 'org.jetbrains.kotlin.plugin.compose'
@@ -32,6 +33,25 @@ android {
exclude 'META-INF/rxjava.properties'
}
+ packaging {
+ resources {
+ pickFirsts += [
+ 'META-INF/INDEX.LIST',
+ 'META-INF/io.netty.versions.properties'
+ ]
+ excludes += [
+ 'META-INF/DEPENDENCIES',
+ 'META-INF/LICENSE',
+ 'META-INF/LICENSE.txt',
+ 'META-INF/NOTICE',
+ 'META-INF/NOTICE.txt',
+ 'META-INF/maven/com.google.guava/guava/pom.properties',
+ 'META-INF/maven/com.google.guava/guava/pom.xml',
+ 'META-INF/rxjava.properties'
+ ]
+ }
+ }
+
defaultConfig {
minSdkVersion Versions.MIN_SDK
targetSdkVersion Versions.TARGET_SDK
@@ -262,6 +282,9 @@ dependencies {
implementation Libs.ANDROIDX_VECTOR
implementation Libs.PLAY_IN_APP_UPDATES
+ /* Analytics */
+ implementation Libs.PENDO
+
/* Firebase */
implementation platform(Libs.FIREBASE_BOM) {
exclude group: 'com.google.firebase', module: 'firebase-analytics'
@@ -271,36 +294,28 @@ dependencies {
transitive = true
}
- testImplementation Libs.ANDROIDX_CORE_TESTING
+ implementation Libs.CAMERA_VIEW
- /* AAC */
- implementation Libs.VIEW_MODEL
- implementation Libs.LIVE_DATA
- implementation Libs.VIEW_MODE_SAVED_STATE
- implementation Libs.ANDROIDX_FRAGMENT_KTX
- kapt Libs.LIFECYCLE_COMPILER
+ testImplementation Libs.ANDROIDX_CORE_TESTING
/* DI */
implementation Libs.HILT
- kapt Libs.HILT_COMPILER
+ ksp Libs.HILT_COMPILER
androidTestImplementation Libs.HILT_TESTING
- kaptAndroidTestQa Libs.HILT_TESTING_COMPILER
+ kspAndroidTest Libs.HILT_TESTING_COMPILER
implementation Libs.HILT_ANDROIDX_WORK
- kapt Libs.HILT_ANDROIDX_COMPILER
-
- androidTestImplementation Libs.UI_AUTOMATOR
-
- /* WorkManager */
- implementation Libs.ANDROIDX_WORK_MANAGER
- implementation Libs.ANDROIDX_WORK_MANAGER_KTX
+ ksp Libs.HILT_ANDROIDX_COMPILER
- implementation Libs.PENDO
-
- implementation Libs.CAMERA_VIEW
+ /* AAC */
+ implementation Libs.VIEW_MODEL
+ implementation Libs.LIVE_DATA
+ implementation Libs.VIEW_MODE_SAVED_STATE
+ implementation Libs.ANDROIDX_FRAGMENT_KTX
+ kapt Libs.LIFECYCLE_COMPILER // Keep kapt for lifecycle if it includes Data Binding
- /* ROOM */
+ /* Room */
implementation Libs.ROOM
- kapt Libs.ROOM_COMPILER
+ ksp Libs.ROOM_COMPILER
implementation Libs.ROOM_COROUTINES
testImplementation Libs.HAMCREST
diff --git a/apps/teacher/proguard-rules.txt b/apps/teacher/proguard-rules.txt
index 0157f11519..41f4c46430 100644
--- a/apps/teacher/proguard-rules.txt
+++ b/apps/teacher/proguard-rules.txt
@@ -258,4 +258,19 @@
-dontwarn java.beans.SimpleBeanInfo
-keep class androidx.navigation.** { *; }
- -keep interface androidx.navigation.** { *; }
\ No newline at end of file
+ -keep interface androidx.navigation.** { *; }
+
+# Netty and BlockHound integration
+-dontwarn reactor.blockhound.integration.BlockHoundIntegration
+-dontwarn io.netty.util.internal.Hidden$NettyBlockHoundIntegration
+-keep class reactor.blockhound.integration.** { *; }
+-keep class io.netty.util.internal.Hidden$NettyBlockHoundIntegration { *; }
+
+# Additional Netty keep rules for R8
+-dontwarn io.netty.**
+-keep class io.netty.** { *; }
+-keepclassmembers class io.netty.** { *; }
+
+# BlockHound related classes
+-dontwarn reactor.blockhound.**
+-keep class reactor.blockhound.** { *; }
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/espresso/TestAppManager.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/espresso/TestAppManager.kt
index fcc640a20e..fb528a505e 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/espresso/TestAppManager.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/espresso/TestAppManager.kt
@@ -16,6 +16,7 @@
*/
package com.instructure.teacher.espresso
+import androidx.work.DefaultWorkerFactory
import androidx.work.WorkerFactory
import com.instructure.pandautils.features.reminder.AlarmScheduler
import com.instructure.teacher.utils.BaseAppManager
@@ -25,7 +26,7 @@ open class TestAppManager : BaseAppManager() {
var workerFactory: WorkerFactory? = null
override fun getWorkManagerFactory(): WorkerFactory {
- return workerFactory ?: WorkerFactory.getDefaultWorkerFactory()
+ return workerFactory ?: DefaultWorkerFactory
}
override fun getScheduler(): AlarmScheduler? = null
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/compose/SpeedGraderPage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/compose/SpeedGraderPage.kt
index 45e402a879..366460115e 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/compose/SpeedGraderPage.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/compose/SpeedGraderPage.kt
@@ -16,7 +16,9 @@
package com.instructure.teacher.ui.pages.compose
import androidx.annotation.StringRes
+import androidx.compose.ui.semantics.SemanticsProperties
import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertTextContains
import androidx.compose.ui.test.hasAnyAncestor
@@ -24,7 +26,7 @@ import androidx.compose.ui.test.hasAnySibling
import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.ComposeTestRule
-import androidx.compose.ui.test.onChildren
+import androidx.compose.ui.test.onAllNodesWithTag
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
@@ -66,7 +68,6 @@ import com.instructure.espresso.swipeToTop
import com.instructure.teacher.R
import org.hamcrest.Matchers
import org.hamcrest.Matchers.allOf
-import org.junit.Assert.assertEquals
import java.util.Locale
/**
@@ -252,15 +253,17 @@ class SpeedGraderPage(private val composeTestRule: ComposeTestRule) : BasePage()
*/
fun selectCommentLibraryResultItem(index: Int? = null) {
- val textNodes = composeTestRule
- .onNodeWithTag("commentLibraryListColumn")
- .onChildren()
- .fetchSemanticsNodes()
-
- val targetText = textNodes[index ?: 0]
- .config[androidx.compose.ui.semantics.SemanticsProperties.Text]
+ val targetText = composeTestRule
+ .onAllNodesWithTag("commentLibraryItem")[index ?: 0]
+ .fetchSemanticsNode()
+ .config[SemanticsProperties.Text]
.joinToString("") { it.text }
- composeTestRule.onNode(hasText(targetText, substring = true) and !(hasTestTag("ownCommentText")))
+ composeTestRule.onNode(
+ hasText(
+ targetText,
+ substring = true
+ ) and (hasTestTag("ownCommentText").not())
+ )
.performScrollTo()
.performClick()
composeTestRule.waitForIdle()
@@ -290,12 +293,9 @@ class SpeedGraderPage(private val composeTestRule: ComposeTestRule) : BasePage()
@OptIn(ExperimentalTestApi::class)
fun assertCommentLibraryItemCount(expectedCount: Int) {
composeTestRule.waitUntilExactlyOneExists(hasTestTagThatContains("commentLibraryListColumn"), timeoutMillis = 5000)
- val textNodes = composeTestRule
- .onNodeWithTag("commentLibraryListColumn")
- .onChildren()
- .fetchSemanticsNodes()
-
- assertEquals(expectedCount, textNodes.size)
+ composeTestRule
+ .onAllNodesWithTag("commentLibraryItem")
+ .assertCountEquals(expectedCount)
}
/**
@@ -359,7 +359,7 @@ class SpeedGraderPage(private val composeTestRule: ComposeTestRule) : BasePage()
getStringFromResource(
R.string.sg_tab_files_w_counter,
fileCount
- ).toUpperCase()
+ ).uppercase()
)
filesTab.click()
}
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/PSPDFKit/AnnotationComments/AnnotationCommentViewHolder.kt b/apps/teacher/src/main/java/com/instructure/teacher/PSPDFKit/AnnotationComments/AnnotationCommentViewHolder.kt
index 03c4108c24..c66fcf72b2 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/PSPDFKit/AnnotationComments/AnnotationCommentViewHolder.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/PSPDFKit/AnnotationComments/AnnotationCommentViewHolder.kt
@@ -51,7 +51,7 @@ class AnnotationCommentViewHolder(private val binding: AdapterAnnotationCommentB
}
commentEditIcon.onClick {
- val popup = PopupMenu(context, it, Gravity.TOP, 0, com.google.android.material.R.style.Widget_AppCompat_PopupMenu_Overflow)
+ val popup = PopupMenu(context, it, Gravity.TOP, 0, com.google.android.material.R.style.Widget_Material3_PopupMenu_Overflow)
popup.inflate(R.menu.menu_edit_annotation_comment)
if(!canEdit) popup.menu.removeItem(R.id.edit)
if(!canDelete) popup.menu.removeItem(R.id.delete)
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/FileListFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/FileListFragment.kt
index eb150acd28..2ecbcaf3df 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/FileListFragment.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/FileListFragment.kt
@@ -345,9 +345,9 @@ class FileListFragment : BaseSyncFragment<
})
}
- override fun workInfoLiveDataCallback(uuid: UUID?, workInfoLiveData: LiveData) {
+ override fun workInfoLiveDataCallback(uuid: UUID?, workInfoLiveData: LiveData) {
workInfoLiveData.observe(viewLifecycleOwner) {
- if (it.state == WorkInfo.State.SUCCEEDED) {
+ if (it?.state == WorkInfo.State.SUCCEEDED) {
presenter.refresh(true)
}
}
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/SpeedGraderCommentsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/SpeedGraderCommentsFragment.kt
index 22fc3b92de..563dddce6c 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/SpeedGraderCommentsFragment.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/SpeedGraderCommentsFragment.kt
@@ -303,8 +303,9 @@ class SpeedGraderCommentsFragment : BaseListFragment) {
+ override fun workInfoLiveDataCallback(uuid: UUID?, workInfoLiveData: LiveData) {
workInfoLiveData.observe(this) {
+ if (it == null) return@observe
presenter.onFileUploadWorkInfoChanged(it)
}
}
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/utils/BaseAppManager.kt b/apps/teacher/src/main/java/com/instructure/teacher/utils/BaseAppManager.kt
index 15296bc223..ebbdb3e8ac 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/utils/BaseAppManager.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/utils/BaseAppManager.kt
@@ -21,7 +21,6 @@ import android.os.Build
import androidx.appcompat.app.AppCompatDelegate
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.google.firebase.crashlytics.FirebaseCrashlytics
-import com.instructure.pandautils.utils.filecache.FileCache
import com.instructure.canvasapi2.utils.AnalyticsEventConstants
import com.instructure.canvasapi2.utils.ApiPrefs
import com.instructure.canvasapi2.utils.Logger
@@ -36,14 +35,14 @@ import com.instructure.pandautils.utils.AppType
import com.instructure.pandautils.utils.ColorKeeper
import com.instructure.pandautils.utils.Const
import com.instructure.pandautils.utils.ThemePrefs
+import com.instructure.pandautils.utils.filecache.FileCache
import com.instructure.teacher.BuildConfig
import com.instructure.teacher.R
import com.instructure.teacher.activities.InitActivity
import com.instructure.teacher.tasks.TeacherLogoutTask
-import com.pspdfkit.PSPDFKit
-import com.pspdfkit.exceptions.InvalidPSPDFKitLicenseException
-import com.pspdfkit.exceptions.PSPDFKitInitializationFailedException
-import com.pspdfkit.initialization.InitializationOptions
+import com.pspdfkit.Nutrient
+import com.pspdfkit.exceptions.InvalidNutrientLicenseException
+import com.pspdfkit.exceptions.NutrientInitializationFailedException
abstract class BaseAppManager : com.instructure.canvasapi2.AppManager() {
@@ -76,11 +75,11 @@ abstract class BaseAppManager : com.instructure.canvasapi2.AppManager() {
ColorKeeper.defaultColor = getColorCompat(R.color.textDarkest)
try {
- PSPDFKit.initialize(this, InitializationOptions(licenseKey = BuildConfig.PSPDFKIT_LICENSE_KEY))
- } catch (e: PSPDFKitInitializationFailedException) {
- Logger.e("Current device is not compatible with PSPDFKIT!")
- } catch (e: InvalidPSPDFKitLicenseException) {
- Logger.e("Invalid or Trial PSPDFKIT License!")
+ Nutrient.initialize(this, BuildConfig.PSPDFKIT_LICENSE_KEY)
+ } catch (e: NutrientInitializationFailedException) {
+ Logger.e("Current device is not compatible with Nutrient!")
+ } catch (e: InvalidNutrientLicenseException) {
+ Logger.e("Invalid or Trial Nutrient License!")
}
MasqueradeHelper.masqueradeLogoutTask = Runnable {
diff --git a/apps/teacher/src/main/res/layout/activity_init.xml b/apps/teacher/src/main/res/layout/activity_init.xml
index 699d4456ba..46c52d9b83 100644
--- a/apps/teacher/src/main/res/layout/activity_init.xml
+++ b/apps/teacher/src/main/res/layout/activity_init.xml
@@ -112,6 +112,7 @@
android:id="@+id/bottomBar"
android:layout_width="match_parent"
android:layout_height="56dp"
+ android:minHeight="48dp"
android:layout_alignParentBottom="true"
android:background="@color/backgroundLightestElevated"
app:itemIconTint="@color/textDarkest"
diff --git a/automation/espresso/build.gradle b/automation/espresso/build.gradle
index 8a48e8502a..4b7874ce96 100644
--- a/automation/espresso/build.gradle
+++ b/automation/espresso/build.gradle
@@ -37,7 +37,7 @@ allprojects {
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-kapt'
+apply plugin: 'com.google.devtools.ksp'
apply plugin: 'dagger.hilt.android.plugin'
android {
@@ -175,9 +175,9 @@ dependencies {
/* DI */
implementation Libs.HILT
- kapt Libs.HILT_COMPILER
+ ksp Libs.HILT_COMPILER
implementation Libs.HILT_TESTING
- kapt Libs.HILT_TESTING_COMPILER
+ ksp Libs.HILT_TESTING_COMPILER
implementation Libs.HILT_ANDROIDX_WORK
implementation Libs.COMPOSE_UI_TEST_MANIFEST
diff --git a/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/TestAppManager.kt b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/TestAppManager.kt
index 9e77a41f1f..f2e876daad 100644
--- a/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/TestAppManager.kt
+++ b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/TestAppManager.kt
@@ -14,6 +14,7 @@
* limitations under the License.
*/package com.instructure.canvas.espresso
+import androidx.work.DefaultWorkerFactory
import android.annotation.SuppressLint
import android.content.Context
import android.util.Log
@@ -27,22 +28,23 @@ import com.instructure.canvasapi2.utils.RemoteConfigUtils
open class TestAppManager: AppManager() {
+ var testDriver: TestDriver? = null
+
+ var workerFactory: WorkerFactory? = null
+
@SuppressLint("RestrictedApi")
override fun onCreate() {
super.onCreate()
RemoteConfigUtils.initialize()
if (workerFactory == null) {
- workerFactory = WorkerFactory.getDefaultWorkerFactory()
+ workerFactory = getWorkManagerFactory()
}
}
- var testDriver: TestDriver? = null
-
- var workerFactory: WorkerFactory? = null
@SuppressLint("RestrictedApi")
override fun getWorkManagerFactory(): WorkerFactory {
- return workerFactory ?: WorkerFactory.getDefaultWorkerFactory()
+ return workerFactory ?: DefaultWorkerFactory
}
override fun performLogoutOnAuthError() = Unit
diff --git a/automation/soseedygrpc/build.gradle b/automation/soseedygrpc/build.gradle
index d83c2030fb..ba6cdfef85 100644
--- a/automation/soseedygrpc/build.gradle
+++ b/automation/soseedygrpc/build.gradle
@@ -23,7 +23,7 @@ dependencies {
compile project(':dataseedingapi')
// https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22io.grpc%22%20a%3A%22grpc-netty%22
- compile 'io.grpc:grpc-netty:1.61.1'
+ compile 'io.grpc:grpc-netty:1.75.0'
compile Libs.KOTLIN_STD_LIB
// https://mvnrepository.com/artifact/io.netty/netty-tcnative-boringssl-static
diff --git a/gradle/gradle/wrapper/gradle-wrapper.properties b/gradle/gradle/wrapper/gradle-wrapper.properties
index b82aa23a4f..37f853b1c8 100644
--- a/gradle/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/libs/DocumentScanner/build.gradle b/libs/DocumentScanner/build.gradle
deleted file mode 100644
index 98764308cc..0000000000
--- a/libs/DocumentScanner/build.gradle
+++ /dev/null
@@ -1,91 +0,0 @@
-apply plugin: 'com.android.library'
-apply plugin: 'kotlin-android'
-
-group="com.zynkware"
-
-def libraryVersionCode = 5
-def libraryVersionName = "1.0.1"
-
-repositories {
- mavenCentral()
- google()
- maven { url "https://jitpack.io" }
-}
-
-android {
- namespace 'com.zynksoftware.documentscanner'
- compileSdkVersion Versions.COMPILE_SDK
- buildToolsVersion Versions.BUILD_TOOLS
-
- defaultConfig {
- minSdkVersion 21
- targetSdkVersion Versions.TARGET_SDK
- versionCode libraryVersionCode
- versionName libraryVersionName
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- consumerProguardFiles "consumer-rules.pro"
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
- }
- }
-
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_17
- targetCompatibility JavaVersion.VERSION_17
- }
-
- kotlinOptions {
- jvmTarget = JavaVersion.VERSION_17
- }
-
- sourceSets {
- main.java.srcDirs += 'src/main/kotlin'
- main.res.srcDirs = ['src/main/res']
- main.manifest.srcFile 'src/main/AndroidManifest.xml'
- }
-
- buildFeatures {
- viewBinding true
- }
-}
-
-repositories {
- mavenCentral()
- google()
- maven { url 'https://jitpack.io' }
-}
-
-dependencies {
- implementation fileTree(dir: "libs", include: ["*.jar"])
- implementation Libs.KOTLIN_STD_LIB
-
- implementation 'androidx.core:core-ktx:1.12.0'
- implementation Libs.ANDROIDX_APPCOMPAT
-
- implementation 'io.reactivex.rxjava3:rxandroid:3.0.2'
-
- implementation 'com.github.zynkware:Tiny-OpenCV:4.4.0-3'
-
- implementation "androidx.camera:camera-camera2:1.4.2"
- implementation "androidx.camera:camera-lifecycle:1.4.2"
- implementation "androidx.camera:camera-view:1.4.2"
-
- implementation 'androidx.exifinterface:exifinterface:1.4.0'
- implementation Libs.KOTLIN_COROUTINES_ANDROID
- implementation 'id.zelory:compressor:3.0.1'
-}
-
-task sourceJar(type: Jar) {
- from android.sourceSets.main.java.srcDirs
- from fileTree(dir: 'src/libs', include: ['*.jar'])
-}
-
-task androidSourcesJar(type: Jar) {
- archiveClassifier.set('sources')
- from android.sourceSets.main.java.srcDirs
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/AndroidManifest.xml b/libs/DocumentScanner/src/main/AndroidManifest.xml
deleted file mode 100644
index 679e8fc75a..0000000000
--- a/libs/DocumentScanner/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ScanActivity.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ScanActivity.kt
deleted file mode 100644
index e388c63ca0..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ScanActivity.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- Copyright 2020 ZynkSoftware SRL
-
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
- associated documentation files (the "Software"), to deal in the Software without restriction,
- including without limitation the rights to use, copy, modify, merge, publish, distribute,
- sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all copies or
- substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-package com.zynksoftware.documentscanner
-
-import com.zynksoftware.documentscanner.ui.scan.InternalScanActivity
-
-abstract class ScanActivity : InternalScanActivity() {
-
- fun addFragmentContentLayout() {
- addFragmentContentLayoutInternal()
- }
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/extensions/BitmapExtensions.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/extensions/BitmapExtensions.kt
deleted file mode 100644
index 8afd1e1f98..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/extensions/BitmapExtensions.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- Copyright 2020 ZynkSoftware SRL
-
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
- associated documentation files (the "Software"), to deal in the Software without restriction,
- including without limitation the rights to use, copy, modify, merge, publish, distribute,
- sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all copies or
- substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-package com.zynksoftware.documentscanner.common.extensions
-
-import android.graphics.Bitmap
-import android.graphics.Matrix
-import android.graphics.RectF
-import org.opencv.android.Utils
-import org.opencv.core.CvType
-import org.opencv.core.Mat
-import org.opencv.core.Scalar
-
-internal fun Bitmap.rotateBitmap(angle: Int): Bitmap {
- val matrix = Matrix()
- matrix.postRotate(angle.toFloat())
- return Bitmap.createBitmap(this, 0, 0, this.width, this.height, matrix, true)
-}
-
-internal fun Bitmap.toMat(): Mat {
- val mat = Mat(this.height, this.width, CvType.CV_8U, Scalar(4.toDouble()))
- val bitmap32 = this.copy(Bitmap.Config.ARGB_8888, true)
- Utils.bitmapToMat(bitmap32, mat)
- return mat
-}
-
-internal fun Bitmap.scaledBitmap(width: Int, height: Int): Bitmap {
- val m = Matrix()
- m.setRectToRect(
- RectF(0f, 0f, this.width.toFloat(), this.height.toFloat()),
- RectF(0f, 0f, width.toFloat(), height.toFloat()),
- Matrix.ScaleToFit.CENTER
- )
- return Bitmap.createBitmap(this, 0, 0, this.width, this.height, m, true)
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/extensions/ImageProxyExtensions.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/extensions/ImageProxyExtensions.kt
deleted file mode 100644
index f0b6716c1f..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/extensions/ImageProxyExtensions.kt
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- Copyright 2020 ZynkSoftware SRL
-
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
- associated documentation files (the "Software"), to deal in the Software without restriction,
- including without limitation the rights to use, copy, modify, merge, publish, distribute,
- sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all copies or
- substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-package com.zynksoftware.documentscanner.common.extensions
-
-import android.graphics.ImageFormat
-import androidx.camera.core.ImageProxy
-import org.opencv.core.CvType
-import org.opencv.core.Mat
-import org.opencv.imgproc.Imgproc
-
-internal fun ImageProxy.yuvToRgba(): Mat {
- val rgbaMat = Mat()
-
- if (format == ImageFormat.YUV_420_888
- && planes.size == 3) {
-
- val chromaPixelStride = planes[1].pixelStride
-
- if (chromaPixelStride == 2) { // Chroma channels are interleaved
- val yPlane = planes[0].buffer
- val uvPlane1 = planes[1].buffer
- val uvPlane2 = planes[2].buffer
-
- val yMat = Mat((height), width, CvType.CV_8UC1, yPlane)
- val uvMat1 = Mat(height / 2, width / 2, CvType.CV_8UC2, uvPlane1)
- val uvMat2 = Mat(height / 2, width / 2, CvType.CV_8UC2, uvPlane2)
- val addrDiff = uvMat2.dataAddr() - uvMat1.dataAddr()
- if (addrDiff > 0) {
- Imgproc.cvtColorTwoPlane(yMat, uvMat1, rgbaMat, Imgproc.COLOR_YUV2RGBA_NV12)
- } else {
- Imgproc.cvtColorTwoPlane(yMat, uvMat2, rgbaMat, Imgproc.COLOR_YUV2RGBA_NV21)
- }
- } else { // Chroma channels are not interleaved
- val yuvBytes = ByteArray(width * (height + height / 2))
- val yPlane = planes[0].buffer
- val uPlane = planes[1].buffer
- val vPlane = planes[2].buffer
-
- yPlane.get(yuvBytes, 0, width * height)
-
- val chromaRowStride = planes[1].rowStride
- val chromaRowPadding = chromaRowStride - width / 2
-
- var offset = width * height
- if (chromaRowPadding == 0) {
- // When the row stride of the chroma channels equals their width, we can copy
- // the entire channels in one go
- uPlane.get(yuvBytes, offset, width * height / 4)
- offset += width * height / 4
- vPlane.get(yuvBytes, offset, width * height / 4)
- } else {
- // When not equal, we need to copy the channels row by row
- for (i in 0 until height / 2) {
- uPlane.get(yuvBytes, offset, width / 2)
- offset += width / 2
- if (i < height / 2 - 1) {
- uPlane.position(uPlane.position() + chromaRowPadding)
- }
- }
- for (i in 0 until height / 2) {
- vPlane.get(yuvBytes, offset, width / 2)
- offset += width / 2
- if (i < height / 2 - 1) {
- vPlane.position(vPlane.position() + chromaRowPadding)
- }
- }
- }
-
- val yuvMat = Mat(height + height / 2, width, CvType.CV_8UC1)
- yuvMat.put(0, 0, yuvBytes)
- Imgproc.cvtColor(yuvMat, rgbaMat, Imgproc.COLOR_YUV2BGR_NV21, 4)
- }
- }
-
- return rgbaMat
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/extensions/OpenCvExtensions.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/extensions/OpenCvExtensions.kt
deleted file mode 100644
index 4d6e7f4779..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/extensions/OpenCvExtensions.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- Copyright 2020 ZynkSoftware SRL
-
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
- associated documentation files (the "Software"), to deal in the Software without restriction,
- including without limitation the rights to use, copy, modify, merge, publish, distribute,
- sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all copies or
- substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-package com.zynksoftware.documentscanner.common.extensions
-
-import android.graphics.Bitmap
-import org.opencv.android.Utils
-import org.opencv.core.*
-import java.util.*
-
-internal fun Mat.toBitmap(): Bitmap {
- val bitmap = Bitmap.createBitmap(this.cols(), this.rows(), Bitmap.Config.ARGB_8888)
- Utils.matToBitmap(this, bitmap)
- return bitmap
-}
-
-internal fun MatOfPoint2f.scaleRectangle(scale: Double): MatOfPoint2f {
- val originalPoints = this.toList()
- val resultPoints: MutableList = ArrayList()
- for (point in originalPoints) {
- resultPoints.add(Point(point.x * scale, point.y * scale))
- }
- val result = MatOfPoint2f()
- result.fromList(resultPoints)
- return result
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/extensions/ViewExtensions.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/extensions/ViewExtensions.kt
deleted file mode 100644
index 17375b9edb..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/extensions/ViewExtensions.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- Copyright 2020 ZynkSoftware SRL
-
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
- associated documentation files (the "Software"), to deal in the Software without restriction,
- including without limitation the rights to use, copy, modify, merge, publish, distribute,
- sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all copies or
- substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-package com.zynksoftware.documentscanner.common.extensions
-
-import android.view.View
-
-internal fun View.hide() {
- visibility = View.GONE
-}
-
-internal fun View.show() {
- visibility = View.VISIBLE
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/utils/FileUriUtils.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/utils/FileUriUtils.kt
deleted file mode 100644
index f65058f84b..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/utils/FileUriUtils.kt
+++ /dev/null
@@ -1,240 +0,0 @@
-package com.zynksoftware.documentscanner.common.utils
-
-import android.content.ContentUris
-import android.content.Context
-import android.database.Cursor
-import android.net.Uri
-import android.os.Build
-import android.os.Environment
-import android.provider.DocumentsContract
-import android.provider.MediaStore
-import java.io.File
-import java.io.FileOutputStream
-import java.io.IOException
-import java.io.InputStream
-import java.io.OutputStream
-
-/**
- * This file was taken from
- * https://gist.github.com/HBiSoft/15899990b8cd0723c3a894c1636550a8
- *
- * Later on it was modified from the below resource:
- * https://raw.githubusercontent.com/iPaulPro/aFileChooser/master/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java
- * https://raw.githubusercontent.com/iPaulPro/aFileChooser/master/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java
- */
-
-internal object FileUriUtils {
-
- fun getRealPath(context: Context, uri: Uri): String? {
- var path = getPathFromLocalUri(context, uri)
- if (path == null) {
- path = getPathFromRemoteUri(context, uri)
- }
- return path
- }
-
- private fun getPathFromLocalUri(context: Context, uri: Uri): String? {
-
- val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
-
- // DocumentProvider
- if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
- // ExternalStorageProvider
- if (isExternalStorageDocument(uri)) {
- val docId = DocumentsContract.getDocumentId(uri)
- val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
- val type = split[0]
-
- // This is for checking Main Memory
- return if ("primary".equals(type, ignoreCase = true)) {
- if (split.size > 1) {
- Environment.getExternalStorageDirectory().toString() + "/" + split[1]
- } else {
- Environment.getExternalStorageDirectory().toString() + "/"
- }
- // This is for checking SD Card
- } else {
- val path = "storage" + "/" + docId.replace(":", "/")
- if (File(path).exists()) {
- path
- } else {
- "/storage/sdcard/" + split[1]
- }
- }
- } else if (isDownloadsDocument(uri)) {
- val fileName = getFilePath(context, uri)
- if (fileName != null) {
- return Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName
- }
-
- val id = DocumentsContract.getDocumentId(uri)
- val contentUri = ContentUris.withAppendedId(
- Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id)
- )
- return getDataColumn(context, contentUri, null, null)
- } else if (isMediaDocument(uri)) {
- val docId = DocumentsContract.getDocumentId(uri)
- val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
- val type = split[0]
-
- var contentUri: Uri? = null
- if ("image" == type) {
- contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
- } else if ("video" == type) {
- contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
- } else if ("audio" == type) {
- contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
- }
-
- val selection = "_id=?"
- val selectionArgs = arrayOf(split[1])
-
- return getDataColumn(context, contentUri, selection, selectionArgs)
- } // MediaProvider
- // DownloadsProvider
- } else if ("content".equals(uri.scheme!!, ignoreCase = true)) {
-
- // Return the remote address
- return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn(context, uri, null, null)
- } else if ("file".equals(uri.scheme!!, ignoreCase = true)) {
- return uri.path
- } // File
- // MediaStore (and general)
-
- return null
- }
-
- private fun getDataColumn(
- context: Context,
- uri: Uri?,
- selection: String?,
- selectionArgs: Array?
- ): String? {
-
- var cursor: Cursor? = null
- val column = "_data"
- val projection = arrayOf(column)
-
- try {
- cursor = context.contentResolver.query(uri!!, projection, selection, selectionArgs, null)
- if (cursor != null && cursor.moveToFirst()) {
- val index = cursor.getColumnIndexOrThrow(column)
- return cursor.getString(index)
- }
- } catch (ex: Exception) {
- } finally {
- cursor?.close()
- }
- return null
- }
-
- private fun getFilePath(context: Context, uri: Uri): String? {
-
- var cursor: Cursor? = null
- val projection = arrayOf(MediaStore.MediaColumns.DISPLAY_NAME)
-
- try {
- cursor = context.contentResolver.query(uri, projection, null, null, null)
- if (cursor != null && cursor.moveToFirst()) {
- val index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME)
- return cursor.getString(index)
- }
- } finally {
- cursor?.close()
- }
- return null
- }
-
- private fun getPathFromRemoteUri(context: Context, uri: Uri): String? {
- // The code below is why Java now has try-with-resources and the Files utility.
- var file: File? = null
- var inputStream: InputStream? = null
- var outputStream: OutputStream? = null
- var success = false
- try {
- val extension = getImageExtension(uri)
- inputStream = context.contentResolver.openInputStream(uri)
- val storageDir = context.cacheDir
- if (!storageDir.exists()) {
- storageDir.mkdirs()
- }
- file = File(storageDir, "remotePicture${extension}")
- file.createNewFile()
- outputStream = FileOutputStream(file)
- if (inputStream != null) {
- inputStream.copyTo(outputStream, bufferSize = 4 * 1024)
- success = true
- }
- } catch (ignored: IOException) {
- } finally {
- try {
- inputStream?.close()
- } catch (ignored: IOException) {
- }
-
- try {
- outputStream?.close()
- } catch (ignored: IOException) {
- // If closing the output stream fails, we cannot be sure that the
- // target file was written in full. Flushing the stream merely moves
- // the bytes into the OS, not necessarily to the file.
- success = false
- }
- }
- return if (success) file!!.path else null
- }
-
- /** @return extension of image with dot, or default .jpg if it none.
- */
- private fun getImageExtension(uriImage: Uri): String {
- var extension: String? = null
-
- try {
- val imagePath = uriImage.path
- if (imagePath != null && imagePath.lastIndexOf(".") != -1) {
- extension = imagePath.substring(imagePath.lastIndexOf(".") + 1)
- }
- } catch (e: Exception) {
- extension = null
- }
-
- if (extension == null || extension.isEmpty()) {
- // default extension for matches the previous behavior of the plugin
- extension = "jpg"
- }
-
- return ".$extension"
- }
-
- /**
- * @param uri The Uri to check.
- * @return Whether the Uri authority is ExternalStorageProvider.
- */
- private fun isExternalStorageDocument(uri: Uri): Boolean {
- return "com.android.externalstorage.documents" == uri.authority
- }
-
- /**
- * @param uri The Uri to check.
- * @return Whether the Uri authority is DownloadsProvider.
- */
- private fun isDownloadsDocument(uri: Uri): Boolean {
- return "com.android.providers.downloads.documents" == uri.authority
- }
-
- /**
- * @param uri The Uri to check.
- * @return Whether the Uri authority is MediaProvider.
- */
- private fun isMediaDocument(uri: Uri): Boolean {
- return "com.android.providers.media.documents" == uri.authority
- }
-
- /**
- * @param uri The Uri to check.
- * @return Whether the Uri authority is Google Photos.
- */
- private fun isGooglePhotosUri(uri: Uri): Boolean {
- return "com.google.android.apps.photos.content" == uri.authority
- }
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/utils/ImageDetectionProperties.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/utils/ImageDetectionProperties.kt
deleted file mode 100644
index 39310e1df5..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/utils/ImageDetectionProperties.kt
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- Copyright 2020 ZynkSoftware SRL
-
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
- associated documentation files (the "Software"), to deal in the Software without restriction,
- including without limitation the rights to use, copy, modify, merge, publish, distribute,
- sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all copies or
- substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-package com.zynksoftware.documentscanner.common.utils
-
-import org.opencv.core.MatOfPoint2f
-import org.opencv.core.Point
-import kotlin.math.abs
-
-internal class ImageDetectionProperties(
- private val previewWidth: Double, private val previewHeight: Double,
- private val topLeftPoint: Point, private val bottomLeftPoint: Point,
- private val bottomRightPoint: Point, private val topRightPoint: Point,
- private val resultWidth: Int, private val resultHeight: Int
-) {
-
- companion object {
- private const val SMALLEST_ANGLE_COS = 0.172 //80 degrees
- }
-
- fun isNotValidImage(approx: MatOfPoint2f): Boolean {
- return isEdgeTouching || isAngleNotCorrect(approx) || isDetectedAreaBelowLimits()
- }
-
- private fun isAngleNotCorrect(approx: MatOfPoint2f): Boolean {
- return getMaxCosine(approx) || isLeftEdgeDistorted || isRightEdgeDistorted
- }
-
- private val isRightEdgeDistorted: Boolean
- get() = abs(topRightPoint.y - bottomRightPoint.y) > 100
-
- private val isLeftEdgeDistorted: Boolean
- get() = abs(topLeftPoint.y - bottomLeftPoint.y) > 100
-
- private fun getMaxCosine(approx: MatOfPoint2f): Boolean {
- var maxCosine = 0.0
- val approxPoints = approx.toArray()
- maxCosine = MathUtils.getMaxCosine(maxCosine, approxPoints)
- return maxCosine >= SMALLEST_ANGLE_COS
- }
-
- private val isEdgeTouching: Boolean
- get() = isTopEdgeTouching || isBottomEdgeTouching || isLeftEdgeTouching || isRightEdgeTouching
-
- private val isBottomEdgeTouching: Boolean
- get() = bottomLeftPoint.x >= previewHeight - 10 || bottomRightPoint.x >= previewHeight - 10
-
- private val isTopEdgeTouching: Boolean
- get() = topLeftPoint.x <= 10 || topRightPoint.x <= 10
-
- private val isRightEdgeTouching: Boolean
- get() = topRightPoint.y >= previewWidth - 10 || bottomRightPoint.y >= previewWidth - 10
-
- private val isLeftEdgeTouching: Boolean
- get() = topLeftPoint.y <= 10 || bottomLeftPoint.y <= 10
-
- private fun isDetectedAreaBelowLimits(): Boolean {
- return !(previewWidth / previewHeight >= 1 &&
- resultWidth.toDouble() / resultHeight.toDouble() >= 0.9 &&
- resultHeight.toDouble() >= 0.70 * previewHeight ||
- previewHeight / previewWidth >= 1 &&
- resultHeight.toDouble() / resultWidth.toDouble() >= 0.9 &&
- resultWidth.toDouble() >= 0.70 * previewWidth)
- }
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/utils/MathUtils.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/utils/MathUtils.kt
deleted file mode 100644
index 1c6b1d16d0..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/utils/MathUtils.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- Copyright 2020 ZynkSoftware SRL
-
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
- associated documentation files (the "Software"), to deal in the Software without restriction,
- including without limitation the rights to use, copy, modify, merge, publish, distribute,
- sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all copies or
- substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-package com.zynksoftware.documentscanner.common.utils
-
-import org.opencv.core.Point
-import kotlin.math.abs
-import kotlin.math.max
-import kotlin.math.sqrt
-
-internal object MathUtils {
-
- private fun angle(p1: Point, p2: Point, p0: Point): Double {
- val dx1 = p1.x - p0.x
- val dy1 = p1.y - p0.y
- val dx2 = p2.x - p0.x
- val dy2 = p2.y - p0.y
- return (dx1 * dx2 + dy1 * dy2) / sqrt((dx1 * dx1 + dy1 * dy1) * (dx2 * dx2 + dy2 * dy2) + 1e-10)
- }
-
- fun getDistance(p1: Point, p2: Point): Double {
- val dx = p2.x - p1.x
- val dy = p2.y - p1.y
- return sqrt(dx * dx + dy * dy)
- }
-
- fun getMaxCosine(maxCosine: Double, approxPoints: Array): Double {
- var newMaxCosine = maxCosine
- for (i in 2..4) {
- val cosine: Double = abs(angle(approxPoints[i % 4], approxPoints[i - 2], approxPoints[i - 1]))
- newMaxCosine = max(cosine, newMaxCosine)
- }
- return newMaxCosine
- }
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/utils/OpenCvNativeBridge.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/utils/OpenCvNativeBridge.kt
deleted file mode 100644
index 0916409902..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/utils/OpenCvNativeBridge.kt
+++ /dev/null
@@ -1,247 +0,0 @@
-/**
- Copyright 2020 ZynkSoftware SRL
-
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
- associated documentation files (the "Software"), to deal in the Software without restriction,
- including without limitation the rights to use, copy, modify, merge, publish, distribute,
- sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all copies or
- substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-package com.zynksoftware.documentscanner.common.utils
-
-import android.graphics.Bitmap
-import android.graphics.PointF
-import com.zynksoftware.documentscanner.common.extensions.scaleRectangle
-import com.zynksoftware.documentscanner.common.extensions.toBitmap
-import com.zynksoftware.documentscanner.common.extensions.toMat
-import com.zynksoftware.documentscanner.ui.components.Quadrilateral
-import org.opencv.core.*
-import org.opencv.imgproc.Imgproc
-import java.util.*
-import kotlin.collections.ArrayList
-import kotlin.math.*
-
-
-internal class OpenCvNativeBridge {
-
- companion object {
- private const val ANGLES_NUMBER = 4
- private const val EPSILON_CONSTANT = 0.02
- private const val CLOSE_KERNEL_SIZE = 10.0
- private const val CANNY_THRESHOLD_LOW = 75.0
- private const val CANNY_THRESHOLD_HIGH = 200.0
- private const val CUTOFF_THRESHOLD = 155.0
- private const val TRUNCATE_THRESHOLD = 150.0
- private const val NORMALIZATION_MIN_VALUE = 0.0
- private const val NORMALIZATION_MAX_VALUE = 255.0
- private const val BLURRING_KERNEL_SIZE = 5.0
- private const val DOWNSCALE_IMAGE_SIZE = 600.0
- private const val FIRST_MAX_CONTOURS = 10
- }
-
- fun getScannedBitmap(bitmap: Bitmap, x1: Float, y1: Float, x2: Float, y2: Float, x3: Float, y3: Float, x4: Float, y4: Float): Bitmap {
- val rectangle = MatOfPoint2f()
- rectangle.fromArray(
- Point(x1.toDouble(), y1.toDouble()),
- Point(x2.toDouble(), y2.toDouble()),
- Point(x3.toDouble(), y3.toDouble()),
- Point(x4.toDouble(), y4.toDouble())
- )
- val dstMat = PerspectiveTransformation.transform(bitmap.toMat(), rectangle)
- return dstMat.toBitmap()
- }
-
- fun getContourEdgePoints(tempBitmap: Bitmap): List {
- var point2f = getPoint(tempBitmap)
- if (point2f == null) point2f = MatOfPoint2f()
- val points: List = point2f.toArray().toList()
- val result: MutableList = ArrayList()
- for (i in points.indices) {
- result.add(PointF(points[i].x.toFloat(), points[i].y.toFloat()))
- }
-
- return result
- }
-
- fun getPoint(bitmap: Bitmap): MatOfPoint2f? {
- val src = bitmap.toMat()
-
- val ratio = DOWNSCALE_IMAGE_SIZE / max(src.width(), src.height())
- val downscaledSize = Size(src.width() * ratio, src.height() * ratio)
- val downscaled = Mat(downscaledSize, src.type())
- Imgproc.resize(src, downscaled, downscaledSize)
- val largestRectangle = detectLargestQuadrilateral(downscaled)
-
- return largestRectangle?.contour?.scaleRectangle(1f / ratio)
- }
-
- // patch from Udayraj123 (https://github.com/Udayraj123/LiveEdgeDetection)
- fun detectLargestQuadrilateral(src: Mat): Quadrilateral? {
- val destination = Mat()
- Imgproc.blur(src, src, Size(BLURRING_KERNEL_SIZE, BLURRING_KERNEL_SIZE))
-
- Core.normalize(src, src, NORMALIZATION_MIN_VALUE, NORMALIZATION_MAX_VALUE, Core.NORM_MINMAX)
-
- Imgproc.threshold(src, src, TRUNCATE_THRESHOLD, NORMALIZATION_MAX_VALUE, Imgproc.THRESH_TRUNC)
- Core.normalize(src, src, NORMALIZATION_MIN_VALUE, NORMALIZATION_MAX_VALUE, Core.NORM_MINMAX)
-
- Imgproc.Canny(src, destination, CANNY_THRESHOLD_HIGH, CANNY_THRESHOLD_LOW)
-
- Imgproc.threshold(destination, destination, CUTOFF_THRESHOLD, NORMALIZATION_MAX_VALUE, Imgproc.THRESH_TOZERO)
-
- Imgproc.morphologyEx(
- destination, destination, Imgproc.MORPH_CLOSE,
- Mat(Size(CLOSE_KERNEL_SIZE, CLOSE_KERNEL_SIZE), CvType.CV_8UC1, Scalar(NORMALIZATION_MAX_VALUE)),
- Point(-1.0, -1.0), 1
- )
-
- val largestContour: List? = findLargestContours(destination)
- if (null != largestContour) {
- return findQuadrilateral(largestContour)
- }
- return null
- }
-
- private fun findQuadrilateral(mContourList: List): Quadrilateral? {
- for (c in mContourList) {
- val c2f = MatOfPoint2f(*c.toArray())
- val peri = Imgproc.arcLength(c2f, true)
- val approx = MatOfPoint2f()
- Imgproc.approxPolyDP(c2f, approx, EPSILON_CONSTANT * peri, true)
- val points = approx.toArray()
- // select biggest 4 angles polygon
- if (approx.rows() == ANGLES_NUMBER) {
- val foundPoints: Array = sortPoints(points)
- return Quadrilateral(approx, foundPoints)
- } else if(approx.rows() == 5) {
- // if document has a bent corner
- var shortestDistance = Int.MAX_VALUE.toDouble()
- var shortestPoint1: Point? = null
- var shortestPoint2: Point? = null
-
- var diagonal = 0.toDouble()
- var diagonalPoint1: Point? = null
- var diagonalPoint2: Point? = null
-
- for (i in 0 until 4) {
- for (j in i + 1 until 5) {
- val d = distance(points[i], points[j])
- if (d < shortestDistance) {
- shortestDistance = d
- shortestPoint1 = points[i]
- shortestPoint2 = points[j]
- }
- if(d > diagonal) {
- diagonal = d
- diagonalPoint1 = points[i]
- diagonalPoint2 = points[j]
- }
- }
- }
-
- val trianglePointWithHypotenuse: Point? = points.toList().minus(arrayListOf(shortestPoint1, shortestPoint2, diagonalPoint1, diagonalPoint2))[0]
-
- val newPoint = if(trianglePointWithHypotenuse!!.x > shortestPoint1!!.x && trianglePointWithHypotenuse.x > shortestPoint2!!.x &&
- trianglePointWithHypotenuse.y > shortestPoint1.y && trianglePointWithHypotenuse.y > shortestPoint2.y) {
- Point(min(shortestPoint1.x, shortestPoint2.x), min(shortestPoint1.y, shortestPoint2.y))
- } else if(trianglePointWithHypotenuse.x < shortestPoint1.x && trianglePointWithHypotenuse.x < shortestPoint2!!.x &&
- trianglePointWithHypotenuse.y > shortestPoint1.y && trianglePointWithHypotenuse.y > shortestPoint2.y) {
- Point(max(shortestPoint1.x, shortestPoint2.x), min(shortestPoint1.y, shortestPoint2.y))
- } else if(trianglePointWithHypotenuse.x < shortestPoint1.x && trianglePointWithHypotenuse.x < shortestPoint2!!.x &&
- trianglePointWithHypotenuse.y < shortestPoint1.y && trianglePointWithHypotenuse.y < shortestPoint2.y) {
- Point(max(shortestPoint1.x, shortestPoint2.x), max(shortestPoint1.y, shortestPoint2.y))
- } else if(trianglePointWithHypotenuse.x > shortestPoint1.x && trianglePointWithHypotenuse.x > shortestPoint2!!.x &&
- trianglePointWithHypotenuse.y < shortestPoint1.y && trianglePointWithHypotenuse.y < shortestPoint2.y) {
- Point(min(shortestPoint1.x, shortestPoint2.x), max(shortestPoint1.y, shortestPoint2.y))
- } else {
- Point(0.0, 0.0)
- }
-
- val sortedPoints = sortPoints(arrayOf(trianglePointWithHypotenuse, diagonalPoint1!!, diagonalPoint2!!, newPoint))
- val newApprox = MatOfPoint2f()
- newApprox.fromArray(*sortedPoints)
- return Quadrilateral(newApprox, sortedPoints)
- }
- }
- return null
- }
-
- private fun distance(p1: Point, p2: Point): Double {
- return sqrt((p1.x - p2.x).pow(2.0) + (p1.y - p2.y).pow(2.0))
- }
-
- private fun sortPoints(src: Array): Array {
- val srcPoints: ArrayList = ArrayList(src.toList())
- val result = arrayOf(null, null, null, null)
- val sumComparator: Comparator = Comparator { lhs, rhs -> (lhs.y + lhs.x).compareTo(rhs.y + rhs.x) }
- val diffComparator: Comparator = Comparator { lhs, rhs -> (lhs.y - lhs.x).compareTo(rhs.y - rhs.x) }
-
- // top-left corner = minimal sum
- result[0] = Collections.min(srcPoints, sumComparator)
- // bottom-right corner = maximal sum
- result[2] = Collections.max(srcPoints, sumComparator)
- // top-right corner = minimal difference
- result[1] = Collections.min(srcPoints, diffComparator)
- // bottom-left corner = maximal difference
- result[3] = Collections.max(srcPoints, diffComparator)
- return result.map {
- it!!
- }.toTypedArray()
- }
-
- private fun findLargestContours(inputMat: Mat): List? {
- val mHierarchy = Mat()
- val mContourList: List = ArrayList()
- //finding contours - as we are sorting by area anyway, we can use RETR_LIST - faster than RETR_EXTERNAL.
- Imgproc.findContours(inputMat, mContourList, mHierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE)
-
- // Convert the contours to their Convex Hulls i.e. removes minor nuances in the contour
- val mHullList: MutableList = ArrayList()
- val tempHullIndices = MatOfInt()
- for (i in mContourList.indices) {
- Imgproc.convexHull(mContourList[i], tempHullIndices)
- mHullList.add(hull2Points(tempHullIndices, mContourList[i]))
- }
- // Release mContourList as its job is done
- for (c in mContourList) {
- c.release()
- }
- tempHullIndices.release()
- mHierarchy.release()
- if (mHullList.size != 0) {
- mHullList.sortWith { lhs, rhs ->
- Imgproc.contourArea(rhs).compareTo(Imgproc.contourArea(lhs))
- }
- return mHullList.subList(0, min(mHullList.size, FIRST_MAX_CONTOURS))
- }
- return null
- }
-
- private fun hull2Points(hull: MatOfInt, contour: MatOfPoint): MatOfPoint {
- val indexes = hull.toList()
- val points: MutableList = ArrayList()
- val ctrList = contour.toList()
- for (index in indexes) {
- points.add(ctrList[index])
- }
- val point = MatOfPoint()
- point.fromList(points)
- return point
- }
-
- fun contourArea(approx: MatOfPoint2f): Double {
- return Imgproc.contourArea(approx)
- }
-
-
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/utils/PerspectiveTransformation.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/utils/PerspectiveTransformation.kt
deleted file mode 100644
index d5a077b8f9..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/common/utils/PerspectiveTransformation.kt
+++ /dev/null
@@ -1,117 +0,0 @@
-/**
- Copyright 2020 ZynkSoftware SRL
-
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
- associated documentation files (the "Software"), to deal in the Software without restriction,
- including without limitation the rights to use, copy, modify, merge, publish, distribute,
- sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all copies or
- substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-package com.zynksoftware.documentscanner.common.utils
-
-import com.zynksoftware.documentscanner.common.utils.MathUtils.getDistance
-import org.opencv.core.Mat
-import org.opencv.core.MatOfPoint2f
-import org.opencv.core.Point
-import org.opencv.core.Size
-import org.opencv.imgproc.Imgproc
-import java.util.*
-
-internal object PerspectiveTransformation {
-
- fun transform(src: Mat, corners: MatOfPoint2f): Mat {
- val sortedCorners = sortCorners(corners)
- val size = getRectangleSize(sortedCorners)
- val result = Mat.zeros(size, src.type())
- val imageOutline = getOutline(result)
- val transformation = Imgproc.getPerspectiveTransform(sortedCorners, imageOutline)
- Imgproc.warpPerspective(src, result, transformation, size)
- return result
- }
-
- private fun getRectangleSize(rectangle: MatOfPoint2f): Size {
- val corners = rectangle.toArray()
- val top = getDistance(corners[0], corners[1])
- val right = getDistance(corners[1], corners[2])
- val bottom = getDistance(corners[2], corners[3])
- val left = getDistance(corners[3], corners[0])
- val averageWidth = (top + bottom) / 2f
- val averageHeight = (right + left) / 2f
- return Size(Point(averageWidth, averageHeight))
- }
-
- private fun getOutline(image: Mat): MatOfPoint2f {
- val topLeft = Point(0.toDouble(), 0.toDouble())
- val topRight = Point(image.cols().toDouble(), 0.toDouble())
- val bottomRight = Point(image.cols().toDouble(), image.rows().toDouble())
- val bottomLeft = Point(0.toDouble(), image.rows().toDouble())
- val points = arrayOf(topLeft, topRight, bottomRight, bottomLeft)
- val result = MatOfPoint2f()
- result.fromArray(*points)
- return result
- }
-
- private fun sortCorners(corners: MatOfPoint2f): MatOfPoint2f {
- val center = getMassCenter(corners)
- val points = corners.toList()
- val topPoints: MutableList = ArrayList()
- val bottomPoints: MutableList = ArrayList()
- for (point in points) {
- if (point.y < center.y) {
- topPoints.add(point)
- } else {
- bottomPoints.add(point)
- }
- }
-
- val topLeft = if (topPoints[0].x > topPoints[1].x) {
- topPoints[1]
- } else {
- topPoints[0]
- }
-
- val topRight = if (topPoints[0].x > topPoints[1].x) {
- topPoints[0]
- } else {
- topPoints[1]
- }
-
- val bottomLeft = if (bottomPoints[0].x > bottomPoints[1].x) {
- bottomPoints[1]
- } else {
- bottomPoints[0]
- }
-
- val bottomRight = if (bottomPoints[0].x > bottomPoints[1].x) {
- bottomPoints[0]
- } else {
- bottomPoints[1]
- }
- val result = MatOfPoint2f()
- val sortedPoints = arrayOf(topLeft, topRight, bottomRight, bottomLeft)
- result.fromArray(*sortedPoints)
- return result
- }
-
- private fun getMassCenter(points: MatOfPoint2f): Point {
- var xSum = 0.0
- var ySum = 0.0
- val pointList = points.toList()
- val len = pointList.size
- for (point in pointList) {
- xSum += point.x
- ySum += point.y
- }
- return Point(xSum / len, ySum / len)
- }
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/manager/SessionManager.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/manager/SessionManager.kt
deleted file mode 100644
index b1d786476a..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/manager/SessionManager.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
- Copyright 2020 ZynkSoftware SRL
-
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
- associated documentation files (the "Software"), to deal in the Software without restriction,
- including without limitation the rights to use, copy, modify, merge, publish, distribute,
- sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all copies or
- substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-package com.zynksoftware.documentscanner.manager
-
-import android.content.Context
-import android.graphics.Bitmap
-import id.zelory.compressor.extension
-import java.util.Locale
-
-internal class SessionManager(context: Context) {
-
- companion object {
- private const val IMAGE_SIZE_KEY = "IMAGE_SIZE_KEY"
- private const val IMAGE_QUALITY_KEY = "IMAGE_QUALITY_KEY"
- private const val IMAGE_TYPE_KEY = "IMAGE_TYPE_KEY"
-
- private const val DEFAULT_IMAGE_TYPE = "jpg"
- }
- private val preferences = context.getSharedPreferences("ZDC_Shared_Preferences", Context.MODE_PRIVATE)
-
-
- fun getImageSize(): Long {
- return preferences.getLong(IMAGE_SIZE_KEY, -1L)
- }
-
- fun setImageSize(size: Long) {
- preferences.edit().putLong(IMAGE_SIZE_KEY, size).apply()
- }
-
- fun getImageQuality(): Int {
- return preferences.getInt(IMAGE_QUALITY_KEY, 100)
- }
-
- fun setImageQuality(quality: Int) {
- preferences.edit().putInt(IMAGE_QUALITY_KEY, quality).apply()
- }
-
- fun getImageType(): Bitmap.CompressFormat {
- return compressFormat(preferences.getString(IMAGE_TYPE_KEY, DEFAULT_IMAGE_TYPE)!!)
- }
-
- fun setImageType(type: Bitmap.CompressFormat) {
- preferences.edit().putString(IMAGE_TYPE_KEY, type.extension()).apply()
- }
-
- private fun compressFormat(format: String) = when (format.lowercase(Locale.getDefault())) {
- "png" -> Bitmap.CompressFormat.PNG
- "webp" -> Bitmap.CompressFormat.WEBP
- else -> Bitmap.CompressFormat.JPEG
- }
-}
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/model/DocumentScannerErrorModel.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/model/DocumentScannerErrorModel.kt
deleted file mode 100644
index 1cc0f9d99a..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/model/DocumentScannerErrorModel.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- Copyright 2020 ZynkSoftware SRL
-
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
- associated documentation files (the "Software"), to deal in the Software without restriction,
- including without limitation the rights to use, copy, modify, merge, publish, distribute,
- sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all copies or
- substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-package com.zynksoftware.documentscanner.model
-
-data class DocumentScannerErrorModel(
- var errorMessage: ErrorMessage? = null,
- var throwable: Throwable? = null
-) {
- enum class ErrorMessage(val error: String){
- TAKE_IMAGE_FROM_GALLERY_ERROR("TAKE_IMAGE_FROM_GALLERY_ERROR"),
- PHOTO_CAPTURE_FAILED("PHOTO_CAPTURE_FAILED"),
- CAMERA_USE_CASE_BINDING_FAILED("CAMERA_USE_CASE_BINDING_FAILED"),
- DETECT_LARGEST_QUADRILATERAL_FAILED("DETECT_LARGEST_QUADRILATERAL_FAILED"),
- INVALID_IMAGE("INVALID_IMAGE"),
- CAMERA_PERMISSION_REFUSED_WITHOUT_NEVER_ASK_AGAIN("CAMERA_PERMISSION_REFUSED_WITHOUT_NEVER_ASK_AGAIN"),
- CAMERA_PERMISSION_REFUSED_GO_TO_SETTINGS("CAMERA_PERMISSION_REFUSED_GO_TO_SETTINGS"),
- STORAGE_PERMISSION_REFUSED_WITHOUT_NEVER_ASK_AGAIN("STORAGE_PERMISSION_REFUSED_WITHOUT_NEVER_ASK_AGAIN"),
- STORAGE_PERMISSION_REFUSED_GO_TO_SETTINGS("STORAGE_PERMISSION_REFUSED_GO_TO_SETTINGS"),
- CROPPING_FAILED("CROPPING_FAILED");
- }
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/model/ScannerResults.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/model/ScannerResults.kt
deleted file mode 100644
index 6bdf126742..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/model/ScannerResults.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- Copyright 2020 ZynkSoftware SRL
-
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
- associated documentation files (the "Software"), to deal in the Software without restriction,
- including without limitation the rights to use, copy, modify, merge, publish, distribute,
- sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all copies or
- substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-package com.zynksoftware.documentscanner.model
-
-import java.io.File
-
-data class ScannerResults (
- val originalImageFile: File? = null,
- val croppedImageFile: File? = null,
- val transformedImageFile: File? = null
-)
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/DocumentScanner.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/DocumentScanner.kt
deleted file mode 100644
index c335a5093c..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/DocumentScanner.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
-Copyright 2020 ZynkSoftware SRL
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
-associated documentation files (the "Software"), to deal in the Software without restriction,
-including without limitation the rights to use, copy, modify, merge, publish, distribute,
-sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or
-substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-
-package com.zynksoftware.documentscanner.ui
-
-import android.content.Context
-import android.graphics.Bitmap
-import com.zynksoftware.documentscanner.manager.SessionManager
-
-object DocumentScanner {
-
- fun init(context: Context, configuration: Configuration = Configuration()) {
- System.loadLibrary("opencv_java4")
- val sessionManager = SessionManager(context)
- if(configuration.imageQuality in 1..100) {
- sessionManager.setImageQuality(configuration.imageQuality)
- }
- sessionManager.setImageSize(configuration.imageSize)
- sessionManager.setImageType(configuration.imageType)
- }
-
-
- data class Configuration(
- var imageQuality: Int = 100,
- var imageSize: Long = -1,
- var imageType: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG
- ){
- }
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/base/BaseFragment.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/base/BaseFragment.kt
deleted file mode 100644
index 0984bb5311..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/base/BaseFragment.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- Copyright 2020 ZynkSoftware SRL
-
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
- associated documentation files (the "Software"), to deal in the Software without restriction,
- including without limitation the rights to use, copy, modify, merge, publish, distribute,
- sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all copies or
- substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-package com.zynksoftware.documentscanner.ui.base
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.RelativeLayout
-import androidx.fragment.app.Fragment
-import androidx.viewbinding.ViewBinding
-import com.zynksoftware.documentscanner.R
-import com.zynksoftware.documentscanner.common.extensions.hide
-import com.zynksoftware.documentscanner.common.extensions.show
-
-internal abstract class BaseFragment(private val bindingInflater: (layoutInflater: LayoutInflater) -> BINDING) : Fragment() {
-
- private var _binding: BINDING? = null
-
- // This property is only valid between onCreateView and onDestroyView.
- val binding get() = _binding!!
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- _binding = bindingInflater(inflater)
- return binding.root
- }
-
- override fun onDestroyView() {
- super.onDestroyView()
- _binding = null
- }
-
- fun showProgressBar() {
- view?.findViewById(R.id.progressLayout)?.show()
- }
-
- fun hideProgressBar() {
- view?.findViewById(R.id.progressLayout)?.hide()
- }
-
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/camerascreen/CameraScreenFragment.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/camerascreen/CameraScreenFragment.kt
deleted file mode 100644
index 903a80640c..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/camerascreen/CameraScreenFragment.kt
+++ /dev/null
@@ -1,213 +0,0 @@
-/**
- Copyright 2020 ZynkSoftware SRL
-
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
- associated documentation files (the "Software"), to deal in the Software without restriction,
- including without limitation the rights to use, copy, modify, merge, publish, distribute,
- sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all copies or
- substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-package com.zynksoftware.documentscanner.ui.camerascreen
-
-import android.Manifest
-import android.net.Uri
-import android.os.Bundle
-import android.util.Log
-import android.view.View
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.core.content.ContextCompat
-import com.zynksoftware.documentscanner.R
-import com.zynksoftware.documentscanner.common.extensions.hide
-import com.zynksoftware.documentscanner.common.extensions.show
-import com.zynksoftware.documentscanner.common.utils.FileUriUtils
-import com.zynksoftware.documentscanner.databinding.FragmentCameraScreenBinding
-import com.zynksoftware.documentscanner.model.DocumentScannerErrorModel
-import com.zynksoftware.documentscanner.ui.base.BaseFragment
-import com.zynksoftware.documentscanner.ui.components.scansurface.ScanSurfaceListener
-import com.zynksoftware.documentscanner.ui.scan.InternalScanActivity
-import java.io.File
-import java.io.FileNotFoundException
-
-
-internal class CameraScreenFragment: BaseFragment(FragmentCameraScreenBinding::inflate), ScanSurfaceListener {
-
- private val requestCameraPermission = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
- if (isGranted) {
- startCamera()
- } else {
- onError(DocumentScannerErrorModel(DocumentScannerErrorModel.ErrorMessage.CAMERA_PERMISSION_REFUSED_GO_TO_SETTINGS))
- }
- }
-
- private val filePickerContract = registerForActivityResult(ActivityResultContracts.GetContent()) {
- handleSelectedFile(it)
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) = with(binding) {
- super.onViewCreated(view, savedInstanceState)
-
- scanSurfaceView.lifecycleOwner = this@CameraScreenFragment
- scanSurfaceView.listener = this@CameraScreenFragment
- scanSurfaceView.originalImageFile = getScanActivity().originalImageFile
-
- checkForCameraPermissions()
- initListeners()
- }
-
- override fun onDestroy() {
- super.onDestroy()
- if(getScanActivity().shouldCallOnClose) {
- getScanActivity().onClose()
- }
- }
-
- override fun onResume() {
- super.onResume()
- getScanActivity().reInitOriginalImageFile()
- binding.scanSurfaceView.originalImageFile = getScanActivity().originalImageFile
- }
-
- private fun initListeners() = with(binding) {
- cameraCaptureButton.setOnClickListener {
- takePhoto()
- }
- cancelButton.setOnClickListener {
- finishActivity()
- }
- flashButton.setOnClickListener {
- switchFlashState()
- }
- galleryButton.setOnClickListener {
- selectImageFromGallery()
- }
- autoButton.setOnClickListener {
- toggleAutoManualButton()
- }
- }
-
- private fun toggleAutoManualButton() = with(binding) {
- scanSurfaceView.isAutoCaptureOn = !scanSurfaceView.isAutoCaptureOn
- if (scanSurfaceView.isAutoCaptureOn) {
- autoButton.text = getString(R.string.zdc_auto)
- } else {
- autoButton.text = getString(R.string.zdc_manual)
- }
- }
-
- private fun checkForCameraPermissions() {
- ContextCompat.checkSelfPermission(requireActivity(), Manifest.permission.CAMERA).let {
- if (it == android.content.pm.PackageManager.PERMISSION_GRANTED) {
- startCamera()
- } else {
- requestCameraPermission.launch(Manifest.permission.CAMERA)
- }
- }
- }
-
- private fun startCamera() {
- binding.scanSurfaceView.start()
- }
-
- private fun takePhoto() {
- binding.scanSurfaceView.takePicture()
- }
-
- private fun getScanActivity(): InternalScanActivity {
- return (requireActivity() as InternalScanActivity)
- }
-
- private fun finishActivity() {
- getScanActivity().finish()
- }
-
- private fun switchFlashState() {
- binding.scanSurfaceView.switchFlashState()
- }
-
- override fun showFlash() {
- binding.flashButton.show()
- }
-
- override fun hideFlash() {
- binding.flashButton.hide()
- }
-
- private fun selectImageFromGallery() {
- filePickerContract.launch("image/*")
- }
-
- private fun handleSelectedFile(imageUri: Uri?) {
- try {
- if (imageUri != null) {
- val realPath = FileUriUtils.getRealPath(getScanActivity(), imageUri)
- if (realPath != null) {
- getScanActivity().reInitOriginalImageFile()
- getScanActivity().originalImageFile = File(realPath)
- startCroppingProcess()
- } else {
- Log.e(TAG, DocumentScannerErrorModel.ErrorMessage.TAKE_IMAGE_FROM_GALLERY_ERROR.error)
- onError(DocumentScannerErrorModel(
- DocumentScannerErrorModel.ErrorMessage.TAKE_IMAGE_FROM_GALLERY_ERROR, null))
- }
- } else {
- Log.e(TAG, DocumentScannerErrorModel.ErrorMessage.TAKE_IMAGE_FROM_GALLERY_ERROR.error)
- onError(DocumentScannerErrorModel(
- DocumentScannerErrorModel.ErrorMessage.TAKE_IMAGE_FROM_GALLERY_ERROR, null))
- }
- } catch (e: FileNotFoundException) {
- Log.e(TAG, "FileNotFoundException", e)
- onError(DocumentScannerErrorModel(
- DocumentScannerErrorModel.ErrorMessage.TAKE_IMAGE_FROM_GALLERY_ERROR, e))
- }
- }
-
- override fun scanSurfacePictureTaken() {
- startCroppingProcess()
- }
-
- private fun startCroppingProcess() {
- if (isAdded) {
- getScanActivity().showImageCropFragment()
- }
- }
-
- override fun scanSurfaceShowProgress() {
- showProgressBar()
- }
-
- override fun scanSurfaceHideProgress() {
- hideProgressBar()
- }
-
- override fun onError(error: DocumentScannerErrorModel) {
- if(isAdded) {
- getScanActivity().onError(error)
- }
- }
-
- override fun showFlashModeOn() {
- binding.flashButton.setImageResource(R.drawable.zdc_flash_on)
- }
-
- override fun showFlashModeOff() {
- binding.flashButton.setImageResource(R.drawable.zdc_flash_off)
- }
-
- companion object {
- private val TAG = CameraScreenFragment::class.simpleName
-
- fun newInstance(): CameraScreenFragment {
- return CameraScreenFragment()
- }
- }
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/components/ProgressView.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/components/ProgressView.kt
deleted file mode 100644
index f8ab5787e8..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/components/ProgressView.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
-Copyright 2020 ZynkSoftware SRL
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
-associated documentation files (the "Software"), to deal in the Software without restriction,
-including without limitation the rights to use, copy, modify, merge, publish, distribute,
-sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or
-substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-
-package com.zynksoftware.documentscanner.ui.components
-
-import android.content.Context
-import android.util.AttributeSet
-import android.view.LayoutInflater
-import android.widget.RelativeLayout
-import com.zynksoftware.documentscanner.R
-
-internal class ProgressView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0
-) : RelativeLayout(context, attrs, defStyleAttr) {
-
- init {
- LayoutInflater.from(context).inflate(R.layout.progress_layout, this, true)
- }
-}
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/components/Quadrilateral.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/components/Quadrilateral.kt
deleted file mode 100644
index e00482089e..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/components/Quadrilateral.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
-Copyright 2020 ZynkSoftware SRL
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
-associated documentation files (the "Software"), to deal in the Software without restriction,
-including without limitation the rights to use, copy, modify, merge, publish, distribute,
-sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or
-substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-
-package com.zynksoftware.documentscanner.ui.components
-
-import org.opencv.core.MatOfPoint2f
-import org.opencv.core.Point
-
-internal class Quadrilateral(val contour: MatOfPoint2f, val points: Array)
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/components/ScanCanvasView.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/components/ScanCanvasView.kt
deleted file mode 100644
index af550b984b..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/components/ScanCanvasView.kt
+++ /dev/null
@@ -1,183 +0,0 @@
-/**
-Copyright 2020 ZynkSoftware SRL
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
-associated documentation files (the "Software"), to deal in the Software without restriction,
-including without limitation the rights to use, copy, modify, merge, publish, distribute,
-sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or
-substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-
-package com.zynksoftware.documentscanner.ui.components
-
-import android.content.Context
-import android.graphics.Canvas
-import android.graphics.Paint
-import android.graphics.Path
-import android.os.Handler
-import android.os.Looper
-import android.util.AttributeSet
-import android.view.View
-import android.view.ViewGroup
-import android.widget.FrameLayout
-import androidx.core.content.ContextCompat
-import com.zynksoftware.documentscanner.R
-import org.opencv.core.Point
-
-internal class ScanCanvasView : FrameLayout {
-
- constructor(context: Context) : super(context)
- constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
- constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle)
-
- companion object {
- private const val CLEAR_SHAPE_DELAY_IN_MILLIS = 600L
- private const val POINTER_ANIMATION_DURATION = 300L
- }
-
- private var paint = Paint()
- private var border = Paint()
- private val handlerClear = Handler(Looper.getMainLooper())
-
- private var shouldAnimate = true
-
- var pointer1: View = View(context)
- var pointer2: View = View(context)
- var pointer3: View = View(context)
- var pointer4: View = View(context)
-
- init {
- paint.color = ContextCompat.getColor(context, R.color.zdc_white_transparent)
- border.color = ContextCompat.getColor(context, android.R.color.white)
- border.strokeWidth = context.resources.getDimension(R.dimen.zdc_polygon_line_stroke_width)
- border.style = Paint.Style.STROKE
- border.isAntiAlias = true
- paint.isAntiAlias = true
-
- pointer1.layoutParams = LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
- pointer2.layoutParams = LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
- pointer3.layoutParams = LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
- pointer4.layoutParams = LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
-
- clearPointersPosition()
-
- addView(pointer1)
- addView(pointer2)
- addView(pointer3)
- addView(pointer4)
- }
-
- private fun clearPointersPosition() {
- pointer1.x = 0F
- pointer1.y = 0F
- pointer2.x = 0F
- pointer2.y = 0F
- pointer3.x = 0F
- pointer3.y = 0F
- pointer4.x = 0F
- pointer4.y = 0F
- }
-
- override fun dispatchDraw(canvas: Canvas) {
- super.dispatchDraw(canvas)
-
- previewWidth?.let { previewWidth ->
- previewHeight?.let { previewHeight ->
- canvas.scale(width / previewWidth, height / previewHeight)
- }
- }
-
- canvas.drawLine(pointer1.x, pointer1.y, pointer4.x, pointer4.y, border)
- canvas.drawLine(pointer1.x, pointer1.y, pointer2.x, pointer2.y, border)
- canvas.drawLine(pointer3.x, pointer3.y, pointer4.x, pointer4.y, border)
- canvas.drawLine(pointer2.x, pointer2.y, pointer3.x, pointer3.y, border)
-
- val path = Path()
- path.moveTo(pointer1.x, pointer1.y)
- path.lineTo(pointer2.x, pointer2.y)
- path.lineTo(pointer3.x, pointer3.y)
- path.lineTo(pointer4.x, pointer4.y)
- path.close()
-
- path.let {
- canvas.drawPath(it, paint)
- }
- }
-
- var previewWidth: Float? = null
- var previewHeight: Float? = null
-
- fun showShape(previewWidth: Float, previewHeight: Float, points: Array) {
- this.previewWidth = previewWidth
- this.previewHeight = previewHeight
-
- val pointer1x = previewWidth - points[0].y.toFloat()
- val pointer1y = points[0].x.toFloat()
- val pointer2x = previewWidth - points[1].y.toFloat()
- val pointer2y = points[1].x.toFloat()
- val pointer3x = previewWidth - points[2].y.toFloat()
- val pointer3y = points[2].x.toFloat()
- val pointer4x = previewWidth - points[3].y.toFloat()
- val pointer4y = points[3].x.toFloat()
-
- if (pointer1.x == 0F && pointer1.y == 0F) {
- pointer1.x = pointer1x
- pointer1.y = pointer1y
- pointer2.x = pointer2x
- pointer2.y = pointer2y
- pointer3.x = pointer3x
- pointer3.y = pointer3y
- pointer4.x = pointer4x
- pointer4.y = pointer4y
- } else {
- if (shouldAnimate) {
- shouldAnimate = false
-
- pointer1.animate().translationX(pointer1x).translationY(pointer1y)
- .setDuration(POINTER_ANIMATION_DURATION).withEndAction {
- shouldAnimate = true
- }.start()
-
- pointer2.animate().translationX(pointer2x).translationY(pointer2y)
- .setDuration(POINTER_ANIMATION_DURATION).withEndAction {
- shouldAnimate = true
- }.start()
-
- pointer3.animate().translationX(pointer3x).translationY(pointer3y)
- .setDuration(POINTER_ANIMATION_DURATION).withEndAction {
- shouldAnimate = true
- }.start()
-
- pointer4.animate().translationX(pointer4x).translationY(pointer4y)
- .setDuration(POINTER_ANIMATION_DURATION).withEndAction {
- shouldAnimate = true
- }.start()
- }
- }
-
- handlerClear.removeCallbacks(runnable)
- invalidate()
- }
-
- fun clearShape() {
- handlerClear.postDelayed(runnable, CLEAR_SHAPE_DELAY_IN_MILLIS)
- }
-
- private val runnable = Runnable {
- pointer1.clearAnimation()
- pointer2.clearAnimation()
- pointer3.clearAnimation()
- pointer4.clearAnimation()
- clearPointersPosition()
- }
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/components/polygon/PolygonPointImageView.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/components/polygon/PolygonPointImageView.kt
deleted file mode 100644
index 15b82ece1c..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/components/polygon/PolygonPointImageView.kt
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
-Copyright 2020 ZynkSoftware SRL
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
-associated documentation files (the "Software"), to deal in the Software without restriction,
-including without limitation the rights to use, copy, modify, merge, publish, distribute,
-sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or
-substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-package com.zynksoftware.documentscanner.ui.components.polygon
-
-import android.content.Context
-import android.graphics.PointF
-import android.util.AttributeSet
-import android.view.MotionEvent
-import androidx.appcompat.widget.AppCompatImageView
-import androidx.core.content.ContextCompat
-import com.zynksoftware.documentscanner.R
-
-internal class PolygonPointImageView @JvmOverloads constructor(
- context: Context,
- private val polygonView: PolygonView? = null,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0
-) : AppCompatImageView(context, attrs, defStyleAttr) {
-
- private var downPoint = PointF()
- private var startPoint = PointF()
-
- override fun onTouchEvent(event: MotionEvent): Boolean {
- super.onTouchEvent(event)
-
- if (polygonView != null) {
- when (event.action) {
- MotionEvent.ACTION_MOVE -> {
- val mv = PointF(event.x - downPoint.x, event.y - downPoint.y)
- if (startPoint.x + mv.x + width < polygonView.width &&
- startPoint.y + mv.y + height < polygonView.height &&
- startPoint.x + mv.x > 0 && startPoint.y + mv.y > 0
- ) {
- x = startPoint.x + mv.x
- y = startPoint.y + mv.y
- startPoint = PointF(x, y)
- }
- }
- MotionEvent.ACTION_DOWN -> {
- downPoint.x = event.x
- downPoint.y = event.y
- startPoint = PointF(x, y)
- }
- MotionEvent.ACTION_UP -> {
- performClick()
- }
- }
- polygonView.invalidate()
- }
- return true
- }
-
- // Because we call this from onTouchEvent, this code will be executed for both
- // normal touch events and for when the system calls this using Accessibility
- override fun performClick(): Boolean {
- super.performClick()
-
- val color = if (polygonView?.isValidShape(polygonView.getPoints()) == true) {
- ContextCompat.getColor(context, android.R.color.white)
- } else {
- ContextCompat.getColor(context, R.color.zdc_red)
- }
- polygonView?.paint?.color = color
-
- return true
- }
-
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/components/polygon/PolygonView.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/components/polygon/PolygonView.kt
deleted file mode 100644
index 001f697f5d..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/components/polygon/PolygonView.kt
+++ /dev/null
@@ -1,183 +0,0 @@
-/**
-Copyright 2020 ZynkSoftware SRL
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
-associated documentation files (the "Software"), to deal in the Software without restriction,
-including without limitation the rights to use, copy, modify, merge, publish, distribute,
-sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or
-substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-
-package com.zynksoftware.documentscanner.ui.components.polygon
-
-import android.content.Context
-import android.graphics.Bitmap
-import android.graphics.Canvas
-import android.graphics.Paint
-import android.graphics.PointF
-import android.util.AttributeSet
-import android.view.ViewGroup
-import android.widget.FrameLayout
-import android.widget.ImageView
-import androidx.core.content.ContextCompat
-import com.zynksoftware.documentscanner.R
-import java.util.*
-
-internal class PolygonView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0
-) : FrameLayout(context, attrs, defStyleAttr) {
-
- var paint: Paint = Paint()
- private var pointer1: ImageView
- private var pointer2: ImageView
- private var pointer3: ImageView
- private var pointer4: ImageView
- private var pointPadding = resources.getDimension(R.dimen.zdc_point_padding).toInt()
-
- companion object {
- private val TAG = PolygonView::class.simpleName
- private const val HALF = 2
- private const val THREE_PARTS = 3
- }
-
- init {
- pointer1 = getImageView(0, 0)
- pointer2 = getImageView(width, 0)
- pointer3 = getImageView(0, height)
- pointer4 = getImageView(width, height)
-
- addView(pointer1)
- addView(pointer2)
- addView(pointer3)
- addView(pointer4)
-
- paint.color = ContextCompat.getColor(context, android.R.color.white)
- paint.strokeWidth = context.resources.getDimension(R.dimen.zdc_polygon_line_stroke_width)
- paint.isAntiAlias = true
- }
-
- fun getOrderedValidEdgePoints(tempBitmap: Bitmap, pointFs: List): Map {
- var orderedPoints: Map = getOrderedPoints(pointFs)
- if (!isValidShape(orderedPoints)) {
- orderedPoints = getOutlinePoints(tempBitmap)
- }
- return orderedPoints
- }
-
- fun setPoints(pointFMap: Map) {
- if (pointFMap.size == 4) {
- setPointsCoordinates(pointFMap)
- }
- }
-
- fun getPoints(): Map {
- val points: MutableList = ArrayList()
- points.add(PointF(pointer1.x, pointer1.y))
- points.add(PointF(pointer2.x, pointer2.y))
- points.add(PointF(pointer3.x, pointer3.y))
- points.add(PointF(pointer4.x, pointer4.y))
- return getOrderedPoints(points)
- }
-
- fun isValidShape(pointFMap: Map): Boolean {
- return pointFMap.size == 4
- }
-
- private fun getOutlinePoints(tempBitmap: Bitmap): Map {
- val offsetWidth = (tempBitmap.width / THREE_PARTS).toFloat()
- val offsetHeight = (tempBitmap.height / THREE_PARTS).toFloat()
- val screenXCenter = tempBitmap.width / HALF
- val screenYCenter = tempBitmap.height / HALF
- val outlinePoints: MutableMap = HashMap()
- outlinePoints[0] = PointF(screenXCenter - offsetWidth, screenYCenter - offsetHeight)
- outlinePoints[1] = PointF(screenXCenter + offsetWidth, screenYCenter - offsetHeight)
- outlinePoints[2] = PointF(screenXCenter - offsetWidth , screenYCenter + offsetHeight)
- outlinePoints[3] = PointF(screenXCenter + offsetWidth, screenYCenter + offsetHeight)
- return outlinePoints
- }
-
- private fun getOrderedPoints(points: List): Map {
- val centerPoint = PointF()
- val size = points.size
- for (pointF in points) {
- centerPoint.x += pointF.x / size
- centerPoint.y += pointF.y / size
- }
- val orderedPoints: MutableMap = HashMap()
- for (pointF in points) {
- var index = -1
- if (pointF.x < centerPoint.x && pointF.y < centerPoint.y) {
- index = 0
- } else if (pointF.x > centerPoint.x && pointF.y < centerPoint.y) {
- index = 1
- } else if (pointF.x < centerPoint.x && pointF.y > centerPoint.y) {
- index = 2
- } else if (pointF.x > centerPoint.x && pointF.y > centerPoint.y) {
- index = 3
- }
- orderedPoints[index] = pointF
- }
- return orderedPoints
- }
-
- private fun setPointsCoordinates(pointFMap: Map) {
- pointer1.x = pointFMap.getValue(0).x - pointPadding
- pointer1.y = pointFMap.getValue(0).y - pointPadding
-
- pointer2.x = pointFMap.getValue(1).x - pointPadding
- pointer2.y = pointFMap.getValue(1).y - pointPadding
-
- pointer3.x = pointFMap.getValue(2).x - pointPadding
- pointer3.y = pointFMap.getValue(2).y - pointPadding
-
- pointer4.x = pointFMap.getValue(3).x - pointPadding
- pointer4.y = pointFMap.getValue(3).y - pointPadding
- }
-
- override fun dispatchDraw(canvas: Canvas) {
- super.dispatchDraw(canvas)
- canvas.drawLine(
- pointer1.x + pointer1.width / 2, pointer1.y + pointer1.height / 2,
- pointer3.x + pointer3.width / 2, pointer3.y + pointer3.height / 2,
- paint
- )
- canvas.drawLine(
- pointer1.x + pointer1.width / 2, pointer1.y + pointer1.height / 2,
- pointer2.x + pointer2.width / 2, pointer2.y + pointer2.height / 2,
- paint
- )
- canvas.drawLine(
- pointer2.x + pointer2.width / 2, pointer2.y + pointer2.height / 2,
- pointer4.x + pointer4.width / 2, pointer4.y + pointer4.height / 2,
- paint
- )
- canvas.drawLine(
- pointer3.x + pointer3.width / 2, pointer3.y + pointer3.height / 2,
- pointer4.x + pointer4.width / 2, pointer4.y + pointer4.height / 2,
- paint
- )
- }
-
- private fun getImageView(x: Int, y: Int): ImageView {
- val imageView = PolygonPointImageView(context, this)
- val layoutParams = LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
- imageView.layoutParams = layoutParams
- imageView.setImageResource(R.drawable.crop_corner_circle)
- imageView.setPadding(pointPadding, pointPadding, pointPadding, pointPadding)
- imageView.x = x.toFloat()
- imageView.y = y.toFloat()
- return imageView
- }
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/components/scansurface/ScanSurfaceListener.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/components/scansurface/ScanSurfaceListener.kt
deleted file mode 100644
index 0c742f817b..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/components/scansurface/ScanSurfaceListener.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
-Copyright 2020 ZynkSoftware SRL
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
-associated documentation files (the "Software"), to deal in the Software without restriction,
-including without limitation the rights to use, copy, modify, merge, publish, distribute,
-sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or
-substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-
-package com.zynksoftware.documentscanner.ui.components.scansurface
-
-import com.zynksoftware.documentscanner.model.DocumentScannerErrorModel
-
-internal interface ScanSurfaceListener {
- fun scanSurfacePictureTaken()
- fun scanSurfaceShowProgress()
- fun scanSurfaceHideProgress()
- fun onError(error: DocumentScannerErrorModel)
-
- fun showFlash()
- fun hideFlash()
- fun showFlashModeOn()
- fun showFlashModeOff()
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/components/scansurface/ScanSurfaceView.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/components/scansurface/ScanSurfaceView.kt
deleted file mode 100755
index 88e14f42fb..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/components/scansurface/ScanSurfaceView.kt
+++ /dev/null
@@ -1,289 +0,0 @@
-/**
-Copyright 2020 ZynkSoftware SRL
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
-associated documentation files (the "Software"), to deal in the Software without restriction,
-including without limitation the rights to use, copy, modify, merge, publish, distribute,
-sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or
-substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-
-package com.zynksoftware.documentscanner.ui.components.scansurface
-
-import android.content.Context
-import android.os.CountDownTimer
-import android.util.AttributeSet
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.Surface
-import android.widget.FrameLayout
-import androidx.camera.core.*
-import androidx.camera.lifecycle.ProcessCameraProvider
-import androidx.core.content.ContextCompat
-import androidx.lifecycle.LifecycleOwner
-import com.zynksoftware.documentscanner.R
-import com.zynksoftware.documentscanner.common.extensions.yuvToRgba
-import com.zynksoftware.documentscanner.common.utils.ImageDetectionProperties
-import com.zynksoftware.documentscanner.common.utils.OpenCvNativeBridge
-import com.zynksoftware.documentscanner.databinding.ScanSurfaceViewBinding
-import com.zynksoftware.documentscanner.model.DocumentScannerErrorModel
-import com.zynksoftware.documentscanner.model.DocumentScannerErrorModel.ErrorMessage
-import org.opencv.core.MatOfPoint2f
-import org.opencv.core.Point
-import org.opencv.core.Size
-import java.io.File
-import kotlin.math.max
-import kotlin.math.min
-import kotlin.math.roundToInt
-
-private val TAG = ScanSurfaceView::class.simpleName
-
-private const val TIME_POST_PICTURE = 1500L
-private const val DEFAULT_TIME_POST_PICTURE = 1500L
-private const val IMAGE_ANALYSIS_SCALE_WIDTH = 400
-
-internal class ScanSurfaceView : FrameLayout {
-
- constructor(context: Context) : super(context)
- constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
- constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle)
-
- private val binding: ScanSurfaceViewBinding
-
- lateinit var lifecycleOwner: LifecycleOwner
- lateinit var listener: ScanSurfaceListener
- lateinit var originalImageFile: File
-
- private val nativeClass = OpenCvNativeBridge()
- private var autoCaptureTimer: CountDownTimer? = null
- private var millisLeft = 0L
- private var isAutoCaptureScheduled = false
- private var isCapturing = false
-
- private var imageAnalysis: ImageAnalysis? = null
- private var camera: Camera? = null
- private var imageCapture: ImageCapture? = null
- private var preview: Preview? = null
- private var cameraProvider: ProcessCameraProvider? = null
- private lateinit var previewSize: android.util.Size
-
- var isAutoCaptureOn: Boolean = true
- private var isFlashEnabled: Boolean = false
- private var flashMode: Int = ImageCapture.FLASH_MODE_OFF
-
- init {
- binding = ScanSurfaceViewBinding.inflate(LayoutInflater.from(context), this, true)
- }
-
- fun start() = with(binding) {
- viewFinder.post {
- viewFinder.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
- previewSize = android.util.Size(viewFinder.width, viewFinder.height)
- openCamera()
- }
- }
-
- private fun clearAndInvalidateCanvas() {
- binding.scanCanvasView.clearShape()
- }
-
- private fun openCamera() {
- val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
-
- cameraProviderFuture.addListener(Runnable {
- cameraProvider = cameraProviderFuture.get()
-
- try {
- bindCamera()
- checkIfFlashIsPresent()
- } catch (exc: Exception) {
- Log.e(TAG, ErrorMessage.CAMERA_USE_CASE_BINDING_FAILED.error, exc)
- listener.onError(DocumentScannerErrorModel(ErrorMessage.CAMERA_USE_CASE_BINDING_FAILED, exc))
- }
- }, ContextCompat.getMainExecutor(context))
- }
-
- private fun bindCamera() {
- cameraProvider?.unbindAll()
- camera = null
- setUseCases()
- }
-
- private fun setImageCapture() {
- if(imageCapture != null && cameraProvider?.isBound(imageCapture!!) == true) {
- cameraProvider?.unbind(imageCapture)
- }
-
- imageCapture = null
- imageCapture = ImageCapture.Builder()
- .setTargetRotation(Surface.ROTATION_0)
- .setFlashMode(flashMode)
- .build()
- }
-
- fun unbindCamera() {
- cameraProvider?.unbind(imageAnalysis)
- }
-
- private fun setUseCases() {
- preview = Preview.Builder()
- .setTargetResolution(previewSize)
- .setTargetRotation(Surface.ROTATION_0)
- .build()
- .also {
- it.setSurfaceProvider(binding.viewFinder.surfaceProvider)
- }
-
- setImageCapture()
-
- val aspectRatio: Float = previewSize.width / previewSize.height.toFloat()
- val width = IMAGE_ANALYSIS_SCALE_WIDTH
- val height = (width / aspectRatio).roundToInt()
-
- imageAnalysis = ImageAnalysis.Builder()
- .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
- .setTargetResolution(android.util.Size(width, height))
- .setTargetRotation(Surface.ROTATION_0)
- .build()
-
- imageAnalysis?.setAnalyzer(ContextCompat.getMainExecutor(context), { image ->
- if (isAutoCaptureOn) {
- try {
- val mat = image.yuvToRgba()
- val originalPreviewSize = mat.size()
- val largestQuad = nativeClass.detectLargestQuadrilateral(mat)
- mat.release()
- if (null != largestQuad) {
- drawLargestRect(largestQuad.contour, largestQuad.points, originalPreviewSize)
- } else {
- clearAndInvalidateCanvas()
- }
- } catch (e: Exception) {
- Log.e(TAG, ErrorMessage.DETECT_LARGEST_QUADRILATERAL_FAILED.error, e)
- listener.onError(DocumentScannerErrorModel(ErrorMessage.DETECT_LARGEST_QUADRILATERAL_FAILED, e))
- clearAndInvalidateCanvas()
- }
- } else {
- clearAndInvalidateCanvas()
- }
- image.close()
- })
-
- camera = cameraProvider!!.bindToLifecycle(lifecycleOwner, CameraSelector.DEFAULT_BACK_CAMERA, preview, imageAnalysis, imageCapture)
- }
-
- private fun drawLargestRect(approx: MatOfPoint2f, points: Array, stdSize: Size) {
- // Attention: axis are swapped
- val previewWidth = stdSize.height.toFloat()
- val previewHeight = stdSize.width.toFloat()
-
- val resultWidth = max(previewWidth - points[0].y.toFloat(), previewWidth - points[1].y.toFloat()) -
- min(previewWidth - points[2].y.toFloat(), previewWidth - points[3].y.toFloat())
-
- val resultHeight = max(points[1].x.toFloat(), points[2].x.toFloat()) - min(points[0].x.toFloat(), points[3].x.toFloat())
-
- val imgDetectionPropsObj = ImageDetectionProperties(previewWidth.toDouble(), previewHeight.toDouble(),
- points[0], points[1], points[2], points[3], resultWidth.toInt(), resultHeight.toInt())
- if (imgDetectionPropsObj.isNotValidImage(approx)) {
- binding.scanCanvasView.clearShape()
- cancelAutoCapture()
- } else {
- if (!isAutoCaptureScheduled) {
- scheduleAutoCapture()
- }
- binding.scanCanvasView.showShape(previewWidth, previewHeight, points)
- }
- }
-
- private fun scheduleAutoCapture() {
- isAutoCaptureScheduled = true
- millisLeft = 0L
- autoCaptureTimer = object : CountDownTimer(DEFAULT_TIME_POST_PICTURE, 100) {
- override fun onTick(millisUntilFinished: Long) {
- if (millisUntilFinished != millisLeft) {
- millisLeft = millisUntilFinished
- }
- }
-
- override fun onFinish() {
- isAutoCaptureScheduled = false
- autoCapture()
- }
- }
- autoCaptureTimer?.start()
- }
-
- private fun autoCapture() {
- if (isCapturing)
- return
- cancelAutoCapture()
- takePicture()
- }
-
- fun takePicture() {
- Log.d(TAG, "ZDCtakePicture Starts ${System.currentTimeMillis()}")
- listener.scanSurfaceShowProgress()
- isCapturing = true
-
- val imageCapture = imageCapture ?: return
- val outputOptions = ImageCapture.OutputFileOptions.Builder(originalImageFile).build()
-
- imageCapture.takePicture(outputOptions, ContextCompat.getMainExecutor(context),
- object : ImageCapture.OnImageSavedCallback {
- override fun onError(exc: ImageCaptureException) {
- listener.scanSurfaceHideProgress()
- Log.e(TAG, "${ErrorMessage.PHOTO_CAPTURE_FAILED.error}: ${exc.message}", exc)
- listener.onError(DocumentScannerErrorModel(ErrorMessage.PHOTO_CAPTURE_FAILED, exc))
- }
-
- override fun onImageSaved(output: ImageCapture.OutputFileResults) {
- listener.scanSurfaceHideProgress()
-
- unbindCamera()
-
- clearAndInvalidateCanvas()
- listener.scanSurfacePictureTaken()
- postDelayed({ isCapturing = false }, TIME_POST_PICTURE)
- Log.d(TAG, "ZDCtakePicture ends ${System.currentTimeMillis()}")
- }
- })
- }
-
- private fun checkIfFlashIsPresent() {
- if (camera?.cameraInfo?.hasFlashUnit() == true) {
- listener.showFlash()
- } else {
- listener.hideFlash()
- }
- }
-
- private fun cancelAutoCapture() {
- if (isAutoCaptureScheduled) {
- isAutoCaptureScheduled = false
- autoCaptureTimer?.cancel()
- }
- }
-
- fun switchFlashState() {
- isFlashEnabled = !isFlashEnabled
- flashMode = if (isFlashEnabled) {
- listener.showFlashModeOn()
- ImageCapture.FLASH_MODE_ON
- } else {
- listener.showFlashModeOff()
- ImageCapture.FLASH_MODE_OFF
- }
- setImageCapture()
- camera = cameraProvider!!.bindToLifecycle(lifecycleOwner, CameraSelector.DEFAULT_BACK_CAMERA, imageCapture)
- }
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/imagecrop/ImageCropFragment.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/imagecrop/ImageCropFragment.kt
deleted file mode 100644
index 8ab1ef6633..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/imagecrop/ImageCropFragment.kt
+++ /dev/null
@@ -1,159 +0,0 @@
-/**
-Copyright 2020 ZynkSoftware SRL
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
-associated documentation files (the "Software"), to deal in the Software without restriction,
-including without limitation the rights to use, copy, modify, merge, publish, distribute,
-sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or
-substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-
-package com.zynksoftware.documentscanner.ui.imagecrop
-
-import android.graphics.Bitmap
-import android.graphics.BitmapFactory
-import android.graphics.PointF
-import android.graphics.drawable.BitmapDrawable
-import android.os.Bundle
-import android.os.Handler
-import android.os.Looper
-import android.util.Log
-import android.view.Gravity
-import android.view.View
-import android.widget.FrameLayout
-import com.zynksoftware.documentscanner.R
-import com.zynksoftware.documentscanner.ScanActivity
-import com.zynksoftware.documentscanner.common.extensions.scaledBitmap
-import com.zynksoftware.documentscanner.common.utils.OpenCvNativeBridge
-import com.zynksoftware.documentscanner.databinding.FragmentImageCropBinding
-import com.zynksoftware.documentscanner.model.DocumentScannerErrorModel
-import com.zynksoftware.documentscanner.ui.base.BaseFragment
-import com.zynksoftware.documentscanner.ui.scan.InternalScanActivity
-import id.zelory.compressor.determineImageRotation
-
-internal class ImageCropFragment : BaseFragment(FragmentImageCropBinding::inflate) {
-
- private val nativeClass = OpenCvNativeBridge()
-
- private var selectedImage: Bitmap? = null
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- val sourceBitmap = BitmapFactory.decodeFile(getScanActivity().originalImageFile.absolutePath)
- if (sourceBitmap != null) {
- selectedImage = determineImageRotation(getScanActivity().originalImageFile, sourceBitmap)
- } else {
- Log.e(TAG, DocumentScannerErrorModel.ErrorMessage.INVALID_IMAGE.error)
- onError(DocumentScannerErrorModel(DocumentScannerErrorModel.ErrorMessage.INVALID_IMAGE))
- Handler(Looper.getMainLooper()).post{
- closeFragment()
- }
- }
- binding.holderImageView.post {
- if (this.view != null) initializeCropping()
- }
-
- initListeners()
- }
-
- private fun initListeners() = with(binding) {
- closeButton.setOnClickListener {
- closeFragment()
- }
- confirmButton.setOnClickListener {
- onConfirmButtonClicked()
- }
- }
-
- private fun getScanActivity(): InternalScanActivity {
- return (requireActivity() as InternalScanActivity)
- }
-
- private fun initializeCropping() = with(binding) {
- if(selectedImage != null && selectedImage!!.width > 0 && selectedImage!!.height > 0) {
- val scaledBitmap: Bitmap = selectedImage!!.scaledBitmap(holderImageCrop.width, holderImageCrop.height)
- imagePreview.setImageBitmap(scaledBitmap)
- val tempBitmap = (imagePreview.drawable as BitmapDrawable).bitmap
- val pointFs = getEdgePoints(tempBitmap)
- Log.d(TAG, "ZDCgetEdgePoints ends ${System.currentTimeMillis()}")
- polygonView.setPoints(pointFs)
- polygonView.visibility = View.VISIBLE
- val padding = resources.getDimension(R.dimen.zdc_polygon_dimens).toInt()
- val layoutParams = FrameLayout.LayoutParams(tempBitmap.width + padding, tempBitmap.height + padding)
- layoutParams.gravity = Gravity.CENTER
- polygonView.layoutParams = layoutParams
- }
- }
-
- private fun onError(error: DocumentScannerErrorModel) {
- if (isAdded) {
- getScanActivity().onError(error)
- }
- }
-
- private fun onConfirmButtonClicked() {
- getCroppedImage()
- (activity as ScanActivity).finalScannerResult()
- }
-
- private fun getEdgePoints(tempBitmap: Bitmap): Map {
- Log.d(TAG, "ZDCgetEdgePoints Starts ${System.currentTimeMillis()}")
- val pointFs: List = nativeClass.getContourEdgePoints(tempBitmap)
- return binding.polygonView.getOrderedValidEdgePoints(tempBitmap, pointFs)
- }
-
- private fun getCroppedImage() {
- if(selectedImage != null) {
- try {
- Log.d(TAG, "ZDCgetCroppedImage starts ${System.currentTimeMillis()}")
- val points: Map = binding.polygonView.getPoints()
- val xRatio: Float = selectedImage!!.width.toFloat() / binding.imagePreview.width
- val yRatio: Float = selectedImage!!.height.toFloat() / binding.imagePreview.height
- val pointPadding = requireContext().resources.getDimension(R.dimen.zdc_point_padding).toInt()
- val x1: Float = (points.getValue(0).x + pointPadding) * xRatio
- val x2: Float = (points.getValue(1).x + pointPadding) * xRatio
- val x3: Float = (points.getValue(2).x + pointPadding) * xRatio
- val x4: Float = (points.getValue(3).x + pointPadding) * xRatio
- val y1: Float = (points.getValue(0).y + pointPadding) * yRatio
- val y2: Float = (points.getValue(1).y + pointPadding) * yRatio
- val y3: Float = (points.getValue(2).y + pointPadding) * yRatio
- val y4: Float = (points.getValue(3).y + pointPadding) * yRatio
- getScanActivity().croppedImage = nativeClass.getScannedBitmap(selectedImage!!, x1, y1, x2, y2, x3, y3, x4, y4)
- Log.d(TAG, "ZDCgetCroppedImage ends ${System.currentTimeMillis()}")
- } catch (e: java.lang.Exception) {
- Log.e(TAG, DocumentScannerErrorModel.ErrorMessage.CROPPING_FAILED.error, e)
- onError(DocumentScannerErrorModel(DocumentScannerErrorModel.ErrorMessage.CROPPING_FAILED, e))
- }
- } else {
- Log.e(TAG, DocumentScannerErrorModel.ErrorMessage.INVALID_IMAGE.error)
- onError(DocumentScannerErrorModel(DocumentScannerErrorModel.ErrorMessage.INVALID_IMAGE))
- }
- }
-
- private fun startImageProcessingFragment() {
- getScanActivity().showImageProcessingFragment()
- }
-
- private fun closeFragment() {
- getScanActivity().closeCurrentFragment()
- }
-
- companion object {
- private val TAG = ImageCropFragment::class.simpleName
-
- fun newInstance(): ImageCropFragment {
- return ImageCropFragment()
- }
- }
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/imageprocessing/ImageProcessingFragment.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/imageprocessing/ImageProcessingFragment.kt
deleted file mode 100644
index 735a8fb1d6..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/imageprocessing/ImageProcessingFragment.kt
+++ /dev/null
@@ -1,136 +0,0 @@
-/**
-Copyright 2020 ZynkSoftware SRL
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
-associated documentation files (the "Software"), to deal in the Software without restriction,
-including without limitation the rights to use, copy, modify, merge, publish, distribute,
-sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or
-substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-
-package com.zynksoftware.documentscanner.ui.imageprocessing
-
-import android.graphics.*
-import android.os.Bundle
-import android.util.Log
-import android.view.View
-import com.zynksoftware.documentscanner.common.extensions.rotateBitmap
-import com.zynksoftware.documentscanner.databinding.FragmentImageProcessingBinding
-import com.zynksoftware.documentscanner.ui.base.BaseFragment
-import com.zynksoftware.documentscanner.ui.scan.InternalScanActivity
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
-
-internal class ImageProcessingFragment : BaseFragment(FragmentImageProcessingBinding::inflate) {
-
- private var isInverted = false
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- binding.imagePreview.setImageBitmap(getScanActivity().croppedImage)
-
- initListeners()
- }
-
- private fun initListeners() = with(binding) {
- closeButton.setOnClickListener {
- closeFragment()
- }
- confirmButton.setOnClickListener {
- selectFinalScannerResults()
- }
- magicButton.setOnClickListener {
- applyGrayScaleFilter()
- }
- rotateButton.setOnClickListener {
- rotateImage()
- }
- }
-
- private fun getScanActivity(): InternalScanActivity {
- return (requireActivity() as InternalScanActivity)
- }
-
- private fun rotateImage() {
- Log.d(TAG, "ZDCrotate starts ${System.currentTimeMillis()}")
- showProgressBar()
- GlobalScope.launch(Dispatchers.IO) {
- if(isAdded) {
- getScanActivity().transformedImage = getScanActivity().transformedImage?.rotateBitmap(ANGLE_OF_ROTATION)
- getScanActivity().croppedImage = getScanActivity().croppedImage?.rotateBitmap(ANGLE_OF_ROTATION)
- }
-
- if(isAdded) {
- getScanActivity().runOnUiThread {
- hideProgressBar()
- if (isInverted) {
- binding.imagePreview?.setImageBitmap(getScanActivity().transformedImage)
- } else {
- binding.imagePreview?.setImageBitmap(getScanActivity().croppedImage)
- }
- }
- }
- Log.d(TAG, "ZDCrotate ends ${System.currentTimeMillis()}")
- }
- }
-
- private fun closeFragment() {
- getScanActivity().closeCurrentFragment()
- }
-
- private fun applyGrayScaleFilter() {
- Log.d(TAG, "ZDCgrayscale starts ${System.currentTimeMillis()}")
- showProgressBar()
- GlobalScope.launch(Dispatchers.IO) {
- if(isAdded) {
- if (!isInverted) {
- val bmpMonochrome = Bitmap.createBitmap(getScanActivity().croppedImage!!.width, getScanActivity().croppedImage!!.height, Bitmap.Config.ARGB_8888)
- val canvas = Canvas(bmpMonochrome)
- val ma = ColorMatrix()
- ma.setSaturation(0f)
- val paint = Paint()
- paint.colorFilter = ColorMatrixColorFilter(ma)
- getScanActivity().croppedImage?.let { canvas.drawBitmap(it, 0f, 0f, paint) }
- getScanActivity().transformedImage =
- bmpMonochrome.config?.let { bmpMonochrome.copy(it, true) }
- getScanActivity().runOnUiThread {
- hideProgressBar()
- binding.imagePreview.setImageBitmap(getScanActivity().transformedImage)
- }
- } else {
- getScanActivity().runOnUiThread {
- hideProgressBar()
- binding.imagePreview.setImageBitmap(getScanActivity().croppedImage)
- }
- }
- isInverted = !isInverted
- Log.d(TAG, "ZDCgrayscale ends ${System.currentTimeMillis()}")
- }
- }
- }
-
- private fun selectFinalScannerResults() {
- getScanActivity().finalScannerResult()
- }
-
- companion object {
- private val TAG = ImageProcessingFragment::class.simpleName
- private const val ANGLE_OF_ROTATION = 90
-
- fun newInstance(): ImageProcessingFragment {
- return ImageProcessingFragment()
- }
- }
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/scan/InternalScanActivity.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/scan/InternalScanActivity.kt
deleted file mode 100644
index 6ae5012f43..0000000000
--- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/scan/InternalScanActivity.kt
+++ /dev/null
@@ -1,196 +0,0 @@
-/**
-Copyright 2020 ZynkSoftware SRL
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
-associated documentation files (the "Software"), to deal in the Software without restriction,
-including without limitation the rights to use, copy, modify, merge, publish, distribute,
-sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or
-substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-
-package com.zynksoftware.documentscanner.ui.scan
-
-import android.graphics.Bitmap
-import android.os.Bundle
-import android.util.Log
-import android.widget.FrameLayout
-import androidx.appcompat.app.AppCompatActivity
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.FragmentManager
-import androidx.fragment.app.FragmentTransaction
-import com.zynksoftware.documentscanner.R
-import com.zynksoftware.documentscanner.common.extensions.hide
-import com.zynksoftware.documentscanner.common.extensions.show
-import com.zynksoftware.documentscanner.manager.SessionManager
-import com.zynksoftware.documentscanner.model.DocumentScannerErrorModel
-import com.zynksoftware.documentscanner.model.ScannerResults
-import com.zynksoftware.documentscanner.ui.camerascreen.CameraScreenFragment
-import com.zynksoftware.documentscanner.ui.components.ProgressView
-import com.zynksoftware.documentscanner.ui.imagecrop.ImageCropFragment
-import com.zynksoftware.documentscanner.ui.imageprocessing.ImageProcessingFragment
-import id.zelory.compressor.Compressor
-import id.zelory.compressor.constraint.format
-import id.zelory.compressor.constraint.quality
-import id.zelory.compressor.constraint.size
-import id.zelory.compressor.extension
-import id.zelory.compressor.saveBitmap
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
-import java.io.File
-
-abstract class InternalScanActivity : AppCompatActivity() {
-
- abstract fun onError(error: DocumentScannerErrorModel)
- abstract fun onSuccess(scannerResults: ScannerResults)
- abstract fun onClose()
-
- companion object {
- private val TAG = InternalScanActivity::class.simpleName
- internal const val CAMERA_SCREEN_FRAGMENT_TAG = "CameraScreenFragmentTag"
- internal const val IMAGE_CROP_FRAGMENT_TAG = "ImageCropFragmentTag"
- internal const val IMAGE_PROCESSING_FRAGMENT_TAG = "ImageProcessingFragmentTag"
- internal const val ORIGINAL_IMAGE_NAME = "original"
- internal const val CROPPED_IMAGE_NAME = "cropped"
- internal const val TRANSFORMED_IMAGE_NAME = "transformed"
- internal const val NOT_INITIALIZED = -1L
- }
-
- internal lateinit var originalImageFile: File
- internal var croppedImage: Bitmap? = null
- internal var transformedImage: Bitmap? = null
- private var imageQuality: Int = 100
- private var imageSize: Long = NOT_INITIALIZED
- private lateinit var imageType: Bitmap.CompressFormat
- internal var shouldCallOnClose = true
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- val sessionManager = SessionManager(this)
- imageType = sessionManager.getImageType()
- imageSize = sessionManager.getImageSize()
- imageQuality = sessionManager.getImageQuality()
- reInitOriginalImageFile()
- }
-
- internal fun reInitOriginalImageFile() {
- originalImageFile = File(filesDir, "${ORIGINAL_IMAGE_NAME}_${System.currentTimeMillis()}.${imageType.extension()}")
- }
-
- private fun showCameraScreen() {
- val cameraScreenFragment = CameraScreenFragment.newInstance()
- addFragmentToBackStack(cameraScreenFragment, CAMERA_SCREEN_FRAGMENT_TAG)
- }
-
- internal fun showImageCropFragment() {
- val imageCropFragment = ImageCropFragment.newInstance()
- addFragmentToBackStack(imageCropFragment, IMAGE_CROP_FRAGMENT_TAG)
- }
-
- internal fun showImageProcessingFragment() {
- val imageProcessingFragment = ImageProcessingFragment.newInstance()
- addFragmentToBackStack(imageProcessingFragment, IMAGE_PROCESSING_FRAGMENT_TAG)
- }
-
- internal fun closeCurrentFragment() {
- supportFragmentManager.popBackStackImmediate()
- }
-
- private fun addFragmentToBackStack(fragment: Fragment, fragmentTag: String) {
- val fragmentTransaction: FragmentTransaction = supportFragmentManager.beginTransaction()
- fragmentTransaction.replace(R.id.zdcContent, fragment, fragmentTag)
- if (supportFragmentManager.findFragmentByTag(fragmentTag) == null) {
- fragmentTransaction.addToBackStack(fragmentTag)
- }
- fragmentTransaction.commit()
- }
-
- internal fun finalScannerResult() {
- findViewById(R.id.zdcContent).hide()
- compressFiles()
- }
-
- private fun compressFiles() {
- Log.d(TAG, "ZDCcompress starts ${System.currentTimeMillis()}")
- findViewById(R.id.zdcProgressView).show()
- GlobalScope.launch(Dispatchers.IO) {
- var croppedImageFile: File? = null
- croppedImage?.let {
- croppedImageFile = File(filesDir, "${CROPPED_IMAGE_NAME}_${System.currentTimeMillis()}.${imageType.extension()}")
- saveBitmap(it, croppedImageFile!!, imageType, imageQuality)
- }
-
- var transformedImageFile: File? = null
- transformedImage?.let {
- transformedImageFile = File(filesDir, "${TRANSFORMED_IMAGE_NAME}_${System.currentTimeMillis()}.${imageType.extension()}")
- saveBitmap(it, transformedImageFile!!, imageType, imageQuality)
- }
-
- originalImageFile = Compressor.compress(this@InternalScanActivity, originalImageFile) {
- quality(imageQuality)
- if (imageSize != NOT_INITIALIZED) size(imageSize)
- format(imageType)
- }
-
- croppedImageFile = croppedImageFile?.let {
- Compressor.compress(this@InternalScanActivity, it) {
- quality(imageQuality)
- if (imageSize != NOT_INITIALIZED) size(imageSize)
- format(imageType)
- }
- }
-
- transformedImageFile = transformedImageFile?.let {
- Compressor.compress(this@InternalScanActivity, it) {
- quality(imageQuality)
- if (imageSize != NOT_INITIALIZED) size(imageSize)
- format(imageType)
- }
- }
-
- val scannerResults = ScannerResults(originalImageFile, croppedImageFile, transformedImageFile)
- runOnUiThread {
- findViewById(R.id.zdcProgressView).hide()
- shouldCallOnClose = false
- supportFragmentManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
- shouldCallOnClose = true
- onSuccess(scannerResults)
- Log.d(TAG, "ZDCcompress ends ${System.currentTimeMillis()}")
- }
- }
- }
-
- internal fun addFragmentContentLayoutInternal() {
- val frameLayout = FrameLayout(this)
- frameLayout.id = R.id.zdcContent
- addContentView(
- frameLayout, FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT,
- FrameLayout.LayoutParams.MATCH_PARENT
- )
- )
-
- val progressView = ProgressView(this)
- progressView.id = R.id.zdcProgressView
- addContentView(
- progressView, FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT,
- FrameLayout.LayoutParams.MATCH_PARENT
- )
- )
-
- progressView.hide()
-
- showCameraScreen()
- }
-}
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/res/drawable/camera_button_circle.xml b/libs/DocumentScanner/src/main/res/drawable/camera_button_circle.xml
deleted file mode 100644
index 5bf8734e68..0000000000
--- a/libs/DocumentScanner/src/main/res/drawable/camera_button_circle.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/res/drawable/crop_corner_circle.xml b/libs/DocumentScanner/src/main/res/drawable/crop_corner_circle.xml
deleted file mode 100644
index c42fe5877a..0000000000
--- a/libs/DocumentScanner/src/main/res/drawable/crop_corner_circle.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/res/drawable/iconclose.png b/libs/DocumentScanner/src/main/res/drawable/iconclose.png
deleted file mode 100644
index 0297199d79..0000000000
Binary files a/libs/DocumentScanner/src/main/res/drawable/iconclose.png and /dev/null differ
diff --git a/libs/DocumentScanner/src/main/res/drawable/zdc_flash_off.png b/libs/DocumentScanner/src/main/res/drawable/zdc_flash_off.png
deleted file mode 100644
index ee5fe4afec..0000000000
Binary files a/libs/DocumentScanner/src/main/res/drawable/zdc_flash_off.png and /dev/null differ
diff --git a/libs/DocumentScanner/src/main/res/drawable/zdc_flash_on.png b/libs/DocumentScanner/src/main/res/drawable/zdc_flash_on.png
deleted file mode 100644
index 7dd46e03b2..0000000000
Binary files a/libs/DocumentScanner/src/main/res/drawable/zdc_flash_on.png and /dev/null differ
diff --git a/libs/DocumentScanner/src/main/res/drawable/zdc_gallery_icon.png b/libs/DocumentScanner/src/main/res/drawable/zdc_gallery_icon.png
deleted file mode 100644
index 0ef7562371..0000000000
Binary files a/libs/DocumentScanner/src/main/res/drawable/zdc_gallery_icon.png and /dev/null differ
diff --git a/libs/DocumentScanner/src/main/res/drawable/zdc_magic_wand_icon.png b/libs/DocumentScanner/src/main/res/drawable/zdc_magic_wand_icon.png
deleted file mode 100644
index 68a9b36a04..0000000000
Binary files a/libs/DocumentScanner/src/main/res/drawable/zdc_magic_wand_icon.png and /dev/null differ
diff --git a/libs/DocumentScanner/src/main/res/drawable/zdc_rotation_icon.png b/libs/DocumentScanner/src/main/res/drawable/zdc_rotation_icon.png
deleted file mode 100644
index 2f8a18d82b..0000000000
Binary files a/libs/DocumentScanner/src/main/res/drawable/zdc_rotation_icon.png and /dev/null differ
diff --git a/libs/DocumentScanner/src/main/res/drawable/zdc_tick_icon.png b/libs/DocumentScanner/src/main/res/drawable/zdc_tick_icon.png
deleted file mode 100644
index 15376f01c0..0000000000
Binary files a/libs/DocumentScanner/src/main/res/drawable/zdc_tick_icon.png and /dev/null differ
diff --git a/libs/DocumentScanner/src/main/res/layout/fragment_camera_screen.xml b/libs/DocumentScanner/src/main/res/layout/fragment_camera_screen.xml
deleted file mode 100644
index 3ed89f9606..0000000000
--- a/libs/DocumentScanner/src/main/res/layout/fragment_camera_screen.xml
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/res/layout/fragment_image_crop.xml b/libs/DocumentScanner/src/main/res/layout/fragment_image_crop.xml
deleted file mode 100644
index 726f2648a0..0000000000
--- a/libs/DocumentScanner/src/main/res/layout/fragment_image_crop.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/res/layout/fragment_image_processing.xml b/libs/DocumentScanner/src/main/res/layout/fragment_image_processing.xml
deleted file mode 100644
index 34901f292d..0000000000
--- a/libs/DocumentScanner/src/main/res/layout/fragment_image_processing.xml
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/libs/DocumentScanner/src/main/res/layout/progress_layout.xml b/libs/DocumentScanner/src/main/res/layout/progress_layout.xml
deleted file mode 100644
index 09ba456ec0..0000000000
--- a/libs/DocumentScanner/src/main/res/layout/progress_layout.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/res/layout/scan_surface_view.xml b/libs/DocumentScanner/src/main/res/layout/scan_surface_view.xml
deleted file mode 100644
index f0274c73b4..0000000000
--- a/libs/DocumentScanner/src/main/res/layout/scan_surface_view.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/res/values-night/colors.xml b/libs/DocumentScanner/src/main/res/values-night/colors.xml
deleted file mode 100644
index 9444cbd565..0000000000
--- a/libs/DocumentScanner/src/main/res/values-night/colors.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
- #232323
- #FFFFFF
-
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/res/values/colors.xml b/libs/DocumentScanner/src/main/res/values/colors.xml
deleted file mode 100644
index 1e7ce9d650..0000000000
--- a/libs/DocumentScanner/src/main/res/values/colors.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
- #99000000
- #afadae
-
- #E9001C
-
- #77ffffff
-
- #FFFFFF
- #000000
-
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/res/values/dimens.xml b/libs/DocumentScanner/src/main/res/values/dimens.xml
deleted file mode 100644
index 6bd0df5ac0..0000000000
--- a/libs/DocumentScanner/src/main/res/values/dimens.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
- 2dp
- 10dp
-
- 12sp
- 14sp
- 18sp
- 50dp
-
- 80dp
- 40dp
- 20dp
- 12dp
- 10dp
- 8dp
-
- 100dp
- 0.5dp
- 0dp
- 15dp
- 50dp
- 40dp
- 32dp
- 1dp
- 2dp
- 4dp
- 20dp
- 50dp
- 40dp
- -20dp
-
-
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/res/values/ids.xml b/libs/DocumentScanner/src/main/res/values/ids.xml
deleted file mode 100644
index 5f7ceae156..0000000000
--- a/libs/DocumentScanner/src/main/res/values/ids.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/res/values/strings.xml b/libs/DocumentScanner/src/main/res/values/strings.xml
deleted file mode 100644
index 1a6465b0bd..0000000000
--- a/libs/DocumentScanner/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
- Cancel
- Auto
- Manual
-
\ No newline at end of file
diff --git a/libs/DocumentScanner/src/main/res/values/styles.xml b/libs/DocumentScanner/src/main/res/values/styles.xml
deleted file mode 100644
index 6070315a53..0000000000
--- a/libs/DocumentScanner/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/libs/annotations/build.gradle b/libs/annotations/build.gradle
index 072df18231..9fbeaa9100 100644
--- a/libs/annotations/build.gradle
+++ b/libs/annotations/build.gradle
@@ -17,7 +17,7 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-kapt'
+apply plugin: 'com.google.devtools.ksp'
static String isTesting() {
if ( System.getenv("IS_TESTING") == "true" ) {
@@ -96,7 +96,7 @@ repositories {
username pspdfMavenUser
password pspdfMavenPass
}
- url 'https://customers.pspdfkit.com/maven/'
+ url 'https://my.nutrient.io/maven'
}
}
@@ -105,7 +105,7 @@ dependencies {
api project(path: ':pandautils')
api project(path: ':pandares')
- api Libs.PSPDFKIT
+ api Libs.NUTRIENT
androidTestImplementation Libs.JUNIT
testImplementation Libs.JUNIT
diff --git a/libs/annotations/src/main/java/com/instructure/annotations/PdfSubmissionView.kt b/libs/annotations/src/main/java/com/instructure/annotations/PdfSubmissionView.kt
index 99fabce95c..18774af417 100644
--- a/libs/annotations/src/main/java/com/instructure/annotations/PdfSubmissionView.kt
+++ b/libs/annotations/src/main/java/com/instructure/annotations/PdfSubmissionView.kt
@@ -115,7 +115,7 @@ abstract class PdfSubmissionView(context: Context, private val studentAnnotation
.setAnnotationInspectorEnabled(true)
.layoutMode(PageLayoutMode.SINGLE)
.textSelectionEnabled(false)
- .disableCopyPaste()
+ .copyPastEnabled(false)
.build()
private val annotationCreationToolbar = AnnotationCreationToolbar(context)
@@ -174,9 +174,8 @@ abstract class PdfSubmissionView(context: Context, private val studentAnnotation
protected fun unregisterPdfFragmentListeners() {
pdfFragment?.removeOnAnnotationCreationModeChangeListener(this)
pdfFragment?.removeOnAnnotationEditingModeChangeListener(this)
- pdfFragment?.document?.annotationProvider?.removeOnAnnotationUpdatedListener(annotationUpdateListener)
+ pdfFragment?.removeOnAnnotationUpdatedListener(annotationUpdateListener)
pdfFragment?.removeOnAnnotationSelectedListener(annotationSelectedListener)
- pdfFragment?.removeOnAnnotationDeselectedListener(annotationDeselectedListener)
}
/**
@@ -198,12 +197,12 @@ abstract class PdfSubmissionView(context: Context, private val studentAnnotation
if (toolbar is AnnotationCreationToolbar) {
setUpGrabAnnotationTool(toolbar)
+ toolbar.setMenuItemGroupingRule(AnnotationCreationGroupingRule(context))
}
}
})
annotationCreationToolbar.closeButton.setGone()
- annotationCreationToolbar.setMenuItemGroupingRule(AnnotationCreationGroupingRule(context))
annotationEditingToolbar.setMenuItemGroupingRule(object : MenuItemGroupingRule {
override fun groupMenuItems(items: MutableList, i: Int) = configureEditMenuItemGrouping(items)
@@ -415,9 +414,6 @@ abstract class PdfSubmissionView(context: Context, private val studentAnnotation
annotationsJob = tryWeave {
// Snag them annotations with the session id
val annotations = awaitApi { CanvaDocsManager.getAnnotations(apiValues.sessionId, apiValues.canvaDocsDomain, it) }
- // We don't want to trigger the annotation events here, so unregister and re-register after
- pdfFragment?.document?.annotationProvider?.removeOnAnnotationUpdatedListener(annotationUpdateListener)
-
// Grab all the annotations and sort them by type (descending).
// This will result in all of the comments being iterated over first as the COMMENT_REPLY type is last in the AnnotationType enum.
val sortedAnnotationList = annotations.data.sortedByDescending { it.annotationType }
@@ -456,9 +452,8 @@ abstract class PdfSubmissionView(context: Context, private val studentAnnotation
}
noteHinter?.notifyDrawablesChanged()
- pdfFragment?.document?.annotationProvider?.addOnAnnotationUpdatedListener(annotationUpdateListener)
+ pdfFragment?.addOnAnnotationUpdatedListener(annotationUpdateListener)
pdfFragment?.addOnAnnotationSelectedListener(annotationSelectedListener)
- pdfFragment?.addOnAnnotationDeselectedListener(annotationDeselectedListener)
} catch {
// Show error
toast(R.string.annotationErrorOccurred)
@@ -618,10 +613,6 @@ abstract class PdfSubmissionView(context: Context, private val studentAnnotation
&& commentRepliesHashMap[currentAnnotation.annotationId]?.isNotEmpty() == true
}
- private val annotationDeselectedListener = AnnotationManager.OnAnnotationDeselectedListener { _, _ ->
- commentsButton.setGone()
- }
-
//region Annotation Manipulation
fun createNewAnnotation(annotation: Annotation) {
if (annotation.type == AnnotationType.FREETEXT) {
@@ -642,9 +633,7 @@ abstract class PdfSubmissionView(context: Context, private val studentAnnotation
// Edit the annotation with the appropriate id
annotation.name = newAnnotation.annotationId
- pdfFragment?.document?.annotationProvider?.removeOnAnnotationUpdatedListener(annotationUpdateListener)
pdfFragment?.notifyAnnotationHasChanged(annotation)
- pdfFragment?.document?.annotationProvider?.addOnAnnotationUpdatedListener(annotationUpdateListener)
commentsButton.isEnabled = true
if (annotation.type == AnnotationType.STAMP) {
commentsButton.setVisible()
@@ -1017,7 +1006,7 @@ abstract class PdfSubmissionView(context: Context, private val studentAnnotation
// If the user has read/write/manage we want to let them delete (and only delete) non-authored annotations
val annotation = pdfFragment?.selectedAnnotations?.get(0)
- if (docSession.annotationMetadata?.canManage() == true && annotation?.flags?.contains(AnnotationFlags.LOCKED) == true) {
+ if (::docSession.isInitialized && docSession.annotationMetadata?.canManage() == true && annotation?.flags?.contains(AnnotationFlags.LOCKED) == true) {
// We need to only return a list with the delete menu item
delete = ContextualToolbarMenuItem.createSingleItem(context, View.generateViewId(),
ContextCompat.getDrawable(context, R.drawable.ic_trash)!!,
diff --git a/libs/canvas-api-2/build.gradle b/libs/canvas-api-2/build.gradle
index 7f69f3ed91..a94afaacf2 100644
--- a/libs/canvas-api-2/build.gradle
+++ b/libs/canvas-api-2/build.gradle
@@ -19,7 +19,7 @@ apply plugin: 'com.android.library'
apply plugin: 'com.apollographql.apollo'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-parcelize'
-apply plugin: 'kotlin-kapt'
+apply plugin: 'com.google.devtools.ksp'
apply plugin: 'dagger.hilt.android.plugin'
def pineDebugBaseUrl = "https://pine-api-dev.domain-svcs.nonprod.inseng.io"
@@ -202,10 +202,10 @@ dependencies {
implementation Libs.FIREBASE_CONFIG
implementation Libs.HILT
- kapt Libs.HILT_COMPILER
+ ksp Libs.HILT_COMPILER
implementation Libs.ROOM
- kapt Libs.ROOM_COMPILER
+ ksp Libs.ROOM_COMPILER
implementation Libs.PENDO
diff --git a/libs/horizon/build.gradle.kts b/libs/horizon/build.gradle.kts
index b10db9822a..41d8a6056d 100644
--- a/libs/horizon/build.gradle.kts
+++ b/libs/horizon/build.gradle.kts
@@ -3,7 +3,7 @@ plugins {
id("org.jetbrains.kotlin.android")
id("org.jetbrains.kotlin.plugin.compose")
id("kotlin-android")
- id("kotlin-kapt")
+ id("com.google.devtools.ksp")
id("dagger.hilt.android.plugin")
kotlin("plugin.serialization") version "2.1.20"
id("jacoco")
@@ -16,7 +16,6 @@ android {
defaultConfig {
minSdk = Versions.MIN_SDK
- targetSdk = Versions.TARGET_SDK
testInstrumentationRunner = "com.instructure.horizon.espresso.HorizonCustomTestRunner"
consumerProguardFiles("consumer-rules.pro")
@@ -69,11 +68,11 @@ dependencies {
implementation(Libs.NAVIGATION_COMPOSE)
implementation(Libs.HILT)
- kapt(Libs.HILT_COMPILER)
+ ksp(Libs.HILT_COMPILER)
implementation(Libs.HILT_ANDROIDX_WORK)
- kapt(Libs.HILT_ANDROIDX_COMPILER)
+ ksp(Libs.HILT_ANDROIDX_COMPILER)
- implementation(Libs.PSPDFKIT)
+ implementation(Libs.NUTRIENT)
implementation(Libs.ANDROIDX_ANNOTATION)
implementation(Libs.ANDROIDX_APPCOMPAT)
diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/account/filepreview/PdfPreview.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/account/filepreview/PdfPreview.kt
index 8e144427f0..fc3443b886 100644
--- a/libs/horizon/src/main/java/com/instructure/horizon/features/account/filepreview/PdfPreview.kt
+++ b/libs/horizon/src/main/java/com/instructure/horizon/features/account/filepreview/PdfPreview.kt
@@ -25,10 +25,8 @@ import com.pspdfkit.configuration.activity.UserInterfaceViewMode
import com.pspdfkit.configuration.page.PageScrollDirection
import com.pspdfkit.configuration.page.PageScrollMode
import com.pspdfkit.jetpack.compose.interactors.rememberDocumentState
-import com.pspdfkit.jetpack.compose.utilities.ExperimentalPSPDFKitApi
import com.pspdfkit.jetpack.compose.views.DocumentView
-@OptIn(ExperimentalPSPDFKitApi::class)
@Composable
fun PdfPreview(
documentUri: Uri,
diff --git a/libs/interactions/build.gradle b/libs/interactions/build.gradle
index f54245df48..05450a79f4 100644
--- a/libs/interactions/build.gradle
+++ b/libs/interactions/build.gradle
@@ -17,7 +17,7 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-kapt'
+apply plugin: 'com.google.devtools.ksp'
apply plugin: 'kotlin-parcelize'
static String isTesting() {
diff --git a/libs/login-api-2/build.gradle b/libs/login-api-2/build.gradle
index 2a49d712e0..38a16be117 100644
--- a/libs/login-api-2/build.gradle
+++ b/libs/login-api-2/build.gradle
@@ -20,7 +20,7 @@ import java.security.MessageDigest
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-parcelize'
-apply plugin: 'kotlin-kapt'
+apply plugin: 'com.google.devtools.ksp'
apply plugin: 'dagger.hilt.android.plugin'
static String isTesting() {
@@ -134,7 +134,7 @@ dependencies {
implementation Libs.ANDROIDX_FRAGMENT_KTX
implementation Libs.HILT
- kapt Libs.HILT_COMPILER
+ ksp Libs.HILT_COMPILER
}
task copySnickerDoodles(type: Copy) {
diff --git a/libs/pandautils/build.gradle b/libs/pandautils/build.gradle
index 9cce7f814b..345053752e 100644
--- a/libs/pandautils/build.gradle
+++ b/libs/pandautils/build.gradle
@@ -19,6 +19,7 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-parcelize'
apply plugin: 'kotlin-kapt'
+apply plugin: 'com.google.devtools.ksp'
apply plugin: 'dagger.hilt.android.plugin'
apply plugin: 'org.jetbrains.kotlin.plugin.compose'
@@ -48,6 +49,9 @@ android {
)
}
}
+ ksp {
+ arg("room.schemaLocation", "$projectDir/schemas".toString())
+ }
}
buildTypes {
@@ -116,6 +120,7 @@ configurations {
androidTestImplementation.exclude module:'protobuf-lite'
all*.resolutionStrategy {
+ force 'io.grpc:grpc-netty:1.75.0'
force Libs.KOTLIN_STD_LIB
}
}
@@ -132,6 +137,9 @@ dependencies {
implementation Libs.LOTTIE
+ // Explicitly add secure grpc-netty version to override any transitive dependencies
+ implementation 'io.grpc:grpc-netty:1.75.0'
+
/* Kotlin */
implementation Libs.KOTLIN_STD_LIB
implementation Libs.ANDROIDX_BROWSER
@@ -158,7 +166,7 @@ dependencies {
api (Libs.GLIDE_OKHTTP) {
exclude group: "com.android.support"
}
- kapt Libs.GLIDE_COMPILER
+ ksp Libs.GLIDE_COMPILER
api Libs.ANDROID_SVG
@@ -197,9 +205,9 @@ dependencies {
/* DI */
implementation Libs.HILT
- kapt Libs.HILT_COMPILER
+ ksp Libs.HILT_COMPILER
implementation Libs.HILT_ANDROIDX_WORK
- kapt Libs.HILT_ANDROIDX_COMPILER
+ ksp Libs.HILT_ANDROIDX_COMPILER
/* AAC */
implementation Libs.VIEW_MODEL
@@ -210,7 +218,7 @@ dependencies {
/* ROOM */
implementation Libs.ROOM
- kapt Libs.ROOM_COMPILER
+ ksp Libs.ROOM_COMPILER
implementation Libs.ROOM_COROUTINES
/* Compose */
@@ -245,8 +253,6 @@ dependencies {
// More details here: classpath https://github.com/google/ExoPlayer/issues/7905
implementation 'com.google.guava:guava:29.0-android'
- kaptTest Libs.ANDROIDX_DATABINDING_COMPILER
-
androidTestImplementation Libs.KOTLIN_COROUTINES_TEST
androidTestImplementation (project(':espresso')) {
exclude group: 'org.checkerframework', module: 'checker'
diff --git a/libs/pandautils/src/androidTest/java/com/instructure/pandautils/compose/features/settings/SettingsScreenTest.kt b/libs/pandautils/src/androidTest/java/com/instructure/pandautils/compose/features/settings/SettingsScreenTest.kt
index 4aed3d92a7..56ee96a137 100644
--- a/libs/pandautils/src/androidTest/java/com/instructure/pandautils/compose/features/settings/SettingsScreenTest.kt
+++ b/libs/pandautils/src/androidTest/java/com/instructure/pandautils/compose/features/settings/SettingsScreenTest.kt
@@ -72,7 +72,20 @@ class SettingsScreenTest {
}
items.forEach { (title, items) ->
- composeTestRule.onNodeWithText(context.getString(title)).assertExists()
+ retry(catchBlock = {
+ val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+ val y = device.displayHeight / 2
+ val x = device.displayWidth / 2
+ device.swipe(
+ x,
+ y,
+ x,
+ 0,
+ 10
+ )
+ }) {
+ composeTestRule.onNodeWithText(context.getString(title)).assertExists()
+ }
items.forEach { item ->
retry(catchBlock = {
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
diff --git a/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/offline/daos/GradingPeriodDaoTest.kt b/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/offline/daos/GradingPeriodDaoTest.kt
index 3b786f8448..1badd32d6e 100644
--- a/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/offline/daos/GradingPeriodDaoTest.kt
+++ b/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/offline/daos/GradingPeriodDaoTest.kt
@@ -61,15 +61,13 @@ class GradingPeriodDaoTest {
Assert.assertEquals(gradingPeriodEntity, result)
}
- @Test
- fun testFindEntityByIdReturnsNullIfNotFound() = runTest {
+ @Test(expected = IllegalStateException::class)
+ fun testFindEntityByIdThrowsExceptionIfNotFound() = runTest {
val gradingPeriodEntity = GradingPeriodEntity(GradingPeriod(id = 1, "Grading period 1"))
val gradingPeriodEntity2 = GradingPeriodEntity(GradingPeriod(id = 2, "Grading period 2"))
gradingPeriodDao.insert(gradingPeriodEntity)
gradingPeriodDao.insert(gradingPeriodEntity2)
val result = gradingPeriodDao.findById(3)
-
- Assert.assertNull(result)
}
}
\ No newline at end of file
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/compose/composables/TriStateBottomSheet.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/compose/composables/TriStateBottomSheet.kt
index 63086fa181..fbabb10864 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/compose/composables/TriStateBottomSheet.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/compose/composables/TriStateBottomSheet.kt
@@ -135,8 +135,8 @@ fun TriStateBottomSheet(
anchoredDraggableState.updateAnchors(newAnchors)
val currentTarget = anchoredDraggableState.targetValue
- if (!newAnchors.hasAnchorFor(currentTarget)) {
- if (newAnchors.hasAnchorFor(initialAnchor)) {
+ if (!newAnchors.hasPositionFor(currentTarget)) {
+ if (newAnchors.hasPositionFor(initialAnchor)) {
anchoredDraggableState.snapTo(initialAnchor)
} else if (newAnchors.size > 0) {
newAnchors.closestAnchor(anchoredDraggableState.offset.takeIf { !it.isNaN() }
@@ -153,7 +153,7 @@ fun TriStateBottomSheet(
LaunchedEffect(anchoredDraggableState.anchors, initialAnchor) {
val currentAnchors = anchoredDraggableState.anchors
- if (currentAnchors.hasAnchorFor(initialAnchor) && anchoredDraggableState.currentValue != initialAnchor) {
+ if (currentAnchors.hasPositionFor(initialAnchor) && anchoredDraggableState.currentValue != initialAnchor) {
if (anchoredDraggableState.targetValue != initialAnchor || !anchoredDraggableState.isAnimationRunning) {
anchoredDraggableState.snapTo(initialAnchor)
}
@@ -168,8 +168,8 @@ fun TriStateBottomSheet(
if (offset.isNaN() || currentAnchors.size == 0) {
getPixelForAnchor(initialAnchor)
} else {
- val minAnchorValue = currentAnchors.minAnchor()
- val maxAnchorValue = currentAnchors.maxAnchor()
+ val minAnchorValue = currentAnchors.minPosition()
+ val maxAnchorValue = currentAnchors.maxPosition()
offset.coerceIn(minAnchorValue, maxAnchorValue)
}
}
@@ -259,7 +259,7 @@ fun TriStateBottomSheet(
coroutineScope.launch {
when (anchoredDraggableState.targetValue) {
AnchorPoints.BOTTOM -> {
- if (anchoredDraggableState.anchors.hasAnchorFor(AnchorPoints.MIDDLE)) {
+ if (anchoredDraggableState.anchors.hasPositionFor(AnchorPoints.MIDDLE)) {
anchoredDraggableState.animateTo(AnchorPoints.MIDDLE)
} else {
anchoredDraggableState.animateTo(AnchorPoints.BOTTOM)
@@ -271,7 +271,7 @@ fun TriStateBottomSheet(
}
AnchorPoints.TOP -> {
- if (anchoredDraggableState.anchors.hasAnchorFor(AnchorPoints.MIDDLE)) {
+ if (anchoredDraggableState.anchors.hasPositionFor(AnchorPoints.MIDDLE)) {
anchoredDraggableState.animateTo(AnchorPoints.MIDDLE)
} else {
anchoredDraggableState.animateTo(AnchorPoints.BOTTOM)
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModel.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModel.kt
index 028f55bfbb..de52b82926 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModel.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModel.kt
@@ -24,7 +24,6 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.work.WorkInfo
import androidx.work.WorkManager
-import androidx.work.await
import com.instructure.canvasapi2.apis.EnrollmentAPI
import com.instructure.canvasapi2.managers.AccountNotificationManager
import com.instructure.canvasapi2.managers.ConferenceManager
@@ -64,6 +63,7 @@ import com.instructure.pandautils.room.offline.daos.StudioMediaProgressDao
import com.instructure.pandautils.utils.orDefault
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import org.threeten.bp.OffsetDateTime
import java.util.Locale
@@ -292,36 +292,33 @@ class DashboardNotificationsViewModel @Inject constructor(
private suspend fun getUploads(fileUploadEntities: List?) =
fileUploadEntities?.mapNotNull { fileUploadEntity ->
val workerId = UUID.fromString(fileUploadEntity.workerId)
- workManager.getWorkInfoById(workerId).await()?.let { workInfo ->
+ val workInfo = workManager.getWorkInfoByIdFlow(workerId).first()
+ workInfo?.let {
val icon: Int
val background: Int
- when (workInfo.state) {
+ when (it.state) {
WorkInfo.State.FAILED -> {
icon = R.drawable.ic_exclamation_mark
background = R.color.backgroundDanger
}
-
WorkInfo.State.SUCCEEDED -> {
icon = R.drawable.ic_check_white_24dp
background = R.color.backgroundSuccess
}
-
else -> {
icon = R.drawable.ic_upload
background = R.color.backgroundInfo
}
}
-
val uploadViewData = UploadViewData(
fileUploadEntity.title.orEmpty(), fileUploadEntity.subtitle.orEmpty(),
- icon, background, workInfo.state == WorkInfo.State.RUNNING
+ icon, background, it.state == WorkInfo.State.RUNNING
)
-
UploadItemViewModel(
workerId = workerId,
workManager = workManager,
data = uploadViewData,
- open = { uuid -> openUploadNotification(workInfo.state, uuid, fileUploadEntity) },
+ open = { uuid -> openUploadNotification(it.state, uuid, fileUploadEntity) },
remove = { removeUploadNotification(fileUploadEntity, workerId) }
)
}
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/itemviewmodels/UploadItemViewModel.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/itemviewmodels/UploadItemViewModel.kt
index f0d2e65c20..48d45185c7 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/itemviewmodels/UploadItemViewModel.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/itemviewmodels/UploadItemViewModel.kt
@@ -19,8 +19,6 @@ package com.instructure.pandautils.features.dashboard.notifications.itemviewmode
import androidx.databinding.BaseObservable
import androidx.databinding.Bindable
-import androidx.lifecycle.Observer
-import androidx.work.WorkInfo
import androidx.work.WorkManager
import com.instructure.pandautils.BR
import com.instructure.pandautils.R
@@ -28,7 +26,12 @@ import com.instructure.pandautils.features.dashboard.notifications.UploadViewDat
import com.instructure.pandautils.features.file.upload.worker.FileUploadWorker.Companion.PROGRESS_DATA_FULL_SIZE
import com.instructure.pandautils.features.file.upload.worker.FileUploadWorker.Companion.PROGRESS_DATA_UPLOADED_SIZE
import com.instructure.pandautils.mvvm.ItemViewModel
-import java.util.*
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
+import java.util.UUID
class UploadItemViewModel(
val workerId: UUID,
@@ -39,23 +42,25 @@ class UploadItemViewModel(
val remove: () -> Unit,
@get:Bindable var loading: Boolean = false
) : ItemViewModel, BaseObservable() {
-
override val layoutId = R.layout.item_dashboard_upload
-
- private val observer = Observer {
- val uploadedSize = it.progress.getLong(PROGRESS_DATA_UPLOADED_SIZE, 0L)
- val fullSize = it.progress.getLong(PROGRESS_DATA_FULL_SIZE, 1L)
-
- progress = ((uploadedSize.toDouble() / fullSize.toDouble()) * 100.0).toInt()
- notifyPropertyChanged(BR.progress)
- }
+ private var job: Job? = null
init {
- workManager.getWorkInfoByIdLiveData(workerId).observeForever(observer)
+ job = CoroutineScope(Dispatchers.Main).launch {
+ workManager.getWorkInfoByIdFlow(workerId).collectLatest { workInfo ->
+ workInfo?.let {
+ val uploadedSize = it.progress.getLong(PROGRESS_DATA_UPLOADED_SIZE, 0L)
+ val fullSize = it.progress.getLong(PROGRESS_DATA_FULL_SIZE, 1L)
+
+ progress = ((uploadedSize.toDouble() / fullSize.toDouble()) * 100.0).toInt()
+ notifyPropertyChanged(BR.progress)
+ }
+ }
+ }
}
fun clear() {
- workManager.getWorkInfoByIdLiveData(workerId).removeObserver(observer)
+ job?.cancel()
}
fun open() = open.invoke(workerId)
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadDialogFragment.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadDialogFragment.kt
index 9a835b82fd..82072ba28a 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadDialogFragment.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadDialogFragment.kt
@@ -248,7 +248,7 @@ class FileUploadDialogFragment : BaseCanvasDialogFragment() {
}
is FileUploadAction.UploadStartedAction -> {
getParent()?.selectedUriStringsCallback(action.selectedUris)
- getParent()?.workInfoLiveDataCallback(action.id, action.liveData)
+ getParent()?.workInfoLiveDataCallback(action.id,action.liveData)
lifecycleScope.launch {
fileUploadEventHandler.postEvent(
FileUploadEvent.FileSelected(action.selectedUris)
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadDialogParent.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadDialogParent.kt
index 11d4099ef7..4a16d76981 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadDialogParent.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadDialogParent.kt
@@ -28,5 +28,5 @@ interface FileUploadDialogParent {
fun selectedUriStringsCallback(filePaths: List) = Unit
- fun workInfoLiveDataCallback(uuid: UUID? = null, workInfoLiveData: LiveData) = Unit
+ fun workInfoLiveDataCallback(uuid: UUID? = null, workInfoLiveData: LiveData) = Unit
}
\ No newline at end of file
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadDialogViewData.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadDialogViewData.kt
index bc47a0eaf8..185b8efb7e 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadDialogViewData.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadDialogViewData.kt
@@ -42,6 +42,6 @@ sealed class FileUploadAction {
object UploadStarted : FileUploadAction()
data class ShowToast(val toast: String) : FileUploadAction()
data class AttachmentSelectedAction(val event: Int, val attachment: FileSubmitObject?) : FileUploadAction()
- data class UploadStartedAction(val id: UUID, val liveData: LiveData, val selectedUris: List) : FileUploadAction()
+ data class UploadStartedAction(val id: UUID, val liveData: LiveData, val selectedUris: List) : FileUploadAction()
}
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadEventHandler.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadEventHandler.kt
index 225cb4f9fc..9510bbc275 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadEventHandler.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadEventHandler.kt
@@ -29,7 +29,7 @@ sealed class FileUploadEvent {
data class FileSelected(val filePaths: List) : FileUploadEvent()
data class UploadStarted(
val uuid: UUID?,
- val workInfoLiveData: LiveData,
+ val workInfoLiveData: LiveData,
val filePaths: List
) : FileUploadEvent()
}
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt
index 1d6f2c845d..0a7d5accce 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt
@@ -331,7 +331,7 @@ class FileUploadWorker @AssistedInject constructor(
}
}).dataOrThrow
- val updatedList = workDataBuilder.build()
+ val updatedList: Array = workDataBuilder.build()
.getStringArray(PROGRESS_DATA_UPLOADED_FILES)
.orEmpty()
.toMutableList()
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/grades/GradesScreen.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/grades/GradesScreen.kt
index c4ab26e01f..ead5c82dd7 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/grades/GradesScreen.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/grades/GradesScreen.kt
@@ -82,7 +82,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.contentDescription
-import androidx.compose.ui.semantics.invisibleToUser
+import androidx.compose.ui.semantics.hideFromAccessibility
import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTag
@@ -291,7 +291,7 @@ private fun GradesScreenContent(
modifier = Modifier
.height(24.dp)
.semantics {
- invisibleToUser()
+ hideFromAccessibility()
}
)
}
@@ -301,19 +301,19 @@ private fun GradesScreenContent(
}
}
- uiState.items.forEach {
+ uiState.items.forEach { item ->
stickyHeader {
GroupHeader(
- name = it.name,
- expanded = it.expanded,
+ name = item.name,
+ expanded = item.expanded,
onClick = {
- actionHandler(GradesAction.GroupHeaderClick(it.id))
+ actionHandler(GradesAction.GroupHeaderClick(item.id))
}
)
}
- if (it.expanded) {
- items(it.assignments) { assignment ->
+ if (item.expanded) {
+ items(item.assignments) { assignment ->
AssignmentItem(assignment, actionHandler, userColor)
}
}
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/inbox/compose/InboxComposeFragment.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/inbox/compose/InboxComposeFragment.kt
index 74f3227384..c8097976b8 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/inbox/compose/InboxComposeFragment.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/inbox/compose/InboxComposeFragment.kt
@@ -111,9 +111,11 @@ class InboxComposeFragment : BaseCanvasFragment(), FragmentInteractions, FileUpl
viewModel.addUploadingAttachments(filePaths)
}
- override fun workInfoLiveDataCallback(uuid: UUID?, workInfoLiveData: LiveData) {
+ override fun workInfoLiveDataCallback(uuid: UUID?, workInfoLiveData: LiveData) {
workInfoLiveData.observe(viewLifecycleOwner) { workInfo ->
- viewModel.updateAttachments(uuid, workInfo)
+ workInfo?.let {
+ viewModel.updateAttachments(uuid, workInfo)
+ }
}
}
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/offline/sync/OfflineSyncHelper.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/offline/sync/OfflineSyncHelper.kt
index 920c6f0367..f97088c9a1 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/offline/sync/OfflineSyncHelper.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/offline/sync/OfflineSyncHelper.kt
@@ -25,10 +25,10 @@ import androidx.work.OneTimeWorkRequest
import androidx.work.PeriodicWorkRequest
import androidx.work.WorkInfo
import androidx.work.WorkManager
-import androidx.work.await
import com.instructure.canvasapi2.utils.ApiPrefs
import com.instructure.pandautils.features.offline.sync.settings.SyncFrequency
import com.instructure.pandautils.room.offline.facade.SyncSettingsFacade
+import kotlinx.coroutines.flow.first
import java.util.concurrent.TimeUnit
class OfflineSyncHelper(
@@ -103,17 +103,16 @@ class OfflineSyncHelper(
}
private suspend fun isWorkScheduled(): Boolean {
- return workManager.getWorkInfosForUniqueWork(apiPrefs.user?.id.toString()).await()
- .any { it.state != WorkInfo.State.CANCELLED }
+ return workManager.getWorkInfosForUniqueWorkFlow(apiPrefs.user?.id.toString()).first().any { it.state != WorkInfo.State.CANCELLED }
}
private suspend fun isPeriodicWorkRunning(): Boolean {
- return workManager.getWorkInfosForUniqueWork(apiPrefs.user?.id.toString()).await()
+ return workManager.getWorkInfosForUniqueWorkFlow(apiPrefs.user?.id.toString()).first()
.any { it.state == WorkInfo.State.RUNNING }
}
private suspend fun getRunningOneTimeWorkInfo(): WorkInfo? {
- return workManager.getWorkInfosByTag(OfflineSyncWorker.ONE_TIME_TAG).await()
+ return workManager.getWorkInfosByTagFlow(OfflineSyncWorker.ONE_TIME_TAG).first()
.firstOrNull { it.state == WorkInfo.State.RUNNING }
}
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/shareextension/ShareExtensionActivity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/shareextension/ShareExtensionActivity.kt
index 058d8d1caf..26c5e20609 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/shareextension/ShareExtensionActivity.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/shareextension/ShareExtensionActivity.kt
@@ -170,7 +170,7 @@ abstract class ShareExtensionActivity : BaseCanvasActivity(), FileUploadDialogPa
}
}
- override fun workInfoLiveDataCallback(uuid: UUID?, workInfoLiveData: LiveData) {
+ override fun workInfoLiveDataCallback(uuid: UUID?, workInfoLiveData: LiveData) {
uuid?.let {
shareExtensionViewModel.workerCallback(it)
}
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/shareextension/progress/ShareExtensionProgressDialogViewModel.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/shareextension/progress/ShareExtensionProgressDialogViewModel.kt
index c88be6a8f3..7bb6fc7ec3 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/shareextension/progress/ShareExtensionProgressDialogViewModel.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/shareextension/progress/ShareExtensionProgressDialogViewModel.kt
@@ -5,7 +5,6 @@ import android.net.Uri
import androidx.annotation.DrawableRes
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.work.*
@@ -24,7 +23,9 @@ import com.instructure.pandautils.utils.fromJson
import com.instructure.pandautils.utils.humanReadableByteCount
import com.instructure.pandautils.utils.orDefault
import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
+import kotlinx.coroutines.flow.collectLatest
import java.io.File
import java.util.*
import javax.inject.Inject
@@ -56,31 +57,35 @@ class ShareExtensionProgressDialogViewModel @Inject constructor(
private var workerId: UUID? = null
private var fileUploadType = FileUploadType.USER
-
- private val observer = Observer {
- when (it.state) {
- WorkInfo.State.SUCCEEDED -> {
- _events.postValue(Event((ShareExtensionProgressAction.ShowSuccessDialog(fileUploadType))))
- }
- WorkInfo.State.RUNNING -> {
- viewModelScope.launch { updateViewData(it.progress, false) }
- }
- WorkInfo.State.FAILED -> {
- viewModelScope.launch { updateViewData(it.outputData, true) }
- }
- else -> {}
- }
- }
+ private var job: Job? = null
fun setUUID(uuid: UUID) {
this.workerId = uuid
_state.postValue(ViewState.Loading)
- workManager.getWorkInfoByIdLiveData(uuid).observeForever(observer)
+ job?.cancel()
+ job = viewModelScope.launch {
+ workManager.getWorkInfoByIdFlow(uuid).collectLatest { it ->
+ it?.let {
+ when (it.state) {
+ WorkInfo.State.SUCCEEDED -> {
+ _events.postValue(Event((ShareExtensionProgressAction.ShowSuccessDialog(fileUploadType))))
+ }
+ WorkInfo.State.RUNNING -> {
+ updateViewData(it.progress, false)
+ }
+ WorkInfo.State.FAILED -> {
+ updateViewData(it.outputData, true)
+ }
+ else -> {}
+ }
+ }
+ }
+ }
}
override fun onCleared() {
super.onCleared()
- workerId?.let { workManager.getWorkInfoByIdLiveData(it).removeObserver(observer) }
+ job?.cancel()
}
private suspend fun updateViewData(progress: Data, failed: Boolean) {
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/speedgrader/grade/comments/SpeedGraderCommentsUiState.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/speedgrader/grade/comments/SpeedGraderCommentsUiState.kt
index 416a4b6e82..a0c0895198 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/speedgrader/grade/comments/SpeedGraderCommentsUiState.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/speedgrader/grade/comments/SpeedGraderCommentsUiState.kt
@@ -77,6 +77,6 @@ sealed class SpeedGraderCommentsAction {
data object ChooseFilesClicked : SpeedGraderCommentsAction()
data object FileUploadDialogClosed : SpeedGraderCommentsAction()
data class FilesSelected(val filePaths: List) : SpeedGraderCommentsAction()
- data class FileUploadStarted(val workInfoLiveData: LiveData) : SpeedGraderCommentsAction()
+ data class FileUploadStarted(val workInfoLiveData: LiveData) : SpeedGraderCommentsAction()
data class MediaRecorded(val file: File) : SpeedGraderCommentsAction()
}
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/speedgrader/grade/comments/SpeedGraderCommentsViewModel.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/speedgrader/grade/comments/SpeedGraderCommentsViewModel.kt
index 891080153a..e45765c333 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/speedgrader/grade/comments/SpeedGraderCommentsViewModel.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/speedgrader/grade/comments/SpeedGraderCommentsViewModel.kt
@@ -356,7 +356,7 @@ class SpeedGraderCommentsViewModel @Inject constructor(
}
}
- private fun onFileUploadStarted(workInfoLiveData: LiveData, filePaths: List) {
+ private fun onFileUploadStarted(workInfoLiveData: LiveData, filePaths: List) {
_uiState.update { state ->
state.copy(
fileSelectorDialogData = null,
@@ -480,7 +480,7 @@ class SpeedGraderCommentsViewModel @Inject constructor(
attemptId = selectedAttemptId,
mediaCommentId = id
).collect { result ->
- when (result.state) {
+ when (result?.state) {
WorkInfo.State.SUCCEEDED -> {
fetchedComments.add(
SpeedGraderComment(
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/speedgrader/grade/comments/commentlibrary/SpeedGraderCommentLibraryScreen.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/speedgrader/grade/comments/commentlibrary/SpeedGraderCommentLibraryScreen.kt
index fc067d1739..0827475a76 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/speedgrader/grade/comments/commentlibrary/SpeedGraderCommentLibraryScreen.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/speedgrader/grade/comments/commentlibrary/SpeedGraderCommentLibraryScreen.kt
@@ -164,6 +164,7 @@ private fun SpeedGraderCommentLibraryContent(
uiState.onCommentValueChanged(item)
}
.padding(vertical = 14.dp)
+ .testTag("commentLibraryItem")
)
if (index != uiState.items.lastIndex) {
CanvasDivider()
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/entities/DiscussionTopicEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/entities/DiscussionTopicEntity.kt
index 32430df43e..f083aaa169 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/entities/DiscussionTopicEntity.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/entities/DiscussionTopicEntity.kt
@@ -10,7 +10,7 @@ import com.instructure.canvasapi2.models.DiscussionTopic
data class DiscussionTopicEntity(
@PrimaryKey
val id: Long,
- val unreadEntries: MutableList,
+ val unreadEntries: List,
val participantIds: List,
val viewIds: List,
) {
@@ -23,7 +23,7 @@ data class DiscussionTopicEntity(
fun toApiModel(participants: List, views: List): DiscussionTopic {
return DiscussionTopic(
- unreadEntries = unreadEntries,
+ unreadEntries = unreadEntries.toMutableList(),
participants = participants,
unreadEntriesMap = hashMapOf(),
entryRatings = hashMapOf(),
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/services/NotoriousUploadWorker.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/services/NotoriousUploadWorker.kt
index 3d09f4304c..5b5d748eba 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/services/NotoriousUploadWorker.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/services/NotoriousUploadWorker.kt
@@ -284,7 +284,7 @@ class NotoriousUploadWorker @AssistedInject constructor(
pageId: String?,
attemptId: Long?,
mediaCommentId: Long?
- ): Flow {
+ ): Flow {
val data = workDataOf(
Const.MEDIA_FILE_PATH to mediaFilePath,
Const.ASSIGNMENT to assignment?.toJson(),
diff --git a/libs/pandautils/src/main/res/layout/view_canvas_web_view_wrapper.xml b/libs/pandautils/src/main/res/layout/view_canvas_web_view_wrapper.xml
index 7b658476ef..301bb65318 100644
--- a/libs/pandautils/src/main/res/layout/view_canvas_web_view_wrapper.xml
+++ b/libs/pandautils/src/main/res/layout/view_canvas_web_view_wrapper.xml
@@ -35,7 +35,7 @@
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:background="@drawable/bg_button_full_rounded"
- android:foreground="?selectableItemBackground"
+ android:foreground="?android:attr/selectableItemBackground"
android:gravity="center"
android:minHeight="48dp"
android:orientation="horizontal"
diff --git a/libs/pandautils/src/test/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModelTest.kt b/libs/pandautils/src/test/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModelTest.kt
index 933fe7d9ea..e1e5f32f13 100644
--- a/libs/pandautils/src/test/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModelTest.kt
+++ b/libs/pandautils/src/test/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModelTest.kt
@@ -23,11 +23,9 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.observe
import androidx.work.Data
import androidx.work.WorkInfo
import androidx.work.WorkManager
-import com.google.common.util.concurrent.Futures
import com.instructure.canvasapi2.apis.EnrollmentAPI
import com.instructure.canvasapi2.managers.AccountNotificationManager
import com.instructure.canvasapi2.managers.ConferenceManager
@@ -73,6 +71,7 @@ import junit.framework.Assert.assertNotNull
import junit.framework.Assert.assertNull
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.setMain
import org.junit.After
@@ -504,7 +503,7 @@ class DashboardNotificationsViewModelTest {
DashboardFileUploadEntity(workerId3.toString(), 1, title3, subTitle3, null, null, null, null)
)
- every { workManager.getWorkInfoById(workerId) } returns Futures.immediateFuture(
+ every { workManager.getWorkInfoByIdFlow(workerId) } returns flowOf(
WorkInfo(
workerId,
WorkInfo.State.RUNNING,
@@ -521,7 +520,7 @@ class DashboardNotificationsViewModelTest {
)
)
- every { workManager.getWorkInfoById(workerId2) } returns Futures.immediateFuture(
+ every { workManager.getWorkInfoByIdFlow(workerId2) } returns flowOf(
WorkInfo(
workerId2,
WorkInfo.State.SUCCEEDED,
@@ -538,7 +537,7 @@ class DashboardNotificationsViewModelTest {
)
)
- every { workManager.getWorkInfoById(workerId3) } returns Futures.immediateFuture(
+ every { workManager.getWorkInfoByIdFlow(workerId3) } returns flowOf(
WorkInfo(
workerId3,
WorkInfo.State.FAILED,
@@ -585,7 +584,7 @@ class DashboardNotificationsViewModelTest {
DashboardFileUploadEntity(workerId.toString(), 1, title, subTitle, null, null, null, null)
)
- every { workManager.getWorkInfoById(workerId) } returns Futures.immediateFuture(
+ every { workManager.getWorkInfoByIdFlow(workerId) } returns flowOf(
WorkInfo(
workerId,
WorkInfo.State.RUNNING,
@@ -602,7 +601,7 @@ class DashboardNotificationsViewModelTest {
assertEquals(1, viewModel.data.value?.uploadItems?.size)
assertEquals(expectedRunning, viewModel.data.value?.uploadItems?.first()?.data)
- every { workManager.getWorkInfoById(workerId) } returns Futures.immediateFuture(
+ every { workManager.getWorkInfoByIdFlow(workerId) } returns flowOf(
WorkInfo(
workerId,
WorkInfo.State.SUCCEEDED,
@@ -630,7 +629,7 @@ class DashboardNotificationsViewModelTest {
DashboardFileUploadEntity(workerId.toString(), 1, "", "", null, null, null, null)
)
- every { workManager.getWorkInfoById(workerId) } returns Futures.immediateFuture(
+ every { workManager.getWorkInfoByIdFlow(workerId) } returns flowOf(
WorkInfo(
workerId,
WorkInfo.State.RUNNING,
@@ -665,7 +664,7 @@ class DashboardNotificationsViewModelTest {
DashboardFileUploadEntity(workerId.toString(), 1, "", "", 1, 2, 3, null)
)
- every { workManager.getWorkInfoById(workerId) } returns Futures.immediateFuture(
+ every { workManager.getWorkInfoByIdFlow(workerId) } returns flowOf(
WorkInfo(
workerId,
WorkInfo.State.SUCCEEDED,
@@ -706,7 +705,7 @@ class DashboardNotificationsViewModelTest {
DashboardFileUploadEntity(workerId.toString(), 1, "", "", null, null, null, 0)
)
- every { workManager.getWorkInfoById(workerId) } returns Futures.immediateFuture(
+ every { workManager.getWorkInfoByIdFlow(workerId) } returns flowOf(
WorkInfo(
workerId,
WorkInfo.State.SUCCEEDED,
diff --git a/libs/pandautils/src/test/java/com/instructure/pandautils/features/offline/sync/OfflineSyncHelperTest.kt b/libs/pandautils/src/test/java/com/instructure/pandautils/features/offline/sync/OfflineSyncHelperTest.kt
index f4bd92e64e..3ea7820f13 100644
--- a/libs/pandautils/src/test/java/com/instructure/pandautils/features/offline/sync/OfflineSyncHelperTest.kt
+++ b/libs/pandautils/src/test/java/com/instructure/pandautils/features/offline/sync/OfflineSyncHelperTest.kt
@@ -29,8 +29,6 @@ import androidx.work.PeriodicWorkRequest
import androidx.work.WorkInfo
import androidx.work.WorkManager
import androidx.work.WorkRequest
-import androidx.work.impl.OperationImpl
-import com.google.common.util.concurrent.Futures
import com.instructure.canvasapi2.models.User
import com.instructure.canvasapi2.utils.ApiPrefs
import com.instructure.pandautils.features.offline.sync.settings.SyncFrequency
@@ -45,6 +43,7 @@ import io.mockk.verify
import junit.framework.TestCase.assertEquals
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
@@ -77,8 +76,10 @@ class OfflineSyncHelperTest {
every { apiPrefs.user } returns User(1L)
- every { workManager.enqueue(any()) } returns OperationImpl()
- every { workManager.enqueueUniquePeriodicWork(any(), any(), any()) } returns OperationImpl()
+ every { workManager.enqueue(any()) } returns mockk()
+ every { workManager.enqueueUniquePeriodicWork(any(), any(), any()) } returns mockk()
+ every { workManager.getWorkInfosForUniqueWorkFlow(any()) } returns flowOf(emptyList())
+ every { workManager.getWorkInfosByTagFlow(any()) } returns flowOf(emptyList())
coEvery { syncSettingsFacade.getSyncSettings() } returns SyncSettingsEntity(
1L,
@@ -100,8 +101,8 @@ class OfflineSyncHelperTest {
SyncFrequency.DAILY,
true
)
- every { workManager.getWorkInfosForUniqueWork(any()) } returns Futures.immediateFuture(mockk(relaxed = true))
- every { workManager.getWorkInfosByTag(OfflineSyncWorker.ONE_TIME_TAG) } returns Futures.immediateFuture(
+ every { workManager.getWorkInfosForUniqueWorkFlow(any()) } returns flowOf(listOf(mockk(relaxed = true)))
+ every { workManager.getWorkInfosByTagFlow(OfflineSyncWorker.ONE_TIME_TAG) } returns flowOf(
emptyList()
)
@@ -116,7 +117,7 @@ class OfflineSyncHelperTest {
val courseIds = listOf(1L, 2L, 3L)
coEvery { syncSettingsFacade.getSyncSettings() } returns SyncSettingsEntity(1L, true, SyncFrequency.DAILY, true)
- every { workManager.getWorkInfosForUniqueWork(any()) } returns Futures.immediateFuture(emptyList())
+ every { workManager.getWorkInfosForUniqueWorkFlow(any()) } returns flowOf(emptyList())
offlineSyncHelper.syncCourses(courseIds)
@@ -134,8 +135,8 @@ class OfflineSyncHelperTest {
SyncFrequency.DAILY,
true
)
- every { workManager.getWorkInfosForUniqueWork(any()) } returns Futures.immediateFuture(emptyList())
- every { workManager.getWorkInfosByTag(OfflineSyncWorker.ONE_TIME_TAG) } returns Futures.immediateFuture(
+ every { workManager.getWorkInfosForUniqueWorkFlow(any()) } returns flowOf(emptyList())
+ every { workManager.getWorkInfosByTagFlow(OfflineSyncWorker.ONE_TIME_TAG) } returns flowOf(
emptyList()
)
@@ -147,7 +148,7 @@ class OfflineSyncHelperTest {
@Test
fun `Cancel should cancel work with correct id`() {
- every { workManager.cancelUniqueWork(any()) } returns OperationImpl()
+ every { workManager.cancelUniqueWork(any()) } returns mockk()
offlineSyncHelper.cancelWork()
@@ -164,8 +165,8 @@ class OfflineSyncHelperTest {
SyncFrequency.DAILY,
true
)
- every { workManager.getWorkInfosForUniqueWork(any()) } returns Futures.immediateFuture(emptyList())
- every { workManager.getWorkInfosByTag(OfflineSyncWorker.ONE_TIME_TAG) } returns Futures.immediateFuture(
+ every { workManager.getWorkInfosForUniqueWorkFlow(any()) } returns flowOf(emptyList())
+ every { workManager.getWorkInfosByTagFlow(OfflineSyncWorker.ONE_TIME_TAG) } returns flowOf(
emptyList()
)
@@ -260,7 +261,7 @@ class OfflineSyncHelperTest {
coVerify { workManager.enqueueUniquePeriodicWork(any(), any(), capture(originalCaptor)) }
val originalRequest = originalCaptor.captured
- every { workManager.getWorkInfosForUniqueWork(any()) } returns Futures.immediateFuture(
+ every { workManager.getWorkInfosForUniqueWorkFlow(any()) } returns flowOf(
listOf(
WorkInfo(originalRequest.id, WorkInfo.State.ENQUEUED, emptySet(), Data.EMPTY, Data.EMPTY, 1, 1)
)
@@ -278,8 +279,8 @@ class OfflineSyncHelperTest {
@Test
fun `Cancel running one time workers`() = runTest {
val uuid = UUID.randomUUID()
- every { workManager.getWorkInfosForUniqueWork(any()) } returns Futures.immediateFuture(emptyList())
- every { workManager.getWorkInfosByTag(OfflineSyncWorker.ONE_TIME_TAG) } returns Futures.immediateFuture(
+ every { workManager.getWorkInfosForUniqueWorkFlow(any()) } returns flowOf(emptyList())
+ every { workManager.getWorkInfosByTagFlow(OfflineSyncWorker.ONE_TIME_TAG) } returns flowOf(
listOf(
WorkInfo(uuid, WorkInfo.State.RUNNING, emptySet(), Data.EMPTY, Data.EMPTY, 1, 1)
)
@@ -295,12 +296,12 @@ class OfflineSyncHelperTest {
@Test
fun `Cancel running periodic workers`() = runTest {
val uuid = UUID.randomUUID()
- every { workManager.getWorkInfosForUniqueWork(any()) } returns Futures.immediateFuture(
+ every { workManager.getWorkInfosForUniqueWorkFlow(any()) } returns flowOf(
listOf(
WorkInfo(uuid, WorkInfo.State.RUNNING, emptySet(), Data.EMPTY, Data.EMPTY, 1, 1)
)
)
- every { workManager.getWorkInfosByTag(OfflineSyncWorker.ONE_TIME_TAG) } returns Futures.immediateFuture(
+ every { workManager.getWorkInfosByTagFlow(OfflineSyncWorker.ONE_TIME_TAG) } returns flowOf(
emptyList()
)
@@ -320,7 +321,7 @@ class OfflineSyncHelperTest {
@Test
fun `scheduleWorkAfterLogin should schedule work when auto sync is enabled and no work is already scheduled`() =
runTest {
- every { workManager.getWorkInfosForUniqueWork(any()) } returns Futures.immediateFuture(emptyList())
+ every { workManager.getWorkInfosForUniqueWorkFlow(any()) } returns flowOf(emptyList())
coEvery { syncSettingsFacade.getSyncSettings() } returns SyncSettingsEntity(
autoSyncEnabled = true,
syncFrequency = SyncFrequency.DAILY,
@@ -348,12 +349,12 @@ class OfflineSyncHelperTest {
@Test
fun `Restart periodic worker`() = runTest {
val uuid = UUID.randomUUID()
- every { workManager.getWorkInfosForUniqueWork(any()) } returns Futures.immediateFuture(
+ every { workManager.getWorkInfosForUniqueWorkFlow(any()) } returns flowOf(
listOf(
WorkInfo(uuid, WorkInfo.State.RUNNING, emptySet(), Data.EMPTY, Data.EMPTY, 1, 1)
)
)
- every { workManager.getWorkInfosByTag(OfflineSyncWorker.ONE_TIME_TAG) } returns Futures.immediateFuture(
+ every { workManager.getWorkInfosByTagFlow(OfflineSyncWorker.ONE_TIME_TAG) } returns flowOf(
emptyList()
)
@@ -372,10 +373,10 @@ class OfflineSyncHelperTest {
@Test
fun `Restart one time worker`() = runTest {
val uuid = UUID.randomUUID()
- every { workManager.getWorkInfosForUniqueWork(any()) } returns Futures.immediateFuture(
+ every { workManager.getWorkInfosForUniqueWorkFlow(any()) } returns flowOf(
emptyList()
)
- every { workManager.getWorkInfosByTag(OfflineSyncWorker.ONE_TIME_TAG) } returns Futures.immediateFuture(
+ every { workManager.getWorkInfosByTagFlow(OfflineSyncWorker.ONE_TIME_TAG) } returns flowOf(
listOf(
WorkInfo(uuid, WorkInfo.State.RUNNING, emptySet(), Data.EMPTY, Data.EMPTY, 1, 1)
)
@@ -390,7 +391,7 @@ class OfflineSyncHelperTest {
@Test
fun `scheduleWorkAfterLogin should not schedule work when work is already scheduled`() = runTest {
- every { workManager.getWorkInfosForUniqueWork(any()) } returns Futures.immediateFuture(mockk(relaxed = true))
+ every { workManager.getWorkInfosForUniqueWorkFlow(any()) } returns flowOf(mockk(relaxed = true))
offlineSyncHelper.scheduleWorkAfterLogin()
diff --git a/libs/pandautils/src/test/java/com/instructure/pandautils/features/shareextension/progress/ShareExtensionProgressViewModelTest.kt b/libs/pandautils/src/test/java/com/instructure/pandautils/features/shareextension/progress/ShareExtensionProgressViewModelTest.kt
index bf6149478d..f334304ab0 100644
--- a/libs/pandautils/src/test/java/com/instructure/pandautils/features/shareextension/progress/ShareExtensionProgressViewModelTest.kt
+++ b/libs/pandautils/src/test/java/com/instructure/pandautils/features/shareextension/progress/ShareExtensionProgressViewModelTest.kt
@@ -5,7 +5,6 @@ import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
-import androidx.lifecycle.MutableLiveData
import androidx.work.Data
import androidx.work.WorkInfo
import androidx.work.WorkManager
@@ -24,6 +23,7 @@ import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain
@@ -51,7 +51,7 @@ class ShareExtensionProgressViewModelTest {
private val dashboardFileUploadDao: DashboardFileUploadDao = mockk(relaxed = true)
private val fileUploadUtilsHelper: FileUploadUtilsHelper = mockk(relaxed = true)
- private lateinit var mockLiveData: MutableLiveData
+ private lateinit var mockFlow: MutableStateFlow
private lateinit var viewModel: ShareExtensionProgressDialogViewModel
private lateinit var uuid: UUID
@@ -64,8 +64,8 @@ class ShareExtensionProgressViewModelTest {
uuid = UUID.randomUUID()
- mockLiveData = MutableLiveData()
- every { workManager.getWorkInfoByIdLiveData(uuid) } returns mockLiveData
+ mockFlow = MutableStateFlow(null)
+ every { workManager.getWorkInfoByIdFlow(uuid) } returns mockFlow
viewModel = createViewModel()
}
@@ -78,7 +78,7 @@ class ShareExtensionProgressViewModelTest {
@Test
fun `Show success dialog after uploading`() {
viewModel.setUUID(uuid)
- mockLiveData.postValue(WorkInfo(uuid, WorkInfo.State.SUCCEEDED, emptySet(), Data.EMPTY, Data.EMPTY, 1, 1))
+ mockFlow.value = WorkInfo(uuid, WorkInfo.State.SUCCEEDED, emptySet(), Data.EMPTY, Data.EMPTY, 1, 1)
viewModel.events.observe(lifecycleOwner) {}
assertEquals(ShareExtensionProgressAction.ShowSuccessDialog(FileUploadType.USER), viewModel.events.value?.getContentIfNotHandled())
@@ -93,7 +93,7 @@ class ShareExtensionProgressViewModelTest {
.putStringArray(FileUploadWorker.PROGRESS_DATA_FILES_TO_UPLOAD, emptyArray())
.build()
- mockLiveData.postValue(WorkInfo(uuid, WorkInfo.State.FAILED, emptySet(), outputData, Data.EMPTY, 1, 1))
+ mockFlow.value = WorkInfo(uuid, WorkInfo.State.FAILED, emptySet(), outputData, Data.EMPTY, 1, 1)
assertEquals("Error", viewModel.data.value?.subtitle)
}
@@ -127,16 +127,14 @@ class ShareExtensionProgressViewModelTest {
.putLong(FileUploadWorker.PROGRESS_DATA_FULL_SIZE, 1L)
.putStringArray(FileUploadWorker.PROGRESS_DATA_FILES_TO_UPLOAD, emptyArray())
.build()
- mockLiveData.postValue(
- WorkInfo(
- uuid,
- WorkInfo.State.RUNNING,
- emptySet(),
- Data.EMPTY,
- progressData,
- 1,
- 1
- )
+ mockFlow.value = WorkInfo(
+ uuid,
+ WorkInfo.State.RUNNING,
+ emptySet(),
+ Data.EMPTY,
+ progressData,
+ 1,
+ 1
)
assertEquals(ViewState.Success, viewModel.state.value)
@@ -147,7 +145,7 @@ class ShareExtensionProgressViewModelTest {
every { resources.getString(R.string.submissionProgressSubtitle, "Assignment") } returns "Uploading submission to \"Assignment\""
- val filesToUpload = listOf(
+ val filesToUpload: Array = listOf(
FileSubmitObject(name = "Test 1", size = 1L, contentType = "text/file", fullPath = ""),
FileSubmitObject(name = "Test 2", size = 1L, contentType = "text/file", fullPath = "")
).map { it.toJson() }.toTypedArray()
@@ -159,16 +157,14 @@ class ShareExtensionProgressViewModelTest {
.putString(FileUploadWorker.PROGRESS_DATA_ASSIGNMENT_NAME, "Assignment")
.build()
- mockLiveData.postValue(
- WorkInfo(
- uuid,
- WorkInfo.State.RUNNING,
- emptySet(),
- Data.EMPTY,
- progressData,
- 1,
- 1
- )
+ mockFlow.value = WorkInfo(
+ uuid,
+ WorkInfo.State.RUNNING,
+ emptySet(),
+ Data.EMPTY,
+ progressData,
+ 1,
+ 1
)
val expectedItemData = listOf(
@@ -205,7 +201,7 @@ class ShareExtensionProgressViewModelTest {
fun `Update view data when live data changes`() {
every { resources.getString(R.string.submissionProgressSubtitle, "Assignment") } returns "Uploading submission to \"Assignment\""
- val filesToUpload = listOf(
+ val filesToUpload: Array = listOf(
FileSubmitObject(name = "Test 1", size = 1L, contentType = "text/file", fullPath = ""),
FileSubmitObject(name = "Test 2", size = 1L, contentType = "text/file", fullPath = "")
).map { it.toJson() }.toTypedArray()
@@ -216,16 +212,14 @@ class ShareExtensionProgressViewModelTest {
.putLong(FileUploadWorker.PROGRESS_DATA_UPLOADED_SIZE, 0L)
.putString(FileUploadWorker.PROGRESS_DATA_ASSIGNMENT_NAME, "Assignment")
- mockLiveData.postValue(
- WorkInfo(
- uuid,
- WorkInfo.State.RUNNING,
- emptySet(),
- Data.EMPTY,
- progressData.build(),
- 1,
- 1
- )
+ mockFlow.value = WorkInfo(
+ uuid,
+ WorkInfo.State.RUNNING,
+ emptySet(),
+ Data.EMPTY,
+ progressData.build(),
+ 1,
+ 1
)
val expectedItemData = listOf(
@@ -251,16 +245,14 @@ class ShareExtensionProgressViewModelTest {
.putStringArray(FileUploadWorker.PROGRESS_DATA_UPLOADED_FILES, arrayOf(filesToUpload[0]))
.putLong(FileUploadWorker.PROGRESS_DATA_UPLOADED_SIZE, 1L)
- mockLiveData.postValue(
- WorkInfo(
- uuid,
- WorkInfo.State.RUNNING,
- emptySet(),
- Data.EMPTY,
- progressData.build(),
- 1,
- 1
- )
+ mockFlow.value = WorkInfo(
+ uuid,
+ WorkInfo.State.RUNNING,
+ emptySet(),
+ Data.EMPTY,
+ progressData.build(),
+ 1,
+ 1
)
val viewData = viewModel.data.value
@@ -277,12 +269,12 @@ class ShareExtensionProgressViewModelTest {
@Test
fun `Failed upload maps correctly`() {
- val filesToUpload = listOf(
+ val filesToUpload: Array = listOf(
FileSubmitObject(name = "Test 1", size = 1L, contentType = "text/file", fullPath = ""),
FileSubmitObject(name = "Test 2", size = 1L, contentType = "text/file", fullPath = "")
).map { it.toJson() }.toTypedArray()
- val uploadedFiles = listOf(
+ val uploadedFiles: Array = listOf(
FileSubmitObject(name = "Test 1", size = 1L, contentType = "text/file", fullPath = "")
).map { it.toJson() }.toTypedArray()
@@ -293,16 +285,14 @@ class ShareExtensionProgressViewModelTest {
.putString(FileUploadWorker.PROGRESS_DATA_ASSIGNMENT_NAME, "Assignment")
.build()
- mockLiveData.postValue(
- WorkInfo(
- uuid,
- WorkInfo.State.FAILED,
- emptySet(),
- progressData,
- Data.EMPTY,
- 1,
- 1
- )
+ mockFlow.value = WorkInfo(
+ uuid,
+ WorkInfo.State.FAILED,
+ emptySet(),
+ progressData,
+ Data.EMPTY,
+ 1,
+ 1
)
val expectedItemData = listOf(
@@ -336,7 +326,7 @@ class ShareExtensionProgressViewModelTest {
@Test
fun `Failed upload retry`() {
- val filesToUpload = listOf(
+ val filesToUpload: Array = listOf(
FileSubmitObject(name = "Test 1", size = 1L, contentType = "text/file", fullPath = "")
).map { it.toJson() }.toTypedArray()
@@ -345,69 +335,65 @@ class ShareExtensionProgressViewModelTest {
.putStringArray(FileUploadWorker.PROGRESS_DATA_FILES_TO_UPLOAD, filesToUpload)
.build()
- mockLiveData.postValue(
- WorkInfo(
- uuid,
- WorkInfo.State.FAILED,
- emptySet(),
- failedOutputData,
- Data.EMPTY,
- 1,
- 1
- )
+ mockFlow.value = WorkInfo(
+ uuid,
+ WorkInfo.State.FAILED,
+ emptySet(),
+ failedOutputData,
+ Data.EMPTY,
+ 1,
+ 1
)
viewModel.setUUID(uuid)
+ viewModel.data.observe(lifecycleOwner) {}
+ viewModel.events.observe(lifecycleOwner) {}
val viewData = viewModel.data.value
assertEquals("File Upload", viewData?.dialogTitle)
assertEquals("Error", viewData?.subtitle)
assertEquals(true, viewData?.failed)
- viewModel.onRetryClick()
-
coEvery { fileUploadInputDao.findByWorkerId(uuid.toString()) } returns FileUploadInputEntity(
workerId = uuid.toString(),
action = "",
filePaths = emptyList()
)
- every { workManager.getWorkInfoByIdLiveData(any()) } returns mockLiveData
+ every { workManager.getWorkInfoByIdFlow(any()) } returns mockFlow
every { resources.getString(R.string.fileUploadProgressSubtitle) } returns "Uploading files"
+ viewModel.onRetryClick()
+
val successProgressData = Data.Builder()
.putLong(FileUploadWorker.PROGRESS_DATA_FULL_SIZE, 1L)
.putStringArray(FileUploadWorker.PROGRESS_DATA_FILES_TO_UPLOAD, filesToUpload)
.putStringArray(FileUploadWorker.PROGRESS_DATA_UPLOADED_FILES, filesToUpload)
.build()
- mockLiveData.postValue(
- WorkInfo(
- uuid,
- WorkInfo.State.RUNNING,
- emptySet(),
- Data.EMPTY,
- successProgressData,
- 1,
- 1
- )
+ mockFlow.value = WorkInfo(
+ uuid,
+ WorkInfo.State.RUNNING,
+ emptySet(),
+ Data.EMPTY,
+ successProgressData,
+ 1,
+ 1
)
val successViewData = viewModel.data.value
assertEquals("Uploading files", successViewData?.subtitle)
assertEquals(false, successViewData?.failed)
- mockLiveData.postValue(
- WorkInfo(
- uuid,
- WorkInfo.State.SUCCEEDED,
- emptySet(),
- Data.EMPTY,
- successProgressData,
- 1,
- 1
- )
+ mockFlow.value = WorkInfo(
+ uuid,
+ WorkInfo.State.SUCCEEDED,
+ emptySet(),
+ Data.EMPTY,
+ successProgressData,
+ 1,
+ 1
)
assertEquals(ShareExtensionProgressAction.ShowSuccessDialog(FileUploadType.USER), viewModel.events.value?.getContentIfNotHandled())