Incorporar GIFs em Cartões de Conteúdo
Aprenda como incorporar GIFs em Cartões de Conteúdo usando o SDK do Braze.

Para SDKs wrapper não listados, use o método nativo relevante do Android ou Swift. Lembre-se de que os SDKs do Braze para Android e Swift não suportam GIFs animados nativamente, então você implementará GIFs em Cartões de Conteúdo usando ferramentas de terceiros.
O suporte a GIF está incluído por padrão na integração do SDK Web.
Sobre GIFs
A Braze oferece a capacidade de usar uma biblioteca de imagens personalizada para exibir GIFs animados. Embora o exemplo a seguir use o Glide, qualquer biblioteca de imagens que ofereça suporte a GIFs é compatível.
Integração de uma biblioteca de imagens personalizada
Etapa 1: Criando o delegate do carregador de imagens
O delegate do carregador de imagens deve implementar os seguintes métodos:
getInAppMessageBitmapFromUrl()getPushBitmapFromUrl()renderUrlIntoCardView()renderUrlIntoInAppMessageView()setOffline()
O exemplo de integração a seguir foi retirado do app de exemplo de integração com Glide incluído no SDK Android da Braze.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import com.braze.support.BrazeLogger;
import com.bumptech.glide.load.resource.gif.GifDrawable;
import android.graphics.drawable.Drawable;
public class GlideBrazeImageLoader implements IBrazeImageLoader {
private static final String TAG = GlideBrazeImageLoader.class.getName();
private RequestOptions mRequestOptions = new RequestOptions();
@Override
public void renderUrlIntoCardView(Context context, Card card, String imageUrl, ImageView imageView, BrazeViewBounds viewBounds) {
renderUrlIntoView(context, imageUrl, imageView);
}
@Override
public void renderUrlIntoInAppMessageView(Context context, IInAppMessage inAppMessage, String imageUrl, ImageView imageView, BrazeViewBounds viewBounds) {
renderUrlIntoView(context, imageUrl, imageView);
}
@Override
public Bitmap getPushBitmapFromUrl(Context context, Bundle extras, String imageUrl, BrazeViewBounds viewBounds) {
return getBitmapFromUrl(context, imageUrl, viewBounds);
}
@Override
public Bitmap getInAppMessageBitmapFromUrl(Context context, IInAppMessage inAppMessage, String imageUrl, BrazeViewBounds viewBounds) {
return getBitmapFromUrl(context, imageUrl, viewBounds);
}
private void renderUrlIntoView(Context context, String imageUrl, ImageView imageView) {
try {
final Drawable drawable = Glide.with(context)
.load(imageUrl)
.apply(mRequestOptions)
.submit()
.get();
imageView.post(() -> {
imageView.setImageDrawable(drawable);
if (drawable instanceof GifDrawable) {
((GifDrawable) drawable).start();
}
});
} catch (Exception e) {
BrazeLogger.e(TAG, "Failed to render URL into view: " + imageUrl, e);
}
}
private Bitmap getBitmapFromUrl(Context context, String imageUrl, BrazeViewBounds viewBounds) {
try {
return Glide.with(context)
.asBitmap()
.apply(mRequestOptions)
.load(imageUrl).submit().get();
} catch (Exception e) {
Log.e(TAG, "Failed to retrieve bitmap at url: " + imageUrl, e);
}
return null;
}
@Override
public void setOffline(boolean isOffline) {
// If the loader is offline, then we should only be retrieving from the cache
mRequestOptions = mRequestOptions.onlyRetrieveFromCache(isOffline);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import com.braze.support.BrazeLogger
import com.bumptech.glide.load.resource.gif.GifDrawable
class GlideBrazeImageLoader : IBrazeImageLoader {
companion object {
private val TAG = GlideBrazeImageLoader::class.qualifiedName
}
private var mRequestOptions = RequestOptions()
override fun renderUrlIntoCardView(context: Context, card: Card, imageUrl: String, imageView: ImageView, viewBounds: BrazeViewBounds) {
renderUrlIntoView(context, imageUrl, imageView)
}
override fun renderUrlIntoInAppMessageView(context: Context, inAppMessage: IInAppMessage, imageUrl: String, imageView: ImageView, viewBounds: BrazeViewBounds) {
renderUrlIntoView(context, imageUrl, imageView)
}
override fun getPushBitmapFromUrl(context: Context, extras: Bundle, imageUrl: String, viewBounds: BrazeViewBounds): Bitmap? {
return getBitmapFromUrl(context, imageUrl, viewBounds)
}
override fun getInAppMessageBitmapFromUrl(context: Context, inAppMessage: IInAppMessage, imageUrl: String, viewBounds: BrazeViewBounds): Bitmap? {
return getBitmapFromUrl(context, imageUrl, viewBounds)
}
private fun renderUrlIntoView(context: Context, imageUrl: String, imageView: ImageView) {
try {
val drawable = Glide.with(context)
.load(imageUrl)
.apply(mRequestOptions)
.submit()
.get()
imageView.post {
imageView.setImageDrawable(drawable)
if (drawable is GifDrawable) {
drawable.start()
}
}
} catch (e: Exception) {
BrazeLogger.e(TAG, "Failed to render URL into view: $imageUrl", e)
}
}
private fun getBitmapFromUrl(context: Context, imageUrl: String, viewBounds: BrazeViewBounds): Bitmap? {
try {
return Glide.with(context)
.asBitmap()
.apply(mRequestOptions)
.load(imageUrl).submit().get()
} catch (e: Exception) {
Log.e(TAG, "Failed to retrieve bitmap at url: $imageUrl", e)
}
return null
}
override fun setOffline(isOffline: Boolean) {
// If the loader is offline, then we should only be retrieving from the cache
mRequestOptions = mRequestOptions.onlyRetrieveFromCache(isOffline)
}
}
Correção do carregamento de imagens para o SDK Android 36.0.0 e posterior
No SDK Android 36.0.0 e posterior, displayInAppMessage() é uma função suspend. Isso significa que renderUrlIntoInAppMessageView() é executado em uma thread de segundo plano em vez da thread principal.
Se o seu carregador de imagens personalizado chamar Glide.into(imageView) em renderUrlIntoInAppMessageView(), seu app pode falhar com “You must call this method on the main thread.”
Para evitar isso:
- Carregue a imagem na thread de segundo plano com
submit().get(). - Publique a atualização da UI na thread principal com
imageView.post { ... }. - Se o resultado carregado for um GIF drawable, inicie a animação após defini-lo na view.
Isso separa o carregamento de imagens da renderização da UI e mantém seu carregador de imagens personalizado compatível com o SDK Android 36.0.0 e posterior.
Essa orientação se aplica a carregadores de imagens personalizados para Android. Mensagens no app para web suportam GIFs nativamente.
O exemplo em Kotlin a seguir usa valores de placeholder para demonstrar esse padrão:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private const val TAG = "SampleGlideLoader"
private const val glideBrazeImageLoaderTag = "sample-loader"
private fun renderUrlIntoView(
context: Context,
imageUrl: String,
imageView: ImageView
) {
try {
val drawable: Drawable = Glide.with(context)
.load(imageUrl)
.apply(mRequestOptions)
.submit()
.get()
imageView.post {
imageView.setImageDrawable(drawable)
if (drawable is GifDrawable) {
drawable.start()
}
}
} catch (e: Exception) {
Log.e(TAG, "$glideBrazeImageLoaderTag renderUrlIntoView failed: url=$imageUrl", e)
}
}
Etapa 2: Definindo o delegate do carregador de imagens
O SDK da Braze usará qualquer carregador de imagens personalizado definido com IBrazeImageLoader. Recomendamos definir o carregador de imagens personalizado em uma subclasse de aplicativo personalizada:
1
2
3
4
5
6
7
public class GlideIntegrationApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Braze.getInstance(context).setImageLoader(new GlideBrazeImageLoader());
}
}
1
2
3
4
5
6
class GlideIntegrationApplication : Application() {
override fun onCreate() {
super.onCreate()
Braze.getInstance(context).imageLoader = GlideBrazeImageLoader()
}
}
Solução de problemas de carregamento de imagens com Glide
Se as imagens pararem de carregar após você definir um IBrazeImageLoader personalizado (por exemplo, com Glide), verifique se um interceptor global do OkHttp está adicionando cabeçalhos de autenticação a cada requisição.
O exemplo com Glide nesta página usa o mesmo caminho de carregamento para Content Cards, mensagens no app e push. As imagens hospedadas pela Braze são URLs de CDN e não utilizam a autenticação da sua REST API. Limite os interceptors aos seus próprios hosts de API ou exclua os hosts de imagens da Braze. Uma imagem de Content Card que falha após a integração com Glide é um sintoma comum desse padrão de interceptor.
Carregamento personalizado de imagens com Jetpack Compose
Para substituir o carregamento de imagens com Jetpack Compose, você pode passar um valor para imageComposable. Essa função receberá um Card e renderizará a imagem e os modificadores necessários. Como alternativa, você pode usar customCardComposer de ContentCardsList para renderizar o cartão inteiro.
No exemplo a seguir, a biblioteca Compose do Glide é usada para os cartões listados na função imageComposable:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
ContentCardsList(
cardStyle = ContentCardStyling(
imageComposable = { card ->
when (card.cardType) {
CardType.CAPTIONED_IMAGE -> {
val captionedImageCard = card as CaptionedImageCard
GlideImage(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.run {
if (captionedImageCard.aspectRatio > 0) {
aspectRatio(captionedImageCard.aspectRatio)
} else {
this
}
},
contentScale = ContentScale.Crop,
model = captionedImageCard.url,
loading = placeholder(R.drawable.pushpin),
contentDescription = ""
)
}
CardType.IMAGE -> {
val imageOnlyCard = card as ImageOnlyCard
GlideImage(
modifier = Modifier
.fillMaxWidth()
.run {
if (imageOnlyCard.aspectRatio > 0) {
aspectRatio(imageOnlyCard.aspectRatio)
} else {
this
}
},
contentScale = ContentScale.Crop,
model = imageOnlyCard.url,
loading = placeholder(R.drawable.pushpin),
contentDescription = ""
)
}
CardType.SHORT_NEWS -> {
val shortNews = card as ShortNewsCard
GlideImage(
modifier = Modifier
.width(100.dp)
.height(100.dp),
model = shortNews.url,
loading = placeholder(R.drawable.pushpin),
contentDescription = ""
)
}
else -> Unit
}
}
)
)
Pré-requisitos
Antes de poder usar esse recurso, você precisará integrar o Swift Braze SDK.
Integração de uma biblioteca de imagens personalizada
Etapa 1: Integre SDWebImage
Integre o repositório SDWebImage ao seu projeto Xcode.
Etapa 2: Crie um novo arquivo Swift
No seu projeto Xcode, crie um novo arquivo chamado SDWebImageGIFViewProvider.swift e importe o seguinte:
1
2
3
import UIKit
import BrazeUI
import SDWebImage
Etapa 3: Adicione GIFViewProvider
Em seguida, adicione nosso exemplo SDWebImage GIFViewProvider. Seu arquivo deve ser semelhante ao seguinte:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import UIKit
import BrazeUI
import SDWebImage
extension GIFViewProvider {
/// A GIF view provider using [SDWebImage](https://github.com/SDWebImage/SDWebImage) as a
/// rendering library.
public static let sdWebImage = Self(
view: { SDAnimatedImageView(image: image(for: $0)) },
updateView: { ($0 as? SDAnimatedImageView)?.image = image(for: $1) }
)
private static func image(for url: URL?) -> UIImage? {
guard let url else { return nil }
return url.pathExtension == "gif"
? SDAnimatedImage(contentsOfFile: url.path)
: UIImage(contentsOfFile: url.path)
}
}
Etapa 4: Modifique seu AppDelegate.swift
No AppDelegate.swift do seu projeto, adicione suporte a GIF aos seus componentes BrazeUI usando GIFViewProvider. Seu arquivo deve ser semelhante ao seguinte:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import UIKit
import BrazeKit
import BrazeUI
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
static var braze: Braze? = nil
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
/* ... */
GIFViewProvider.shared = .sdWebImage
return true
}
}