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

package com.facebook.share.model

import android.os.Bundle
import android.os.Parcel
import androidx.annotation.RestrictTo

/** Base class for shared media (photos, videos, etc). */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
abstract class ShareMedia<M : ShareMedia<M, B>, B : ShareMedia.Builder<M, B>> : ShareModel {
  enum class Type {
    PHOTO,
    VIDEO,
  }

  private val params: Bundle

  @Deprecated("This method is deprecated. Use GraphRequest directly to set parameters.")
  fun getParameters(): Bundle = Bundle(params)

  protected constructor(builder: Builder<M, B>) {
    params = Bundle(builder.params)
  }

  internal constructor(parcel: Parcel) {
    params = parcel.readBundle(javaClass.classLoader) ?: Bundle()
  }

  override fun describeContents(): Int = 0

  override fun writeToParcel(dest: Parcel, flags: Int) {
    dest.writeBundle(params)
  }

  abstract val mediaType: Type

  /** Builder for the {@link com.facebook.share.model.ShareMedia} class. */
  abstract class Builder<M : ShareMedia<M, B>, B : Builder<M, B>> : ShareModelBuilder<M, B> {
    internal var params: Bundle = Bundle()

    @Deprecated("This method is deprecated. Use GraphRequest directly to set parameters.")
    fun setParameter(key: String, value: String): B {
      params.putString(key, value)
      return this as B
    }

    @Deprecated("This method is deprecated. Use GraphRequest directly to set parameters.")
    fun setParameters(parameters: Bundle): B {
      params.putAll(parameters)
      return this as B
    }

    override fun readFrom(model: M?): B {
      if (model == null) {
        return this as B
      }
      return this.setParameters(model.params)
    }

    companion object {
      @JvmStatic
      internal fun writeListTo(out: Parcel, parcelFlags: Int, media: List<ShareMedia<*, *>>) {
        out.writeParcelableArray(media.toTypedArray(), parcelFlags)
      }

      @JvmStatic
      internal fun readListFrom(parcel: Parcel): List<ShareMedia<*, *>> {
        val parcelables =
            parcel.readParcelableArray(ShareMedia::class.java.classLoader) ?: return listOf()
        return parcelables.filterIsInstance<ShareMedia<*, *>>()
      }
    }
  }
}


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

package com.facebook.share.model

import android.os.Parcel
import android.os.Parcelable
import java.lang.IllegalArgumentException

class ShareMediaContent : ShareContent<ShareMediaContent, ShareMediaContent.Builder> {
  val media: List<ShareMedia<*, *>>

  private constructor(builder: Builder) : super(builder) {
    media = builder.media.toList()
  }

  internal constructor(source: Parcel) : super(source) {
    this.media =
        source.readParcelableArray(ShareMedia::class.java.classLoader)?.mapNotNull {
          it as ShareMedia<*, *>?
        }
            ?: listOf()
  }

  override fun describeContents(): Int = 0

  override fun writeToParcel(out: Parcel, flags: Int) {
    super.writeToParcel(out, flags)
    out.writeParcelableArray(media.toTypedArray(), flags)
  }

  /** Builder for the [SharePhotoContent] interface. */
  class Builder : ShareContent.Builder<ShareMediaContent, Builder>() {
    internal val media: MutableList<ShareMedia<*, *>> = ArrayList()

    /**
     * Adds a medium to the content.
     *
     * @param medium [com.facebook.share.model.ShareMedia] to add.
     * @return The builder.
     */
    fun addMedium(medium: ShareMedia<*, *>?): Builder {
      if (medium != null) {
        val mediumToAdd: ShareMedia<*, *> =
            when (medium) {
              is SharePhoto -> {
                SharePhoto.Builder().readFrom(medium).build()
              }
              is ShareVideo -> {
                ShareVideo.Builder().readFrom(medium).build()
              }
              else -> {
                throw IllegalArgumentException("medium must be either a SharePhoto or ShareVideo")
              }
            }
        media.add(mediumToAdd)
      }
      return this
    }

    /**
     * Adds multiple media to the content.
     *
     * @param media [java.util.List] of [com.facebook.share.model.ShareMedia] to add.
     * @return The builder.
     */
    fun addMedia(media: List<ShareMedia<*, *>>?): Builder {
      if (media != null) {
        for (medium in media) {
          addMedium(medium)
        }
      }
      return this
    }

    override fun build(): ShareMediaContent {
      return ShareMediaContent(this)
    }

    override fun readFrom(content: ShareMediaContent?): Builder {
      return if (content == null) {
        this
      } else super.readFrom(content).addMedia(content.media)
    }

    /**
     * Replaces the media for the builder.
     *
     * @param media [java.util.List] of [com.facebook.share.model.ShareMedia] to add.
     * @return The builder.
     */
    fun setMedia(media: List<ShareMedia<*, *>>?): Builder {
      this.media.clear()
      addMedia(media)
      return this
    }
  }

  companion object {
    @JvmField
    val CREATOR: Parcelable.Creator<ShareMediaContent> =
        object : Parcelable.Creator<ShareMediaContent> {
          override fun createFromParcel(source: Parcel): ShareMediaContent {
            return ShareMediaContent(source)
          }

          override fun newArray(size: Int): Array<ShareMediaContent?> {
            return arrayOfNulls(size)
          }
        }
  }
}


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

package com.facebook.share.model

import android.os.Parcel

/** The base class for Messenger share action buttons. */
abstract class ShareMessengerActionButton : ShareModel {
  /** The title displayed to the user for the Messenger action button. */
  val title: String?

  protected constructor(builder: Builder<*, *>) {
    title = builder.title
  }

  internal constructor(parcel: Parcel) {
    title = parcel.readString()
  }

  override fun describeContents(): Int = 0

  override fun writeToParcel(dest: Parcel, flags: Int) {
    dest.writeString(title)
  }

  /** Abstract builder for [com.facebook.share.model.ShareMessengerActionButton] */
  abstract class Builder<M : ShareMessengerActionButton, B : Builder<M, B>> :
      ShareModelBuilder<M, B> {
    internal var title: String? = null

    /** Sets the title for the Messenger action button. */
    fun setTitle(title: String?): B {
      this.title = title
      return this as B
    }

    override fun readFrom(model: M?): B {
      return if (model == null) {
        this as B
      } else setTitle(model.title)
    }
  }
}


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

package com.facebook.share.model

import android.net.Uri
import android.os.Parcel
import android.os.Parcelable

/** Provides a data model class for a Messenger share URL action button. */
class ShareMessengerURLActionButton : ShareMessengerActionButton {
  /** The display height ratio of the webview when shown in the Messenger app. */
  enum class WebviewHeightRatio {
    /** The webview will cover 100% screen. */
    WebviewHeightRatioFull,

    /** The webview will cover 75% of the screen. */
    WebviewHeightRatioTall,

    /** The webview will cover 50% of the screen. */
    WebviewHeightRatioCompact
  }

  /** Get the URL that this button should open when tapped. */
  val url: Uri?
  /** Get the fallback URL of the button. */
  val fallbackUrl: Uri?
  /** Get whether the URL is enabled with Messenger Extensions. */
  val isMessengerExtensionURL: Boolean
  /** Get whether the webview shows the share button. */
  val shouldHideWebviewShareButton: Boolean
  /** Get the display height ratio of browser. */
  val webviewHeightRatio: WebviewHeightRatio?

  @Deprecated(
      "getIsMessengerExtensionURL is deprecated. Use isMessengerExtensionURL instead",
      replaceWith = ReplaceWith("isMessengerExtensionURL"))
  fun getIsMessengerExtensionURL(): Boolean = isMessengerExtensionURL

  private constructor(builder: Builder) : super(builder) {
    url = builder.url
    isMessengerExtensionURL = builder.isMessengerExtensionURL
    fallbackUrl = builder.fallbackUrl
    webviewHeightRatio = builder.webviewHeightRatio
    shouldHideWebviewShareButton = builder.shouldHideWebviewShareButton
  }

  internal constructor(parcel: Parcel) : super(parcel) {
    url = parcel.readParcelable(Uri::class.java.classLoader)
    isMessengerExtensionURL = parcel.readByte().toInt() != 0
    fallbackUrl = parcel.readParcelable(Uri::class.java.classLoader)
    webviewHeightRatio = parcel.readSerializable() as WebviewHeightRatio?
    shouldHideWebviewShareButton = parcel.readByte().toInt() != 0
  }

  override fun writeToParcel(dest: Parcel, flags: Int) {
    super.writeToParcel(dest, flags)

    dest.writeParcelable(url, 0)
    dest.writeByte((if (isMessengerExtensionURL) 1 else 0).toByte())
    dest.writeParcelable(fallbackUrl, 0)
    dest.writeSerializable(webviewHeightRatio)
    dest.writeByte((if (isMessengerExtensionURL) 1 else 0).toByte())
  }

  /** Builder class for [ShareMessengerURLActionButton] class. */
  class Builder : ShareMessengerActionButton.Builder<ShareMessengerURLActionButton, Builder>() {
    internal var url: Uri? = null
    internal var isMessengerExtensionURL = false
    internal var fallbackUrl: Uri? = null
    internal var webviewHeightRatio: WebviewHeightRatio? = null
    internal var shouldHideWebviewShareButton = false

    /** Set the URL of this action button. */
    fun setUrl(url: Uri?): Builder {
      this.url = url
      return this
    }

    /**
     * Set whether the url is a Messenger Extensions url. This must be true if the URL is a
     * Messenger Extensions url. Defaults to NO.
     */
    fun setIsMessengerExtensionURL(isMessengerExtensionURL: Boolean): Builder {
      this.isMessengerExtensionURL = isMessengerExtensionURL
      return this
    }

    /**
     * Set the fallback URL for a Messenger Extensions enabled button. This is a fallback url for a
     * Messenger Extensions enabled button. It is used on clients that do not support Messenger
     * Extensions. If this is not defined, the url will be used as a fallback. Optional, but ignored
     * unless messengerExtensions == YES.
     */
    fun setFallbackUrl(fallbackUrl: Uri?): Builder {
      this.fallbackUrl = fallbackUrl
      return this
    }

    /**
     * Set the display height ratio of the webview when shown in the Messenger app. This controls
     * the display height of the webview when shown in the Messenger app. Defaults to Full
     */
    fun setWebviewHeightRatio(webviewHeightRatio: WebviewHeightRatio?): Builder {
      this.webviewHeightRatio = webviewHeightRatio
      return this
    }

    /**
     * Set whether we want to hide the share button in the webview or not. This controls whether we
     * want to hide the share button in the webview or not. It is useful to hide the share button
     * when the webview is user-specific and contains sensitive information Defaults to NO.
     */
    fun setShouldHideWebviewShareButton(shouldHideWebviewShareButton: Boolean): Builder {
      this.shouldHideWebviewShareButton = shouldHideWebviewShareButton
      return this
    }

    override fun readFrom(model: ShareMessengerURLActionButton?): Builder {
      return if (model == null) {
        this
      } else
          setUrl(model.url)
              .setIsMessengerExtensionURL(model.isMessengerExtensionURL)
              .setFallbackUrl(model.fallbackUrl)
              .setWebviewHeightRatio(model.webviewHeightRatio)
              .setShouldHideWebviewShareButton(model.shouldHideWebviewShareButton)
    }

    override fun build(): ShareMessengerURLActionButton {
      return ShareMessengerURLActionButton(this)
    }
  }

  companion object {
    @JvmField
    val CREATOR: Parcelable.Creator<ShareMessengerURLActionButton> =
        object : Parcelable.Creator<ShareMessengerURLActionButton> {
          override fun createFromParcel(parcel: Parcel): ShareMessengerURLActionButton {
            return ShareMessengerURLActionButton(parcel)
          }

          override fun newArray(size: Int): Array<ShareMessengerURLActionButton?> {
            return arrayOfNulls(size)
          }
        }
  }
}


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

package com.facebook.share.model

import android.os.Parcelable

/** Base interface for share models. */
interface ShareModel : Parcelable


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

package com.facebook.share.model

import com.facebook.share.ShareBuilder

/**
 * Interface for builders related to sharing.
 *
 * @param <M> The model protocol to be built.
 * @param <B> The concrete builder class.
 */
interface ShareModelBuilder<M : ShareModel, B : ShareModelBuilder<M, B>> : ShareBuilder<M, B> {
  /**
   * Reads the values from a ShareModel into the builder.
   *
   * @param model The source ShareModel
   * @return The builder.
   */
  fun readFrom(model: M?): B
}


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

package com.facebook.share.model

import android.graphics.Bitmap
import android.net.Uri
import android.os.Parcel
import android.os.Parcelable

/**
 * Describes a photo for sharing.
 *
 * Use [SharePhoto.Builder] to build instances
 */
class SharePhoto : ShareMedia<SharePhoto, SharePhoto.Builder> {
  /**
   * If the photo is resident in memory, this method supplies the data.
   *
   * @return [android.graphics.Bitmap] representation of the photo.
   */
  val bitmap: Bitmap?

  /**
   * The URL to the photo.
   *
   * @return [android.net.Uri] that points to a network location or the location of the photo on
   * disk.
   */
  val imageUrl: Uri?

  /**
   * Specifies whether the photo represented by this object was generated by the user or by the
   * application.
   *
   * @return Indication of whether the photo is user-generated.
   */
  val userGenerated: Boolean

  /**
   * Gets the user generated caption. Note that the 'caption' must come from the user, as pre-filled
   * content is forbidden by the Platform Policies (2.3).
   *
   * @return The user generated caption.
   */
  val caption: String?

  private constructor(builder: Builder) : super(builder) {
    bitmap = builder.bitmap
    imageUrl = builder.imageUrl
    userGenerated = builder.userGenerated
    caption = builder.caption
  }

  internal constructor(parcel: Parcel) : super(parcel) {
    bitmap = parcel.readParcelable(Bitmap::class.java.classLoader)
    imageUrl = parcel.readParcelable(Uri::class.java.classLoader)
    userGenerated = parcel.readByte().toInt() != 0
    caption = parcel.readString()
  }

  override fun describeContents(): Int = 0

  override fun writeToParcel(out: Parcel, flags: Int) {
    super.writeToParcel(out, flags)
    out.writeParcelable(bitmap, 0)
    out.writeParcelable(imageUrl, 0)
    out.writeByte((if (userGenerated) 1 else 0).toByte())
    out.writeString(caption)
  }

  override val mediaType: Type = Type.PHOTO

  /** Builder for the [com.facebook.share.model.SharePhoto] class. */
  class Builder : ShareMedia.Builder<SharePhoto, Builder>() {
    internal var bitmap: Bitmap? = null
      private set
    internal var imageUrl: Uri? = null
      private set
    internal var userGenerated = false
      private set
    internal var caption: String? = null
      private set

    /**
     * Sets the bitmap representation of the photo.
     *
     * @param bitmap [android.graphics.Bitmap] representation of the photo.
     * @return The builder.
     */
    fun setBitmap(bitmap: Bitmap?): Builder {
      this.bitmap = bitmap
      return this
    }

    /**
     * Sets the URL to the photo.
     *
     * @param imageUrl [android.net.Uri] that points to a network location or the location of the
     * photo on disk.
     * @return The builder.
     */
    fun setImageUrl(imageUrl: Uri?): Builder {
      this.imageUrl = imageUrl
      return this
    }

    /**
     * Sets whether the photo represented by this object was generated by the user or by the
     * application.
     *
     * @param userGenerated Indication of whether the photo is user-generated.
     * @return The builder.
     */
    fun setUserGenerated(userGenerated: Boolean): Builder {
      this.userGenerated = userGenerated
      return this
    }

    /**
     * Sets the user generated caption for the photo. Note that the 'caption' must come from the
     * user, as pre-filled content is forbidden by the Platform Policies (2.3).
     *
     * @param caption [java.lang.String] of a [com.facebook.share.model.SharePhoto]
     * @return The builder.
     */
    fun setCaption(caption: String?): Builder {
      this.caption = caption
      return this
    }

    override fun build(): SharePhoto {
      return SharePhoto(this)
    }

    override fun readFrom(model: SharePhoto?): Builder {
      return if (model == null) {
        this
      } else
          super.readFrom(model)
              .setBitmap(model.bitmap)
              .setImageUrl(model.imageUrl)
              .setUserGenerated(model.userGenerated)
              .setCaption(model.caption)
    }

    internal fun readFrom(parcel: Parcel): Builder {
      return this.readFrom(parcel.readParcelable(SharePhoto::class.java.classLoader))
    }

    companion object {
      /*
       * For backwards compatibility with SharePhotoContent.  Prefer ShareMediaContent for new
       * code.
       */
      internal fun writePhotoListTo(out: Parcel, parcelFlags: Int, photos: List<SharePhoto>) {
        val array = photos.toTypedArray()
        out.writeParcelableArray(array, parcelFlags)
      }

      /*
       * For backwards compatibility with SharePhotoContent.  Prefer ShareMediaContent for new
       * code.
       */
      internal fun readPhotoListFrom(parcel: Parcel): List<SharePhoto> {
        val media = readListFrom(parcel)
        return media.filterIsInstance<SharePhoto>()
      }
    }
  }

  companion object {
    @JvmField
    val CREATOR: Parcelable.Creator<SharePhoto> =
        object : Parcelable.Creator<SharePhoto> {
          override fun createFromParcel(source: Parcel): SharePhoto {
            return SharePhoto(source)
          }

          override fun newArray(size: Int): Array<SharePhoto?> {
            return arrayOfNulls(size)
          }
        }
  }
}


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

package com.facebook.share.model

import android.os.Parcel
import android.os.Parcelable

/**
 * Describes photo content to be shared.
 *
 * Use [SharePhotoContent.Builder] to create instances
 */
class SharePhotoContent : ShareContent<SharePhotoContent, SharePhotoContent.Builder> {
  /** Photos to be shared. */
  val photos: List<SharePhoto>

  private constructor(builder: Builder) : super(builder) {
    photos = builder.photos.toList()
  }

  internal constructor(parcel: Parcel) : super(parcel) {
    photos = SharePhoto.Builder.readPhotoListFrom(parcel).toList()
  }

  override fun describeContents(): Int = 0

  override fun writeToParcel(out: Parcel, flags: Int) {
    super.writeToParcel(out, flags)
    SharePhoto.Builder.writePhotoListTo(out, flags, photos)
  }

  /** Builder for the [SharePhotoContent] interface. */
  class Builder : ShareContent.Builder<SharePhotoContent, Builder>() {
    internal val photos: MutableList<SharePhoto> = ArrayList()

    /**
     * Adds a photo to the content.
     *
     * @param photo [com.facebook.share.model.SharePhoto] to add.
     * @return The builder.
     */
    fun addPhoto(photo: SharePhoto?): Builder {
      if (photo != null) {
        photos.add(SharePhoto.Builder().readFrom(photo).build())
      }
      return this
    }

    /**
     * Adds multiple photos to the content.
     *
     * @param photos [java.util.List] of [com.facebook.share.model.SharePhoto]s to add.
     * @return The builder.
     */
    fun addPhotos(photos: List<SharePhoto>?): Builder {
      if (photos != null) {
        for (photo in photos) {
          addPhoto(photo)
        }
      }
      return this
    }

    override fun build(): SharePhotoContent = SharePhotoContent(this)

    override fun readFrom(content: SharePhotoContent?): Builder {
      return if (content == null) {
        this
      } else super.readFrom(content).addPhotos(content.photos)
    }

    /**
     * Replaces the photos for the builder.
     *
     * @param photos [java.util.List] of [com.facebook.share.model.SharePhoto]s to add.
     * @return The builder.
     */
    fun setPhotos(photos: List<SharePhoto>?): Builder {
      this.photos.clear()
      addPhotos(photos)
      return this
    }
  }

  companion object {
    @JvmField
    val CREATOR: Parcelable.Creator<SharePhotoContent> =
        object : Parcelable.Creator<SharePhotoContent> {
          override fun createFromParcel(parcel: Parcel): SharePhotoContent {
            return SharePhotoContent(parcel)
          }

          override fun newArray(size: Int): Array<SharePhotoContent?> {
            return arrayOfNulls(size)
          }
        }
  }
}


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

package com.facebook.share.model

import android.os.Parcel
import android.os.Parcelable

/** Describes content (video, photo, sticker) to be shared into story. */
class ShareStoryContent : ShareContent<ShareStoryContent, ShareStoryContent.Builder> {
  /** The background asset of the story, could be a photo or video */
  val backgroundAsset: ShareMedia<*, *>?

  /** The sticker asset of the story, should be a photo */
  val stickerAsset: SharePhoto?

  /** A list of color which will be draw from top to bottom */
  val backgroundColorList: List<String>?
    get() = field?.toList()

  /** The link that set by 3rd party app */
  val attributionLink: String?

  private constructor(builder: Builder) : super(builder) {
    backgroundAsset = builder.backgroundAsset
    stickerAsset = builder.stickerAsset
    backgroundColorList = builder.backgroundColorList
    attributionLink = builder.attributionLink
  }

  internal constructor(parcel: Parcel) : super(parcel) {
    backgroundAsset = parcel.readParcelable(ShareMedia::class.java.classLoader)
    stickerAsset = parcel.readParcelable(SharePhoto::class.java.classLoader)
    backgroundColorList = readUnmodifiableStringList(parcel)
    attributionLink = parcel.readString()
  }

  override fun describeContents(): Int = 0

  override fun writeToParcel(out: Parcel, flags: Int) {
    super.writeToParcel(out, flags)
    out.writeParcelable(backgroundAsset, 0)
    out.writeParcelable(stickerAsset, 0)
    out.writeStringList(backgroundColorList)
    out.writeString(attributionLink)
  }

  private fun readUnmodifiableStringList(parcel: Parcel): List<String>? {
    val list: List<String> = ArrayList()
    parcel.readStringList(list)
    return if (list.isEmpty()) null else list.toList()
  }

  /** Builder for the [ShareStoryContent]. */
  class Builder : ShareContent.Builder<ShareStoryContent, Builder>() {
    internal var backgroundAsset: ShareMedia<*, *>? = null
    internal var stickerAsset: SharePhoto? = null
    internal var backgroundColorList: List<String>? = null
    internal var attributionLink: String? = null

    /**
     * Set the Background Asset to display
     *
     * @param backgroundAsset the background asset of the story, could be a photo or video
     * @return The builder.
     */
    fun setBackgroundAsset(backgroundAsset: ShareMedia<*, *>?): Builder {
      this.backgroundAsset = backgroundAsset
      return this
    }

    /**
     * Set the Sticker Asset to display
     *
     * @param stickerAsset the sticker asset of the story, should be a photo
     * @return The builder.
     */
    fun setStickerAsset(stickerAsset: SharePhoto?): Builder {
      this.stickerAsset = stickerAsset
      return this
    }

    /**
     * Set the background color list to display
     *
     * @param backgroundColorList a list of color which will be draw from top to bottom
     * @return The builder.
     */
    fun setBackgroundColorList(backgroundColorList: List<String>?): Builder {
      this.backgroundColorList = backgroundColorList?.toList()
      return this
    }

    /**
     * Set the attribution link
     *
     * @param attributionLink link that set by 3rd party app
     * @return The builder.
     */
    fun setAttributionLink(attributionLink: String?): Builder {
      this.attributionLink = attributionLink
      return this
    }

    override fun build(): ShareStoryContent {
      return ShareStoryContent(this)
    }

    override fun readFrom(model: ShareStoryContent?): Builder {
      return if (model == null) {
        this
      } else
          super.readFrom(model)
              .setBackgroundAsset(model.backgroundAsset)
              .setStickerAsset(model.stickerAsset)
              .setBackgroundColorList(model.backgroundColorList)
              .setAttributionLink(model.attributionLink)
    }
  }

  companion object {
    @JvmField
    val CREATOR: Parcelable.Creator<ShareStoryContent> =
        object : Parcelable.Creator<ShareStoryContent> {
          override fun createFromParcel(parcel: Parcel): ShareStoryContent {
            return ShareStoryContent(parcel)
          }

          override fun newArray(size: Int): Array<ShareStoryContent?> {
            return arrayOfNulls(size)
          }
        }
  }
}


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

package com.facebook.share.model

import android.net.Uri
import android.os.Parcel
import android.os.Parcelable

/**
 * Describes a video for sharing.
 *
 * Use [ShareVideo.Builder] to create instances
 */
class ShareVideo : ShareMedia<ShareVideo, ShareVideo.Builder> {
  /**
   * This method supplies the URL to locate the video.
   *
   * @return [android.net.Uri] that points to the location of the video on disk.
   */
  val localUrl: Uri?

  private constructor(builder: Builder) : super(builder) {
    localUrl = builder.localUrl
  }

  internal constructor(parcel: Parcel) : super(parcel) {
    localUrl = parcel.readParcelable(Uri::class.java.classLoader)
  }

  override fun describeContents(): Int = 0

  override fun writeToParcel(out: Parcel, flags: Int) {
    super.writeToParcel(out, flags)
    out.writeParcelable(localUrl, 0)
  }

  override val mediaType: Type = Type.VIDEO

  /** Builder for the [com.facebook.share.model.ShareVideo] class. */
  class Builder : ShareMedia.Builder<ShareVideo, Builder>() {
    internal var localUrl: Uri? = null

    /**
     * Sets the URL to locate the video.
     *
     * @param localUrl [android.net.Uri] that points to the location of the video on disk.
     * @return The builder.
     */
    fun setLocalUrl(localUrl: Uri?): Builder {
      this.localUrl = localUrl
      return this
    }

    override fun build(): ShareVideo = ShareVideo(this)

    override fun readFrom(model: ShareVideo?): Builder {
      return if (model == null) {
        this
      } else setLocalUrl(model.localUrl)
    }

    internal fun readFrom(parcel: Parcel): Builder {
      return this.readFrom(parcel.readParcelable(ShareVideo::class.java.classLoader))
    }
  }

  companion object {
    @JvmField
    val CREATOR: Parcelable.Creator<ShareVideo> =
        object : Parcelable.Creator<ShareVideo> {
          override fun createFromParcel(source: Parcel): ShareVideo = ShareVideo(source)

          override fun newArray(size: Int): Array<ShareVideo?> = arrayOfNulls(size)
        }
  }
}


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

package com.facebook.share.model

import android.os.Parcel
import android.os.Parcelable

/**
 * Provides the interface for video content to be shared.
 *
 * A general use builder is available in [ShareVideoContent.Builder].
 */
class ShareVideoContent : ShareContent<ShareVideoContent, ShareVideoContent.Builder>, ShareModel {
  /** The description of the video. */
  val contentDescription: String?

  /** The title to display for this video. */
  val contentTitle: String?

  /** Photo to be used as a preview for the video. */
  val previewPhoto: SharePhoto?

  /** Video to be shared. */
  val video: ShareVideo?

  private constructor(builder: Builder) : super(builder) {
    contentDescription = builder.contentDescription
    contentTitle = builder.contentTitle
    previewPhoto = builder.previewPhoto
    video = builder.video
  }

  internal constructor(parcel: Parcel) : super(parcel) {
    contentDescription = parcel.readString()
    contentTitle = parcel.readString()
    val previewPhotoBuilder = SharePhoto.Builder().readFrom(parcel)
    previewPhoto =
        if (previewPhotoBuilder.imageUrl != null || previewPhotoBuilder.bitmap != null) {
          previewPhotoBuilder.build()
        } else {
          null
        }
    video = ShareVideo.Builder().readFrom(parcel).build()
  }

  override fun describeContents(): Int = 0

  override fun writeToParcel(out: Parcel, flags: Int) {
    super.writeToParcel(out, flags)
    out.writeString(contentDescription)
    out.writeString(contentTitle)
    out.writeParcelable(previewPhoto, 0)
    out.writeParcelable(video, 0)
  }

  /** Builder for the [com.facebook.share.model.ShareVideoContent] interface. */
  class Builder : ShareContent.Builder<ShareVideoContent, Builder>() {
    internal var contentDescription: String? = null
    internal var contentTitle: String? = null
    internal var previewPhoto: SharePhoto? = null
    internal var video: ShareVideo? = null

    /**
     * Sets the description of the video.
     *
     * @param contentDescription The description of the video.
     * @return The builder.
     */
    fun setContentDescription(contentDescription: String?): Builder {
      this.contentDescription = contentDescription
      return this
    }

    /**
     * Sets the title to display for this video.
     *
     * @param contentTitle The video title.
     * @return The builder.
     */
    fun setContentTitle(contentTitle: String?): Builder {
      this.contentTitle = contentTitle
      return this
    }

    /**
     * Sets the photo to be used as a preview for the video.
     *
     * @param previewPhoto Preview [com.facebook.share.model.SharePhoto] for the content.
     * @return The builder.
     */
    fun setPreviewPhoto(previewPhoto: SharePhoto?): Builder {
      this.previewPhoto = previewPhoto?.let { SharePhoto.Builder().readFrom(previewPhoto).build() }
      return this
    }

    /**
     * Sets the video to be shared.
     *
     * @param video [com.facebook.share.model.ShareVideo]
     * @return The builder.
     */
    fun setVideo(video: ShareVideo?): Builder {
      if (video == null) {
        return this
      }
      this.video = ShareVideo.Builder().readFrom(video).build()
      return this
    }

    override fun build(): ShareVideoContent {
      return ShareVideoContent(this)
    }

    override fun readFrom(content: ShareVideoContent?): Builder {
      return if (content == null) {
        this
      } else
          super.readFrom(content)
              .setContentDescription(content.contentDescription)
              .setContentTitle(content.contentTitle)
              .setPreviewPhoto(content.previewPhoto)
              .setVideo(content.video)
    }
  }

  companion object {
    @JvmField
    val CREATOR: Parcelable.Creator<ShareVideoContent> =
        object : Parcelable.Creator<ShareVideoContent> {
          override fun createFromParcel(parcel: Parcel): ShareVideoContent {
            return ShareVideoContent(parcel)
          }

          override fun newArray(size: Int): Array<ShareVideoContent?> {
            return arrayOfNulls(size)
          }
        }
  }
}


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

package com.facebook.share.widget

import android.app.Activity
import android.content.Context
import android.net.Uri
import android.os.Bundle
import androidx.fragment.app.Fragment
import com.facebook.AccessToken
import com.facebook.FacebookCallback
import com.facebook.FacebookSdk
import com.facebook.appevents.InternalAppEventsLogger
import com.facebook.internal.AnalyticsEvents
import com.facebook.internal.AppCall
import com.facebook.internal.CallbackManagerImpl
import com.facebook.internal.DialogFeature
import com.facebook.internal.DialogPresenter
import com.facebook.internal.FacebookDialogBase
import com.facebook.internal.FragmentWrapper
import com.facebook.internal.NativeAppCallAttachmentStore
import com.facebook.share.Sharer
import com.facebook.share.internal.CameraEffectFeature
import com.facebook.share.internal.LegacyNativeDialogParameters
import com.facebook.share.internal.NativeDialogParameters
import com.facebook.share.internal.ShareContentValidation
import com.facebook.share.internal.ShareDialogFeature
import com.facebook.share.internal.ShareFeedContent
import com.facebook.share.internal.ShareInternalUtility
import com.facebook.share.internal.ShareStoryFeature
import com.facebook.share.internal.WebDialogParameters
import com.facebook.share.model.ShareCameraEffectContent
import com.facebook.share.model.ShareContent
import com.facebook.share.model.ShareLinkContent
import com.facebook.share.model.ShareMediaContent
import com.facebook.share.model.SharePhoto
import com.facebook.share.model.SharePhotoContent
import com.facebook.share.model.ShareStoryContent
import com.facebook.share.model.ShareVideoContent
import java.util.UUID

/** Provides functionality to share content via the Facebook Share Dialog */
open class ShareDialog : FacebookDialogBase<ShareContent<*, *>, Sharer.Result>, Sharer {
  /** The mode for the share dialog. */
  enum class Mode {
    /** The mode is determined automatically. */
    AUTOMATIC,
    /** The native dialog is used. */
    NATIVE,
    /** The web dialog is used. */
    WEB,
    /** The feed dialog is used. */
    FEED
  }

  private var shouldFailOnDataError = false

  // Keep track of Mode overrides for logging purposes.
  private var isAutomaticMode = true

  /**
   * Constructs a new ShareDialog.
   *
   * @param activity Activity to use to share the provided content.
   */
  constructor(activity: Activity) : this(activity, DEFAULT_REQUEST_CODE)

  /**
   * Constructs a new ShareDialog without any context. For androidx to create ActivityResultContract
   * only.
   */
  constructor(requestCode: Int = DEFAULT_REQUEST_CODE) : super(requestCode) {
    ShareInternalUtility.registerStaticShareCallback(requestCode)
  }

  /**
   * Constructs a new ShareDialog.
   *
   * @param fragment androidx.fragment.app.Fragment to use to share the provided content.
   */
  constructor(fragment: Fragment) : this(FragmentWrapper(fragment))

  /**
   * Constructs a new ShareDialog.
   *
   * @param fragment android.app.Fragment to use to share the provided content.
   */
  constructor(fragment: android.app.Fragment) : this(FragmentWrapper(fragment))

  /** for internal use only */
  constructor(activity: Activity, requestCode: Int) : super(activity, requestCode) {
    ShareInternalUtility.registerStaticShareCallback(requestCode)
  }
  /** for internal use only */
  constructor(fragment: Fragment, requestCode: Int) : this(FragmentWrapper(fragment), requestCode)
  /** for internal use only */
  constructor(
      fragment: android.app.Fragment,
      requestCode: Int
  ) : this(FragmentWrapper(fragment), requestCode)
  /** for internal use only */
  constructor(
      fragmentWrapper: FragmentWrapper,
      requestCode: Int = DEFAULT_REQUEST_CODE
  ) : super(fragmentWrapper, requestCode) {
    ShareInternalUtility.registerStaticShareCallback(requestCode)
  }

  override fun registerCallbackImpl(
      callbackManager: CallbackManagerImpl,
      callback: FacebookCallback<Sharer.Result>
  ) {
    ShareInternalUtility.registerSharerCallback(requestCode, callbackManager, callback)
  }

  override fun getShouldFailOnDataError(): Boolean = shouldFailOnDataError

  override fun setShouldFailOnDataError(shouldFailOnDataError: Boolean) {
    this.shouldFailOnDataError = shouldFailOnDataError
  }

  /**
   * Call this to check if the Share Dialog can be shown in a specific mode.
   *
   * @param mode Mode of the Share Dialog
   * @return True if the dialog can be shown in the passed in Mode
   */
  open fun canShow(content: ShareContent<*, *>, mode: Mode): Boolean {
    return canShowImpl(content, if (mode == Mode.AUTOMATIC) BASE_AUTOMATIC_MODE else mode)
  }

  /**
   * Call this to show the Share Dialog in a specific mode
   *
   * @param mode Mode of the Share Dialog
   */
  open fun show(content: ShareContent<*, *>, mode: Mode) {
    isAutomaticMode = mode == Mode.AUTOMATIC
    showImpl(content, if (isAutomaticMode) BASE_AUTOMATIC_MODE else mode)
  }

  override fun createBaseAppCall(): AppCall = AppCall(requestCode)

  // Feed takes precedence for link-shares for Mode.AUTOMATIC
  // Share into story
  override val orderedModeHandlers: List<ModeHandler> =
      arrayListOf(
          NativeHandler(),
          FeedHandler(),
          WebShareHandler(),
          CameraEffectHandler(),
          ShareStoryHandler())

  private inner class NativeHandler : ModeHandler() {
    override var mode: Any = Mode.NATIVE

    override fun canShow(content: ShareContent<*, *>, isBestEffort: Boolean): Boolean {
      if (content is ShareCameraEffectContent || content is ShareStoryContent) {
        return false
      }
      var canShowResult = true
      if (!isBestEffort) {
        // The following features are considered best-effort and will not prevent the
        // native share dialog from being presented, even if the installed version does
        // not support the feature.
        // However, to let apps pivot to a different approach or dialog (for example, Web),
        // we need to be able to signal back when native support is lacking.
        if (content.shareHashtag != null) {
          canShowResult =
              DialogPresenter.canPresentNativeDialogWithFeature(ShareDialogFeature.HASHTAG)
        }
        if (content is ShareLinkContent && !content.quote.isNullOrEmpty()) {
          canShowResult =
              canShowResult &&
                  DialogPresenter.canPresentNativeDialogWithFeature(
                      ShareDialogFeature.LINK_SHARE_QUOTES)
        }
      }
      return canShowResult && canShowNative(content.javaClass)
    }

    override fun createAppCall(content: ShareContent<*, *>): AppCall? {
      logDialogShare(activityContext, content, Mode.NATIVE)
      ShareContentValidation.validateForNativeShare(content)
      val appCall = createBaseAppCall()
      val shouldFailOnDataError = getShouldFailOnDataError()
      val feature = getFeature(content.javaClass) ?: return null
      DialogPresenter.setupAppCallForNativeDialog(
          appCall,
          object : DialogPresenter.ParameterProvider {
            override val parameters: Bundle?
              get() = NativeDialogParameters.create(appCall.callId, content, shouldFailOnDataError)
            override val legacyParameters: Bundle?
              get() =
                  LegacyNativeDialogParameters.create(
                      appCall.callId, content, shouldFailOnDataError)
          },
          feature)
      return appCall
    }
  }

  private inner class WebShareHandler : ModeHandler() {
    override var mode: Any = Mode.WEB

    override fun canShow(content: ShareContent<*, *>, isBestEffort: Boolean): Boolean {
      return canShowWebCheck(content)
    }

    override fun createAppCall(content: ShareContent<*, *>): AppCall? {
      logDialogShare(activityContext, content, Mode.WEB)
      val appCall = createBaseAppCall()
      ShareContentValidation.validateForWebShare(content)
      val params =
          when (content) {
            is ShareLinkContent -> {
              WebDialogParameters.create(content)
            }
            is SharePhotoContent -> {
              val photoContent = createAndMapAttachments(content, appCall.callId)
              WebDialogParameters.create(photoContent)
            }
            else -> {
              return null
            }
          }
      DialogPresenter.setupAppCallForWebDialog(appCall, getActionName(content), params)
      return appCall
    }

    private fun getActionName(shareContent: ShareContent<*, *>): String? {
      return when {
        (shareContent is ShareLinkContent || shareContent is SharePhotoContent) -> WEB_SHARE_DIALOG
        else -> null
      }
    }

    private fun createAndMapAttachments(
        content: SharePhotoContent,
        callId: UUID
    ): SharePhotoContent {
      val contentBuilder = SharePhotoContent.Builder().readFrom(content)
      val photos: MutableList<SharePhoto> = ArrayList()
      val attachments: MutableList<NativeAppCallAttachmentStore.Attachment> = ArrayList()
      for (i in content.photos.indices) {
        var sharePhoto = content.photos[i]
        val photoBitmap = sharePhoto.bitmap
        if (photoBitmap != null) {
          val attachment = NativeAppCallAttachmentStore.createAttachment(callId, photoBitmap)
          sharePhoto =
              SharePhoto.Builder()
                  .readFrom(sharePhoto)
                  .setImageUrl(Uri.parse(attachment.attachmentUrl))
                  .setBitmap(null)
                  .build()
          attachments.add(attachment)
        }
        photos.add(sharePhoto)
      }
      contentBuilder.setPhotos(photos)
      NativeAppCallAttachmentStore.addAttachments(attachments)
      return contentBuilder.build()
    }
  }

  private inner class FeedHandler : ModeHandler() {
    override var mode: Any = Mode.FEED

    override fun canShow(content: ShareContent<*, *>, isBestEffort: Boolean): Boolean {
      return content is ShareLinkContent || content is ShareFeedContent
    }

    override fun createAppCall(content: ShareContent<*, *>): AppCall? {
      logDialogShare(activityContext, content, Mode.FEED)
      val appCall = createBaseAppCall()
      val params =
          when (content) {
            is ShareLinkContent -> {
              ShareContentValidation.validateForWebShare(content)
              WebDialogParameters.createForFeed(content)
            }
            is ShareFeedContent -> {
              WebDialogParameters.createForFeed(content)
            }
            else -> {
              return null
            }
          }
      DialogPresenter.setupAppCallForWebDialog(appCall, FEED_DIALOG, params)
      return appCall
    }
  }

  private inner class CameraEffectHandler : ModeHandler() {
    override var mode: Any = Mode.NATIVE

    override fun canShow(content: ShareContent<*, *>, isBestEffort: Boolean): Boolean {
      val canShowResult = content is ShareCameraEffectContent
      return canShowResult && canShowNative(content.javaClass)
    }

    override fun createAppCall(content: ShareContent<*, *>): AppCall? {
      ShareContentValidation.validateForNativeShare(content)
      val appCall = createBaseAppCall()
      val shouldFailOnDataError = getShouldFailOnDataError()
      val feature = getFeature(content.javaClass) ?: return null
      DialogPresenter.setupAppCallForNativeDialog(
          appCall,
          object : DialogPresenter.ParameterProvider {
            override val parameters: Bundle?
              get() = NativeDialogParameters.create(appCall.callId, content, shouldFailOnDataError)
            override val legacyParameters: Bundle?
              get() =
                  LegacyNativeDialogParameters.create(
                      appCall.callId, content, shouldFailOnDataError)
          },
          feature)
      return appCall
    }
  }

  private inner class ShareStoryHandler : ModeHandler() {
    override var mode: Any = Mode.NATIVE

    override fun canShow(content: ShareContent<*, *>, isBestEffort: Boolean): Boolean {
      val canShowResult = content is ShareStoryContent
      return canShowResult && canShowNative(content.javaClass)
    }

    override fun createAppCall(content: ShareContent<*, *>): AppCall? {
      ShareContentValidation.validateForStoryShare(content)
      val appCall = createBaseAppCall()
      val shouldFailOnDataError = getShouldFailOnDataError()
      val feature = getFeature(content.javaClass) ?: return null
      DialogPresenter.setupAppCallForNativeDialog(
          appCall,
          object : DialogPresenter.ParameterProvider {
            override val parameters: Bundle?
              get() = NativeDialogParameters.create(appCall.callId, content, shouldFailOnDataError)
            override val legacyParameters: Bundle?
              get() =
                  LegacyNativeDialogParameters.create(
                      appCall.callId, content, shouldFailOnDataError)
          },
          feature)
      return appCall
    }
  }

  private fun logDialogShare(context: Context?, content: ShareContent<*, *>, mode: Mode) {
    val displayMode = if (isAutomaticMode) Mode.AUTOMATIC else mode
    val displayType =
        when (displayMode) {
          Mode.AUTOMATIC -> AnalyticsEvents.PARAMETER_SHARE_DIALOG_SHOW_AUTOMATIC
          Mode.WEB -> AnalyticsEvents.PARAMETER_SHARE_DIALOG_SHOW_WEB
          Mode.NATIVE -> AnalyticsEvents.PARAMETER_SHARE_DIALOG_SHOW_NATIVE
          else -> AnalyticsEvents.PARAMETER_SHARE_DIALOG_SHOW_UNKNOWN
        }
    val contentType: String
    val dialogFeature = getFeature(content.javaClass)
    contentType =
        when {
          dialogFeature === ShareDialogFeature.SHARE_DIALOG -> {
            AnalyticsEvents.PARAMETER_SHARE_DIALOG_CONTENT_STATUS
          }
          dialogFeature === ShareDialogFeature.PHOTOS -> {
            AnalyticsEvents.PARAMETER_SHARE_DIALOG_CONTENT_PHOTO
          }
          dialogFeature === ShareDialogFeature.VIDEO -> {
            AnalyticsEvents.PARAMETER_SHARE_DIALOG_CONTENT_VIDEO
          }
          else -> {
            AnalyticsEvents.PARAMETER_SHARE_DIALOG_CONTENT_UNKNOWN
          }
        }
    val logger = InternalAppEventsLogger.createInstance(context, FacebookSdk.getApplicationId())
    val parameters = Bundle()
    parameters.putString(AnalyticsEvents.PARAMETER_SHARE_DIALOG_SHOW, displayType)
    parameters.putString(AnalyticsEvents.PARAMETER_SHARE_DIALOG_CONTENT_TYPE, contentType)
    logger.logEventImplicitly(AnalyticsEvents.EVENT_SHARE_DIALOG_SHOW, parameters)
  }

  companion object {
    private val TAG = ShareDialog::class.java.simpleName
    private const val FEED_DIALOG = "feed"
    const val WEB_SHARE_DIALOG = "share"
    private const val WEB_OG_SHARE_DIALOG = "share_open_graph"
    private val DEFAULT_REQUEST_CODE = CallbackManagerImpl.RequestCodeOffset.Share.toRequestCode()

    /**
     * Helper to show the provided [com.facebook.share.model.ShareContent] using the provided
     * Activity. No callback will be invoked.
     *
     * @param activity Activity to use to share the provided content
     * @param shareContent Content to share
     */
    @JvmStatic
    open fun show(activity: Activity, shareContent: ShareContent<*, *>) {
      ShareDialog(activity).show(shareContent)
    }

    /**
     * Helper to show the provided [com.facebook.share.model.ShareContent] using the provided
     * Fragment. No callback will be invoked.
     *
     * @param fragment androidx.fragment.app.Fragment to use to share the provided content
     * @param shareContent Content to share
     */
    @JvmStatic
    open fun show(fragment: Fragment, shareContent: ShareContent<*, *>) {
      show(FragmentWrapper(fragment), shareContent)
    }

    /**
     * Helper to show the provided [com.facebook.share.model.ShareContent] using the provided
     * Fragment. No callback will be invoked.
     *
     * @param fragment android.app.Fragment to use to share the provided content
     * @param shareContent Content to share
     */
    @JvmStatic
    open fun show(fragment: android.app.Fragment, shareContent: ShareContent<*, *>) {
      show(FragmentWrapper(fragment), shareContent)
    }

    private fun show(fragmentWrapper: FragmentWrapper, shareContent: ShareContent<*, *>) {
      ShareDialog(fragmentWrapper).show(shareContent)
    }

    /**
     * Indicates whether it is possible to show the dialog for [ ] of the specified type.
     *
     * @param contentType Class of the intended [com.facebook.share.model.ShareContent] to share.
     * @return True if the specified content type can be shown via the dialog
     */
    @JvmStatic
    open fun canShow(contentType: Class<out ShareContent<*, *>>): Boolean {
      return canShowWebTypeCheck(contentType) || canShowNative(contentType)
    }

    private fun canShowNative(contentType: Class<out ShareContent<*, *>>): Boolean {
      val feature = getFeature(contentType)
      return feature != null && DialogPresenter.canPresentNativeDialogWithFeature(feature)
    }

    private fun canShowWebTypeCheck(contentType: Class<out ShareContent<*, *>>): Boolean {
      // If we don't have an instance of a ShareContent, then all we can do is check whether
      // this is a ShareLinkContent, which can be shared if configured properly.
      // The instance method version of this check is more accurate and should be used on
      // ShareDialog instances.

      // SharePhotoContent currently requires the user staging endpoint, so we need a user access
      // token, so we need to see if we have one
      return (ShareLinkContent::class.java.isAssignableFrom(contentType) ||
          (SharePhotoContent::class.java.isAssignableFrom(contentType) &&
              AccessToken.isCurrentAccessTokenActive()))
    }

    private fun canShowWebCheck(content: ShareContent<*, *>): Boolean {
      if (!canShowWebTypeCheck(content.javaClass)) {
        return false
      }
      return true
    }

    private fun getFeature(contentType: Class<out ShareContent<*, *>>): DialogFeature? {
      return when {
        ShareLinkContent::class.java.isAssignableFrom(contentType) -> {
          ShareDialogFeature.SHARE_DIALOG
        }
        SharePhotoContent::class.java.isAssignableFrom(contentType) -> {
          ShareDialogFeature.PHOTOS
        }
        ShareVideoContent::class.java.isAssignableFrom(contentType) -> {
          ShareDialogFeature.VIDEO
        }
        ShareMediaContent::class.java.isAssignableFrom(contentType) -> {
          ShareDialogFeature.MULTIMEDIA
        }
        ShareCameraEffectContent::class.java.isAssignableFrom(contentType) -> {
          CameraEffectFeature.SHARE_CAMERA_EFFECT
        }
        ShareStoryContent::class.java.isAssignableFrom(contentType) -> {
          ShareStoryFeature.SHARE_STORY_ASSET
        }
        else -> null
      }
    }
  }
}


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

package com.facebook

import android.content.Intent
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.facebook.FacebookSdk.getApplicationContext
import com.facebook.FacebookSdk.isInitialized
import com.facebook.util.common.mockLocalBroadcastManager
import java.util.Arrays
import java.util.Date
import java.util.concurrent.Executor
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito.mockStatic
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.Whitebox
import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.LooperMode

@LooperMode(LooperMode.Mode.LEGACY)
@PrepareForTest(FacebookSdk::class, LocalBroadcastManager::class)
class AccessTokenTrackerTest : FacebookPowerMockTestCase() {
  private val PERMISSIONS = Arrays.asList("walk", "chew gum")
  private val EXPIRES = Date(2025, 5, 3)
  private val LAST_REFRESH = Date(2023, 8, 15)
  private val DATA_ACCESS_EXPIRATION_TIME = Date(2025, 5, 3)
  private val APP_ID = "1234"
  private val USER_ID = "1000"
  private var accessTokenTracker: AccessTokenTracker? = null
  private var localBroadcastManager: LocalBroadcastManager? = null
  private val mockExecutor: Executor = FacebookSerialExecutor()
  @Before
  fun before() {
    mockStatic(FacebookSdk::class.java)
    whenever(isInitialized()).thenReturn(true)
    whenever(getApplicationContext()).thenReturn(RuntimeEnvironment.application)
    Whitebox.setInternalState(FacebookSdk::class.java, "executor", mockExecutor)
    localBroadcastManager = mockLocalBroadcastManager(RuntimeEnvironment.application)
    mockStatic(LocalBroadcastManager::class.java)
    whenever(LocalBroadcastManager.getInstance(getApplicationContext()))
        .thenReturn(localBroadcastManager)
  }

  @After
  fun after() {
    accessTokenTracker?.let {
      if (it.isTracking) {
        it.stopTracking()
      }
    }
  }

  @Test(expected = FacebookSdkNotInitializedException::class)
  fun testRequiresSdkToBeInitialized() {
    whenever(isInitialized()).thenReturn(false)
    accessTokenTracker = TestAccessTokenTracker()
  }

  @Test
  fun testDefaultsToTracking() {
    accessTokenTracker = TestAccessTokenTracker()
    assertThat(accessTokenTracker?.isTracking).isTrue
  }

  @Test
  fun testCanTurnTrackingOff() {
    accessTokenTracker = TestAccessTokenTracker()
    accessTokenTracker?.let {
      it.stopTracking()
      assertThat(it.isTracking).isFalse
    }
  }

  @Test
  fun testCanTurnTrackingOn() {
    accessTokenTracker = TestAccessTokenTracker()
    accessTokenTracker?.let {
      it.stopTracking()
      it.startTracking()
      assertThat(it.isTracking).isTrue
    }
  }

  @Test
  fun testCallbackCalledOnBroadcastReceived() {
    accessTokenTracker = TestAccessTokenTracker()
    val oldAccessToken = createAccessToken("I'm old!")
    val currentAccessToken = createAccessToken("I'm current!")
    sendBroadcast(oldAccessToken, currentAccessToken)
    val tracker = checkNotNull(accessTokenTracker) as TestAccessTokenTracker
    assertNotNull(tracker.currentAccessToken)
    assertEquals(currentAccessToken.token, tracker.currentAccessToken?.token)
    assertNotNull(tracker.oldAccessToken)
    assertEquals(oldAccessToken.token, tracker.oldAccessToken?.token)
  }

  private fun createAccessToken(tokenString: String): AccessToken {
    return AccessToken(
        tokenString,
        APP_ID,
        USER_ID,
        PERMISSIONS,
        null,
        null,
        AccessTokenSource.WEB_VIEW,
        EXPIRES,
        LAST_REFRESH,
        DATA_ACCESS_EXPIRATION_TIME)
  }

  private fun sendBroadcast(oldAccessToken: AccessToken, currentAccessToken: AccessToken) {
    val intent = Intent(AccessTokenManager.ACTION_CURRENT_ACCESS_TOKEN_CHANGED)
    intent.putExtra(AccessTokenManager.EXTRA_OLD_ACCESS_TOKEN, oldAccessToken)
    intent.putExtra(AccessTokenManager.EXTRA_NEW_ACCESS_TOKEN, currentAccessToken)
    localBroadcastManager?.sendBroadcast(intent)
  }

  internal inner class TestAccessTokenTracker : AccessTokenTracker() {
    var currentAccessToken: AccessToken? = null
    var oldAccessToken: AccessToken? = null
    override fun onCurrentAccessTokenChanged(
        oldAccessToken: AccessToken?,
        currentAccessToken: AccessToken?
    ) {
      this.oldAccessToken = oldAccessToken
      this.currentAccessToken = currentAccessToken
    }
  }
}


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

package com.facebook

import android.content.Intent
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.test.core.app.ApplicationProvider
import com.facebook.internal.security.OidcSecurityUtil
import com.facebook.login.AuthenticationTokenTestUtil
import com.facebook.util.common.mockLocalBroadcastManager
import java.security.PublicKey
import java.util.concurrent.Executor
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.Whitebox
import org.robolectric.RuntimeEnvironment

@PrepareForTest(FacebookSdk::class, LocalBroadcastManager::class, OidcSecurityUtil::class)
class AuthenticationTokenTrackerTest : FacebookPowerMockTestCase() {

  private lateinit var authenticationTokenTracker: TestAuthenticationTokenTracker
  private lateinit var localBroadcastManager: LocalBroadcastManager

  private val mockExecutor: Executor = FacebookSerialExecutor()

  @Before
  @Throws(Exception::class)
  fun before() {
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationContext())
        .thenReturn(ApplicationProvider.getApplicationContext())
    whenever(FacebookSdk.getApplicationId()).thenReturn(AuthenticationTokenTestUtil.APP_ID)

    // mock and bypass signature verification
    PowerMockito.mockStatic(OidcSecurityUtil::class.java)
    whenever(OidcSecurityUtil.getRawKeyFromEndPoint(any())).thenReturn("key")
    whenever(OidcSecurityUtil.getPublicKeyFromString(any()))
        .thenReturn(PowerMockito.mock(PublicKey::class.java))
    whenever(OidcSecurityUtil.verify(any(), any(), any())).thenReturn(true)

    Whitebox.setInternalState(FacebookSdk::class.java, "executor", mockExecutor)
    localBroadcastManager = mockLocalBroadcastManager(RuntimeEnvironment.application)
    PowerMockito.mockStatic(LocalBroadcastManager::class.java)
    whenever(LocalBroadcastManager.getInstance(FacebookSdk.getApplicationContext()))
        .thenReturn(localBroadcastManager)
    authenticationTokenTracker = TestAuthenticationTokenTracker()
  }

  @After
  fun after() {
    if (authenticationTokenTracker != null && authenticationTokenTracker.isTracking) {
      authenticationTokenTracker.stopTracking()
    }
  }

  @Test(expected = FacebookSdkNotInitializedException::class)
  fun testRequiresSdkToBeInitialized() {
    whenever(FacebookSdk.isInitialized()).thenReturn(false)
    authenticationTokenTracker = TestAuthenticationTokenTracker()
  }

  @Test
  fun testDefaultsToTracking() {
    authenticationTokenTracker = TestAuthenticationTokenTracker()
    assertThat(authenticationTokenTracker.isTracking).isTrue
  }

  @Test
  fun testCanTurnTrackingOff() {
    authenticationTokenTracker = TestAuthenticationTokenTracker()
    authenticationTokenTracker.stopTracking()
    assertThat(authenticationTokenTracker.isTracking).isFalse
  }

  @Test
  fun testCanTurnTrackingOn() {
    authenticationTokenTracker = TestAuthenticationTokenTracker()
    authenticationTokenTracker.stopTracking()
    authenticationTokenTracker.startTracking()
    assertThat(authenticationTokenTracker.isTracking).isTrue
  }

  @Test
  @Throws(java.lang.Exception::class)
  fun testCallbackCalledOnBroadcastReceived() {
    authenticationTokenTracker = TestAuthenticationTokenTracker()
    val oldAuthenticationToken = AuthenticationTokenTestUtil.getAuthenticationTokenForTest()
    val currentAuthenticationToken =
        AuthenticationTokenTestUtil.getAuthenticationTokenEmptyOptionalClaimsForTest()
    sendBroadcast(oldAuthenticationToken, currentAuthenticationToken)
    assertThat(authenticationTokenTracker.currentAuthenticationToken).isNotNull
    assertThat(currentAuthenticationToken.token)
        .isEqualTo(authenticationTokenTracker.currentAuthenticationToken?.token)
    assertThat(authenticationTokenTracker.oldAuthenticationToken).isNotNull
    assertThat(oldAuthenticationToken.token)
        .isEqualTo(authenticationTokenTracker.oldAuthenticationToken?.token)
  }

  private fun sendBroadcast(
      oldAuthenticationToken: AuthenticationToken,
      currentAuthenticationToken: AuthenticationToken
  ) {
    val intent = Intent(AuthenticationTokenManager.ACTION_CURRENT_AUTHENTICATION_TOKEN_CHANGED)
    intent.putExtra(
        AuthenticationTokenManager.EXTRA_OLD_AUTHENTICATION_TOKEN, oldAuthenticationToken)
    intent.putExtra(
        AuthenticationTokenManager.EXTRA_NEW_AUTHENTICATION_TOKEN, currentAuthenticationToken)
    localBroadcastManager.sendBroadcast(intent)
  }

  private class TestAuthenticationTokenTracker : AuthenticationTokenTracker() {
    var oldAuthenticationToken: AuthenticationToken? = null
    var currentAuthenticationToken: AuthenticationToken? = null

    override fun onCurrentAuthenticationTokenChanged(
        oldAuthenticationToken: AuthenticationToken?,
        currentAuthenticationToken: AuthenticationToken?
    ) {
      this.oldAuthenticationToken = oldAuthenticationToken
      this.currentAuthenticationToken = currentAuthenticationToken
    }
  }
}


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

package com.facebook

import android.app.Activity
import android.content.Intent
import android.net.Uri
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.robolectric.Robolectric
import org.robolectric.Shadows
import org.robolectric.android.controller.ActivityController

@PrepareForTest(LocalBroadcastManager::class)
class CustomTabActivityTest : FacebookPowerMockTestCase() {
  private lateinit var launchIntent: Intent
  private lateinit var mockLocalBroadcastManager: LocalBroadcastManager
  private lateinit var activityController: ActivityController<CustomTabActivity>
  override fun setup() {
    super.setup()
    launchIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://developers.facebook.com"))
    mockLocalBroadcastManager = mock()
    PowerMockito.mockStatic(LocalBroadcastManager::class.java)
    whenever(LocalBroadcastManager.getInstance(any())).thenReturn(mockLocalBroadcastManager)
    activityController = Robolectric.buildActivity(CustomTabActivity::class.java, launchIntent)
    activityController.create(null).start()
  }

  @Test
  fun `test CustomTabActivity start CustomTabMainActivity when it's created`() {
    val shadowCustomTabActivity = Shadows.shadowOf(activityController.get())
    val capturedIntent = shadowCustomTabActivity.nextStartedActivityForResult.intent
    assertThat(capturedIntent.action).isEqualTo(CustomTabActivity.CUSTOM_TAB_REDIRECT_ACTION)
    assertThat(capturedIntent.getStringExtra(CustomTabMainActivity.EXTRA_URL))
        .isEqualTo("https://developers.facebook.com")
  }

  @Test
  fun `test broadcasting intent if start activity failed`() {
    val shadowCustomTabActivity = Shadows.shadowOf(activityController.get())
    val capturedIntent = shadowCustomTabActivity.nextStartedActivityForResult.intent

    shadowCustomTabActivity.receiveResult(capturedIntent, Activity.RESULT_CANCELED, Intent())

    val intentCaptor = argumentCaptor<Intent>()
    verify(mockLocalBroadcastManager).sendBroadcast(intentCaptor.capture())
    verify(mockLocalBroadcastManager).registerReceiver(any(), any())

    assertThat(intentCaptor.firstValue.action)
        .isEqualTo(CustomTabActivity.CUSTOM_TAB_REDIRECT_ACTION)
    assertThat(intentCaptor.firstValue.getStringExtra(CustomTabMainActivity.EXTRA_URL))
        .isEqualTo("https://developers.facebook.com")
  }

  @Test
  fun `test destroy the activity will unregister the close receiver`() {
    val shadowCustomTabActivity = Shadows.shadowOf(activityController.get())
    val capturedIntent = shadowCustomTabActivity.nextStartedActivityForResult.intent
    shadowCustomTabActivity.receiveResult(capturedIntent, Activity.RESULT_CANCELED, Intent())
    activityController.destroy()
    verify(mockLocalBroadcastManager).unregisterReceiver(any())
  }

  @Test
  fun `test destroy the activity won't call unregister if there is no close receiver`() {
    activityController.destroy()
    verify(mockLocalBroadcastManager, never()).unregisterReceiver(any())
  }
}


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

package com.facebook

import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Intent
import android.content.IntentFilter
import androidx.browser.customtabs.CustomTabsIntent
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.Whitebox
import org.robolectric.Robolectric
import org.robolectric.Shadows
import org.robolectric.android.controller.ActivityController

@PrepareForTest(LocalBroadcastManager::class, CustomTabsIntent::class)
class CustomTabMainActivityTest : FacebookPowerMockTestCase() {
  private lateinit var activityController: ActivityController<CustomTabMainActivity>
  private lateinit var launchIntent: Intent
  private lateinit var mockLocalBroadcastManager: LocalBroadcastManager
  private lateinit var mockCustomTabsIntent: CustomTabsIntent
  private val mockChromePackageString = "mock chrome package string"

  override fun setup() {
    super.setup()
    mockLocalBroadcastManager = mock()
    PowerMockito.mockStatic(LocalBroadcastManager::class.java)
    PowerMockito.`when`(LocalBroadcastManager.getInstance(any()))
        .thenReturn(mockLocalBroadcastManager)
    mockCustomTabsIntent = mock()
    Whitebox.setInternalState(mockCustomTabsIntent, "intent", Intent(Intent.ACTION_VIEW))
    PowerMockito.whenNew(CustomTabsIntent::class.java)
        .withAnyArguments()
        .thenReturn(mockCustomTabsIntent)

    launchIntent = Intent(Intent.ACTION_VIEW)
    launchIntent.putExtra(CustomTabMainActivity.EXTRA_ACTION, "oauth_dialog")
    launchIntent.putExtra(CustomTabMainActivity.EXTRA_CHROME_PACKAGE, mockChromePackageString)
  }

  @Test
  fun `test launch with custom tab redirect action`() {
    launchIntent = Intent(CustomTabActivity.CUSTOM_TAB_REDIRECT_ACTION)
    activityController = Robolectric.buildActivity(CustomTabMainActivity::class.java, launchIntent)
    activityController.create(null)
    val activity = activityController.get()

    // verify the state of the activity
    assertThat(activity.isFinishing).isTrue
    val shadowActivity = Shadows.shadowOf(activity)
    assertThat(shadowActivity.resultCode).isEqualTo(Activity.RESULT_CANCELED)
  }

  @Test
  fun `test launch with an valid action will open custom tab`() {
    activityController = Robolectric.buildActivity(CustomTabMainActivity::class.java, launchIntent)
    activityController.create(null)
    val activity = activityController.get()
    val shadowActivity = Shadows.shadowOf(activity)
    // verify that custom tab is launched with CustomTabsIntent
    verify(mockCustomTabsIntent).launchUrl(eq(activity), any())

    // verify the redirect receiver is registered
    val receiverCaptor = argumentCaptor<BroadcastReceiver>()
    val intentFilterCaptor = argumentCaptor<IntentFilter>()
    verify(mockLocalBroadcastManager)
        .registerReceiver(receiverCaptor.capture(), intentFilterCaptor.capture())
    assertThat(
            intentFilterCaptor.firstValue.hasAction(CustomTabActivity.CUSTOM_TAB_REDIRECT_ACTION))
        .isTrue
    val capturedReceiver = receiverCaptor.firstValue

    // verify the redirect receiver will start refresh action on receive
    capturedReceiver.onReceive(activity, mock())
    assertThat(shadowActivity.nextStartedActivity.action)
        .isEqualTo(CustomTabMainActivity.REFRESH_ACTION)
  }

  @Test
  fun `test receiving intent of refresh action`() {
    activityController = Robolectric.buildActivity(CustomTabMainActivity::class.java, launchIntent)
    activityController.create(null)
    activityController.newIntent(Intent(CustomTabMainActivity.REFRESH_ACTION))

    // verify that it will broadcast destroy action
    val intentCaptor = argumentCaptor<Intent>()
    verify(mockLocalBroadcastManager).sendBroadcast(intentCaptor.capture())
    assertThat(intentCaptor.firstValue.action).isEqualTo(CustomTabActivity.DESTROY_ACTION)

    // verify the result code
    val shadowActivity = Shadows.shadowOf(activityController.get())
    assertThat(shadowActivity.resultCode).isEqualTo(Activity.RESULT_OK)
    assertThat(activityController.get().isFinishing).isTrue
  }

  @Test
  fun `test receiving intent of redirect action`() {
    activityController = Robolectric.buildActivity(CustomTabMainActivity::class.java, launchIntent)
    activityController.create(null)
    activityController.newIntent(Intent(CustomTabActivity.CUSTOM_TAB_REDIRECT_ACTION))

    // verify the result code
    val shadowActivity = Shadows.shadowOf(activityController.get())
    assertThat(shadowActivity.resultCode).isEqualTo(Activity.RESULT_OK)
    assertThat(activityController.get().isFinishing).isTrue
  }

  @Test
  fun `test resume`() {
    activityController = Robolectric.buildActivity(CustomTabMainActivity::class.java, launchIntent)
    activityController.create(null)
    activityController.resume()
    val activity = activityController.get()
    // first resume should not finish the activity.
    assertThat(activity.isFinishing).isFalse

    activityController.resume()
    // second resume will cancel the activity
    assertThat(activity.isFinishing).isTrue
    val shadowActivity = Shadows.shadowOf(activity)
    assertThat(shadowActivity.resultCode).isEqualTo(Activity.RESULT_CANCELED)
  }
}


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

package com.facebook

import android.app.Activity.RESULT_CANCELED
import android.content.Intent
import android.os.Bundle
import com.facebook.internal.FacebookDialogFragment
import com.facebook.internal.NativeProtocol
import com.facebook.login.LoginFragment
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.robolectric.Robolectric

@PrepareForTest(FacebookSdk::class, NativeProtocol::class)
class FacebookActivityTest : FacebookPowerMockTestCase() {
  private lateinit var activity: FacebookActivity
  @Before
  fun init() {
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)

    activity = spy(Robolectric.buildActivity(FacebookActivity::class.java).get())
    PowerMockito.doNothing().`when`(activity).setContentView(any<Int>())

    PowerMockito.mockStatic(NativeProtocol::class.java)
  }

  @Test
  fun `test start activity with default fragment`() {
    activity.onCreate(Bundle())
    val fragment = activity.currentFragment
    assertThat(fragment).isNotNull
    assertThat(fragment is LoginFragment).isTrue
  }

  @Test
  fun `test start activity with dialog fragment`() {
    val intent = Intent()
    intent.action = FacebookDialogFragment.TAG
    whenever(activity.intent).thenReturn(intent)
    whenever(NativeProtocol.getMethodArgumentsFromIntent(any())).thenReturn(Bundle())

    activity.onCreate(Bundle())
    val fragment = activity.currentFragment
    assertThat(fragment).isNotNull
    assertThat(fragment is FacebookDialogFragment).isTrue
  }

  @Test
  fun `test start activity to handle error`() {
    val intent = Intent()
    intent.action = FacebookActivity.PASS_THROUGH_CANCEL_ACTION
    whenever(activity.intent).thenReturn(intent)

    var nativeProtocolCount = 0
    val exception = FacebookException()
    val resultIntent = Intent()

    whenever(NativeProtocol.getMethodArgumentsFromIntent(any())).thenAnswer {
      nativeProtocolCount++
      Bundle()
    }
    whenever(NativeProtocol.getExceptionFromErrorData(any())).thenAnswer {
      nativeProtocolCount++
      exception
    }

    whenever(NativeProtocol.createProtocolResultIntent(intent, null, exception)).thenAnswer {
      nativeProtocolCount++
      resultIntent
    }

    activity.onCreate(Bundle())

    assertThat(nativeProtocolCount).isEqualTo(3)
    verify(activity).setResult(RESULT_CANCELED, resultIntent)
    verify(activity).finish()
  }
}


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

package com.facebook

import android.app.Activity
import android.content.Context
import android.util.AttributeSet
import android.view.View
import com.facebook.appevents.InternalAppEventsLogger
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.atLeastOnce
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.powermock.reflect.Whitebox
import org.robolectric.Robolectric
import org.robolectric.android.controller.ActivityController

class FacebookButtonBaseTest : FacebookPowerMockTestCase() {
  class TestButton(context: Context, attrs: AttributeSet) :
      FacebookButtonBase(
          context, attrs, 0, 0, BUTTON_CREATED_EVENT_NAME, BUTTON_TAPPED_EVENT_NAME) {
    override val defaultRequestCode: Int = DEFAULT_REQUEST_CODE
    public override val activity: Activity = super.activity

    public override fun setInternalOnClickListener(listener: OnClickListener?) {
      super.setInternalOnClickListener(listener)
    }
  }

  private lateinit var activityController: ActivityController<Activity>
  private lateinit var testAttributeSet: AttributeSet
  private lateinit var testButton: TestButton
  private lateinit var mockInternalAppEventsLogger: InternalAppEventsLogger

  override fun setup() {
    super.setup()
    mockInternalAppEventsLogger = mock()
    val mockInternalAppEventsLoggerCompanion = mock<InternalAppEventsLogger.Companion>()
    whenever(mockInternalAppEventsLoggerCompanion.createInstance(anyOrNull(), anyOrNull()))
        .thenReturn(mockInternalAppEventsLogger)
    Whitebox.setInternalState(
        InternalAppEventsLogger::class.java, "Companion", mockInternalAppEventsLoggerCompanion)

    activityController = Robolectric.buildActivity(Activity::class.java)
    val activity = activityController.get()
    testAttributeSet = Robolectric.buildAttributeSet().build()
    testButton = TestButton(activity, testAttributeSet)
  }

  @Test
  fun `facebook button is clickable and focusable`() {
    assertThat(testButton.isFocusable).isTrue
    assertThat(testButton.isClickable).isTrue
  }

  @Test
  fun `test getting activity will return the host activity`() {
    assertThat(testButton.activity).isEqualTo(activityController.get())
  }

  @Test
  fun `test getting request code will return the default one`() {
    assertThat(testButton.requestCode).isEqualTo(DEFAULT_REQUEST_CODE)
  }

  @Test
  fun `test only the internal onClick listener will be invoked when the button is clicked`() {
    val mockInternalOnClickListener = mock<View.OnClickListener>()
    val mockExternalOnClickListener = mock<View.OnClickListener>()
    testButton.setInternalOnClickListener(mockInternalOnClickListener)
    testButton.setOnClickListener(mockExternalOnClickListener)

    testButton.performClick()

    verify(mockInternalOnClickListener, atLeastOnce()).onClick(testButton)
    verify(mockInternalAppEventsLogger, atLeastOnce()).logEventImplicitly(BUTTON_TAPPED_EVENT_NAME)
    verify(mockExternalOnClickListener, never()).onClick(anyOrNull())
  }

  @Test
  fun `test external onClick listener will be invoked when the button is clicked and the internal listener is not available`() {
    val mockExternalOnClickListener = mock<View.OnClickListener>()
    testButton.setInternalOnClickListener(null)
    testButton.setOnClickListener(mockExternalOnClickListener)

    testButton.performClick()

    verify(mockInternalAppEventsLogger, atLeastOnce()).logEventImplicitly(BUTTON_TAPPED_EVENT_NAME)
    verify(mockExternalOnClickListener, atLeastOnce()).onClick(anyOrNull())
  }

  companion object {
    private const val BUTTON_CREATED_EVENT_NAME = "testAnalyticsButtonCreatedEventName"
    private const val BUTTON_TAPPED_EVENT_NAME = "testAnalyticsButtonTappedEventName"
    private const val DEFAULT_REQUEST_CODE = 42
  }
}


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

package com.facebook

import android.annotation.TargetApi
import android.os.Parcel
import android.os.Parcelable
import org.assertj.core.api.Assertions.fail
import org.json.JSONArray
import org.json.JSONObject

object TestUtils {
  const val DOUBLE_EQUALS_DELTA = 0.00001

  @JvmStatic
  fun <E : Parcelable> parcelAndUnparcel(obj: E): E? {
    val writeParcel = Parcel.obtain()
    val readParcel = Parcel.obtain()
    return try {
      writeParcel.writeParcelable(obj, 0)
      val bytes = writeParcel.marshall()
      readParcel.unmarshall(bytes, 0, bytes.size)
      readParcel.setDataPosition(0)
      readParcel.readParcelable(obj.javaClass.classLoader)
    } finally {
      writeParcel.recycle()
      readParcel.recycle()
    }
  }

  @TargetApi(16)
  @JvmStatic
  fun assertEquals(expected: JSONObject?, actual: JSONObject?) {
    // JSONObject.equals does not do an order-independent comparison, so let's roll our own  :(
    if (areEqual(expected, actual)) {
      return
    }
    return fail("expected: <$expected>, but was: <$actual>")
  }

  @TargetApi(16)
  @JvmStatic
  fun assertEquals(expected: JSONArray?, actual: JSONArray?) {
    // JSONObject.equals does not do an order-independent comparison, so let's roll our own  :(
    if (areEqual(expected, actual)) {
      return
    }
    return fail("expected: <$expected>, but was: <$actual>")
  }

  private fun areEqual(expected: JSONObject?, actual: JSONObject?): Boolean {
    // JSONObject.equals does not do an order-independent comparison, so let's roll our own  :(
    if (expected === actual) {
      return true
    }
    if (expected == null || actual == null) {
      return false
    }
    val expectedKeys = expected.keys().asSequence().toHashSet()
    for (key in actual.keys()) {
      if (!areEqual(expected.opt(key), actual.opt(key))) {
        return false
      }
      expectedKeys.remove(key)
    }
    return expectedKeys.isEmpty()
  }

  private fun areEqual(expected: JSONArray?, actual: JSONArray?): Boolean {
    // JSONObject.equals does not do an order-independent comparison, so we need to check values
    // that are JSONObject
    // manually
    if (expected === actual) {
      return true
    }
    if (expected == null || actual == null) {
      return false
    }
    if (expected.length() != actual.length()) {
      return false
    }
    val length = expected.length()
    for (i in 0 until length) {
      if (!areEqual(expected.opt(i), actual.opt(i))) {
        return false
      }
    }
    return true
  }

  private fun areEqual(expected: Any?, actual: Any?): Boolean {
    if (expected === actual) {
      return true
    }
    if (expected == null || actual == null) {
      return false
    }
    if (expected is JSONObject && actual is JSONObject) {
      return areEqual(expected as JSONObject?, actual as JSONObject?)
    }
    return if (expected is JSONArray && actual is JSONArray) {
      areEqual(expected as JSONArray?, actual as JSONArray?)
    } else expected == actual
  }
}


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

package com.facebook.devicerequests.internal

import android.content.Context
import android.graphics.Color
import android.net.nsd.NsdManager
import android.net.nsd.NsdServiceInfo
import android.os.Build
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.internal.FetchedAppSettings
import com.facebook.internal.FetchedAppSettingsManager
import com.facebook.internal.SmartLoginOption
import java.util.EnumSet
import org.assertj.core.api.Assertions.assertThat
import org.json.JSONObject
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(FetchedAppSettingsManager::class, FacebookSdk::class)
class DeviceRequestsHelperTest : FacebookPowerMockTestCase() {
  private lateinit var mockApplicationContext: Context
  private lateinit var mockNsdManager: NsdManager

  override fun setup() {
    super.setup()
    PowerMockito.mockStatic(FetchedAppSettingsManager::class.java)
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationId()).thenReturn(APP_ID)
    whenever(FacebookSdk.getSdkVersion()).thenReturn(TEST_VERSION_NUMBER)

    mockApplicationContext = mock()
    mockNsdManager = mock()
    whenever(FacebookSdk.getApplicationContext()).thenReturn(mockApplicationContext)
    whenever(mockApplicationContext.getSystemService(Context.NSD_SERVICE))
        .thenReturn(mockNsdManager)
  }

  @Test
  fun `test getting device info`() {
    val deviceInfoStr = DeviceRequestsHelper.getDeviceInfo()
    val deviceInfo = JSONObject(deviceInfoStr)
    assertThat(deviceInfo.getString(DeviceRequestsHelper.DEVICE_INFO_MODEL)).isEqualTo(Build.MODEL)
    assertThat(deviceInfo.getString(DeviceRequestsHelper.DEVICE_INFO_DEVICE))
        .isEqualTo(Build.DEVICE)
  }

  @Test
  fun `test getting device info associated with other information`() {
    val extraInfo = mutableMapOf("key" to "value")
    val deviceInfoStr = DeviceRequestsHelper.getDeviceInfo(extraInfo)
    val deviceInfo = JSONObject(deviceInfoStr)
    assertThat(deviceInfo.getString(DeviceRequestsHelper.DEVICE_INFO_MODEL)).isEqualTo(Build.MODEL)
    assertThat(deviceInfo.getString(DeviceRequestsHelper.DEVICE_INFO_DEVICE))
        .isEqualTo(Build.DEVICE)
    assertThat(deviceInfo.getString("key")).isEqualTo("value")
    assertThat(extraInfo[DeviceRequestsHelper.DEVICE_INFO_DEVICE]).isEqualTo(Build.DEVICE)
    assertThat(extraInfo[DeviceRequestsHelper.DEVICE_INFO_MODEL]).isEqualTo(Build.MODEL)
  }

  @Test
  fun `test obtaining smart login option availability when it's enabled`() {
    val mockFetchedAppSettings = mock<FetchedAppSettings>()
    whenever(mockFetchedAppSettings.smartLoginOptions)
        .thenReturn(EnumSet.of(SmartLoginOption.Enabled))
    whenever(FetchedAppSettingsManager.getAppSettingsWithoutQuery(anyOrNull()))
        .thenReturn(mockFetchedAppSettings)

    assertThat(DeviceRequestsHelper.isAvailable()).isTrue
  }

  @Test
  fun `test obtaining smart login option when it's disabled`() {
    val mockFetchedAppSettings = mock<FetchedAppSettings>()
    whenever(mockFetchedAppSettings.smartLoginOptions).thenReturn(EnumSet.of(SmartLoginOption.None))
    whenever(FetchedAppSettingsManager.getAppSettingsWithoutQuery(anyOrNull()))
        .thenReturn(mockFetchedAppSettings)

    assertThat(DeviceRequestsHelper.isAvailable()).isFalse
  }

  @Test
  fun `test start and clean up advertisement service`() {
    val mockFetchedAppSettings = mock<FetchedAppSettings>()
    whenever(mockFetchedAppSettings.smartLoginOptions)
        .thenReturn(EnumSet.of(SmartLoginOption.Enabled))
    whenever(FetchedAppSettingsManager.getAppSettingsWithoutQuery(anyOrNull()))
        .thenReturn(mockFetchedAppSettings)

    DeviceRequestsHelper.startAdvertisementService(TEST_USER_CODE)

    val nsdServiceInfoCaptor = argumentCaptor<NsdServiceInfo>()
    val nsdListenerCaptor = argumentCaptor<NsdManager.RegistrationListener>()
    verify(mockNsdManager)
        .registerService(nsdServiceInfoCaptor.capture(), any(), nsdListenerCaptor.capture())
    val capturedNsdServiceInfo = nsdServiceInfoCaptor.firstValue
    // verify the service name
    assertThat(capturedNsdServiceInfo.serviceName)
        .isEqualTo("fbsdk_android-12|34|567_$TEST_USER_CODE")

    DeviceRequestsHelper.cleanUpAdvertisementService(TEST_USER_CODE)
    verify(mockNsdManager).unregisterService(nsdListenerCaptor.firstValue)
  }

  @Test
  fun `test generated QR code only black and white`() {
    val encodedURL = "https://facebook.com/test"
    val image = DeviceRequestsHelper.generateQRCode(encodedURL)
    val pixels = IntArray(200 * 200)
    checkNotNull(image)
    image.getPixels(pixels, 0, 200, 0, 0, 200, 200)
    assertThat(pixels).containsOnly(Color.BLACK, Color.WHITE)
  }

  companion object {
    private const val TEST_USER_CODE = "123456"
    private const val APP_ID = "123456789"
    private const val TEST_VERSION_NUMBER = "12.34.567"
  }
}


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

package com.facebook.internal

import com.facebook.FacebookPowerMockTestCase
import java.util.UUID
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test

class AppCallTest : FacebookPowerMockTestCase() {
  private lateinit var uuid1: UUID
  private lateinit var uuid2: UUID
  private lateinit var appCall1: AppCall
  private lateinit var appCall2: AppCall
  @Before
  fun init() {
    uuid1 = UUID.randomUUID()
    uuid2 = UUID.randomUUID()
    appCall1 = AppCall(1, uuid1)
    appCall2 = AppCall(2, uuid2)
  }
  @Test
  fun `test reset current pending AppCall`() {
    appCall1.setPending()
    assertThat(appCall2.setPending()).isTrue
  }

  @Test
  fun `test finish current pending AppCall`() {
    appCall1.setPending()
    val pendingAppCall = AppCall.finishPendingCall(uuid1, 1)
    assertThat(pendingAppCall).isEqualTo(appCall1)
  }

  @Test
  fun `test finish wrong current pending AppCall`() {
    appCall1.setPending()
    val pendingAppCall = AppCall.finishPendingCall(uuid2, 2)
    assertThat(pendingAppCall).isNull()
  }
}


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

package com.facebook.internal

import android.content.Intent
import android.os.Bundle
import androidx.browser.customtabs.CustomTabsIntent
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.login.CustomTabLoginMethodHandler
import com.facebook.login.CustomTabPrefetchHelper
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.Whitebox

@PrepareForTest(CustomTabsIntent::class)
class CustomTabTest : FacebookPowerMockTestCase() {
  private lateinit var mockCustomTabsIntent: CustomTabsIntent
  private lateinit var parameters: Bundle

  @Before
  fun init() {
    parameters = Bundle()
    parameters.putString(ServerProtocol.DIALOG_PARAM_SCOPE, "user_name,user_birthday")

    mockCustomTabsIntent = mock()
    Whitebox.setInternalState(mockCustomTabsIntent, "intent", Intent(Intent.ACTION_VIEW))

    PowerMockito.whenNew(CustomTabsIntent::class.java)
        .withAnyArguments()
        .thenReturn(mockCustomTabsIntent)

    val mockCustomTabPrefetchHelperCompanion = mock<CustomTabPrefetchHelper.Companion>()
    Whitebox.setInternalState(
        CustomTabPrefetchHelper::class.java, "Companion", mockCustomTabPrefetchHelperCompanion)
  }

  @Test
  fun `test get URI for action`() {
    val uri = CustomTab.getURIForAction(CustomTabLoginMethodHandler.OAUTH_DIALOG, parameters)
    val version = FacebookSdk.getGraphApiVersion()
    assertThat(uri.toString())
        .isEqualTo("https://m.facebook.com/$version/dialog/oauth?scope=user_name%2Cuser_birthday")
  }

  @Test
  fun `test open custom tab`() {
    val customTab = CustomTab(CustomTabLoginMethodHandler.OAUTH_DIALOG, parameters)
    customTab.openCustomTab(mock(), "com.facebook.internal")
    verify(mockCustomTabsIntent).launchUrl(any(), any())
  }
}


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

package com.facebook.internal

import androidx.test.core.app.ApplicationProvider
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(FacebookSdk::class, Validate::class)
class CustomTabUtilsTest : FacebookPowerMockTestCase() {
  override fun setup() {
    super.setup()
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationContext())
        .thenReturn(ApplicationProvider.getApplicationContext())
    PowerMockito.mockStatic(Validate::class.java)
  }

  @Test
  fun `test get valid redirect URI if developer defined redirect is valid`() {
    whenever(Validate.hasCustomTabRedirectActivity(any(), any())).thenReturn(true)
    assertThat(CustomTabUtils.getValidRedirectURI(TEST_DOMAIN)).isEqualTo(TEST_DOMAIN)
  }

  @Test
  fun `test get valid redirect URI if no redirect is not valid`() {
    whenever(Validate.hasCustomTabRedirectActivity(any(), any())).thenReturn(false)
    assertThat(CustomTabUtils.getValidRedirectURI(TEST_DOMAIN)).isEqualTo("")
  }

  companion object {
    const val TEST_DOMAIN = "protocol://test-domain/1234"
  }
}