Skip to content

GIFs in Content-Cards einbetten

Erfahren Sie, wie Sie mit dem Braze SDK GIFs in Content-Cards einbetten können.

Die GIF-Unterstützung ist standardmäßig in der Web SDK-Integration enthalten.

Über GIFs

Braze bietet die Möglichkeit, eine angepasste Bilderbibliothek zu verwenden, um animierte GIFs anzuzeigen. Obwohl das folgende Beispiel Glide verwendet, ist jede Bilderbibliothek kompatibel, die GIFs unterstützt.

Eigene Bilderbibliotheken einbinden

Schritt 1: Image-Loader-Delegate erstellen

Das Image-Loader-Delegate muss die folgenden Methoden implementieren:

Das folgende Integrationsbeispiel stammt aus der Glide-Integrations-Beispiel-App, die im Braze Android SDK enthalten ist.

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)
  }
}

Fehlerbehebung beim Laden von Bildern ab Android SDK 36.0.0

Ab Android SDK 36.0.0 ist displayInAppMessage() eine suspend-Funktion. Das bedeutet, dass renderUrlIntoInAppMessageView() auf einem Hintergrund-Thread statt auf dem Haupt-Thread ausgeführt wird.

Wenn Ihr angepasster Image-Loader Glide.into(imageView) in renderUrlIntoInAppMessageView() aufruft, kann Ihre App mit der Meldung „You must call this method on the main thread.“ fehlschlagen.

Um dies zu vermeiden:

  1. Laden Sie das Bild auf dem Hintergrund-Thread mit submit().get().
  2. Posten Sie das UI-Update auf den Haupt-Thread mit imageView.post { ... }.
  3. Wenn das geladene Ergebnis ein GIF-Drawable ist, starten Sie die Animation, nachdem Sie es in der View gesetzt haben.

Dadurch wird das Laden von Bildern von der UI-Darstellung getrennt, und Ihr angepasster Image-Loader bleibt kompatibel mit Android SDK 36.0.0 und höher.

Diese Anleitung gilt für angepasste Image-Loader unter Android. In-App-Nachrichten im Web unterstützen GIFs standardmäßig.

Das folgende Kotlin-Beispiel verwendet Platzhalter, um dieses Muster zu veranschaulichen:

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)
    }
}

Schritt 2: Image-Loader-Delegate festlegen

Das Braze SDK verwendet jeden angepassten Image-Loader, der mit IBrazeImageLoader gesetzt wird. Wir empfehlen, den angepassten Image-Loader in einer angepassten Application-Unterklasse festzulegen:

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()
  }
}

Fehlerbehebung beim Laden von Glide-Bildern

Wenn Bilder nach dem Setzen eines angepassten IBrazeImageLoader (zum Beispiel mit Glide) nicht mehr geladen werden, prüfen Sie, ob ein globaler OkHttp-Interceptor allen Anfragen Authentifizierungs-Header hinzufügt.

Das Glide-Beispiel auf dieser Seite verwendet denselben Ladepfad für Content Cards, In-App-Nachrichten und Push. Von Braze gehostete Bilder sind CDN-URLs und verwenden nicht Ihre REST API-Authentifizierung. Beschränken Sie Interceptors auf Ihre eigenen API-Hosts oder schließen Sie Braze-Bild-Hosts aus. Ein Content-Card-Bild, das nach einer Glide-Integration fehlschlägt, ist ein häufiges Symptom für dieses Interceptor-Muster.

Eigenes Laden von Bildern mit Jetpack Compose

Um das Laden von Bildern mit Jetpack Compose zu überschreiben, können Sie einen Wert an imageComposable übergeben. Diese Funktion nimmt eine Card entgegen und rendert das Bild sowie die benötigten Modifier. Alternativ können Sie customCardComposer von ContentCardsList verwenden, um die gesamte Card zu rendern.

Im folgenden Beispiel wird die Compose-Bibliothek von Glide für die in der Funktion imageComposable aufgelisteten Cards verwendet:

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
            }
        }
    )
)

Voraussetzungen

Bevor Sie dieses Feature nutzen können, müssen Sie das Swift Braze SDK integrieren.

Eigene Bilderbibliotheken einbinden

Schritt 1: SDWebImage integrieren

Integrieren Sie das SDWebImage Repository in Ihr Xcode Projekt.

Schritt 2: Erstellen Sie eine neue Swift-Datei

Erstellen Sie in Ihrem Xcode-Projekt eine neue Datei mit dem Namen SDWebImageGIFViewProvider.swift und importieren Sie Folgendes:

1
2
3
import UIKit
import BrazeUI
import SDWebImage

Schritt 3: hinzufügen GIFViewProvider

Als nächstes fügen Sie unser SDWebImage-Beispiel hinzu GIFViewProvider. Ihre Datei sollte in etwa so aussehen wie die folgende:

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)
  }


}

Schritt 4: Ändern Sie Ihr AppDelegate.swift

Fügen Sie in Ihrem Projekt AppDelegate.swift mit GIFViewProvider die GIF-Unterstützung zu Ihren BrazeUI Komponenten hinzu. Ihre Datei sollte in etwa so aussehen wie die folgende:

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
  }
}
New Stuff!