Switch back to cloud env (#2241)
diff --git a/.github/workflows/build_snapshot_tests.yml b/.github/workflows/build_snapshot_tests.yml
deleted file mode 100644
index e798e7f..0000000
--- a/.github/workflows/build_snapshot_tests.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-name: Build snapshot tests
-
-on:
- pull_request:
- push:
- branches:
- - master
-
-jobs:
- build-snapshot-tests:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout the code
- uses: actions/checkout@v3
- - name: Setup JDK
- uses: actions/setup-java@v2
- with:
- distribution: 'zulu'
- java-version: 11
- cache: 'gradle'
- - name: Build app
- run: ./gradlew snapshot-tests:assembleDebug snapshot-tests:assembleDebugAndroidTest --no-daemon
- - name: Upload artifact
- uses: actions/upload-artifact@v3
- with:
- name: apks
- path: |
- snapshot-tests/build/outputs/apk/androidTest/debug/snapshot-tests-debug-androidTest.apk
- snapshot-tests/build/outputs/apk/debug/snapshot-tests-debug.apk
\ No newline at end of file
diff --git a/.github/workflows/snapshot_tests.yml b/.github/workflows/snapshot_tests.yml
deleted file mode 100644
index 3f2f1f2..0000000
--- a/.github/workflows/snapshot_tests.yml
+++ /dev/null
@@ -1,66 +0,0 @@
-name: Snapshot tests
-
-on:
- workflow_run:
- workflows: ['Build snapshot tests']
- types:
- - completed
-
-jobs:
- snapshot-tests:
- runs-on: ubuntu-latest
- steps:
- - uses: haya14busa/action-workflow_run-status@v1
- - name: Download artifact
- # From https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_run
- uses: actions/github-script@v6
- with:
- script: |
- let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
- owner: context.repo.owner,
- repo: context.repo.repo,
- run_id: context.payload.workflow_run.id,
- });
- let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
- return artifact.name == "apks"
- })[0];
- let download = await github.rest.actions.downloadArtifact({
- owner: context.repo.owner,
- repo: context.repo.repo,
- artifact_id: matchArtifact.id,
- archive_format: 'zip',
- });
- let fs = require('fs');
- fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/apks.zip`, Buffer.from(download.data));
- - name: Unzip artifact
- run: |
- unzip apks.zip
- - name: Run tests
- uses: emulator-wtf/run-tests@master
- with:
- api-token: ${{ secrets.EW_API_TOKEN }}
- app: debug/snapshot-tests-debug.apk
- test: androidTest/debug/snapshot-tests-debug-androidTest.apk
- devices: |
- model=Pixel2,version=23
- model=Pixel2,version=31
- outputs-dir: build/test-results
- directories-to-pull: /sdcard/Download/
- - name: Check files
- run: |
- ls -ltR build/test-results/Pixel2_api31/sdcard/Download
- ls -ltR build/test-results/Pixel2_api23/sdcard/Download
- - name: 'Post PR comment'
- uses: mshick/add-pr-comment@v2
- if: github.event.workflow_run.event == 'pull_request'
- with:
- message-id: ${{ github.sha }}
- message: |
- **Snapshot Tests**
- **API 23**: [Report](https://happo.io/a/27/report/${{ github.sha }}-android23) [Diff](https://happo.io/a/27/p/27/compare/master-android23/${{ github.sha }}-android23)
- **API 31**: [Report](https://happo.io/a/27/report/${{ github.sha }}-android31) [Diff](https://happo.io/a/27/p/27/compare/master-android31/${{ github.sha }}-android31)
- - name: "Deploy Snapshot"
- env:
- SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
- SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
- run: ./deploy_snapshot.sh
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index f2dedf6..bf8ee9a 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -55,4 +55,60 @@
uses: actions/upload-artifact@v2
with:
name: unit_test_reports
- path: reports.zip
\ No newline at end of file
+ path: reports.zip
+ snapshot-tests:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout the code
+ uses: actions/checkout@v3
+ - name: Setup env
+ shell: bash
+ run: |
+ curl https://us-central1-lottie-snapshots.cloudfunctions.net/snapshot-env-v1/emulator > snapshot-env
+ while read line; do
+ echo "$line" >> $GITHUB_ENV
+ done < snapshot-env
+ - name: Setup JDK
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'zulu'
+ java-version: 11
+ cache: 'gradle'
+ - name: Build app
+ run: ./gradlew snapshot-tests:assembleDebug snapshot-tests:assembleDebugAndroidTest --no-daemon
+ - name: Run tests
+ uses: emulator-wtf/run-tests@master
+ with:
+ api-token: ${{ env.EW_API_TOKEN }}
+ app: snapshot-tests/build/outputs/apk/debug/snapshot-tests-debug.apk
+ test: snapshot-tests/build/outputs/apk/androidTest/debug/snapshot-tests-debug-androidTest.apk
+ devices: |
+ model=Pixel2,version=23
+ model=Pixel2,version=31
+ outputs-dir: build/test-results
+ - uses: mshick/add-pr-comment@v2
+ if: github.event_name == 'pull_request'
+ with:
+ message-id: ${{ github.sha }}
+ message: |
+ **Snapshot Tests**
+ **API 23**: [Report](https://happo.io/a/27/report/${{ github.sha }}-android23) [Diff](https://happo.io/a/27/p/27/compare/master-android23/${{ github.sha }}-android23)
+ **API 31**: [Report](https://happo.io/a/27/report/${{ github.sha }}-android31) [Diff](https://happo.io/a/27/p/27/compare/master-android31/${{ github.sha }}-android31)
+ deploy:
+ if: github.event_name == 'push' && github.repository == 'airbnb/lottie-android' && github.ref == 'refs/heads/master'
+ runs-on: ubuntu-latest
+ needs: [lint, unit-test, gradle-wrapper, snapshot-tests]
+ steps:
+ - name: Checkout the code
+ uses: actions/checkout@v3
+ - name: Setup JDK
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'zulu'
+ java-version: 11
+ cache: 'gradle'
+ - name: "Deploy Snapshot"
+ env:
+ SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
+ SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
+ run: ./deploy_snapshot.sh
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 0000000..4515aa3
--- /dev/null
+++ b/.idea/kotlinc.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="KotlinJpsPluginSettings">
+ <option name="version" value="1.6.10" />
+ </component>
+</project>
\ No newline at end of file
diff --git a/snapshot-tests/build.gradle b/snapshot-tests/build.gradle
index 80fd816..d3beaef 100644
--- a/snapshot-tests/build.gradle
+++ b/snapshot-tests/build.gradle
@@ -9,7 +9,7 @@
defaultConfig {
applicationId "com.airbnb.lottie.snapshots"
minSdk 21
- targetSdk 29
+ targetSdk 30
versionCode 1
versionName VERSION_NAME
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -39,7 +39,7 @@
freeCompilerArgs += [
"-Xallow-jvm-ir-dependencies",
"-Xskip-prerelease-check",
- "-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
"-Xopt-in=kotlin.RequiresOptIn",
]
}
diff --git a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/LottieSnapshotTest.kt b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/LottieSnapshotTest.kt
index 51a7b6a..3341722 100644
--- a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/LottieSnapshotTest.kt
+++ b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/LottieSnapshotTest.kt
@@ -6,7 +6,6 @@
import android.content.res.Configuration
import android.util.Log
import android.widget.FrameLayout
-import androidx.compose.animation.core.snap
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -44,6 +43,9 @@
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import org.json.JSONObject
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -61,11 +63,13 @@
@get:Rule
val permissionRule: GrantPermissionRule = GrantPermissionRule.grant(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
- Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.READ_EXTERNAL_STORAGE
)
lateinit var testCaseContext: SnapshotTestCaseContext
lateinit var snapshotter: HappoSnapshotter
+ private lateinit var s3AccessKey: String
+ private lateinit var s3SecretKey: String
@Before
fun setup() {
@@ -76,12 +80,28 @@
.build()
)
val context = ApplicationProvider.getApplicationContext<Context>()
- snapshotter = HappoSnapshotter(context) { name, variant ->
+ s3AccessKey = BuildConfig.S3AccessKey
+ s3SecretKey = BuildConfig.S3SecretKey
+ var happoApiKey = BuildConfig.HappoApiKey
+ var happoSecretKey = BuildConfig.HappoSecretKey
+ @Suppress("KotlinConstantConditions")
+ if (BuildConfig.S3AccessKey == "" || BuildConfig.S3AccessKey == "null") {
+ val client = OkHttpClient()
+ val request = Request.Builder()
+ .url("https://us-central1-lottie-snapshots.cloudfunctions.net/snapshot-env-v1/snapshots")
+ .build()
+ val response = client.newCall(request).execute()
+ val json = JSONObject(response.body?.string() ?: "{}")
+ s3AccessKey = json.getString("LOTTIE_S3_API_KEY")
+ s3SecretKey = json.getString("LOTTIE_S3_SECRET_KEY")
+ happoApiKey = json.getString("LOTTIE_HAPPO_API_KEY")
+ happoSecretKey = json.getString("LOTTIE_HAPPO_SECRET_KEY")
+ }
+ snapshotter = HappoSnapshotter(context, s3AccessKey, s3SecretKey, happoApiKey, happoSecretKey) { name, variant ->
snapshotActivityRule.scenario.onActivity { activity ->
activity.updateUiForSnapshot(name, variant)
}
}
- snapshotter.setupCacheDir()
testCaseContext = object : SnapshotTestCaseContext {
override val context: Context = context
override val snapshotter: HappoSnapshotter = this@LottieSnapshotTest.snapshotter
@@ -120,26 +140,26 @@
fun testAll() = runBlocking {
val testCases = listOf(
CustomBoundsTestCase(),
-// ColorStateListColorFilterTestCase(),
-// FailureTestCase(),
-// FrameBoundariesTestCase(),
-// ScaleTypesTestCase(),
-// ComposeScaleTypesTestCase(),
-// DynamicPropertiesTestCase(),
-// MarkersTestCase(),
-// AssetsTestCase(),
-// TextTestCase(),
-// PartialFrameProgressTestCase(),
-// NightModeTestCase(),
-// ApplyOpacityToLayerTestCase(),
-// OutlineMasksAndMattesTestCase(),
-// LargeCompositionSoftwareRendering(),
-// ComposeDynamicPropertiesTestCase(),
-// ProdAnimationsTestCase(),
-// ClipChildrenTestCase(),
-// SoftwareRenderingDynamicPropertiesInvalidationTestCase(),
-// SeekBarTestCase(),
-// CompositionFrameRate(),
+ ColorStateListColorFilterTestCase(),
+ FailureTestCase(),
+ FrameBoundariesTestCase(),
+ ScaleTypesTestCase(),
+ ComposeScaleTypesTestCase(),
+ DynamicPropertiesTestCase(),
+ MarkersTestCase(),
+ AssetsTestCase(),
+ TextTestCase(),
+ PartialFrameProgressTestCase(),
+ NightModeTestCase(),
+ ApplyOpacityToLayerTestCase(),
+ OutlineMasksAndMattesTestCase(),
+ LargeCompositionSoftwareRendering(),
+ ComposeDynamicPropertiesTestCase(),
+ ProdAnimationsTestCase(s3AccessKey, s3SecretKey),
+ ClipChildrenTestCase(),
+ SoftwareRenderingDynamicPropertiesInvalidationTestCase(),
+ SeekBarTestCase(),
+ CompositionFrameRate(),
)
withTimeout(TimeUnit.MINUTES.toMillis(45)) {
diff --git a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ProdAnimationsTestCase.kt b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ProdAnimationsTestCase.kt
index 903d319..56d280f 100644
--- a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ProdAnimationsTestCase.kt
+++ b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ProdAnimationsTestCase.kt
@@ -25,11 +25,7 @@
import java.util.concurrent.atomic.AtomicInteger
import java.util.zip.ZipInputStream
-/**
- * TODO:
- * prod-com.eharmony-lottie-loader-data
- */
-class ProdAnimationsTestCase : SnapshotTestCase {
+class ProdAnimationsTestCase(private val s3AccessKey: String, private val s3SecretKey: String) : SnapshotTestCase {
private val filesChannel = Channel<File>(capacity = 2_048)
override suspend fun SnapshotTestCaseContext.run() = coroutineScope {
@@ -45,8 +41,7 @@
}
}
- @Suppress("BlockingMethodInNonBlockingContext")
- fun CoroutineScope.parseCompositions(files: ReceiveChannel<File>) = produce(
+ private fun CoroutineScope.parseCompositions(files: ReceiveChannel<File>) = produce(
context = Dispatchers.IO,
capacity = 50,
) {
@@ -63,7 +58,7 @@
suspend fun SnapshotTestCaseContext.downloadAnimations() = coroutineScope {
val transferUtility = TransferUtility.builder()
.context(context)
- .s3Client(AmazonS3Client(BasicAWSCredentials(BuildConfig.S3AccessKey, BuildConfig.S3SecretKey)))
+ .s3Client(AmazonS3Client(BasicAWSCredentials(s3AccessKey, s3SecretKey)))
.defaultBucket("lottie-prod-animations")
.build()
@@ -92,7 +87,7 @@
private fun fetchAllObjects(bucket: String): List<S3ObjectSummary> {
val allObjects = mutableListOf<S3ObjectSummary>()
- val s3Client = AmazonS3Client(BasicAWSCredentials(BuildConfig.S3AccessKey, BuildConfig.S3SecretKey))
+ val s3Client = AmazonS3Client(BasicAWSCredentials(s3AccessKey, s3SecretKey))
var request = ListObjectsV2Request().apply {
bucketName = bucket
}
diff --git a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/utils/HappoSnapshotter.kt b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/utils/HappoSnapshotter.kt
index 4c68723..82ea63a 100644
--- a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/utils/HappoSnapshotter.kt
+++ b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/utils/HappoSnapshotter.kt
@@ -2,24 +2,36 @@
import android.content.Context
import android.graphics.Bitmap
-import android.os.Environment
+import android.os.Build
import android.util.Log
import com.airbnb.lottie.L
+import com.airbnb.lottie.snapshots.BuildConfig
+import com.amazonaws.auth.BasicAWSCredentials
+import com.amazonaws.mobileconnectors.s3.transferutility.TransferObserver
+import com.amazonaws.mobileconnectors.s3.transferutility.TransferUtility
+import com.amazonaws.services.s3.AmazonS3Client
+import com.amazonaws.services.s3.model.CannedAccessControlList
import com.google.gson.JsonArray
+import com.google.gson.JsonElement
import com.google.gson.JsonObject
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.withContext
-import java.io.BufferedInputStream
-import java.io.BufferedOutputStream
+import kotlinx.coroutines.*
+import okhttp3.*
+import okhttp3.MediaType.Companion.toMediaType
+import okhttp3.RequestBody.Companion.toRequestBody
import java.io.ByteArrayOutputStream
import java.io.File
-import java.io.FileInputStream
import java.io.FileOutputStream
+import java.io.IOException
import java.math.BigInteger
+import java.net.URLEncoder
+import java.nio.charset.Charset
import java.security.MessageDigest
-import java.util.UUID
-import java.util.zip.ZipEntry
-import java.util.zip.ZipOutputStream
+import java.util.*
+import kotlin.coroutines.resume
+import kotlin.coroutines.resumeWithException
+import kotlin.coroutines.suspendCoroutine
+
+private const val TAG = "HappoSnapshotter"
/**
* Use this class to record Bitmap snapshots and upload them to happo.
@@ -30,42 +42,36 @@
*/
class HappoSnapshotter(
private val context: Context,
+ s3AccessKey: String,
+ s3SecretKey: String,
+ private val happoApiKey: String,
+ private val happoSecretKey: String,
private val onSnapshotRecorded: (snapshotName: String, snapshotVariant: String) -> Unit,
) {
- private val bucket = "lottie-happo"
- private val cacheDir by lazy {
- val file = File("/sdcard/Download", "lottie")
- if (!file.exists()) {
- if (!file.mkdirs()) {
- throw IllegalStateException("Unable to make cache dir.")
- }
- }
- file
- }
- private val snapshotTempDir by lazy {
- val file = File(cacheDir, "snapshots-temp")
- if (!file.exists()) {
- if (!file.mkdirs()) {
- throw IllegalStateException("Unable to make cache dir.")
- }
- }
- file
- }
- private val snapshotDir by lazy {
- val file = File(cacheDir, "snapshots")
- if (!file.exists()) {
- if (!file.mkdirs()) {
- throw IllegalStateException("Unable to make cache dir.")
- }
- }
- file
- }
+ private val recordJob = Job()
+ private val recordScope = CoroutineScope(Dispatchers.IO + recordJob)
+ private val bucket = "lottie-happo"
+ private val gitBranch = URLEncoder.encode((BuildConfig.GIT_BRANCH).replace("/", "_"), "UTF-8")
+ private val androidVersion = "android${Build.VERSION.SDK_INT}"
+ private val reportNamePrefixes = listOf(BuildConfig.GIT_SHA, gitBranch, BuildConfig.VERSION_NAME).filter { it.isNotBlank() }
+
+ // Use this when running snapshots locally.
+ // private val reportNamePrefixes = listOf(System.currentTimeMillis().toString()).filter { it.isNotBlank() }
+ private val reportNames = reportNamePrefixes.map { "$it-$androidVersion" }
+
+ private val okhttp = OkHttpClient()
+
+ private val transferUtility = TransferUtility.builder()
+ .context(context)
+ .s3Client(AmazonS3Client(BasicAWSCredentials(s3AccessKey, s3SecretKey)))
+ .defaultBucket(bucket)
+ .build()
private val snapshots = mutableListOf<Snapshot>()
suspend fun record(bitmap: Bitmap, animationName: String, variant: String) = withContext(Dispatchers.IO) {
val tempUuid = UUID.randomUUID().toString()
- val file = File(snapshotTempDir, "$tempUuid.png")
+ val file = File(context.cacheDir, "$tempUuid.png")
val fileOutputStream = FileOutputStream(file)
val byteOutputStream = ByteArrayOutputStream()
@@ -74,12 +80,10 @@
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
val md5 = byteOutputStream.toByteArray().md5
val key = "snapshots/$md5.png"
- val md5File = File(snapshotDir, "$md5.png")
- if (!file.renameTo(md5File)) {
- throw IllegalStateException("Unable to rename ${file.absolutePath} to ${md5File.absolutePath}")
- }
- Log.d("Gabe", "Renamed file to ${md5File.absolutePath}")
+ val md5File = File(context.cacheDir, "$md5.png")
+ file.renameTo(md5File)
+ recordScope.launch { uploadDeferred(key, md5File) }
Log.d(L.TAG, "Adding snapshot for $animationName-$variant")
synchronized(snapshots) {
snapshots += Snapshot(bucket, key, bitmap.width, bitmap.height, animationName, variant)
@@ -87,15 +91,16 @@
onSnapshotRecorded(animationName, variant)
}
- fun setupCacheDir() {
- val files = cacheDir.listFiles() ?: return
- for (file in files) {
- file.deleteRecursively()
- }
- }
-
- fun finalizeReportAndUpload() {
+ suspend fun finalizeReportAndUpload() {
val recordJobStart = System.currentTimeMillis()
+ fun Job.activeJobs() = children.filter { it.isActive }.count()
+ var activeJobs = recordJob.activeJobs()
+ while (activeJobs > 0) {
+ activeJobs = recordJob.activeJobs()
+ Log.d(L.TAG, "Waiting for record $activeJobs jobs to finish.")
+ delay(1000)
+ }
+ recordJob.children.forEach { it.join() }
Log.d(L.TAG, "Waited ${System.currentTimeMillis() - recordJobStart}ms for recordings to finish saving.")
val json = JsonObject()
val snaps = JsonArray()
@@ -104,11 +109,44 @@
snaps.add(s.toJson())
}
Log.d(L.TAG, "Finished creating snapshot report")
- val reportFile = File(cacheDir, "report.json")
- FileOutputStream(reportFile).use { fos ->
- fos.write(json.toString().toByteArray())
+ reportNames.forEach { reportName ->
+ Log.d(L.TAG, "Uploading $reportName")
+ upload(reportName, json)
}
- createZip()
+ }
+
+ private suspend fun upload(reportName: String, json: JsonElement) {
+ val body = json.toString().toRequestBody("application/json".toMediaType())
+ val request = Request.Builder()
+ .addHeader("Authorization", Credentials.basic(happoApiKey, happoSecretKey, Charset.forName("UTF-8")))
+ .url("https://happo.io/api/reports/$reportName")
+ .post(body)
+ .build()
+
+ val response = okhttp.executeDeferred(request)
+ if (response.isSuccessful) {
+ Log.d(TAG, "Uploaded $reportName to happo")
+ } else {
+ throw IllegalStateException("Failed to upload $reportName to Happo. Failed with code ${response.code}. " + response.body?.string())
+ }
+ }
+
+ private suspend fun uploadDeferred(key: String, file: File): TransferObserver {
+ return retry { _, _ ->
+ transferUtility.upload(key, file, CannedAccessControlList.PublicRead).await()
+ }
+ }
+
+ private suspend fun OkHttpClient.executeDeferred(request: Request): Response = suspendCoroutine { continuation ->
+ newCall(request).enqueue(object : Callback {
+ override fun onFailure(call: Call, e: IOException) {
+ continuation.resumeWithException(e)
+ }
+
+ override fun onResponse(call: Call, response: Response) {
+ continuation.resume(response)
+ }
+ })
}
private val ByteArray.md5: String
@@ -117,20 +155,4 @@
digest.update(this, 0, this.size)
return BigInteger(1, digest.digest()).toString(16)
}
-
- private fun createZip() {
- val files = (snapshotDir.listFiles() ?: emptyArray()) + File(cacheDir, "report.json")
- ZipOutputStream(BufferedOutputStream(FileOutputStream("/sdcard/Download/snapshots.zip"))).use { out ->
- for (file in files) {
- FileInputStream(file).use { fi ->
- BufferedInputStream(fi).use { origin ->
- val entryName = file.absolutePath.substring(file.absolutePath.lastIndexOf("/"))
- val entry = ZipEntry(entryName)
- out.putNextEntry(entry)
- origin.copyTo(out, 1024)
- }
- }
- }
- }
- }
}
\ No newline at end of file
diff --git a/snapshot-tests/src/main/AndroidManifest.xml b/snapshot-tests/src/main/AndroidManifest.xml
index 3eca9ac..0004b3b 100644
--- a/snapshot-tests/src/main/AndroidManifest.xml
+++ b/snapshot-tests/src/main/AndroidManifest.xml
@@ -1,26 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
package="com.airbnb.lottie.snapshots">
- <uses-permission
- android:name="android.permission.READ_EXTERNAL_STORAGE"
- android:maxSdkVersion="31"
- tools:ignore="ScopedStorage" />
- <uses-permission
- android:name="android.permission.WRITE_EXTERNAL_STORAGE"
- android:maxSdkVersion="31"
- tools:ignore="ScopedStorage" />
- <uses-permission
- android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
- tools:ignore="ScopedStorage" />
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true"
- android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher"
android:theme="@style/Theme.LottieCompose">