নেটিভ বিজ্ঞাপন কাস্টম ইভেন্ট

প্ল্যাটফর্ম নির্বাচন করুন: অ্যান্ড্রয়েড (বিটা)নতুন অ্যান্ড্রয়েড আইওএস

পূর্বশর্ত

কাস্টম ইভেন্ট সেটআপ সম্পূর্ণ করুন।

স্থানীয় বিজ্ঞাপন অনুরোধ করুন

ওয়াটারফল মিডিয়েশন চেইনে যখন কাস্টম ইভেন্ট লাইন আইটেমটিতে পৌঁছানো হয়, তখন কাস্টম ইভেন্ট তৈরি করার সময় আপনার দেওয়া ক্লাস নেমটিতে ` loadNativeAd() ` মেথডটি কল করা হয়। এই ক্ষেত্রে, সেই মেথডটি SampleCustomEvent এর মধ্যে রয়েছে, যা পরবর্তীতে SampleNativeCustomEventLoader এর loadNativeAd() ` মেথডটিকে কল করে।

একটি নেটিভ অ্যাডের অনুরোধ করতে, Adapter এক্সটেন্ড করে এমন একটি ক্লাস তৈরি বা পরিবর্তন করুন যা loadNativeAd() ইমপ্লিমেন্ট করে। যদি Adapter এক্সটেন্ড করে এমন কোনো ক্লাস আগে থেকেই থাকে, তাহলে সেখানেই loadNativeAd() ইমপ্লিমেন্ট করুন। এছাড়াও, UnifiedNativeAdMapper ইমপ্লিমেন্ট করার জন্য একটি নতুন ক্লাস তৈরি করুন।

আমাদের কাস্টম ইভেন্টের উদাহরণে , SampleCustomEvent Adapter ক্লাসকে এক্সটেন্ড করে এবং তারপর SampleNativeCustomEventLoader কাছে দায়িত্ব অর্পণ করে।

জাভা

package com.google.ads.mediation.sample.customevent;

import com.google.android.gms.ads.mediation.Adapter;
import com.google.android.gms.ads.mediation.MediationAdConfiguration;
import com.google.android.gms.ads.mediation.MediationAdLoadCallback;

import com.google.android.gms.ads.mediation.MediationNativeAdCallback;
...
public class SampleCustomEvent extends Adapter {
  private SampleNativeCustomEventLoader nativeLoader;

  @Override
  public void loadNativeAd(
      @NonNull MediationNativeAdConfiguration adConfiguration,
      @NonNull MediationAdLoadCallback<UnifiedNativeAdMapper, MediationNativeAdCallback> callback) {
    nativeLoader = new SampleNativeCustomEventLoader(adConfiguration, callback);
    nativeLoader.loadAd();
  }
}

SampleNativeCustomEventLoader নিম্নলিখিত কাজগুলোর জন্য দায়ী:

  • নেটিভ বিজ্ঞাপনটি লোড হচ্ছে।

  • UnifiedNativeAdMapper ক্লাসটি বাস্তবায়ন করা হচ্ছে।

  • Google Mobile Ads SDK -তে বিজ্ঞাপন ইভেন্ট কলব্যাক গ্রহণ এবং রিপোর্ট করা।

AdMob UI-তে সংজ্ঞায়িত ঐচ্ছিক প্যারামিটারটি অ্যাড কনফিগারেশনে অন্তর্ভুক্ত থাকে। adConfiguration.getServerParameters().getString(MediationConfiguration.CUSTOM_EVENT_SERVER_PARAMETER_FIELD) -এর মাধ্যমে প্যারামিটারটি অ্যাক্সেস করা যায়। এই প্যারামিটারটি সাধারণত একটি অ্যাড ইউনিট আইডেন্টিফায়ার, যা একটি অ্যাড নেটওয়ার্ক SDK-এর অ্যাড অবজেক্ট ইনস্ট্যানশিয়েট করার সময় প্রয়োজন হয়।

জাভা

package com.google.ads.mediation.sample.customevent;

import com.google.android.gms.ads.mediation.Adapter;
import com.google.android.gms.ads.mediation.MediationNativeAdConfiguration;
import com.google.android.gms.ads.mediation.MediationAdLoadCallback;
import com.google.android.gms.ads.mediation.MediationNativeAdCallback;
...

public class SampleNativeCustomEventLoader extends SampleNativeAdListener {
  /** Configuration for requesting the native ad from the third-party network. */
  private final MediationNativeAdConfiguration mediationNativeAdConfiguration;

  /** Callback that fires on loading success or failure. */
  private final MediationAdLoadCallback<UnifiedNativeAdMapper, MediationNativeAdCallback>
      mediationAdLoadCallback;

  /** Callback for native ad events. */
  private MediationNativeAdCallback nativeAdCallback;

  /** Constructor */
  public SampleNativeCustomEventLoader(
      @NonNull MediationNativeAdConfiguration mediationNativeAdConfiguration,
      @NonNull MediationAdLoadCallback<MediationNativeAd, MediationNativeAdCallback>
              mediationAdLoadCallback) {
    this.mediationNativeAdConfiguration = mediationNativeAdConfiguration;
    this.mediationAdLoadCallback = mediationAdLoadCallback;
  }

  /** Loads the native ad from the third-party ad network. */
  public void loadAd() {
    // Create one of the Sample SDK's ad loaders to request ads.
    Log.i("NativeCustomEvent", "Begin loading native ad.");
    SampleNativeAdLoader loader =
        new SampleNativeAdLoader(mediationNativeAdConfiguration.getContext());

    // All custom events have a server parameter named "parameter" that returns
    // back the parameter entered into the UI when defining the custom event.
    String serverParameter = mediationNativeAdConfiguration
        .getServerParameters()
        .getString(MediationConfiguration
        .CUSTOM_EVENT_SERVER_PARAMETER_FIELD);
    Log.d("NativeCustomEvent", "Received server parameter.");

    loader.setAdUnit(serverParameter);

    // Create a native request to give to the SampleNativeAdLoader.
    SampleNativeAdRequest request = new SampleNativeAdRequest();
    NativeAdOptions options = mediationNativeAdConfiguration.getNativeAdOptions();
    if (options != null) {
      // If the NativeAdOptions' shouldReturnUrlsForImageAssets is true, the adapter should
      // send just the URLs for the images.
      request.setShouldDownloadImages(!options.shouldReturnUrlsForImageAssets());

      request.setShouldDownloadMultipleImages(options.shouldRequestMultipleImages());
      switch (options.getMediaAspectRatio()) {
        case NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_LANDSCAPE:
          request.setPreferredImageOrientation(SampleNativeAdRequest.IMAGE_ORIENTATION_LANDSCAPE);
          break;
        case NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_PORTRAIT:
          request.setPreferredImageOrientation(SampleNativeAdRequest.IMAGE_ORIENTATION_PORTRAIT);
          break;
        case NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_SQUARE:
        case NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_ANY:
        case NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_UNKNOWN:
        default:
          request.setPreferredImageOrientation(SampleNativeAdRequest.IMAGE_ORIENTATION_ANY);
      }
    }

    loader.setNativeAdListener(this);

    // Begin a request.
    Log.i("NativeCustomEvent", "Start fetching native ad.");
    loader.fetchAd(request);
  }
}

বিজ্ঞাপনটি সফলভাবে আনা হয়েছে নাকি কোনো ত্রুটির সম্মুখীন হয়েছে, তার উপর নির্ভর করে আপনি onSuccess() অথবা onFailure() কল করবেন। onSuccess() কল করার জন্য, MediationNativeAd ইমপ্লিমেন্ট করা ক্লাসের একটি ইনস্ট্যান্স পাস করতে হয়।

সাধারণত, এই মেথডগুলো আপনার অ্যাডাপ্টার দ্বারা বাস্তবায়িত থার্ড-পার্টি SDK-এর কলব্যাকের ভিতরে প্রয়োগ করা হয়। এই উদাহরণের জন্য, স্যাম্পল SDK-তে প্রাসঙ্গিক কলব্যাকসহ একটি SampleAdListener রয়েছে:

জাভা

@Override
public void onNativeAdFetched(SampleNativeAd ad) {
  SampleUnifiedNativeAdMapper mapper = new SampleUnifiedNativeAdMapper(ad);
  mediationNativeAdCallback = mediationAdLoadCallback.onSuccess(mapper);
}

@Override
public void onAdFetchFailed(SampleErrorCode errorCode) {
  mediationAdLoadCallback.onFailure(SampleCustomEventError.createSampleSdkError(errorCode));
}

মানচিত্রের স্থানীয় বিজ্ঞাপন

বিভিন্ন SDK-এর নেটিভ বিজ্ঞাপনের জন্য নিজস্ব স্বতন্ত্র ফরম্যাট থাকে। উদাহরণস্বরূপ, একটি SDK এমন অবজেক্ট রিটার্ন করতে পারে যাতে একটি 'টাইটেল' ফিল্ড থাকে, যেখানে অন্যটিতে 'হেডলাইন' থাকতে পারে। এছাড়াও, ইম্প্রেশন ট্র্যাক করা এবং ক্লিক প্রসেস করার জন্য ব্যবহৃত পদ্ধতিগুলোও এক SDK থেকে অন্যটিতে ভিন্ন হতে পারে।

UnifiedNativeAdMapper এই পার্থক্যগুলোর মধ্যে সমন্বয় সাধন করে এবং একটি মিডিয়েটেড SDK-এর নেটিভ অ্যাড অবজেক্টকে Google Mobile Ads SDK এর প্রত্যাশিত ইন্টারফেসের সাথে মেলানোর জন্য অভিযোজিত করে। কাস্টম ইভেন্টগুলোকে তাদের মিডিয়েটেড SDK-এর জন্য নির্দিষ্ট নিজস্ব ম্যাপার তৈরি করতে এই ক্লাসটি এক্সটেন্ড করা উচিত। এখানে আমাদের উদাহরণ কাস্টম ইভেন্ট প্রজেক্ট থেকে একটি নমুনা অ্যাড ম্যাপার দেওয়া হলো:

জাভা

package com.google.ads.mediation.sample.customevent;

import com.google.android.gms.ads.mediation.UnifiedNativeAdMapper;
import com.google.android.gms.ads.nativead.NativeAd;
...

public class SampleUnifiedNativeAdMapper extends UnifiedNativeAdMapper {

  private final SampleNativeAd sampleAd;

  public SampleUnifiedNativeAdMapper(SampleNativeAd ad) {
    sampleAd = ad;
    setHeadline(sampleAd.getHeadline());
    setBody(sampleAd.getBody());
    setCallToAction(sampleAd.getCallToAction());
    setStarRating(sampleAd.getStarRating());
    setStore(sampleAd.getStoreName());
    setIcon(
        new SampleNativeMappedImage(
            ad.getIcon(), ad.getIconUri(), SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE));
    setAdvertiser(ad.getAdvertiser());

    List<NativeAd.Image> imagesList = new ArrayList<NativeAd.Image>();
    imagesList.add(new SampleNativeMappedImage(ad.getImage(), ad.getImageUri(),
        SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE));
    setImages(imagesList);

    if (sampleAd.getPrice() != null) {
      NumberFormat formatter = NumberFormat.getCurrencyInstance();
      String priceString = formatter.format(sampleAd.getPrice());
      setPrice(priceString);
    }

    Bundle extras = new Bundle();
    extras.putString(SampleCustomEvent.DEGREE_OF_AWESOMENESS, ad.getDegreeOfAwesomeness());
    this.setExtras(extras);

    setOverrideClickHandling(false);
    setOverrideImpressionRecording(false);

    setAdChoicesContent(sampleAd.getInformationIcon());
  }

  @Override
  public void recordImpression() {
    sampleAd.recordImpression();
  }

  @Override
  public void handleClick(View view) {
    sampleAd.handleClick(view);
  }

  // The Sample SDK doesn't do its own impression/click tracking, instead relies on its
  // publishers calling the recordImpression and handleClick methods on its native ad object. So
  // there's no need to pass a reference to the View being used to display the native ad. If
  // your mediated network does need a reference to the view, the following method can be used
  // to provide one.

  @Override
  public void trackViews(View containerView, Map<String, View> clickableAssetViews,
      Map<String, View> nonClickableAssetViews) {
    super.trackViews(containerView, clickableAssetViews, nonClickableAssetViews);
    // If your ad network SDK does its own impression tracking, here is where you can track the
    // top level native ad view and its individual asset views.
  }

  @Override
  public void untrackView(View view) {
    super.untrackView(view);
    // Here you would remove any trackers from the View added in trackView.
  }
}

এবার আমরা কনস্ট্রাক্টর কোডটি আরও ভালোভাবে দেখব।

মধ্যস্থতাকৃত নেটিভ বিজ্ঞাপন অবজেক্টের একটি রেফারেন্স ধরে রাখুন

কনস্ট্রাক্টরটি SampleNativeAd প্যারামিটার গ্রহণ করে, যা হলো স্যাম্পল এসডিকে (Sample SDK) কর্তৃক তার নেটিভ বিজ্ঞাপনগুলোর জন্য ব্যবহৃত নেটিভ অ্যাড ক্লাস। ম্যাপারটির মিডিয়েটেড অ্যাডের একটি রেফারেন্স প্রয়োজন, যাতে এটি ক্লিক এবং ইম্প্রেশন ইভেন্টগুলো প্রেরণ করতে পারে। SampleNativeAd একটি লোকাল ভ্যারিয়েবল হিসেবে সংরক্ষিত থাকে।

ম্যাপ করা সম্পদের বৈশিষ্ট্য সেট করুন

কনস্ট্রাক্টরটি UnifiedNativeAdMapper এ অ্যাসেটসমূহ যুক্ত করার জন্য SampleNativeAd অবজেক্টটি ব্যবহার করে।

এই কোড স্নিপেটটি মিডিয়েটেড অ্যাডের মূল্যের ডেটা সংগ্রহ করে এবং তা ব্যবহার করে ম্যাপারের মূল্য নির্ধারণ করে:

জাভা

if (sampleAd.getPrice() != null) {
    NumberFormat formatter = NumberFormat.getCurrencyInstance();
    String priceString = formatter.format(sampleAd.getPrice());
    setPrice(priceString);
}

এই উদাহরণে, মিডিয়েটেড অ্যাডটি মূল্যটিকে একটি double হিসেবে সংরক্ষণ করে, যেখানে AdMob একই অ্যাসেটের জন্য একটি String ব্যবহার করে। এই ধরনের কনভার্সনগুলো পরিচালনা করার দায়িত্ব ম্যাপারের।

মানচিত্রের ছবির সম্পদ

double বা String মতো ডেটা টাইপ ম্যাপ করার চেয়ে ইমেজ অ্যাসেট ম্যাপ করা আরও জটিল। ইমেজগুলো স্বয়ংক্রিয়ভাবে ডাউনলোড হতে পারে অথবা ইউআরএল ভ্যালু হিসেবেও ফেরত আসতে পারে। এগুলোর পিক্সেল-টু-ডিপিআই স্কেলও ভিন্ন হতে পারে।

এই বিবরণগুলি পরিচালনা করতে আপনাকে সাহায্য করার জন্য, Google Mobile Ads SDK NativeAd.Image ক্লাসটি প্রদান করে। ঠিক যেমনভাবে একটি মিডিয়েটেড নেটিভ অ্যাড ম্যাপ করার জন্য আপনাকে UnifiedNativeAdMapper এর একটি সাবক্লাস তৈরি করতে হয়, ঠিক একইভাবে ইমেজ অ্যাসেট ম্যাপ করার সময়ও আপনার NativeAd.Image এর একটি সাবক্লাস তৈরি করা উচিত।

কাস্টম ইভেন্টের SampleNativeMappedImage ক্লাসের একটি উদাহরণ নিচে দেওয়া হলো:

জাভা

public class SampleNativeMappedImage extends NativeAd.Image {

  private Drawable drawable;
  private Uri imageUri;
  private double scale;

  public SampleNativeMappedImage(Drawable drawable, Uri imageUri, double scale) {
    this.drawable = drawable;
    this.imageUri = imageUri;
    this.scale = scale;
  }

  @Override
  public Drawable getDrawable() {
    return drawable;
  }

  @Override
  public Uri getUri() {
    return imageUri;
  }

  @Override
  public double getScale() {
    return scale;
  }
}

SampleNativeAdMapper এই লাইনে তার ম্যাপ করা ইমেজ ক্লাস ব্যবহার করে ম্যাপারের আইকন ইমেজ অ্যাসেট সেট করে:

জাভা

setIcon(new SampleNativeMappedImage(ad.getAppIcon(), ad.getAppIconUri(),
    SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE));

অতিরিক্ত বান্ডেলে ফিল্ড যোগ করুন

কিছু মেডিয়েটেড SDK, AdMob-এর নেটিভ অ্যাড ফরম্যাটের অ্যাসেটগুলো ছাড়াও অতিরিক্ত অ্যাসেট প্রদান করে। UnifiedNativeAdMapper ক্লাসে একটি setExtras() মেথড রয়েছে যা এই অ্যাসেটগুলোকে পাবলিশারদের কাছে পাঠানোর জন্য ব্যবহৃত হয়। SampleNativeAdMapper Sample SDK-এর "degree of awesomeness" অ্যাসেটটির জন্য এটি ব্যবহার করে:

জাভা

Bundle extras = new Bundle();
extras.putString(SampleCustomEvent.DEGREE_OF_AWESOMENESS, ad.getDegreeOfAwesomeness());
this.setExtras(extras);

প্রকাশকরা NativeAd ক্লাসের getExtras() মেথড ব্যবহার করে ডেটা পুনরুদ্ধার করতে পারেন।

AdChoices

আপনার কাস্টম ইভেন্টটি UnifiedNativeAdMapper এর setAdChoicesContent() মেথড ব্যবহার করে একটি AdChoices আইকন প্রদান করার জন্য দায়ী। AdChoices আইকনটি কীভাবে প্রদান করতে হয় তা দেখানোর জন্য SampleNativeAdMapper থেকে একটি কোড স্নিপেট নিচে দেওয়া হলো:

জাভা

public SampleNativeAdMapper(SampleNativeAd ad) {
    ...
    setAdChoicesContent(sampleAd.getInformationIcon());
}

ইম্প্রেশন এবং ক্লিক ইভেন্ট

Google Mobile Ads SDK এবং মিডিয়েটেড এসডিকে, উভয়েরই জানা প্রয়োজন কখন একটি ইম্প্রেশন বা ক্লিক ঘটে, কিন্তু শুধুমাত্র একটি এসডিকে-রই এই ইভেন্টগুলো ট্র্যাক করা প্রয়োজন। কাস্টম ইভেন্টগুলো দুটি ভিন্ন পদ্ধতি ব্যবহার করতে পারে, যা নির্ভর করে মিডিয়েটেড এসডিকে নিজে থেকে ইম্প্রেশন এবং ক্লিক ট্র্যাক করা সমর্থন করে কি না তার উপর।

Google Mobile Ads SDK দিয়ে ক্লিক ও ইম্প্রেশন ট্র্যাক করুন।

যদি মিডিয়েটেড SDK নিজে ইম্প্রেশন এবং ক্লিক ট্র্যাকিং না করে, কিন্তু ক্লিক ও ইম্প্রেশন রেকর্ড করার জন্য মেথড সরবরাহ করে, Google Mobile Ads SDK এই ইভেন্টগুলো ট্র্যাক করতে পারে এবং অ্যাডাপ্টারকে অবহিত করতে পারে। UnifiedNativeAdMapper ক্লাসে recordImpression() এবং handleClick() নামে দুটি মেথড রয়েছে, যা কাস্টম ইভেন্টগুলো মিডিয়েটেড নেটিভ অ্যাড অবজেক্টের সংশ্লিষ্ট মেথড কল করার জন্য ইমপ্লিমেন্ট করতে পারে।

জাভা

@Override
public void recordImpression() {
  sampleAd.recordImpression();
}

@Override
public void handleClick(View view) {
  sampleAd.handleClick(view);
}

যেহেতু SampleNativeAdMapper Sample SDK-এর নেটিভ অ্যাড অবজেক্টের একটি রেফারেন্স ধারণ করে, তাই এটি একটি ক্লিক বা ইম্প্রেশন রিপোর্ট করার জন্য সেই অবজেক্টের উপযুক্ত মেথডটি কল করতে পারে। উল্লেখ্য যে, handleClick() মেথডটি একটিমাত্র প্যারামিটার গ্রহণ করে: যে নেটিভ অ্যাড অ্যাসেটটিতে ক্লিকটি ঘটেছে, তার সাথে সম্পর্কিত View অবজেক্টটি।

মেডিয়েটেড এসডিকে-এর মাধ্যমে ক্লিক এবং ইম্প্রেশন ট্র্যাক করুন।

কিছু মেডিয়েটেড SDK নিজেরাই ক্লিক এবং ইম্প্রেশন ট্র্যাক করতে পছন্দ করতে পারে। সেক্ষেত্রে, আপনার UnifiedNativeAdMapper এর কনস্ট্রাক্টরে নিম্নলিখিত দুটি কল করার মাধ্যমে ডিফল্ট ক্লিক এবং ইম্প্রেশন ট্র্যাকিংকে ওভাররাইড করা উচিত:

জাভা

setOverrideClickHandling(true);
setOverrideImpressionRecording(true);

Google Mobile Ads SDK-তে onAdClicked() এবং onAdImpression() ইভেন্টগুলো রিপোর্ট করার জন্য, ক্লিক ও ইম্প্রেশন ট্র্যাকিংকে ওভাররাইড করে এমন কাস্টম ইভেন্ট থাকা আবশ্যক।

ইম্প্রেশন এবং ক্লিক ট্র্যাক করার জন্য, ট্র্যাকিং সক্ষম করতে মিডিয়েটেড SDK-এর সম্ভবত ভিউগুলিতে অ্যাক্সেসের প্রয়োজন হবে। কাস্টম ইভেন্টটির উচিত trackViews() মেথডটি ওভাররাইড করা এবং ট্র্যাক করার জন্য নেটিভ অ্যাডের ভিউকে মিডিয়েটেড SDK-তে পাঠাতে এটি ব্যবহার করা। আমাদের কাস্টম ইভেন্ট উদাহরণ প্রজেক্টের স্যাম্পল SDK (যেখান থেকে এই গাইডের কোড স্নিপেটগুলো নেওয়া হয়েছে) এই পদ্ধতি ব্যবহার করে না; কিন্তু যদি করত, তাহলে কাস্টম ইভেন্টের কোডটি দেখতে অনেকটা এরকম হতো:

জাভা

@Override
public void trackViews(View containerView,
    Map<String, View> clickableAssetViews,
    Map<String, View> nonClickableAssetViews) {
  sampleAd.setNativeAdViewForTracking(containerView);
}

যদি মধ্যস্থতাকারী SDK স্বতন্ত্র অ্যাসেট ট্র্যাক করা সমর্থন করে, তবে কোন ভিউগুলোকে ক্লিকযোগ্য করা উচিত তা দেখার জন্য এটি clickableAssetViews ভেতরে দেখতে পারে। এই ম্যাপটি NativeAdAssetNames এ থাকা একটি অ্যাসেটের নাম দ্বারা কী (key) হিসেবে কাজ করে। ' UnifiedNativeAdMapper একটি অনুরূপ untrackView() মেথড রয়েছে, যা কাস্টম ইভেন্টগুলো ওভাররাইড করে ভিউটির যেকোনো রেফারেন্স মুক্ত করতে এবং এটিকে নেটিভ অ্যাড অবজেক্ট থেকে বিচ্ছিন্ন করতে পারে।

মেডিয়েশন ইভেন্টগুলো Google Mobile Ads SDK তে ফরোয়ার্ড করুন

মিডিয়েশন যেসব কলব্যাক সমর্থন করে, তার সবই আপনি MediationNativeAdCallback ডক্স- এ খুঁজে পাবেন।

এটা গুরুত্বপূর্ণ যে আপনার কাস্টম ইভেন্টটি যেন এই কলব্যাকগুলোর যতটা সম্ভব বেশি ফরওয়ার্ড করে, যাতে আপনার অ্যাপ Google Mobile Ads SDK থেকে এই সমতুল্য ইভেন্টগুলো গ্রহণ করতে পারে। কলব্যাক ব্যবহারের একটি উদাহরণ নিচে দেওয়া হলো:

এর মাধ্যমে নেটিভ বিজ্ঞাপনের জন্য কাস্টম ইভেন্ট বাস্তবায়ন সম্পন্ন হলো। সম্পূর্ণ উদাহরণটি গিটহাবে পাওয়া যাবে।