Loading components/browser/thumbnails/build.gradle +1 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ dependencies { implementation Dependencies.androidx_core_ktx implementation Dependencies.kotlin_coroutines implementation Dependencies.kotlin_stdlib implementation Dependencies.thirdparty_disklrucache testImplementation project(':support-test') testImplementation project(':support-test-libstate') Loading components/browser/thumbnails/src/main/java/mozilla/components/browser/thumbnails/utils/ThumbnailDiskCache.kt 0 → 100644 +86 −0 Original line number Diff line number Diff line /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package mozilla.components.browser.thumbnails.utils import android.content.Context import android.graphics.Bitmap import com.jakewharton.disklrucache.DiskLruCache import mozilla.components.support.base.log.logger.Logger import java.io.File import java.io.IOException private const val MAXIMUM_CACHE_THUMBNAIL_DATA_BYTES: Long = 1024 * 1024 * 100 // 100 MB private const val THUMBNAIL_DISK_CACHE_VERSION = 1 private const val WEBP_QUALITY = 90 /** * Caching thumbnail bitmaps on disk. */ class ThumbnailDiskCache { private val logger = Logger("ThumbnailDiskCache") private var thumbnailCache: DiskLruCache? = null private val thumbnailCacheWriteLock = Any() /** * Retrieves the thumbnail data from the disk cache for the given session ID or URL. * * @param context the application [Context]. * @param sessionIdOrUrl the session ID or URL of the thumbnail to retrieve. * @return the [ByteArray] of the thumbnail or null if the snapshot of the entry does not exist. */ internal fun getThumbnailData(context: Context, sessionIdOrUrl: String): ByteArray? { val snapshot = getThumbnailCache(context).get(sessionIdOrUrl) ?: return null return try { snapshot.getInputStream(0).use { it.buffered().readBytes() } } catch (e: IOException) { logger.info("Failed to read thumbnail bitmap from disk", e) null } } /** * Stores the given session ID or URL's thumbnail [Bitmap] into the disk cache. * * @param context the application [Context]. * @param sessionIdOrUrl the session ID or URL. * @param bitmap the thumbnail [Bitmap] to store. */ internal fun putThumbnailBitmap(context: Context, sessionIdOrUrl: String, bitmap: Bitmap) { try { synchronized(thumbnailCacheWriteLock) { val editor = getThumbnailCache(context) .edit(sessionIdOrUrl) ?: return editor.newOutputStream(0).use { stream -> bitmap.compress(Bitmap.CompressFormat.WEBP, WEBP_QUALITY, stream) } editor.commit() } } catch (e: IOException) { logger.info("Failed to save thumbnail bitmap to disk", e) } } private fun getThumbnailCacheDirectory(context: Context): File { val cacheDirectory = File(context.cacheDir, "mozac_browser_thumbnails") return File(cacheDirectory, "thumbnails") } @Synchronized private fun getThumbnailCache(context: Context): DiskLruCache { thumbnailCache?.let { return it } return DiskLruCache.open( getThumbnailCacheDirectory(context), THUMBNAIL_DISK_CACHE_VERSION, 1, MAXIMUM_CACHE_THUMBNAIL_DATA_BYTES ).also { thumbnailCache = it } } } components/browser/thumbnails/src/test/java/mozilla/components/browser/thumbnails/utils/ThumbnailDiskCacheTest.kt 0 → 100644 +47 −0 Original line number Diff line number Diff line /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package mozilla.components.browser.thumbnails.utils import android.graphics.Bitmap import androidx.test.ext.junit.runners.AndroidJUnit4 import mozilla.components.support.test.any import mozilla.components.support.test.mock import mozilla.components.support.test.robolectric.testContext import org.junit.Assert import org.junit.Assert.assertNotNull import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers import org.mockito.Mockito import java.io.OutputStream @RunWith(AndroidJUnit4::class) class ThumbnailDiskCacheTest { @Test fun `Writing and reading bitmap bytes`() { val cache = ThumbnailDiskCache() val sessionIdOrUrl = "123" val bitmap: Bitmap = mock() Mockito.`when`(bitmap.compress(any(), ArgumentMatchers.anyInt(), any())).thenAnswer { Assert.assertEquals( Bitmap.CompressFormat.WEBP, it.arguments[0] as Bitmap.CompressFormat ) Assert.assertEquals(90, it.arguments[1] as Int) // Quality val stream = it.arguments[2] as OutputStream stream.write("Hello World".toByteArray()) true } cache.putThumbnailBitmap(testContext, sessionIdOrUrl, bitmap) val data = cache.getThumbnailData(testContext, sessionIdOrUrl) assertNotNull(data!!) Assert.assertEquals("Hello World", String(data)) } } docs/changelog.md +3 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,9 @@ permalink: /changelog/ into a new component `support-images`, which provides helpers for handling images. `AndroidIconDecoder` and `IconDecoder` are renamed to `AndroidImageDecoder` and `ImageDecoder` in `support-images`. * **browser-thumbnails** * Adds `ThumbnailDiskCache` for storing and restoring thumbnail bitmaps into a disk cache. # 42.0.0 * [Commits](https://github.com/mozilla-mobile/android-components/compare/v41.0.0...42.0.0) Loading Loading
components/browser/thumbnails/build.gradle +1 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ dependencies { implementation Dependencies.androidx_core_ktx implementation Dependencies.kotlin_coroutines implementation Dependencies.kotlin_stdlib implementation Dependencies.thirdparty_disklrucache testImplementation project(':support-test') testImplementation project(':support-test-libstate') Loading
components/browser/thumbnails/src/main/java/mozilla/components/browser/thumbnails/utils/ThumbnailDiskCache.kt 0 → 100644 +86 −0 Original line number Diff line number Diff line /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package mozilla.components.browser.thumbnails.utils import android.content.Context import android.graphics.Bitmap import com.jakewharton.disklrucache.DiskLruCache import mozilla.components.support.base.log.logger.Logger import java.io.File import java.io.IOException private const val MAXIMUM_CACHE_THUMBNAIL_DATA_BYTES: Long = 1024 * 1024 * 100 // 100 MB private const val THUMBNAIL_DISK_CACHE_VERSION = 1 private const val WEBP_QUALITY = 90 /** * Caching thumbnail bitmaps on disk. */ class ThumbnailDiskCache { private val logger = Logger("ThumbnailDiskCache") private var thumbnailCache: DiskLruCache? = null private val thumbnailCacheWriteLock = Any() /** * Retrieves the thumbnail data from the disk cache for the given session ID or URL. * * @param context the application [Context]. * @param sessionIdOrUrl the session ID or URL of the thumbnail to retrieve. * @return the [ByteArray] of the thumbnail or null if the snapshot of the entry does not exist. */ internal fun getThumbnailData(context: Context, sessionIdOrUrl: String): ByteArray? { val snapshot = getThumbnailCache(context).get(sessionIdOrUrl) ?: return null return try { snapshot.getInputStream(0).use { it.buffered().readBytes() } } catch (e: IOException) { logger.info("Failed to read thumbnail bitmap from disk", e) null } } /** * Stores the given session ID or URL's thumbnail [Bitmap] into the disk cache. * * @param context the application [Context]. * @param sessionIdOrUrl the session ID or URL. * @param bitmap the thumbnail [Bitmap] to store. */ internal fun putThumbnailBitmap(context: Context, sessionIdOrUrl: String, bitmap: Bitmap) { try { synchronized(thumbnailCacheWriteLock) { val editor = getThumbnailCache(context) .edit(sessionIdOrUrl) ?: return editor.newOutputStream(0).use { stream -> bitmap.compress(Bitmap.CompressFormat.WEBP, WEBP_QUALITY, stream) } editor.commit() } } catch (e: IOException) { logger.info("Failed to save thumbnail bitmap to disk", e) } } private fun getThumbnailCacheDirectory(context: Context): File { val cacheDirectory = File(context.cacheDir, "mozac_browser_thumbnails") return File(cacheDirectory, "thumbnails") } @Synchronized private fun getThumbnailCache(context: Context): DiskLruCache { thumbnailCache?.let { return it } return DiskLruCache.open( getThumbnailCacheDirectory(context), THUMBNAIL_DISK_CACHE_VERSION, 1, MAXIMUM_CACHE_THUMBNAIL_DATA_BYTES ).also { thumbnailCache = it } } }
components/browser/thumbnails/src/test/java/mozilla/components/browser/thumbnails/utils/ThumbnailDiskCacheTest.kt 0 → 100644 +47 −0 Original line number Diff line number Diff line /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package mozilla.components.browser.thumbnails.utils import android.graphics.Bitmap import androidx.test.ext.junit.runners.AndroidJUnit4 import mozilla.components.support.test.any import mozilla.components.support.test.mock import mozilla.components.support.test.robolectric.testContext import org.junit.Assert import org.junit.Assert.assertNotNull import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers import org.mockito.Mockito import java.io.OutputStream @RunWith(AndroidJUnit4::class) class ThumbnailDiskCacheTest { @Test fun `Writing and reading bitmap bytes`() { val cache = ThumbnailDiskCache() val sessionIdOrUrl = "123" val bitmap: Bitmap = mock() Mockito.`when`(bitmap.compress(any(), ArgumentMatchers.anyInt(), any())).thenAnswer { Assert.assertEquals( Bitmap.CompressFormat.WEBP, it.arguments[0] as Bitmap.CompressFormat ) Assert.assertEquals(90, it.arguments[1] as Int) // Quality val stream = it.arguments[2] as OutputStream stream.write("Hello World".toByteArray()) true } cache.putThumbnailBitmap(testContext, sessionIdOrUrl, bitmap) val data = cache.getThumbnailData(testContext, sessionIdOrUrl) assertNotNull(data!!) Assert.assertEquals("Hello World", String(data)) } }
docs/changelog.md +3 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,9 @@ permalink: /changelog/ into a new component `support-images`, which provides helpers for handling images. `AndroidIconDecoder` and `IconDecoder` are renamed to `AndroidImageDecoder` and `ImageDecoder` in `support-images`. * **browser-thumbnails** * Adds `ThumbnailDiskCache` for storing and restoring thumbnail bitmaps into a disk cache. # 42.0.0 * [Commits](https://github.com/mozilla-mobile/android-components/compare/v41.0.0...42.0.0) Loading