/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase

import android.graphics.Color
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout
import androidx.fragment.app.Fragment
import androidx.preference.PreferenceManager
import com.facebook.fresco.samples.showcase.databinding.ActivityMainBinding
import com.facebook.fresco.samples.showcase.permissions.StoragePermissionHelper
import com.google.android.material.navigation.NavigationView
import com.google.android.material.snackbar.Snackbar

class MainActivity : AppCompatActivity() {

  private lateinit var binding: ActivityMainBinding
  private val toolbar: Toolbar by lazy { binding.appBarMain.toolbar }
  private val drawerLayout: DrawerLayout by lazy { binding.drawerLayout }
  private val navView: NavigationView by lazy { binding.navView }
  private val contentMain by lazy { binding.appBarMain.contentMain.root }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)
    setSupportActionBar(toolbar)

    val toggle =
        ActionBarDrawerToggle(
            this,
            drawerLayout,
            toolbar,
            R.string.navigation_drawer_open,
            R.string.navigation_drawer_close)
    drawerLayout.addDrawerListener(toggle)
    toggle.syncState()

    addNavDrawerEntry(navView.menu, ExampleDatabase.welcome)
    for (category in ExampleDatabase.examples) {
      val submenu = navView.menu.addSubMenu(category.name)
      for (item in category.examples) {
        addNavDrawerEntry(submenu, item)
      }
    }
    addNavDrawerEntry(navView.menu.addSubMenu("More"), ExampleDatabase.settings)

    if (savedInstanceState == null) {
      navView.menu.performIdentifierAction(
          PreferenceManager.getDefaultSharedPreferences(this)
              .getInt(KEY_SELECTED_NAVDRAWER_ITEM_ID, ExampleDatabase.welcome.itemId),
          0)
    }
  }

  private fun addNavDrawerEntry(menu: Menu, item: ExampleItem) {
    menu
        .add(Menu.NONE, item.itemId, Menu.NONE, item.title)
        .setCheckable(true)
        .setOnMenuItemClickListener {
          showFragment(item.createFragment(), item.title, item.backstackTag)
          drawerLayout.closeDrawer(GravityCompat.START)
          navView.setCheckedItem(it)
          PreferenceManager.getDefaultSharedPreferences(this)
              .edit()
              .putInt(KEY_SELECTED_NAVDRAWER_ITEM_ID, it.itemId)
              .apply()
          true
        }
  }

  override fun onResume() {
    super.onResume()
    maybeShowUriOverrideReminder()
  }

  override fun onBackPressed() {
    if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
      drawerLayout.closeDrawer(GravityCompat.START)
    } else {
      super.onBackPressed()
    }
  }

  override fun onCreateOptionsMenu(menu: Menu): Boolean {
    menuInflater.inflate(R.menu.main, menu)
    // the support toolbar should probably do this by default
    val styles =
        obtainStyledAttributes(R.style.AppTheme_Toolbar, intArrayOf(R.attr.colorControlNormal))
    try {
      val tintColor = styles.getColor(0, Color.BLACK)
      for (i in 0 until menu.size()) {
        val icon = menu.getItem(i).icon
        if (icon != null) {
          DrawableCompat.setTint(icon, tintColor)
        }
      }
    } finally {
      styles.recycle()
    }
    return super.onCreateOptionsMenu(menu)
  }

  override fun onOptionsItemSelected(item: MenuItem): Boolean {
    when (item.itemId) {
      R.id.action_next_image -> ShowcaseApplication.imageSelector.selectNext(this)
      R.id.action_prev_image -> ShowcaseApplication.imageSelector.selectPrevious(this)
      R.id.action_settings -> showFragment(ExampleDatabase.settings)
      R.id.action_edit_image -> showImageOptionsEditor()
    }
    return super.onOptionsItemSelected(item)
  }

  private fun showImageOptionsEditor() {
    supportFragmentManager.let {
      ImageOptionsBottomSheet.newInstance(ShowcaseApplication.imageSelector, Bundle()).apply {
        show(it, tag)
      }
    }
  }

  override fun onRequestPermissionsResult(
      requestCode: Int,
      permissions: Array<out String>,
      grantResults: IntArray
  ) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    StoragePermissionHelper.onRequestPermissionsResult(this, requestCode, permissions, grantResults)
  }

  /**
   * Utility method to display a specific Fragment. If the tag is not null we add a backstack
   *
   * @param fragment The Fragment to add
   */
  private fun showFragment(fragment: Fragment, title: String, backstackTag: String? = null) {
    val fragmentTransaction =
        supportFragmentManager.beginTransaction().replace(R.id.content_main, fragment)
    if (backstackTag != null) {
      fragmentTransaction.addToBackStack(backstackTag)
    }
    fragmentTransaction.commit()

    setTitle(title)
    ShowcaseApplication.imageTracker.reset()
  }

  private fun maybeShowUriOverrideReminder() {
    if (ShowcaseApplication.imageUriProvider.uriOverride == null) {
      return
    }
    Snackbar.make(contentMain, R.string.snackbar_uri_override_reminder_text, Snackbar.LENGTH_LONG)
        .setAction(R.string.snackbar_uri_override_reminder_change_button) {
          showFragment(ExampleDatabase.settings)
        }
        .show()
  }

  private fun showFragment(item: ExampleItem) {
    showFragment(item.createFragment(), item.title, item.backstackTag)
  }

  companion object {
    private const val KEY_SELECTED_NAVDRAWER_ITEM_ID = "selected_navdrawer_item_id"
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase

import android.app.Application
import android.content.res.Resources
import androidx.preference.PreferenceManager
import com.facebook.common.executors.UiThreadImmediateExecutorService
import com.facebook.common.internal.Suppliers
import com.facebook.common.logging.FLog
import com.facebook.common.memory.manager.NoOpDebugMemoryManager
import com.facebook.drawee.backends.pipeline.DraweeConfig
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.flipper.android.AndroidFlipperClient
import com.facebook.flipper.android.utils.FlipperUtils
import com.facebook.flipper.perflogger.NoOpFlipperPerfLogger
import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin
import com.facebook.flipper.plugins.fresco.FrescoFlipperRequestListener
import com.facebook.flipper.plugins.inspector.DescriptorMapping
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin
import com.facebook.fresco.samples.showcase.misc.DebugOverlaySupplierSingleton
import com.facebook.fresco.samples.showcase.misc.ImageUriProvider
import com.facebook.fresco.samples.showcase.misc.LogcatRequestListener2
import com.facebook.fresco.samples.showcase.settings.SettingsFragment.KEY_VITO_KOTLIN
import com.facebook.fresco.ui.common.ImageLoadStatus
import com.facebook.fresco.ui.common.ImagePerfData
import com.facebook.fresco.ui.common.ImagePerfDataListener
import com.facebook.fresco.ui.common.VisibilityState
import com.facebook.fresco.vito.core.DefaultFrescoVitoConfig
import com.facebook.fresco.vito.core.FrescoVitoConfig
import com.facebook.fresco.vito.core.impl.DebugOverlayHandler
import com.facebook.fresco.vito.init.FrescoVito
import com.facebook.fresco.vito.provider.FrescoVitoProvider
import com.facebook.fresco.vito.provider.impl.NoOpCallerContextVerifier
import com.facebook.fresco.vito.provider.impl.kotlin.KFrescoVitoProvider
import com.facebook.fresco.vito.tools.liveeditor.ImageSelector
import com.facebook.fresco.vito.tools.liveeditor.ImageTracker
import com.facebook.imagepipeline.backends.okhttp3.OkHttpImagePipelineConfigFactory
import com.facebook.imagepipeline.core.ImagePipelineConfig
import com.facebook.imagepipeline.core.ImagePipelineFactory
import com.facebook.imagepipeline.debug.FlipperCacheKeyFactory
import com.facebook.imagepipeline.debug.FlipperImageTracker
import com.facebook.imagepipeline.decoder.SimpleProgressiveJpegConfig
import com.facebook.imagepipeline.listener.ForwardingRequestListener
import com.facebook.imagepipeline.listener.RequestListener
import com.facebook.imagepipeline.listener.RequestLoggingListener
import com.facebook.imagepipeline.stetho.FrescoStethoPlugin
import com.facebook.stetho.Stetho
import com.facebook.stetho.okhttp3.StethoInterceptor
import okhttp3.OkHttpClient

/** Showcase Application implementation where we set up Fresco */
class ShowcaseApplication : Application() {

  private var frescoFlipperPlugin: FrescoFlipperPlugin? = null

  override fun onCreate() {
    super.onCreate()
    imageUriProvider = ImageUriProvider(applicationContext)
    FLog.setMinimumLoggingLevel(FLog.VERBOSE)
    val forwardingRequestListener = ForwardingRequestListener()
    val requestListeners =
        HashSet<RequestListener>().apply {
          add(forwardingRequestListener)
          add(RequestLoggingListener())
        }

    val requestListener2s = setOf(LogcatRequestListener2())

    val okHttpClient = OkHttpClient.Builder().addNetworkInterceptor(StethoInterceptor()).build()

    val imagePipelineConfigBuilder =
        OkHttpImagePipelineConfigFactory.newBuilder(this, okHttpClient)
            .setRequestListeners(requestListeners)
            .setRequestListener2s(requestListener2s)
            .setProgressiveJpegConfig(SimpleProgressiveJpegConfig())
            .setImageDecoderConfig(CustomImageFormatConfigurator.createImageDecoderConfig(this))
            .experiment()
            .setBitmapPrepareToDraw(true, 0, Integer.MAX_VALUE, true)

    if (shouldEnableFlipper()) {
      imagePipelineConfigBuilder.setCacheKeyFactory(FlipperCacheKeyFactory(sFlipperImageTracker))
    }

    imagePipelineConfigBuilder.experiment().setDownsampleIfLargeBitmap(true)

    val imagePipelineConfig = imagePipelineConfigBuilder.build()
    ImagePipelineConfig.defaultImageRequestConfig.isProgressiveRenderingEnabled = true

    val draweeConfigBuilder = DraweeConfig.newBuilder()
    CustomImageFormatConfigurator.addCustomDrawableFactories(this, draweeConfigBuilder)

    draweeConfigBuilder.setDebugOverlayEnabledSupplier(
        DebugOverlaySupplierSingleton.getInstance(applicationContext))

    if (shouldEnableFlipper()) {
      draweeConfigBuilder.setImagePerfDataListener(
          object : ImagePerfDataListener {
            override fun onImageLoadStatusUpdated(
                imagePerfData: ImagePerfData,
                imageLoadStatus: ImageLoadStatus
            ) {
              frescoFlipperPlugin
                  ?.flipperImageTracker
                  ?.onImageLoadStatusUpdated(imagePerfData, imageLoadStatus)
              frescoFlipperPlugin?.onImageLoadStatusUpdated(imagePerfData, imageLoadStatus)
            }

            override fun onImageVisibilityUpdated(
                imagePerfData: ImagePerfData,
                visibilityState: VisibilityState
            ) {
              // nop
            }
          })
    }

    Fresco.initialize(this, imagePipelineConfig, draweeConfigBuilder.build())
    imageTracker = ImageTracker()
    initVito(resources)
    imageSelector =
        ImageSelector(
            imageTracker, FrescoVitoProvider.getImagePipeline(), FrescoVitoProvider.getController())
    val context = this
    Stetho.initialize(
        Stetho.newInitializerBuilder(context)
            .enableDumpapp {
              Stetho.DefaultDumperPluginsBuilder(context).provide(FrescoStethoPlugin()).finish()
            }
            .enableWebKitInspector(Stetho.defaultInspectorModulesProvider(context))
            .build())

    if (shouldEnableFlipper()) {
      frescoFlipperPlugin =
          FrescoFlipperPlugin(
              sFlipperImageTracker,
              Fresco.getImagePipelineFactory().platformBitmapFactory,
              null,
              NoOpDebugMemoryManager(),
              NoOpFlipperPerfLogger(),
              null,
              null)
      forwardingRequestListener.addRequestListener(
          FrescoFlipperRequestListener(frescoFlipperPlugin!!.flipperImageTracker))
      AndroidFlipperClient.getInstance(context).apply {
        addPlugin(InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()))
        addPlugin(frescoFlipperPlugin)
        start()
      }
    }
  }

  private fun initVito(
      resources: Resources,
      vitoConfig: FrescoVitoConfig = DefaultFrescoVitoConfig(),
  ) {
    if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(KEY_VITO_KOTLIN, false)) {
      FrescoVito.initialize(
          KFrescoVitoProvider(
              vitoConfig,
              ImagePipelineFactory.getInstance().imagePipeline,
              FrescoVito.createImagePipelineUtils(Suppliers.BOOLEAN_TRUE),
              ImagePipelineFactory.getInstance()
                  .imagePipeline
                  .config
                  .executorSupplier
                  .forLightweightBackgroundTasks(),
              UiThreadImmediateExecutorService.getInstance(),
              NoOpCallerContextVerifier,
              DebugOverlayHandler(DebugOverlaySupplierSingleton.getInstance(applicationContext))))
    } else {
      FrescoVito.initialize(
          vitoConfig = vitoConfig,
          debugOverlayEnabledSupplier =
              DebugOverlaySupplierSingleton.getInstance(applicationContext),
          vitoImagePerfListener = imageTracker)
    }
  }

  private fun shouldEnableFlipper(): Boolean {
    return BuildConfig.DEBUG && FlipperUtils.shouldEnableFlipper(this)
  }

  companion object {
    private val sFlipperImageTracker = FlipperImageTracker()
    lateinit var imageTracker: ImageTracker
      private set

    lateinit var imageUriProvider: ImageUriProvider
      private set

    lateinit var imageSelector: ImageSelector
      private set
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.common

import android.content.Context
import android.util.TypedValue

fun Float.dpToPx(context: Context): Float =
    TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this, context.resources.displayMetrics)

fun Int.dpToPx(context: Context): Int = toFloat().dpToPx(context).toInt()


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.common

import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Spinner

object SpinnerUtils {
  fun <T> Spinner.setupWithList(
      data: Pair<List<Pair<String, T>>, String>,
      clickListener: (T) -> Unit
  ) {
    adapter =
        ArrayAdapter(
            context,
            android.R.layout.simple_spinner_dropdown_item,
            data.first.map { data.second + ": " + it.first })
    onItemSelectedListener =
        object : AdapterView.OnItemSelectedListener {
          override fun onNothingSelected(p0: AdapterView<*>?) = Unit

          override fun onItemSelected(
              parent: AdapterView<*>?,
              view: View?,
              position: Int,
              id: Long
          ) {
            clickListener(data.first[position].second)
          }
        }
  }

  fun Spinner.setupWithCallbacks(data: List<Pair<String, () -> Unit>>) {
    adapter =
        ArrayAdapter(context, android.R.layout.simple_spinner_dropdown_item, data.map { it.first })
    onItemSelectedListener =
        object : AdapterView.OnItemSelectedListener {
          override fun onNothingSelected(p0: AdapterView<*>?) = Unit

          override fun onItemSelected(
              parent: AdapterView<*>?,
              view: View?,
              position: Int,
              id: Long
          ) {
            data[position].second()
          }
        }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.drawee

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.drawee.view.SimpleDraweeView
import com.facebook.fresco.samples.showcase.BaseShowcaseFragment
import com.facebook.fresco.samples.showcase.databinding.FragmentDraweeMultiUriBinding
import com.facebook.fresco.samples.showcase.misc.ImageUriProvider
import com.facebook.fresco.vito.ktx.ImageSourceExtensions.asImageSource
import com.facebook.fresco.vito.source.ImageSourceProvider
import com.facebook.fresco.vito.view.VitoView
import com.facebook.imagepipeline.request.ImageRequest
import com.google.android.material.switchmaterial.SwitchMaterial

/**
 * Fragment that displays an image based on a set of image requests, either with increasing quality
 * or the first available.
 */
class MultiUriFragment : BaseShowcaseFragment() {

  private var _binding: FragmentDraweeMultiUriBinding? = null
  private val binding
    get() = _binding!!

  private val draweeView: SimpleDraweeView
    get() = binding.draweeView

  private val switchUseVito: SwitchMaterial
    get() = binding.switchUseVito

  private val btnFirstAvailable: Button
    get() = binding.btnFirstAvailable

  private val btnIncreasingQuality
    get() = binding.btnIncreasingQuality

  private val btnBoth
    get() = binding.btnBoth

  override fun onCreateView(
      inflater: LayoutInflater,
      container: ViewGroup?,
      savedInstanceState: Bundle?
  ): View {
    _binding = FragmentDraweeMultiUriBinding.inflate(inflater, container, false)
    return binding.root
  }

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    btnFirstAvailable.setOnClickListener {
      resetImageView()
      if (switchUseVito.isChecked) {
        val requests =
            sampleUris().createSampleUriSet().map(ImageSourceProvider::forUri).toTypedArray()
        VitoView.show(ImageSourceProvider.firstAvailable(*requests), draweeView)
      } else {
        val requests = sampleUris().createSampleUriSet().map(ImageRequest::fromUri).toTypedArray()
        draweeView.controller =
            Fresco.newDraweeControllerBuilder().setFirstAvailableImageRequests(requests).build()
      }
    }

    btnIncreasingQuality.setOnClickListener {
      resetImageView()

      if (switchUseVito.isChecked) {
        val lowRes = sampleUris().createSampleUri(ImageUriProvider.ImageSize.XS).asImageSource()
        val highRes = sampleUris().createSampleUri(ImageUriProvider.ImageSize.XXL).asImageSource()
        VitoView.show(ImageSourceProvider.increasingQuality(lowRes, highRes), draweeView)
      } else {
        val lowRes =
            ImageRequest.fromUri(sampleUris().createSampleUri(ImageUriProvider.ImageSize.XS))
        val highRes =
            ImageRequest.fromUri(sampleUris().createSampleUri(ImageUriProvider.ImageSize.XXL))
        val draweeControllerBuilder = Fresco.newDraweeControllerBuilder().setImageRequest(highRes)
        lowRes?.let { draweeControllerBuilder.setLowResImageRequest(it) }
        draweeView.controller = draweeControllerBuilder.build()
      }
    }

    btnBoth.setOnClickListener {
      resetImageView()
      if (switchUseVito.isChecked) {
        val lowRes = sampleUris().createSampleUri(ImageUriProvider.ImageSize.XS).asImageSource()
        val anyHighRes =
            listOf(
                    sampleUris().createSampleUri(ImageUriProvider.ImageSize.L),
                    sampleUris().createSampleUri(ImageUriProvider.ImageSize.XL),
                    sampleUris().createSampleUri(ImageUriProvider.ImageSize.XXL))
                .map(ImageSourceProvider::forUri)
                .toTypedArray()
        VitoView.show(
            ImageSourceProvider.increasingQuality(
                lowRes, ImageSourceProvider.firstAvailable(*anyHighRes)),
            draweeView)
      } else {
        val lowRes =
            ImageRequest.fromUri(sampleUris().createSampleUri(ImageUriProvider.ImageSize.XS))
        val anyHighRes =
            listOf(
                    sampleUris().createSampleUri(ImageUriProvider.ImageSize.L),
                    sampleUris().createSampleUri(ImageUriProvider.ImageSize.XL),
                    sampleUris().createSampleUri(ImageUriProvider.ImageSize.XXL))
                .map(ImageRequest::fromUri)
                .toTypedArray()
        val draweeControllerBuilder =
            Fresco.newDraweeControllerBuilder().setFirstAvailableImageRequests(anyHighRes)
        lowRes?.let { draweeControllerBuilder.setLowResImageRequest(it) }
        draweeView.controller = draweeControllerBuilder.build()
      }
    }
  }

  private fun resetImageView() {
    draweeView.controller = null
    VitoView.show(null, draweeView)
  }

  override fun onDestroyView() {
    super.onDestroyView()
    _binding = null
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.misc

import android.graphics.drawable.Drawable
import android.util.Log
import com.facebook.drawee.backends.pipeline.info.ImageOriginUtils
import com.facebook.fresco.ui.common.DimensionsInfo
import com.facebook.fresco.vito.listener.ImageListener
import com.facebook.imagepipeline.image.ImageInfo

class DebugImageListener(val log: (String) -> Unit = { Log.d("DebugImageListener", it) }) :
    ImageListener {

  override fun onSubmit(id: Long, callerContext: Any?) {
    log("onSubmit: id=$id, cc=$callerContext")
  }

  override fun onPlaceholderSet(id: Long, placeholder: Drawable?) {
    log("onPlaceholderSet: id=$id, placeholder=$placeholder")
  }

  override fun onFinalImageSet(
      id: Long,
      imageOrigin: Int,
      imageInfo: ImageInfo?,
      drawable: Drawable?
  ) {
    log(
        "onFinalImageSet: id=$id, origin=${ImageOriginUtils.toString(imageOrigin)}, imageInfo=$imageInfo, drawable=$drawable")
  }

  override fun onIntermediateImageSet(id: Long, imageInfo: ImageInfo?) {
    log("onIntermediateImageSet: id=$id, imageInfo=$imageInfo")
  }

  override fun onIntermediateImageFailed(id: Long, throwable: Throwable?) {
    log("onIntermediateImageFailed: id=$id, throwable=$throwable")
  }

  override fun onFailure(id: Long, error: Drawable?, throwable: Throwable?) {
    log("onFailure: id=$id, errorDrawable=$error, throwable=$throwable")
  }

  override fun onRelease(id: Long) {
    log("onRelease: id=$id")
  }

  override fun onImageDrawn(id: String, imageInfo: ImageInfo, dimensionsInfo: DimensionsInfo) {
    log("onImageDrawn: id=$id, imageInfo=$imageInfo, dimensionsInfo=$dimensionsInfo")
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.misc

import android.net.Uri
import android.widget.Spinner
import com.facebook.fresco.samples.showcase.common.SpinnerUtils.setupWithCallbacks

object ImageSourceSpinner {

  @JvmOverloads
  fun Spinner.setup(
      imageUriProvider: ImageUriProvider,
      callback: (List<@JvmSuppressWildcards Uri>) -> Unit,
      numEntries: Int = 256
  ) {
    setupWithCallbacks(
        listOf(
            "Small images" to
                {
                  callback.invoke(
                      imageUriProvider.getRandomSampleUris(
                          ImageUriProvider.ImageSize.S, numEntries))
                },
            "Large images" to
                {
                  callback.invoke(
                      imageUriProvider.getRandomSampleUris(
                          ImageUriProvider.ImageSize.M, numEntries))
                },
            "Media" to { callback.invoke(imageUriProvider.getMediaStoreUris(context)) },
            "Empty list" to { callback.invoke(emptyList()) }))
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.misc

import android.content.ContentUris
import android.content.Context
import android.content.SharedPreferences
import android.net.Uri
import android.provider.MediaStore
import androidx.preference.PreferenceManager
import com.facebook.common.internal.Preconditions
import com.facebook.fresco.samples.showcase.R
import com.facebook.fresco.samples.showcase.imageformat.keyframes.KeyframesDecoderExample
import com.facebook.imageformat.DefaultImageFormats
import com.facebook.imageformat.ImageFormat
import java.util.*

/** Provider for sample URIs that are used by the samples in the showcase app */
class ImageUriProvider constructor(context: Context) {

  private val sharedPreferences: SharedPreferences =
      PreferenceManager.getDefaultSharedPreferences(context)

  var uriOverride: String?
    get() = sharedPreferences.getString(PREF_KEY_URI_OVERRIDE, null)
    set(uri) =
        if (uri.isNullOrEmpty()) {
          sharedPreferences.edit().remove(PREF_KEY_URI_OVERRIDE).apply()
        } else {
          Preconditions.checkArgument(Uri.parse(uri).isAbsolute, "URI must be absolute")

          sharedPreferences.edit().putString(PREF_KEY_URI_OVERRIDE, uri).apply()
        }

  val sampleGifUris = SAMPLE_URIS_GIFS.map(Uri::parse)

  private val isShouldBreakCacheByDefault: Boolean
    get() = sharedPreferences.getBoolean(PREF_KEY_CACHE_BREAKING_BY_DEFAULT, false)

  /** The orientation of a sample image */
  enum class Orientation {
    /** height > width */
    PORTRAIT,

    /** width > height */
    LANDSCAPE,

    /** Any orientation */
    ANY
  }

  /** Indicates whether to perform some action on the URI before returning */
  enum class UriModification {

    /** Do not perform any modification */
    NONE,

    /** Add a unique parameter to the URI to prevent it to be served from any cache */
    CACHE_BREAKER
  }

  enum class ImageSize(val sizeSuffix: String) {
    /** Within ~250x250 px bounds */
    XS("xs"),

    /** Within ~450x450 px bounds */
    S("s"),

    /** Within ~800x800 px bounds */
    M("m"),

    /** Within ~1400x1400 px bounds */
    L("l"),

    /** Within ~2500x2500 px bounds */
    XL("xl"),

    /** Within ~4096x4096 px bounds */
    XXL("xxl")
  }

  /** Creates an URI of an image that will result in a 404 (not found) HTTP error */
  val nonExistingUri: Uri by lazy { Uri.parse(NON_EXISTING_URI) }

  fun create(imageFormat: ImageFormat = DefaultImageFormats.JPEG): Uri? {
    return when (imageFormat) {
      DefaultImageFormats.JPEG -> createSampleUri()
      DefaultImageFormats.PNG -> createPngUri()
      DefaultImageFormats.GIF -> createGifUri()
      DefaultImageFormats.WEBP_SIMPLE -> createWebpStaticUri()
      DefaultImageFormats.WEBP_EXTENDED_WITH_ALPHA -> createWebpTranslucentUri()
      DefaultImageFormats.WEBP_ANIMATED -> createWebpAnimatedUri()
      KeyframesDecoderExample.IMAGE_FORMAT_KEYFRAMES -> createKeyframesUri()
      else -> null
    }
  }

  @JvmOverloads
  fun createSampleUri(
      imageSize: ImageSize = ImageSize.M,
      orientation: Orientation = Orientation.ANY,
      urlModification: UriModification = UriModification.NONE
  ): Uri {
    val fullUri = String.format(randomBaseJpegUri(orientation), imageSize.sizeSuffix)
    return applyOverrideSettings(fullUri, urlModification)
  }

  @JvmOverloads
  fun createSampleUriSet(
      orientation: Orientation = Orientation.ANY,
      urlModification: UriModification = UriModification.NONE
  ): List<Uri> {
    val baseUri: String = randomBaseJpegUri(orientation)
    return listOf(
        applyOverrideSettings(String.format(baseUri, ImageSize.XS.sizeSuffix), urlModification),
        applyOverrideSettings(String.format(baseUri, ImageSize.S.sizeSuffix), urlModification),
        applyOverrideSettings(String.format(baseUri, ImageSize.M.sizeSuffix), urlModification),
        applyOverrideSettings(String.format(baseUri, ImageSize.L.sizeSuffix), urlModification),
        applyOverrideSettings(String.format(baseUri, ImageSize.XL.sizeSuffix), urlModification),
        applyOverrideSettings(String.format(baseUri, ImageSize.XXL.sizeSuffix), urlModification))
  }

  fun createPJPEGSlow(): Uri = applyOverrideSettings(SAMPLE_URI_PJPEG_SLOW, UriModification.NONE)

  @JvmOverloads
  fun createPngUri(
      orientation: Orientation = Orientation.ANY,
      urlModification: UriModification = UriModification.NONE
  ): Uri {
    val baseUri =
        when (orientation) {
          Orientation.PORTRAIT -> chooseRandom(*SAMPLE_URIS_PORTRAIT_PNG)
          Orientation.LANDSCAPE -> chooseRandom(*SAMPLE_URIS_LANDSCAPE_PNG)
          Orientation.ANY -> chooseRandom(*SAMPLE_URIS_LANDSCAPE_PNG, *SAMPLE_URIS_PORTRAIT_PNG)
        }
    return applyOverrideSettings(baseUri, urlModification)
  }

  fun createWebpStaticUri(): Uri =
      applyOverrideSettings(SAMPLE_URI_WEBP_STATIC, UriModification.NONE)

  fun createWebpTranslucentUri(): Uri =
      applyOverrideSettings(SAMPLE_URI_WEBP_TRANSLUCENT, UriModification.NONE)

  fun createWebpAnimatedUri(): Uri =
      applyOverrideSettings(SAMPLE_URI_WEBP_ANIMATED, UriModification.NONE)

  fun createGifUri(imageSize: ImageSize = ImageSize.M): Uri {
    return applyOverrideSettings(
        String.format(SAMPLE_URI_GIF_PATTERN, imageSize.sizeSuffix), UriModification.NONE)
  }

  fun createGifUriWithPause(imageSize: ImageSize): Uri {
    return applyOverrideSettings(
        String.format(SAMPLE_URI_GIF_WITH_PAUSE_PATTERN, imageSize.sizeSuffix),
        UriModification.NONE)
  }

  fun createKeyframesUri() = applyOverrideSettings(SAMPLE_URI_KEYFRAMES, UriModification.NONE)

  fun createSvgUri() = applyOverrideSettings(SAMPLE_URI_SVG, UriModification.NONE)

  fun createNinePatchUri() = applyOverrideSettings(SAMPLE_URI_NINE_PATCH, UriModification.NONE)

  fun getRandomSampleUris(imageSize: ImageSize, numImages: Int): List<Uri> {
    val uriFormat: String =
        when (imageSize) {
          ImageSize.S -> RANDOM_URI_PATTERN_S
          ImageSize.M -> RANDOM_URI_PATTERN_M
          ImageSize.XS,
          ImageSize.L,
          ImageSize.XL,
          ImageSize.XXL ->
              throw IllegalArgumentException(
                  "Don't have random sample URIs for image size: $imageSize")
        }

    val random = Random(0) // fix seed for reproducible order
    val data =
        List<Uri>(numImages) {
          val imageId = random.nextInt(RANDOM_URI_MAX_IMAGE_ID)
          Uri.parse(String.format(uriFormat, imageId))
        }
    return data
  }

  fun getMediaStoreUris(context: Context): List<Uri> {
    val uris = mutableListOf<Uri>()
    context.contentResolver
        .query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            arrayOf(MediaStore.Images.Media._ID),
            null,
            null,
            null)
        ?.use {
          val dataIndex = it.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
          while (it.moveToNext()) {
            uris.add(
                ContentUris.withAppendedId(
                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI, it.getLong(dataIndex)))
          }
        }
    return uris
  }

  private fun applyOverrideSettings(uriString: String, urlModification: UriModification): Uri {
    var uriString = uriString
    var urlModification = urlModification
    if (isShouldBreakCacheByDefault) {
      urlModification = UriModification.CACHE_BREAKER
    }

    val overrideUriString = uriOverride
    if (overrideUriString != null) {
      uriString = overrideUriString
    }

    var result = Uri.parse(uriString)
    if (UriModification.CACHE_BREAKER == urlModification) {
      result =
          result
              .buildUpon()
              .appendQueryParameter("cache_breaker", UUID.randomUUID().toString())
              .build()
    }
    return result
  }

  private fun randomBaseJpegUri(orientation: Orientation = Orientation.ANY): String {
    return when (orientation) {
      Orientation.PORTRAIT -> chooseRandom(*SAMPLE_URIS_PORTRAIT)
      Orientation.LANDSCAPE -> chooseRandom(*SAMPLE_URIS_LANDSCAPE)
      Orientation.ANY -> chooseRandom(*SAMPLE_URIS_LANDSCAPE, *SAMPLE_URIS_PORTRAIT)
    }
  }

  /** @return a random element from a given set of data (uniform distribution) */
  private fun <T> chooseRandom(vararg data: T): T = data.random()

  companion object {

    private const val PREF_KEY_CACHE_BREAKING_BY_DEFAULT = "uri_cache_breaking"
    private const val PREF_KEY_URI_OVERRIDE = "uri_override"

    private const val RANDOM_URI_MAX_IMAGE_ID = 1000
    private const val RANDOM_URI_PATTERN_S = "https://picsum.photos/400/400?random=%d"
    private const val RANDOM_URI_PATTERN_M = "https://picsum.photos/800/800?random=%d"

    private val SAMPLE_URIS_LANDSCAPE =
        arrayOf(
            "https://frescolib.org/static/sample-images/animal_a_%s.jpg",
            "https://frescolib.org/static/sample-images/animal_b_%s.jpg",
            "https://frescolib.org/static/sample-images/animal_c_%s.jpg",
            "https://frescolib.org/static/sample-images/animal_e_%s.jpg",
            "https://frescolib.org/static/sample-images/animal_f_%s.jpg",
            "https://frescolib.org/static/sample-images/animal_g_%s.jpg")

    private val SAMPLE_URIS_PORTRAIT =
        arrayOf("https://frescolib.org/static/sample-images/animal_d_%s.jpg")

    private val SAMPLE_URI_PJPEG_SLOW = "http://pooyak.com/p/progjpeg/jpegload.cgi?o=1"

    private val SAMPLE_URIS_LANDSCAPE_PNG =
        arrayOf(
            "https://frescolib.org/static/sample-images/animal_a.png",
            "https://frescolib.org/static/sample-images/animal_b.png",
            "https://frescolib.org/static/sample-images/animal_c.png",
            "https://frescolib.org/static/sample-images/animal_e.png",
            "https://frescolib.org/static/sample-images/animal_f.png",
            "https://frescolib.org/static/sample-images/animal_g.png")

    private val SAMPLE_URIS_PORTRAIT_PNG =
        arrayOf("https://frescolib.org/static/sample-images/animal_d.png")

    private val NON_EXISTING_URI = "https://frescolib.org/static/sample-images/does_not_exist.jpg"

    private val SAMPLE_URI_WEBP_STATIC = "https://www.gstatic.com/webp/gallery/2.webp"

    private val SAMPLE_URI_WEBP_TRANSLUCENT = "https://www.gstatic.com/webp/gallery3/5_webp_ll.webp"

    private val SAMPLE_URI_WEBP_ANIMATED = "https://www.gstatic.com/webp/animated/1.webp"

    private val SAMPLE_URI_GIF_PATTERN =
        "https://frescolib.org/static/sample-images/fresco_logo_anim_full_frames_%s.gif"

    private val SAMPLE_URI_GIF_WITH_PAUSE_PATTERN =
        "https://frescolib.org/static/sample-images/fresco_logo_anim_full_frames_with_pause_%s.gif"

    private val SAMPLE_URIS_GIFS =
        arrayOf(
            "https://media2.giphy.com/media/3oge84qhopFbFFkwec/giphy.gif",
            "https://media3.giphy.com/media/uegrGBitPHtKM/giphy.gif",
            "https://media0.giphy.com/media/SWd9mTHEMIxQ4/giphy.gif")

    private val SAMPLE_URI_KEYFRAMES =
        "https://frescolib.org/static/sample-images/animation.keyframes"

    private val SAMPLE_URI_SVG =
        "https://frescolib.org/static/sample-images/fresco_logo_half_transparent.svg"

    private val SAMPLE_URI_NINE_PATCH = "res:/" + R.drawable.ninepatch
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.misc

import android.util.Log
import com.facebook.imagepipeline.listener.RequestListener2
import com.facebook.imagepipeline.producers.ProducerContext

class LogcatRequestListener2(
    val logExtraMap: Boolean = true,
    val tag: String = "LogcatRequestListener2"
) : RequestListener2 {

  override fun onRequestStart(producerContext: ProducerContext) {
    Log.d(tag, "onRequestStart ${toString(producerContext)}")
  }

  override fun onRequestSuccess(producerContext: ProducerContext) {
    Log.d(tag, "onRequestSuccess ${toString(producerContext)}")
  }

  override fun onRequestFailure(producerContext: ProducerContext, throwable: Throwable?) {
    Log.d(tag, "onRequestFailure ${toString(producerContext)}")
  }

  override fun onRequestCancellation(producerContext: ProducerContext) {
    Log.d(tag, "onRequestCancellation ${toString(producerContext)}")
  }

  override fun onProducerFinishWithCancellation(
      producerContext: ProducerContext,
      producerName: String,
      extraMap: MutableMap<String, String>?
  ) {
    Log.d(
        tag,
        "onProducerFinishWithCancellation $producerContext, producerName=$producerName, extras=$extraMap")
  }

  override fun onProducerStart(producerContext: ProducerContext, producerName: String) {
    Log.d(tag, "onProducerStart ${toString(producerContext)}, producerName=$producerName")
  }

  override fun onProducerEvent(
      producerContext: ProducerContext,
      producerName: String,
      eventName: String
  ) {
    Log.d(
        tag,
        "onProducerEvent ${toString(producerContext)}, producerName=$producerName, event=$eventName")
  }

  override fun onProducerFinishWithSuccess(
      producerContext: ProducerContext,
      producerName: String,
      extraMap: MutableMap<String, String>?
  ) {
    Log.d(
        tag,
        "onProducerFinishWithSuccess ${toString(producerContext)}, producerName=$producerName, extras=$extraMap")
  }

  override fun onProducerFinishWithFailure(
      producerContext: ProducerContext,
      producerName: String?,
      t: Throwable?,
      extraMap: MutableMap<String, String>?
  ) {
    Log.d(
        tag,
        "onProducerFinishWithFailure ${toString(producerContext)}, producerName=$producerName, extras=$extraMap")
  }

  override fun onUltimateProducerReached(
      producerContext: ProducerContext,
      producerName: String,
      successful: Boolean
  ) {
    Log.d(
        tag,
        "onUltimateProducerReached ${toString(producerContext)}, producer=$producerName, successful=$successful")
  }

  override fun requiresExtraMap(producerContext: ProducerContext, producerName: String) =
      logExtraMap

  private fun toString(producerContext: ProducerContext) =
      producerContext.run { "id=$id, extras=${getExtras()}, request=$imageRequest" }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.permissions

import android.Manifest.permission.READ_EXTERNAL_STORAGE
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat

object StoragePermissionHelper {

  private const val PERMISSION_REQUEST_CODE = 42

  private var pendingAction: (Unit) -> Unit = {}

  /**
   * Execute a given action iff the storage permission is granted. Returns true if storage
   * permissions were requested, false otherwise
   */
  fun withStoragePermission(activity: Activity, action: (Unit) -> Unit = {}) {
    if (ContextCompat.checkSelfPermission(activity, READ_EXTERNAL_STORAGE) !=
        PackageManager.PERMISSION_GRANTED) {
      ActivityCompat.requestPermissions(
          activity, arrayOf(READ_EXTERNAL_STORAGE), PERMISSION_REQUEST_CODE)
      pendingAction = action
    } else {
      action(Unit)
    }
  }

  /**
   * Utility method to execute a pending action when the storage permission was requested using this
   * class.
   */
  fun onRequestPermissionsResult(
      context: Context,
      requestCode: Int,
      permissions: Array<out String>,
      grantResults: IntArray
  ) {
    if (requestCode != PERMISSION_REQUEST_CODE) {
      return
    }

    for ((i, permission) in permissions.withIndex()) {
      if (permission != READ_EXTERNAL_STORAGE) {
        continue
      }
      if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
        pendingAction(Unit)
        pendingAction = {}
      } else {
        Toast.makeText(
                context,
                "This sample app needs storage permissions to load images",
                Toast.LENGTH_LONG)
            .show()
      }
      return
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.vito

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.facebook.fresco.samples.showcase.BaseShowcaseFragment
import com.facebook.fresco.samples.showcase.R
import com.facebook.fresco.samples.showcase.common.SpinnerUtils.setupWithList
import com.facebook.fresco.samples.showcase.databinding.FragmentVitoImageOptionsConfigBinding
import com.facebook.fresco.samples.showcase.misc.DebugImageListener
import com.facebook.fresco.vito.litho.FrescoVitoImage2
import com.facebook.fresco.vito.litho.FrescoVitoTapToRetryImage
import com.facebook.fresco.vito.options.ImageOptions
import com.facebook.litho.ComponentContext
import com.facebook.litho.LithoView

/** Experimental Fresco Vito fragment that allows to configure ImageOptions via a simple UI. */
class FrescoVitoLithoImageOptionsConfigFragment : BaseShowcaseFragment() {

  private var _binding: FragmentVitoImageOptionsConfigBinding? = null
  private val binding
    get() = _binding!!

  private val container
    get() = binding.container

  private val spinnerRounding
    get() = binding.spinnerRounding

  private val spinnerBorder
    get() = binding.spinnerBorder

  private val spinnerScaleType
    get() = binding.spinnerScaleType

  private val spinnerImageSource
    get() = binding.spinnerImageSource

  private val spinnerImageFormat
    get() = binding.spinnerImageFormat

  private val spinnerColorFilter
    get() = binding.spinnerColorFilter

  private val spinnerPlaceholder
    get() = binding.spinnerPlaceholder

  private val spinnerError
    get() = binding.spinnerError

  private val spinnerOverlay
    get() = binding.spinnerOverlay

  private val spinnerFading
    get() = binding.spinnerFading

  private val spinnerProgress
    get() = binding.spinnerProgress

  private val spinnerPostprocessor
    get() = binding.spinnerPostprocessor

  private val spinnerRotation
    get() = binding.spinnerRotation

  private val spinnerResize
    get() = binding.spinnerResize

  private val spinnerCustomDrawableFactory
    get() = binding.spinnerCustomDrawableFactory

  private val switchAutoPlayAnimations
    get() = binding.switchAutoPlayAnimations

  private val imageListener = DebugImageListener()
  private val imageOptionsBuilder = ImageOptions.create().placeholderApplyRoundingOptions(true)
  private val imageSourceProvider = ImageSourceConfigurator(sampleUris())

  private var componentContext: ComponentContext? = null
  private var lithoView: LithoView? = null
  private var useTapToRetry = false

  override fun onCreateView(
      inflater: LayoutInflater,
      container: ViewGroup?,
      savedInstanceState: Bundle?
  ): View {
    _binding = FragmentVitoImageOptionsConfigBinding.inflate(inflater, container, false)
    return binding.root
  }

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    componentContext = ComponentContext(context)

    lithoView = LithoView.create(componentContext, createImage(imageOptionsBuilder.build()))
    container.addView(lithoView)

    container.setOnClickListener { refresh() }

    spinnerRounding.setupWithList(VitoSpinners.roundingOptions) {
      refresh(imageOptionsBuilder.round(it))
    }
    spinnerBorder.setupWithList(VitoSpinners.borderOptions) {
      refresh(imageOptionsBuilder.borders(it))
    }
    spinnerScaleType.setupWithList(VitoSpinners.scaleTypes) {
      refresh(imageOptionsBuilder.scale(it.first).focusPoint(it.second))
    }
    spinnerImageSource.setupWithList(imageSourceProvider.imageSources) {
      it()
      refresh()
    }
    spinnerImageFormat.setupWithList(imageSourceProvider.imageFormatUpdater) {
      it()
      refresh()
    }
    spinnerColorFilter.setupWithList(VitoSpinners.colorFilters) {
      refresh(imageOptionsBuilder.colorFilter(it))
    }
    spinnerPlaceholder.setupWithList(VitoSpinners.placeholderOptions) {
      refresh(it(imageOptionsBuilder))
    }
    spinnerError.setupWithList(VitoSpinners.errorOptions) { refresh(it(imageOptionsBuilder)) }
    spinnerOverlay.setupWithList(VitoSpinners.overlayOptions) { refresh(it(imageOptionsBuilder)) }
    spinnerFading.setupWithList(VitoSpinners.fadingOptions) {
      refresh(imageOptionsBuilder.fadeDurationMs(it))
    }
    spinnerProgress.setupWithList(VitoSpinners.progressOptions) {
      refresh(it(requireContext(), imageOptionsBuilder))
    }
    spinnerPostprocessor.setupWithList(VitoSpinners.postprocessorOptions) {
      refresh(it(imageOptionsBuilder))
    }
    spinnerRotation.setupWithList(VitoSpinners.rotationOptions) {
      refresh(imageOptionsBuilder.rotate(it))
    }
    spinnerResize.setupWithList(VitoSpinners.resizeOptions) { refresh(it(imageOptionsBuilder)) }
    spinnerCustomDrawableFactory.setupWithList(VitoSpinners.customDrawableFactoryOptions) {
      refresh(it(imageOptionsBuilder))
    }
    switchAutoPlayAnimations.setOnCheckedChangeListener { _, isChecked ->
      refresh(imageOptionsBuilder.autoPlay(isChecked))
    }
    imageOptionsBuilder.autoPlay(switchAutoPlayAnimations.isChecked)
  }

  private fun refresh(builder: ImageOptions.Builder = imageOptionsBuilder) {
    lithoView?.setComponentAsync(createImage(builder.build()))
  }

  private fun createImage(imageOptions: ImageOptions) =
      if (!useTapToRetry) {
        FrescoVitoImage2.create(componentContext)
            .imageSource(imageSourceProvider.imageSource)
            .imageOptions(imageOptions)
            .imageListener(imageListener)
            .build()
      } else {
        FrescoVitoTapToRetryImage.create(componentContext)
            .imageSource(imageSourceProvider.imageSource)
            .imageOptions(imageOptions)
            .retryImageRes(R.drawable.ic_retry_black_48dp)
            .maxTapCount(5)
            .imageListener(imageListener)
            .build()
      }

  override fun onDestroyView() {
    super.onDestroyView()
    _binding = null
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.vito

import com.facebook.fresco.samples.showcase.LithoSample
import com.facebook.fresco.samples.showcase.R
import com.facebook.fresco.samples.showcase.misc.DebugImageListener
import com.facebook.fresco.samples.showcase.misc.ImageUriProvider
import com.facebook.fresco.vito.litho.FrescoVitoImage2
import com.facebook.fresco.vito.options.ImageOptions
import com.facebook.fresco.vito.options.RoundingOptions
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext

/** Simple experimental Fresco Vito fragment that just displays an image. */
object FrescoVitoLithoListenerExample : LithoSample {

  private val IMAGE_OPTIONS =
      ImageOptions.create()
          .placeholderRes(R.drawable.logo)
          .round(RoundingOptions.asCircle())
          .fadeDurationMs(3000)
          .build()

  private val listener = DebugImageListener()

  override fun createLithoComponent(
      c: ComponentContext,
      uris: ImageUriProvider,
      callerContext: Any
  ): Component {
    val uri = uris.createSampleUri()
    return FrescoVitoImage2.create(c)
        .uri(uri)
        .imageListener(listener)
        .imageOptions(IMAGE_OPTIONS)
        .build()
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.vito

import com.facebook.fresco.samples.showcase.LithoSample
import com.facebook.fresco.samples.showcase.R
import com.facebook.fresco.samples.showcase.misc.ImageUriProvider
import com.facebook.fresco.vito.litho.FrescoVitoImage2
import com.facebook.fresco.vito.options.ImageOptions
import com.facebook.fresco.vito.options.RoundingOptions
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext

/** Simple experimental Fresco Vito fragment that just displays an image. */
object FrescoVitoLithoSimpleExample : LithoSample {

  private val IMAGE_OPTIONS =
      ImageOptions.create()
          .placeholderRes(R.drawable.logo)
          .round(RoundingOptions.asCircle())
          .fadeDurationMs(3000)
          .build()

  override fun createLithoComponent(
      c: ComponentContext,
      uris: ImageUriProvider,
      callerContext: Any
  ): Component {
    val uri = uris.createSampleUri()
    return FrescoVitoImage2.create(c).uri(uri).imageOptions(IMAGE_OPTIONS).build()
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.vito

import com.facebook.common.util.UriUtil
import com.facebook.fresco.samples.showcase.R
import com.facebook.fresco.samples.showcase.imageformat.keyframes.KeyframesDecoderExample
import com.facebook.fresco.samples.showcase.misc.ImageUriProvider
import com.facebook.fresco.vito.source.*
import com.facebook.imageformat.DefaultImageFormats
import com.facebook.imageformat.ImageFormat

data class ImageSourceConfigurator(
    private val imageUriProvider: ImageUriProvider,
    var imageSource: ImageSource? = null,
    private var activeImageSourceProvider: () -> Unit = {},
    private var currentImageFormat: ImageFormat = DefaultImageFormats.JPEG
) {

  private val imageFormats =
      listOf(
          "JPEG" to DefaultImageFormats.JPEG,
          "PNG" to DefaultImageFormats.PNG,
          "Animated GIF" to DefaultImageFormats.GIF,
          "WebP simple" to DefaultImageFormats.WEBP_SIMPLE,
          "WebP with alpha" to DefaultImageFormats.WEBP_EXTENDED_WITH_ALPHA,
          "Animated WebP" to DefaultImageFormats.WEBP_ANIMATED,
          "Keyframes" to KeyframesDecoderExample.IMAGE_FORMAT_KEYFRAMES)

  val imageFormatUpdater =
      Pair(
          imageFormats.map {
            Pair(
                it.first,
                {
                  currentImageFormat = it.second
                  activeImageSourceProvider()
                })
          },
          "Image format")

  val imageSources =
      Pair(
          listOf(
              "Single URI" to
                  {
                    set { ImageSourceProvider.forUri(imageUriProvider.create(currentImageFormat)) }
                  },
              "Single URI String" to
                  {
                    set {
                      ImageSourceProvider.forUri(
                          imageUriProvider.create(currentImageFormat)?.toString())
                    }
                  },
              "Increasing quality" to
                  {
                    set {
                      ImageSourceProvider.increasingQuality(
                          imageUriProvider.create(currentImageFormat)!!, // TODO: low res
                          imageUriProvider.create(currentImageFormat)!!)
                    }
                  },
              "First available" to
                  {
                    set {
                      ImageSourceProvider.firstAvailable(
                          ImageSourceProvider.forUri(imageUriProvider.create(currentImageFormat)),
                          ImageSourceProvider.forUri(imageUriProvider.create(currentImageFormat)))
                    }
                  },
              "Empty Image Source" to { set { ImageSourceProvider.emptySource() } },
              "Non-existing URI" to
                  {
                    set { ImageSourceProvider.forUri(imageUriProvider.nonExistingUri) }
                  },
              "null" to { set { null } },
              "Increasing quality (low res error)" to
                  {
                    set {
                      ImageSourceProvider.increasingQuality(
                          ImageSourceProvider.forUri(imageUriProvider.nonExistingUri),
                          ImageSourceProvider.forUri(imageUriProvider.create(currentImageFormat)))
                    }
                  },
              "First available (all error)" to
                  {
                    set {
                      ImageSourceProvider.firstAvailable(
                          ImageSourceProvider.forUri(imageUriProvider.nonExistingUri),
                          ImageSourceProvider.forUri(imageUriProvider.nonExistingUri))
                    }
                  },
              "Local icon" to
                  {
                    set {
                      ImageSourceProvider.forUri(UriUtil.getUriForResourceId(R.drawable.ic_done))
                    }
                  }),
          "Image Source")

  private fun set(create: () -> ImageSource?) {
    activeImageSourceProvider = { this.imageSource = create() }
    activeImageSourceProvider()
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.vito

import com.facebook.fresco.samples.showcase.LithoSample
import com.facebook.fresco.samples.showcase.R
import com.facebook.fresco.samples.showcase.misc.ImageUriProvider
import com.facebook.fresco.vito.litho.slideshow.FrescoVitoSlideshowComponent
import com.facebook.fresco.vito.options.ImageOptions
import com.facebook.fresco.vito.options.RoundingOptions
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext

class LithoSlideshowSample : LithoSample {
  private val imageOptions =
      ImageOptions.create()
          .placeholderRes(R.drawable.logo)
          .round(RoundingOptions.asCircle())
          .fadeDurationMs(0)
          .build()

  override fun createLithoComponent(
      c: ComponentContext,
      uris: ImageUriProvider,
      callerContext: Any
  ): Component {
    return FrescoVitoSlideshowComponent.create(c)
        .fadeTransitionMs(1000)
        .photoTransitionMs(4000)
        .uris(uris.getRandomSampleUris(ImageUriProvider.ImageSize.M, 5))
        .imageOptions(imageOptions)
        .callerContext(callerContext)
        .build()
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.vito

import android.os.Bundle
import android.text.SpannableStringBuilder
import android.view.View
import android.widget.TextView
import com.facebook.fresco.samples.showcase.BaseShowcaseKotlinFragment
import com.facebook.fresco.samples.showcase.R
import com.facebook.fresco.samples.showcase.misc.ImageUriProvider
import com.facebook.fresco.vito.options.ImageOptions
import com.facebook.fresco.vito.options.RoundingOptions
import com.facebook.fresco.vito.source.ImageSourceProvider
import com.facebook.fresco.vito.textspan.VitoSpan
import com.facebook.fresco.vito.textspan.VitoSpanLoader
import com.facebook.fresco.vito.textspan.VitoSpanLoader.setImageSpan

class VitoSpanFragment : BaseShowcaseKotlinFragment(R.layout.fragment_vito_text_span) {

  private val imageOptions =
      ImageOptions.create()
          .round(RoundingOptions.asCircle())
          .placeholderRes(R.drawable.logo)
          .autoPlay(true)
          .build()

  private var imageSpan: VitoSpan? = null

  override fun onViewCreated(container: View, savedInstanceState: Bundle?) {
    val uri = sampleUris().createGifUri(ImageUriProvider.ImageSize.S)
    val textView = container.findViewById<TextView>(R.id.text_view)
    val span: VitoSpan = VitoSpanLoader.createSpan(resources)
    val text = "Text with [] inline image"
    textView.text =
        SpannableStringBuilder(text).apply {
          setImageSpan(span, text.indexOf('['), text.indexOf(']'), 100, 100, textView)
        }

    VitoSpanLoader.show(ImageSourceProvider.forUri(uri), imageOptions, null, null, null, span)
    imageSpan = span
  }

  override fun onDestroyView() {
    super.onDestroyView()
    VitoSpanLoader.release(imageSpan)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.vito

import android.content.Context
import android.content.res.Resources
import android.graphics.Color
import android.graphics.PointF
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.InsetDrawable
import androidx.core.content.ContextCompat
import com.facebook.drawee.drawable.AutoRotateDrawable
import com.facebook.drawee.drawable.ProgressBarDrawable
import com.facebook.drawee.drawable.RoundedCornersDrawable
import com.facebook.drawee.drawable.ScalingUtils
import com.facebook.fresco.samples.showcase.R
import com.facebook.fresco.samples.showcase.common.CustomScaleTypes
import com.facebook.fresco.samples.showcase.postprocessor.CachedWatermarkPostprocessor
import com.facebook.fresco.samples.showcase.postprocessor.FasterGreyScalePostprocessor
import com.facebook.fresco.samples.showcase.postprocessor.ScalingBlurPostprocessor
import com.facebook.fresco.samples.showcase.postprocessor.SlowGreyScalePostprocessor
import com.facebook.fresco.samples.showcase.postprocessor.WatermarkPostprocessor
import com.facebook.fresco.vito.options.BorderOptions
import com.facebook.fresco.vito.options.ImageOptions
import com.facebook.fresco.vito.options.ImageOptionsDrawableFactory
import com.facebook.fresco.vito.options.RoundingOptions
import com.facebook.imagepipeline.common.ResizeOptions
import com.facebook.imagepipeline.common.RotationOptions
import com.facebook.imagepipeline.image.CloseableImage
import com.facebook.imagepipeline.postprocessors.IterativeBoxBlurPostProcessor
import com.facebook.imagepipeline.postprocessors.RoundAsCirclePostprocessor
import com.facebook.imagepipeline.postprocessors.RoundPostprocessor

object VitoSpinners {

  val roundingOptions =
      Pair(
          listOf(
              "none" to null,
              "as circle" to RoundingOptions.asCircle(),
              "corner radius" to RoundingOptions.forCornerRadiusPx(20f),
              "different radii" to RoundingOptions.forCornerRadii(0f, 20f, 30f, 40f)),
          "Rounding")

  val borderOptions =
      Pair(
          listOf(
              "none" to null,
              "red border" to BorderOptions.create(Color.RED, 20f),
              "blue border" to BorderOptions.create(Color.BLUE, 40f),
              "border with no padding" to BorderOptions.create(Color.GREEN, 20f, 0f),
              "border with small padding" to BorderOptions.create(Color.GREEN, 20f, 10f),
              "border with same padding" to BorderOptions.create(Color.GREEN, 20f, 20f),
              "border with more padding" to BorderOptions.create(Color.GREEN, 20f, 40f)),
          "Border")

  val scaleTypes =
      Pair(
          listOf(
              "center" to Pair(ScalingUtils.ScaleType.CENTER, null),
              "center_crop" to Pair(ScalingUtils.ScaleType.CENTER_CROP, null),
              "center_inside" to Pair(ScalingUtils.ScaleType.CENTER_INSIDE, null),
              "fit_center" to Pair(ScalingUtils.ScaleType.FIT_CENTER, null),
              "fit_start" to Pair(ScalingUtils.ScaleType.FIT_START, null),
              "fit_end" to Pair(ScalingUtils.ScaleType.FIT_END, null),
              "fit_xy" to Pair(ScalingUtils.ScaleType.FIT_XY, null),
              "focus_crop (0, 0)" to Pair(ScalingUtils.ScaleType.FOCUS_CROP, PointF(0f, 0f)),
              "focus_crop (1, 0.5)" to Pair(ScalingUtils.ScaleType.FOCUS_CROP, PointF(1f, 0.5f)),
              "custom: fit_x" to Pair(CustomScaleTypes.FIT_X, null),
              "custom: fit_y" to Pair(CustomScaleTypes.FIT_Y, null),
              "null" to Pair(null, null)),
          "Scale type")

  val colorFilters =
      Pair(
          listOf(
              "none" to PorterDuffColorFilter(Color.TRANSPARENT, PorterDuff.Mode.ADD),
              "red" to PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DARKEN),
              "green" to PorterDuffColorFilter(Color.GREEN, PorterDuff.Mode.DARKEN),
              "blue" to PorterDuffColorFilter(Color.BLUE, PorterDuff.Mode.DARKEN),
              "tint: black" to PorterDuffColorFilter(Color.BLACK, PorterDuff.Mode.SRC_ATOP),
              "tint: white" to PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP),
              "tint: gray" to PorterDuffColorFilter(Color.GRAY, PorterDuff.Mode.SRC_ATOP),
              "tint: red" to PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_ATOP),
              "tint: green" to PorterDuffColorFilter(Color.GREEN, PorterDuff.Mode.SRC_ATOP),
              "tint: blue" to PorterDuffColorFilter(Color.BLUE, PorterDuff.Mode.SRC_ATOP)),
          "Color filter")

  val fadingOptions =
      Pair(
          listOf(
              "none" to 0,
              "50 ms" to 50,
              "100 ms" to 100,
              "200 ms" to 200,
              "500 ms" to 500,
              "1000 ms" to 1000,
              "2000 ms" to 2000,
              "5000 ms" to 5000,
              "10000 ms" to 10000),
          "Fading")

  val placeholderOptions =
      Pair(
          listOf(
              "none" to { builder: ImageOptions.Builder -> builder.placeholder(null) },
              "image" to
                  { builder: ImageOptions.Builder ->
                    builder.placeholderRes(R.drawable.logo, ScalingUtils.ScaleType.FIT_CENTER)
                  },
              "block color" to
                  { builder: ImageOptions.Builder ->
                    builder.placeholder(ColorDrawable(Color.RED))
                  },
              "color res" to
                  { builder: ImageOptions.Builder ->
                    builder.placeholderRes(R.color.placeholder_color)
                  }),
          "Placeholder")
  val errorOptions =
      Pair(
          listOf(
              "none" to { builder: ImageOptions.Builder -> builder.errorRes(0) },
              "image" to { builder: ImageOptions.Builder -> builder.errorRes(R.drawable.logo) },
              "color res" to
                  { builder: ImageOptions.Builder ->
                    builder.errorRes(R.color.error_color)
                  }),
          "Error")

  val progressOptions =
      Pair(
          listOf(
              "none" to { _, builder: ImageOptions.Builder -> builder.progress(null) },
              "image" to
                  { _, builder: ImageOptions.Builder ->
                    builder.progressRes(R.drawable.logo)
                  },
              "color drawable" to
                  { _, builder: ImageOptions.Builder ->
                    builder.progress(ColorDrawable(Color.YELLOW))
                  },
              "color res" to
                  { _, builder: ImageOptions.Builder ->
                    builder.progressRes(R.color.progress_bar_color)
                  },
              "indeterminate" to
                  { _, builder: ImageOptions.Builder ->
                    builder.progress(
                        AutoRotateDrawable(InsetDrawable(ColorDrawable(Color.BLUE), 50), 1000))
                  },
              "progress bar" to
                  { c: Context, builder: ImageOptions.Builder ->
                    builder.progress(
                        ProgressBarDrawable().apply {
                          color = ContextCompat.getColor(c, R.color.progress_bar_color)
                          backgroundColor =
                              ContextCompat.getColor(c, R.color.progress_bar_background)
                        })
                  }),
          "Progress")

  val overlayOptions =
      Pair(
          listOf(
              "none" to { builder: ImageOptions.Builder -> builder.overlayRes(0) },
              "logo" to { builder: ImageOptions.Builder -> builder.overlayRes(R.drawable.logo) },
              "orange color res" to
                  { builder: ImageOptions.Builder ->
                    builder.overlayRes(R.color.overlay_color)
                  },
              "green color drawable" to
                  { builder: ImageOptions.Builder ->
                    builder.overlay(ColorDrawable(0x8800ff00.toInt()))
                  },
              "9-patch" to
                  { builder: ImageOptions.Builder ->
                    builder.overlayRes(R.drawable.ninepatch)
                  },
              "color overlay circular rounding" to
                  { builder: ImageOptions.Builder ->
                    builder.overlay(
                        RoundedCornersDrawable(ColorDrawable(Color.TRANSPARENT)).apply {
                          isCircle = true
                          overlayColor = Color.BLUE
                        })
                  },
              "color overlay rounded corners" to
                  { builder: ImageOptions.Builder ->
                    builder.overlay(
                        RoundedCornersDrawable(ColorDrawable(Color.TRANSPARENT)).apply {
                          setRadius(50f)
                          overlayColor = Color.GREEN
                        })
                  }),
          "Overlay")

  val postprocessorOptions =
      Pair(
          listOf(
              "none" to { builder: ImageOptions.Builder -> builder.postprocess(null) },
              "Grey Scale Post-Processor(Slow)" to
                  { builder: ImageOptions.Builder ->
                    builder.postprocess(SlowGreyScalePostprocessor())
                  },
              "Grey Scale Post-Processor" to
                  { builder: ImageOptions.Builder ->
                    builder.postprocess(FasterGreyScalePostprocessor())
                  },
              "Watermark Post-Processor" to
                  { builder: ImageOptions.Builder ->
                    builder.postprocess(WatermarkPostprocessor(10, "FRESCO"))
                  },
              "Watermark Post-Processor(Cached)" to
                  { builder: ImageOptions.Builder ->
                    builder.postprocess(CachedWatermarkPostprocessor(10, "FRESCO"))
                  },
              "Native Blur Post-Processor" to
                  { builder: ImageOptions.Builder ->
                    builder.postprocess(IterativeBoxBlurPostProcessor(25, 3))
                  },
              "Scaling Blur Post-Processor" to
                  { builder: ImageOptions.Builder ->
                    builder.postprocess(ScalingBlurPostprocessor(25, 3, 4))
                  },
              "Native Round As Circle Postprocessor" to
                  { builder: ImageOptions.Builder ->
                    builder.postprocess(RoundAsCirclePostprocessor(false))
                  },
              "Antialiased As Circle Postprocessor" to
                  { builder: ImageOptions.Builder ->
                    builder.postprocess(RoundAsCirclePostprocessor(true))
                  },
              "Round As Circle Postprocessor" to
                  { builder: ImageOptions.Builder ->
                    builder.postprocess(RoundPostprocessor())
                  }),
          "Postprocessor")

  val rotationOptions =
      Pair(
          listOf(
              "disabled" to RotationOptions.disableRotation(),
              "auto rotate" to RotationOptions.autoRotate(),
              "auto rotate at render time" to RotationOptions.autoRotateAtRenderTime(),
              "no rotation" to RotationOptions.forceRotation(RotationOptions.NO_ROTATION),
              "rotate 90" to RotationOptions.forceRotation(RotationOptions.ROTATE_90),
              "rotate 180" to RotationOptions.forceRotation(RotationOptions.ROTATE_180),
              "rotate 270" to RotationOptions.forceRotation(RotationOptions.ROTATE_270)),
          "Rotation")

  val customDrawableFactoryOptions =
      Pair(
          listOf(
              "none" to { builder: ImageOptions.Builder -> builder.customDrawableFactory(null) },
              "blue color" to
                  { builder: ImageOptions.Builder ->
                    builder.customDrawableFactory(
                        object : ImageOptionsDrawableFactory {
                          override fun createDrawable(
                              resources: Resources,
                              closeableImage: CloseableImage,
                              imageOptions: ImageOptions
                          ): Drawable = ColorDrawable(Color.BLUE)
                        })
                  }),
          "Custom Drawable Factory")

  val resizeOptions =
      Pair(
          listOf(
              "none" to { builder: ImageOptions.Builder -> builder.resize(null) },
              "100 x 100" to
                  { builder: ImageOptions.Builder ->
                    builder.resize(ResizeOptions.forSquareSize(100))
                  },
              "500 x 500" to
                  { builder: ImageOptions.Builder ->
                    builder.resize(ResizeOptions.forSquareSize(500))
                  },
              "1000 x 1000" to
                  { builder: ImageOptions.Builder ->
                    builder.resize(ResizeOptions.forSquareSize(1000))
                  },
              "200 x 100" to
                  { builder: ImageOptions.Builder ->
                    builder.resize(ResizeOptions.forDimensions(200, 100))
                  },
              "500 x 100" to
                  { builder: ImageOptions.Builder ->
                    builder.resize(ResizeOptions.forDimensions(500, 100))
                  },
              "500 x 400" to
                  { builder: ImageOptions.Builder ->
                    builder.resize(ResizeOptions.forDimensions(500, 400))
                  },
              "800 x 600" to
                  { builder: ImageOptions.Builder ->
                    builder.resize(ResizeOptions.forDimensions(800, 600))
                  },
              "600 x 800 " to
                  { builder: ImageOptions.Builder ->
                    builder.resize(ResizeOptions.forDimensions(600, 800))
                  },
              "960 x 720" to
                  { builder: ImageOptions.Builder ->
                    builder.resize(ResizeOptions.forDimensions(960, 720))
                  },
              "1280 x 720" to
                  { builder: ImageOptions.Builder ->
                    builder.resize(ResizeOptions.forDimensions(1280, 720))
                  },
              "1280 x 800" to
                  { builder: ImageOptions.Builder ->
                    builder.resize(ResizeOptions.forDimensions(1280, 800))
                  },
              "1920 x 1080" to
                  { builder: ImageOptions.Builder ->
                    builder.resize(ResizeOptions.forDimensions(1920, 1080))
                  },
              "1080 x 1920" to
                  { builder: ImageOptions.Builder ->
                    builder.resize(ResizeOptions.forDimensions(1080, 1920))
                  },
              "3840 x 2160" to
                  { builder: ImageOptions.Builder ->
                    builder.resize(ResizeOptions.forDimensions(3840, 2160))
                  },
              "4096 x 2160" to
                  { builder: ImageOptions.Builder ->
                    builder.resize(ResizeOptions.forDimensions(4096, 2160))
                  },
              "2160 x 4096" to
                  { builder: ImageOptions.Builder ->
                    builder.resize(ResizeOptions.forDimensions(2160, 4096))
                  }),
          "Resize options")
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.vito

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.facebook.fresco.samples.showcase.BaseShowcaseFragment
import com.facebook.fresco.samples.showcase.R
import com.facebook.fresco.samples.showcase.databinding.FragmentVitoViewKtxBinding
import com.facebook.fresco.vito.ktx.ViewExtensions.show
import com.facebook.fresco.vito.options.ImageOptions
import com.facebook.fresco.vito.options.RoundingOptions

class VitoViewKtxFragment : BaseShowcaseFragment() {

  private var _binding: FragmentVitoViewKtxBinding? = null
  private val binding
    get() = _binding!!

  private val imageOptions =
      ImageOptions.create()
          .round(RoundingOptions.asCircle())
          .placeholderRes(R.color.placeholder_color)
          .build()

  override fun onCreateView(
      inflater: LayoutInflater,
      container: ViewGroup?,
      savedInstanceState: Bundle?
  ): View {
    _binding = FragmentVitoViewKtxBinding.inflate(inflater, container, false)
    return binding.root
  }

  override fun onViewCreated(container: View, savedInstanceState: Bundle?) {
    val uri = sampleUris().createSampleUri()
    binding.image.show(uri, imageOptions)
  }

  override fun onDestroyView() {
    super.onDestroyView()
    _binding = null
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.vito

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.fresco.samples.showcase.BaseShowcaseFragment
import com.facebook.fresco.samples.showcase.R
import com.facebook.fresco.samples.showcase.databinding.FragmentVitoViewPrefetchBinding
import com.facebook.fresco.samples.showcase.misc.ImageUriProvider
import com.facebook.fresco.vito.options.ImageOptions
import com.facebook.fresco.vito.options.RoundingOptions
import com.facebook.fresco.vito.provider.FrescoVitoProvider
import com.facebook.fresco.vito.view.VitoView

class VitoViewPrefetchFragment : BaseShowcaseFragment() {

  private var _binding: FragmentVitoViewPrefetchBinding? = null
  private val binding
    get() = _binding!!

  private val buttonPrefetchBitmap
    get() = binding.buttonPrefetchBitmap

  private val buttonPrefetchEncoded
    get() = binding.buttonPrefetchEncoded

  private val buttonPrefetchDisk
    get() = binding.buttonPrefetchDisk

  private val buttonToggleImages
    get() = binding.buttonToggleImages

  private val buttonClearCache
    get() = binding.buttonClearCache

  private val imageOptions =
      ImageOptions.create()
          .round(RoundingOptions.asCircle())
          .placeholderRes(R.color.placeholder_color)
          .build()

  private var imageVisible = false

  override fun onCreateView(
      inflater: LayoutInflater,
      container: ViewGroup?,
      savedInstanceState: Bundle?
  ): View {
    _binding = FragmentVitoViewPrefetchBinding.inflate(inflater, container, false)
    return binding.root
  }

  override fun onViewCreated(container: View, savedInstanceState: Bundle?) {
    val uri = sampleUris().createSampleUri(ImageUriProvider.ImageSize.M)
    val view = container.findViewById<View>(R.id.view)

    val prefetcher = FrescoVitoProvider.getPrefetcher()

    buttonPrefetchBitmap.setOnClickListener {
      prefetcher.prefetchToBitmapCache(uri, imageOptions, "prefetch_bitmap", "sample")
    }

    buttonPrefetchEncoded.setOnClickListener {
      prefetcher.prefetchToEncodedCache(uri, imageOptions, "prefetch_encoded", "sample")
    }

    buttonPrefetchDisk.setOnClickListener {
      prefetcher.prefetchToDiskCache(uri, imageOptions, "prefetch_disk", "sample")
    }

    buttonToggleImages.setOnClickListener {
      if (imageVisible) {
        VitoView.show(null, view)
      } else {
        VitoView.show(uri, imageOptions, view)
      }
      imageVisible = !imageVisible
    }

    buttonClearCache.setOnClickListener { Fresco.getImagePipeline().clearCaches() }
  }

  override fun onDestroyView() {
    super.onDestroyView()
    _binding = null
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.vito.ninepatch

import com.facebook.drawee.drawable.ScalingUtils
import com.facebook.fresco.samples.showcase.LithoSample
import com.facebook.fresco.samples.showcase.misc.ImageUriProvider
import com.facebook.fresco.vito.litho.FrescoVitoImage2
import com.facebook.fresco.vito.options.ImageOptions
import com.facebook.imagepipeline.common.ImageDecodeOptions
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext

class LithoNinePatchSample : LithoSample {

  override fun createLithoComponent(
      c: ComponentContext,
      uris: ImageUriProvider,
      callerContext: Any
  ): Component {
    val imageOptions =
        ImageOptions.create()
            .imageDecodeOptions(
                ImageDecodeOptions.newBuilder()
                    .setCustomImageDecoder(NinePatchExample.NinePatchDecoder())
                    .build())
            .customDrawableFactory(NinePatchExample.NinePatchDrawableFactory(c.resources))
            .scale(ScalingUtils.ScaleType.FIT_XY)
            .build()
    return FrescoVitoImage2.create(c)
        .uri(uris.createNinePatchUri())
        .imageOptions(imageOptions)
        .build()
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.vito.renderer

import android.graphics.Path
import android.graphics.Rect
import android.graphics.RectF
import android.os.Bundle
import android.view.View
import android.widget.LinearLayout
import androidx.core.content.ContextCompat
import com.facebook.fresco.samples.showcase.BaseShowcaseKotlinFragment
import com.facebook.fresco.samples.showcase.R
import com.facebook.fresco.samples.showcase.vito.VitoSpinners
import com.facebook.fresco.vito.renderer.BitmapImageDataModel
import com.facebook.fresco.vito.renderer.CircleShape
import com.facebook.fresco.vito.renderer.ColorIntImageDataModel
import com.facebook.fresco.vito.renderer.DrawableImageDataModel
import com.facebook.fresco.vito.renderer.PathShape
import com.facebook.fresco.vito.renderer.RectShape
import com.facebook.fresco.vito.renderer.RoundedRectShape
import java.lang.Math.min

class RendererColorFilterExampleFragment : BaseShowcaseKotlinFragment() {

  override fun onViewCreated(container: View, savedInstanceState: Bundle?) {
    val w = 100.dpToPx()
    val h = 100.dpToPx()
    val exampleRect = Rect(0, 0, w, h)
    val exampleRectF = RectF(exampleRect)

    val shapes =
        listOf(
            RectShape(RectF(exampleRect)),
            CircleShape(
                exampleRect.exactCenterX(),
                exampleRect.exactCenterY(),
                min(exampleRect.width(), exampleRect.height()) / 2f),
            RoundedRectShape(exampleRectF, 20f.dpToPx(), 80f.dpToPx()),
            PathShape(
                Path().apply {
                  addRoundRect(
                      exampleRectF,
                      floatArrayOf(
                          10f.dpToPx(),
                          20f.dpToPx(),
                          30f.dpToPx(),
                          40f.dpToPx(),
                          50f.dpToPx(),
                          60f.dpToPx(),
                          70f.dpToPx(),
                          80f.dpToPx()),
                      Path.Direction.CW)
                }))

    container.findViewById<LinearLayout>(R.id.list).apply {
      addText("Example View dimensions: $w x $h px")
      for (colorFilter in VitoSpinners.colorFilters.first) {
        addText("Color filter ${colorFilter.first}")
        for (shape in shapes) {
          addRow {
            addExample(
                w,
                h,
                ColorIntImageDataModel(ContextCompat.getColor(context, R.color.primary)),
                shape,
                null,
                colorFilter.second)
            addExample(
                w,
                h,
                DrawableImageDataModel(
                    ContextCompat.getDrawable(requireContext(), R.mipmap.ic_launcher)!!),
                shape,
                null,
                colorFilter.second)
            addExample(
                w,
                h,
                BitmapImageDataModel(createSampleBitmap(w, h)),
                shape,
                null,
                colorFilter.second)
          }
        }
      }
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.vito.renderer

import android.graphics.Canvas
import android.graphics.ColorFilter
import android.graphics.Matrix
import android.graphics.Paint
import android.graphics.PixelFormat
import android.graphics.drawable.Drawable
import com.facebook.fresco.vito.renderer.ImageDataModel
import com.facebook.fresco.vito.renderer.ImageRenderer
import com.facebook.fresco.vito.renderer.Shape

open class RendererExampleDrawable(
    private val imageDataModel: ImageDataModel,
    private val shape: Shape,
    private val transformationMatrix: Matrix? = null,
    private var imageColorFilter: ColorFilter? = null,
) : Drawable() {
  override fun draw(canvas: Canvas) {
    ImageRenderer.createImageDataModelRenderCommand(
        imageDataModel,
        shape,
        Paint(Paint.ANTI_ALIAS_FLAG).apply { colorFilter = imageColorFilter },
        transformationMatrix)(canvas)
  }

  override fun setAlpha(alpha: Int) = Unit

  override fun setColorFilter(colorFilter: ColorFilter?) {
    imageColorFilter = colorFilter
  }

  override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.vito.renderer

import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.ColorFilter
import android.graphics.Matrix
import android.view.ViewGroup
import android.widget.HorizontalScrollView
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import com.facebook.fresco.samples.showcase.common.dpToPx
import com.facebook.fresco.samples.showcase.misc.CheckerBoardDrawable
import com.facebook.fresco.vito.renderer.ImageDataModel
import com.facebook.fresco.vito.renderer.Shape

fun createSampleBitmap(w: Int, h: Int, blue: Int = 0) =
    Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888).apply {
      for (x in 0 until w) {
        for (y in 0 until h) {
          setPixel(x, y, Color.rgb(255 * x / w, 255 * y / h, blue))
        }
      }
    }

fun LinearLayout.createLinearLayout(
    isHorizontal: Boolean = false,
    block: LinearLayout.() -> Unit
): LinearLayout {
  return LinearLayout(context).apply {
    this.orientation = if (isHorizontal) LinearLayout.HORIZONTAL else LinearLayout.VERTICAL
    layoutParams =
        LinearLayout.LayoutParams(
            ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
    block()
  }
}

fun LinearLayout.addImageViewWithText(
    title: String,
    w: Int,
    h: Int,
    padding: Int = 0,
    block: ImageView.() -> Unit
): LinearLayout {
  val ll = createLinearLayout {
    addText(title)
    addImageView(w, h, padding).block()
  }
  addView(ll)
  return ll
}

fun LinearLayout.addImageView(w: Int, h: Int, padding: Int = 0): ImageView {
  val margin = 8.dpToPx(context)
  val view =
      ImageView(context).apply {
        layoutParams =
            LinearLayout.LayoutParams(w, h).apply {
              setMargins(margin, margin, margin, margin)
              setPadding(padding, padding, padding, padding)
              setBackgroundDrawable(CheckerBoardDrawable(resources))
            }
      }
  addView(view)
  return view
}

fun LinearLayout.addRow(
    text: String? = null,
    scrollHorizontally: Boolean = false,
    block: LinearLayout.() -> Unit
) {
  addText(text)
  addView(
      if (scrollHorizontally) {
        HorizontalScrollView(context).apply {
          layoutParams =
              LinearLayout.LayoutParams(
                  ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
          addView(createLinearLayout(true, block))
        }
      } else {
        createLinearLayout(true, block)
      })
}

fun LinearLayout.addExample(
    w: Int,
    h: Int,
    imageDataModel: ImageDataModel,
    shape: Shape,
    transformationMatrix: Matrix? = null,
    colorFilter: ColorFilter? = null,
) {
  addImageView(w, h)
      .setImageDrawable(
          RendererExampleDrawable(imageDataModel, shape, transformationMatrix, colorFilter))
}

fun LinearLayout.addText(text: String?) {
  val padding = 8.dpToPx(context)
  if (text != null) {
    addView(
        TextView(context).apply {
          this.text = text
          layoutParams =
              LinearLayout.LayoutParams(
                  ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
          setPadding(padding, padding, padding, padding)
        })
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.vito.renderer

import android.graphics.Canvas
import android.graphics.Color
import android.graphics.ColorFilter
import android.graphics.PixelFormat
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.view.View
import android.widget.LinearLayout
import androidx.core.content.ContextCompat
import com.facebook.fresco.samples.showcase.BaseShowcaseKotlinFragment
import com.facebook.fresco.samples.showcase.R
import com.facebook.fresco.vito.core.impl.ImageLayerDataModel
import com.facebook.fresco.vito.renderer.BitmapImageDataModel
import com.facebook.fresco.vito.renderer.ColorIntImageDataModel
import com.facebook.fresco.vito.renderer.DrawableImageDataModel

private const val FADE_DURATION = 1000

class RendererFadeExampleFragment : BaseShowcaseKotlinFragment() {

  class FadingDrawable(private val imageLayerDataModel: ImageLayerDataModel) : Drawable() {
    override fun draw(canvas: Canvas) {
      imageLayerDataModel.configure(bounds = bounds)
      imageLayerDataModel.draw(canvas)
    }

    override fun setAlpha(alpha: Int) = Unit

    override fun setColorFilter(colorFilter: ColorFilter?) = Unit

    override fun getOpacity(): Int = PixelFormat.TRANSPARENT
  }

  override fun onViewCreated(container: View, savedInstanceState: Bundle?) {
    super.onViewCreated(container, savedInstanceState)

    val w = 300.dpToPx()
    val h = 300.dpToPx()
    val bitmapImageDataModel =
        ImageLayerDataModel().also {
          it.configure(dataModel = BitmapImageDataModel(createSampleBitmap(w, h)))
        }

    val drawableImageDataModel =
        ImageLayerDataModel().also {
          it.configure(
              dataModel =
                  DrawableImageDataModel(
                      ContextCompat.getDrawable(requireContext(), R.drawable.logo)!!))
        }

    val colorImageDataModel =
        ImageLayerDataModel().also { it.configure(dataModel = ColorIntImageDataModel(Color.RED)) }

    container.findViewById<LinearLayout>(R.id.list).apply {
      addFadingExampleWithImageData(
          this, bitmapImageDataModel, "Bitmap: Click to Fade In&Out Bitmap", w, h)
      addFadingExampleWithImageData(
          this, drawableImageDataModel, "Drawable: Click to Fade In&Out Bitmap", w, h)
      addFadingExampleWithImageData(
          this, colorImageDataModel, "Color: Click to Fade In&Out Bitmap", w, h)
    }
  }

  private fun addFadingExampleWithImageData(
      linearLayout: LinearLayout,
      imageDataModel: ImageLayerDataModel,
      title: String,
      w: Int,
      h: Int
  ) {
    linearLayout
        .addImageViewWithText(title, w, h) {
          val drawable = FadingDrawable(imageDataModel)
          imageDataModel.invalidateLayerCallback = { drawable.invalidateSelf() }
          setImageDrawable(drawable)
        }
        .setOnClickListener {
          if (imageDataModel.getAlpha() == 0) {
            imageDataModel.fadeIn(FADE_DURATION)
          } else {
            imageDataModel.fadeOut(FADE_DURATION)
          }
        }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.vito.renderer

import android.graphics.Path
import android.graphics.Rect
import android.graphics.RectF
import android.os.Bundle
import android.view.View
import android.widget.LinearLayout
import androidx.core.content.ContextCompat
import com.facebook.fresco.samples.showcase.BaseShowcaseKotlinFragment
import com.facebook.fresco.samples.showcase.R
import com.facebook.fresco.vito.renderer.BitmapImageDataModel
import com.facebook.fresco.vito.renderer.CircleShape
import com.facebook.fresco.vito.renderer.ColorIntImageDataModel
import com.facebook.fresco.vito.renderer.DrawableImageDataModel
import com.facebook.fresco.vito.renderer.PathShape
import com.facebook.fresco.vito.renderer.RectShape
import com.facebook.fresco.vito.renderer.RoundedRectShape
import java.lang.Math.min

class RendererShapeExampleFragment : BaseShowcaseKotlinFragment() {

  override fun onViewCreated(container: View, savedInstanceState: Bundle?) {
    val w = 100.dpToPx()
    val h = 100.dpToPx()
    val exampleRect = Rect(0, 0, w, h)
    val exampleRectF = RectF(exampleRect)

    val shapes =
        listOf(
            RectShape(RectF(exampleRect)),
            CircleShape(
                exampleRect.exactCenterX(),
                exampleRect.exactCenterY(),
                min(exampleRect.width(), exampleRect.height()) / 2f),
            RoundedRectShape(exampleRectF, 40f.dpToPx(), 40f.dpToPx()),
            RoundedRectShape(exampleRectF, 20f.dpToPx(), 80f.dpToPx()),
            PathShape(
                Path().apply {
                  addRoundRect(
                      exampleRectF,
                      floatArrayOf(
                          10f.dpToPx(),
                          20f.dpToPx(),
                          30f.dpToPx(),
                          40f.dpToPx(),
                          50f.dpToPx(),
                          60f.dpToPx(),
                          70f.dpToPx(),
                          80f.dpToPx()),
                      Path.Direction.CW)
                }))

    container.findViewById<LinearLayout>(R.id.list).apply {
      addText("Example View dimensions: $w x $h px")
      for (shape in shapes) {
        addRow {
          addExample(
              w, h, ColorIntImageDataModel(ContextCompat.getColor(context, R.color.primary)), shape)
          addExample(
              w,
              h,
              DrawableImageDataModel(
                  ContextCompat.getDrawable(requireContext(), R.mipmap.ic_launcher)!!),
              shape)
          addExample(w, h, BitmapImageDataModel(createSampleBitmap(w, h)), shape)
        }
      }
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.fresco.samples.showcase.vito.renderer

import android.graphics.Canvas
import android.graphics.Color
import android.graphics.ColorFilter
import android.graphics.PixelFormat
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.view.View
import android.widget.LinearLayout
import androidx.core.content.ContextCompat
import com.facebook.fresco.samples.showcase.BaseShowcaseKotlinFragment
import com.facebook.fresco.samples.showcase.R
import com.facebook.fresco.samples.showcase.vito.VitoSpinners
import com.facebook.fresco.vito.core.impl.ImageLayerDataModel
import com.facebook.fresco.vito.core.impl.getCanvasTransformation
import com.facebook.fresco.vito.options.RoundingOptions
import com.facebook.fresco.vito.renderer.BitmapImageDataModel
import com.facebook.fresco.vito.renderer.ColorIntImageDataModel
import com.facebook.fresco.vito.renderer.DrawableImageDataModel
import com.facebook.fresco.vito.renderer.ImageDataModel

class VitoLayerExample : BaseShowcaseKotlinFragment() {

  class ExampleImageLayerDrawable(private val imageLayerDataModel: ImageLayerDataModel) :
      Drawable() {
    override fun draw(canvas: Canvas) {
      imageLayerDataModel.configure(bounds = bounds)
      imageLayerDataModel.draw(canvas)
    }

    override fun setAlpha(alpha: Int) = Unit

    override fun setColorFilter(colorFilter: ColorFilter?) = Unit

    override fun getOpacity(): Int = PixelFormat.TRANSPARENT
  }

  override fun onViewCreated(container: View, savedInstanceState: Bundle?) {
    val w = 100.dpToPx()
    val h = 100.dpToPx()
    container.findViewById<LinearLayout>(R.id.list).apply {
      addText("Example View dimensions: $w x $h px")
      addScaleTypeExamples(
          w,
          h,
          "Scale Types, bitmap 160x90",
          BitmapImageDataModel(createSampleBitmap(160, 90, 127)))
      addScaleTypeExamples(
          w,
          h,
          "Scale Types, bitmap 320x90",
          BitmapImageDataModel(createSampleBitmap(320, 90, 127)))
      addScaleTypeExamples(
          w,
          h,
          "Scale Types, bitmap 90x160",
          BitmapImageDataModel(createSampleBitmap(90, 160, 127)))
      addScaleTypeExamples(
          w,
          h,
          "Scale Types, bitmap 90x320",
          BitmapImageDataModel(createSampleBitmap(90, 320, 127)))
      addScaleTypeExamples(
          w,
          h,
          "Scale Types, bitmap 800x600",
          BitmapImageDataModel(createSampleBitmap(800, 600, 127)))
      addScaleTypeExamples(w, h, "Scale Types, color int", ColorIntImageDataModel(Color.RED))
      addScaleTypeExamples(
          w,
          h,
          "Scale Types, drawable",
          DrawableImageDataModel(ContextCompat.getDrawable(context, R.drawable.logo)!!))
    }
  }

  fun LinearLayout.addScaleTypeExamples(
      w: Int,
      h: Int,
      title: String,
      imageDataModel: ImageDataModel
  ) {
    addRow(title, true) {
      for (entry in VitoSpinners.scaleTypes.first) {
        addLayerExample(
            entry.first,
            w,
            h,
            ImageLayerDataModel().apply {
              configure(
                  dataModel = imageDataModel,
                  canvasTransformation =
                      entry.second.first?.getCanvasTransformation(entry.second.second),
                  roundingOptions = RoundingOptions.asCircle())
            })
      }
    }
  }

  fun LinearLayout.addLayerExample(
      title: String,
      w: Int,
      h: Int,
      imageLayerDataModel: ImageLayerDataModel
  ) {
    addImageViewWithText(title, w, h) {
      setImageDrawable(ExampleImageLayerDrawable(imageLayerDataModel))
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.imagepipeline.debug

import com.facebook.cache.common.CacheKey
import com.facebook.imagepipeline.request.ImageRequest

/** Image tracker that tracks additional information, such as the original URI. */
interface DebugImageTracker {
  fun trackImage(imageRequest: ImageRequest?, cacheKey: CacheKey)

  fun trackImageRequest(imageRequest: ImageRequest?, requestId: String?)
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.imagepipeline.debug

import com.facebook.cache.common.CacheKey
import com.facebook.imagepipeline.cache.DefaultCacheKeyFactory
import com.facebook.imagepipeline.request.ImageRequest

class FlipperCacheKeyFactory(private val debugImageTracker: DebugImageTracker?) :
    DefaultCacheKeyFactory() {

  override fun getBitmapCacheKey(request: ImageRequest, callerContext: Any?): CacheKey {
    val bitmapCacheKey = super.getBitmapCacheKey(request, callerContext)
    debugImageTracker?.trackImage(request, bitmapCacheKey)
    return bitmapCacheKey
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.imagepipeline.debug

import android.util.Log
import com.facebook.common.references.SharedReference

class FlipperCloseableReferenceLeakTracker : CloseableReferenceLeakTracker {

  private var listener: CloseableReferenceLeakTracker.Listener? = null

  override fun trackCloseableReferenceLeak(
      reference: SharedReference<Any?>,
      stacktrace: Throwable?
  ) {
    if (listener == null) {
      Log.w("FRESCO", "No Flipper listener registered to track CloseableReference leak.")
      return
    }
    listener?.onCloseableReferenceLeak(reference, stacktrace)
  }

  override fun setListener(listener: CloseableReferenceLeakTracker.Listener?) {
    this.listener = listener
  }

  override val isSet: Boolean
    get() = (listener != null)
}