crowdhub-plugin-flurryanalytics v5.0.0
crowdhub-plugin-flurryanalytics
This plugin provides an interface for Capacitor apps to use the Flurry Analytics API
Install
Capacitor 5:
npm install crowdhub-plugin-flurryanalytics@latest
npx cap syncCapacitor 4:
npm install crowdhub-plugin-flurryanalytics@4Implementation
Web
In order to initialize a session, you simply need to call once within your app's session, so you'd likely want to include this call on a deviceready event.
import { FlurryAnalytics } from "crowdhub-plugin-flurryanalytics";
FlurryAnalytics.initialize({
apiKey: "YOUR-API-KEY-HERE",
});Afterwards you can safely call any of the provided methods from the FlurryAnalytics class!
For demographics methods (setAge, setGender, setUserId), these must be called prior to initializing a Flurry session.
iOS
Android
As with many community created Capacitor plugins, you may need to manually register this plugin in your MainActivity.java like so:
package your.app.bundle;
import android.os.Bundle;
import com.crowdhubapps.plugin.flurryanalytics.FlurryAnalyticsPlugin;
import com.getcapacitor.BridgeActivity;
public class MainActivity extends BridgeActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
registerPlugin(FlurryAnalyticsPlugin.class);
super.onCreate(savedInstanceState);
}
}If you get an error related to finding the package, you may also need to Right-Click the crowdhub-flurry-analytics folder in Android Studio and select the 'Convert Java File to Kotlin File' option. This will prompt you to configure Kotlin in the project, which is what the plugin is written in. You only need to configure Single module: android.crowdhubapps-plugin-flurryanalytics.
API
initialize(...)logContentRated(...)logContentViewed(...)logContentSaved(...)logProductCustomized()logSubscriptionStarted(...)logSubscriptionEnded(...)logGroupJoined(...)logGroupLeft(...)logLogin(...)logLogout(...)logUserRegistered(...)logSearchResultViewed(...)logKeywordSearched(...)logLocationSearched(...)logInvite(...)logShare(...)logLike(...)logComment(...)logMediaCaptured(...)logMediaStarted(...)logMediaStopped(...)logMediaPaused(...)logCustomEvent(...)endTimedEvent(...)setUserId(...)setAge(...)setGender(...)logError(...)logAdClick(...)logAdImpression(...)logAdRewarded(...)logAdSkipped(...)logCreditsSpent(...)logCreditsPurchased(...)logCreditsEarned(...)logAchievementUnlocked(...)logLevelCompleted(...)logLevelFailed(...)logLevelUp(...)logLevelStarted(...)logLevelSkip(...)logScorePosted(...)logAppActivated()logApplicationSubmitted()logAddItemToCart(...)logAddItemToWishList(...)logCompletedCheckout(...)logPaymentInfoAdded(...)logItemViewed(...)logItemListViewed(...)logPurchased(...)logPurchaseRefunded(...)logRemoveItemFromCart(...)logCheckoutInitiated(...)logFundsDonated(...)logUserScheduled()logOfferPresented(...)logTutorialStarted(...)logTutorialCompleted(...)logTutorialStepCompleted(...)logTutorialSkipped(...)logPrivacyPromptDisplayed()logPrivacyOptIn()logPrivacyOptOut()
initialize(...)
initialize(options: { apiKey: string; logLevel?: "verbose" | "debug" | "info" | "warn" | "error"; crashReportingEnabled?: boolean; appVersion?: string; iapReportingEnabled?: boolean; }) => Promise<{ value: string; }>Initialize Flurry once within the session by passing through an API key
| Param | Type |
|---|---|
options | { apiKey: string; logLevel?: 'error' | 'warn' | 'info' | 'verbose' | 'debug'; crashReportingEnabled?: boolean; appVersion?: string; iapReportingEnabled?: boolean; } |
Returns: Promise<{ value: string; }>
logContentRated(...)
logContentRated(options: { contentId: string; contentRating: string; contentName?: string; contentType?: string; }) => Promise<{ value: string; }>Log this event when a user rates a content in the App
| Param | Type |
|---|---|
options | { contentId: string; contentRating: string; contentName?: string; contentType?: string; } |
Returns: Promise<{ value: string; }>
logContentViewed(...)
logContentViewed(options: { contentId: string; contentName?: string; contentType?: string; }) => Promise<{ value: string; }>Log this event when a specific content is viewed by a user
| Param | Type |
|---|---|
options | { contentId: string; contentName?: string; contentType?: string; } |
Returns: Promise<{ value: string; }>
logContentSaved(...)
logContentSaved(options: { contentId: string; contentName?: string; contentType?: string; }) => Promise<{ value: string; }>Log this event when a user saves the content in the App
| Param | Type |
|---|---|
options | { contentId: string; contentName?: string; contentType?: string; } |
Returns: Promise<{ value: string; }>
logProductCustomized()
logProductCustomized() => Promise<{ value: string; }>Log this event when a user customizes the App/product
Returns: Promise<{ value: string; }>
logSubscriptionStarted(...)
logSubscriptionStarted(options: { price: number; isAnnualSubscription: boolean; trialDays?: number; predictedLTV?: string; currencyType?: string; subscriptionCountry?: string; }) => Promise<{ value: string; }>Log this event at the start of a paid subscription for a service or product
| Param | Type |
|---|---|
options | { price: number; isAnnualSubscription: boolean; trialDays?: number; predictedLTV?: string; currencyType?: string; subscriptionCountry?: string; } |
Returns: Promise<{ value: string; }>
logSubscriptionEnded(...)
logSubscriptionEnded(options: { isAnnualSubscription: boolean; currencyType?: string; subscriptionCountry?: string; }) => Promise<{ value: string; }>Log this event when a user unsubscribes from a paid subscription for a service or product
| Param | Type |
|---|---|
options | { isAnnualSubscription: boolean; currencyType?: string; subscriptionCountry?: string; } |
Returns: Promise<{ value: string; }>
logGroupJoined(...)
logGroupJoined(options: { groupName?: string; }) => Promise<{ value: string; }>Log this event when user joins a group.
| Param | Type |
|---|---|
options | { groupName?: string; } |
Returns: Promise<{ value: string; }>
logGroupLeft(...)
logGroupLeft(options: { groupName?: string; }) => Promise<{ value: string; }>Log this event when user leaves a group
| Param | Type |
|---|---|
options | { groupName?: string; } |
Returns: Promise<{ value: string; }>
logLogin(...)
logLogin(options: { userId?: string; method?: string; }) => Promise<{ value: string; }>Log this event when a user login on the App
| Param | Type |
|---|---|
options | { userId?: string; method?: string; } |
Returns: Promise<{ value: string; }>
logLogout(...)
logLogout(options: { userId?: string; method?: string; }) => Promise<{ value: string; }>Log this event when a user logout of the App
| Param | Type |
|---|---|
options | { userId?: string; method?: string; } |
Returns: Promise<{ value: string; }>
logUserRegistered(...)
logUserRegistered(options: { userId?: string; method?: string; }) => Promise<{ value: string; }>Log the event when a user registers (signup). Helps capture the method used to sign-up (signup with google/apple or email address)
| Param | Type |
|---|---|
options | { userId?: string; method?: string; } |
Returns: Promise<{ value: string; }>
logSearchResultViewed(...)
logSearchResultViewed(options: { query?: string; searchType?: string; }) => Promise<{ value: string; }>Log this event when user views search results
| Param | Type |
|---|---|
options | { query?: string; searchType?: string; } |
Returns: Promise<{ value: string; }>
logKeywordSearched(...)
logKeywordSearched(options: { query?: string; searchType?: string; }) => Promise<{ value: string; }>Log this event when a user searches for a keyword using Search
| Param | Type |
|---|---|
options | { query?: string; searchType?: string; } |
Returns: Promise<{ value: string; }>
logLocationSearched(...)
logLocationSearched(options: { query?: string; }) => Promise<{ value: string; }>Log this event when a user searches for a location using Search
| Param | Type |
|---|---|
options | { query?: string; } |
Returns: Promise<{ value: string; }>
logInvite(...)
logInvite(options: { userId?: string; method?: string; }) => Promise<{ value: string; }>Log this event when a user invites another user
| Param | Type |
|---|---|
options | { userId?: string; method?: string; } |
Returns: Promise<{ value: string; }>
logShare(...)
logShare(options: { socialContentId: string; socialContentName?: string; method?: string; }) => Promise<{ value: string; }>Log this event when a user shares content with another user in the App
| Param | Type |
|---|---|
options | { socialContentId: string; socialContentName?: string; method?: string; } |
Returns: Promise<{ value: string; }>
logLike(...)
logLike(options: { socialContentId: string; socialContentName?: string; likeType?: string; }) => Promise<{ value: string; }>Log this event when a user likes a social content. e.g. likeType captures what kind of like is logged (“celebrate”, “insightful”, etc)
| Param | Type |
|---|---|
options | { socialContentId: string; socialContentName?: string; likeType?: string; } |
Returns: Promise<{ value: string; }>
logComment(...)
logComment(options: { socialContentId: string; socialContentName?: string; }) => Promise<{ value: string; }>Log this event when a user comments or replies on a social post
| Param | Type |
|---|---|
options | { socialContentId: string; socialContentName?: string; } |
Returns: Promise<{ value: string; }>
logMediaCaptured(...)
logMediaCaptured(options: { mediaId?: string; mediaName?: string; mediaType?: string; }) => Promise<{ value: string; }>Log this event when an image, audio or a video is captured
| Param | Type |
|---|---|
options | { mediaId?: string; mediaName?: string; mediaType?: string; } |
Returns: Promise<{ value: string; }>
logMediaStarted(...)
logMediaStarted(options: { mediaId?: string; mediaName?: string; mediaType?: string; }) => Promise<{ value: string; }>Log this event when an audio or video starts
| Param | Type |
|---|---|
options | { mediaId?: string; mediaName?: string; mediaType?: string; } |
Returns: Promise<{ value: string; }>
logMediaStopped(...)
logMediaStopped(options: { duration: number; mediaId?: string; mediaName?: string; mediaType?: string; }) => Promise<{ value: string; }>Log this event when an audio or video is stopped
| Param | Type |
|---|---|
options | { duration: number; mediaId?: string; mediaName?: string; mediaType?: string; } |
Returns: Promise<{ value: string; }>
logMediaPaused(...)
logMediaPaused(options: { duration: number; mediaId?: string; mediaName?: string; mediaType?: string; }) => Promise<{ value: string; }>Log this event when an audio or video is paused
| Param | Type |
|---|---|
options | { duration: number; mediaId?: string; mediaName?: string; mediaType?: string; } |
Returns: Promise<{ value: string; }>
logCustomEvent(...)
logCustomEvent(options: { eventName: string; eventParams?: { [key: string]: string; } | undefined; eventTimed?: boolean | undefined; }) => Promise<{ value: string; }>Log a custom event in the app. You may provide up to ten additional parameters as key/value pairs, both of which must be strings You may also enable this event to be timed, calling endTimedEvent to terminate its logging
| Param | Type |
|---|---|
options | { eventName: string; eventParams?: { key: string: string; }; eventTimed?: boolean; } |
Returns: Promise<{ value: string; }>
endTimedEvent(...)
endTimedEvent(options: { eventName: string; }) => Promise<{ value: string; }>| Param | Type |
|---|---|
options | { eventName: string; } |
Returns: Promise<{ value: string; }>
setUserId(...)
setUserId(options: { userId: string; }) => Promise<{ value: string; }>After identifying the user, use this to log the user’s assigned ID or username in your system. You must call this function prior to starting the Flurry session
| Param | Type |
|---|---|
options | { userId: string; } |
Returns: Promise<{ value: string; }>
setAge(...)
setAge(options: { userAge: number; }) => Promise<{ value: string; }>After identifying the user, use this to log the user’s age. Valid inputs are between 1 and 109. You must call this function prior to starting the Flurry session
| Param | Type |
|---|---|
options | { userAge: number; } |
Returns: Promise<{ value: string; }>
setGender(...)
setGender(options: { userGender: "m" | "f"; }) => Promise<{ value: string; }>After identifying the user, use this to log the user’s gender. Valid inputs are m (male) or f (female). You must call this function prior to starting the Flurry session
| Param | Type |
|---|---|
options | { userGender: 'm' | 'f'; } |
Returns: Promise<{ value: string; }>
logError(...)
logError(options: { errorId?: string; errorMessage?: string; error?: string; }) => Promise<{ value: string; }>Use this to log exceptions and/or errors that occur in your app. Flurry will report the first 10 errors that occur in each session.
| Param | Type |
|---|---|
options | { errorId?: string; errorMessage?: string; error?: string; } |
Returns: Promise<{ value: string; }>
logAdClick(...)
logAdClick(options: { adType?: string; }) => Promise<{ value: string; }>Log this event when a user clicks on an Ad
| Param | Type |
|---|---|
options | { adType?: string; } |
Returns: Promise<{ value: string; }>
logAdImpression(...)
logAdImpression(options: { adType?: string; }) => Promise<{ value: string; }>Log this event when a user views an Ad impression
| Param | Type |
|---|---|
options | { adType?: string; } |
Returns: Promise<{ value: string; }>
logAdRewarded(...)
logAdRewarded(options: { adType?: string; }) => Promise<{ value: string; }>Log this event when a user is granted a reward for viewing a rewarded Ad
| Param | Type |
|---|---|
options | { adType?: string; } |
Returns: Promise<{ value: string; }>
logAdSkipped(...)
logAdSkipped(options: { adType?: string; }) => Promise<{ value: string; }>Log this event when a user skips an Ad
| Param | Type |
|---|---|
options | { adType?: string; } |
Returns: Promise<{ value: string; }>
logCreditsSpent(...)
logCreditsSpent(options: { levelNumber?: number; totalAmount: number; isCurrencySoft?: boolean; creditType?: string; creditId?: string; creditName?: string; currencyType?: string; }) => Promise<{ value: string; }>Log this event when a user spends credit in the app
| Param | Type |
|---|---|
options | { levelNumber?: number; totalAmount: number; isCurrencySoft?: boolean; creditType?: string; creditId?: string; creditName?: string; currencyType?: string; } |
Returns: Promise<{ value: string; }>
logCreditsPurchased(...)
logCreditsPurchased(options: { levelNumber?: number; totalAmount: number; isCurrencySoft?: boolean; creditType?: string; creditId?: string; creditName?: string; currencyType?: string; }) => Promise<{ value: string; }>Log this event when a user purchases credit in the app
| Param | Type |
|---|---|
options | { levelNumber?: number; totalAmount: number; isCurrencySoft?: boolean; creditType?: string; creditId?: string; creditName?: string; currencyType?: string; } |
Returns: Promise<{ value: string; }>
logCreditsEarned(...)
logCreditsEarned(options: { levelNumber?: number; totalAmount: number; isCurrencySoft?: boolean; creditType?: string; creditId?: string; creditName?: string; currencyType?: string; }) => Promise<{ value: string; }>Log this event when a user earns credit in the app
| Param | Type |
|---|---|
options | { levelNumber?: number; totalAmount: number; isCurrencySoft?: boolean; creditType?: string; creditId?: string; creditName?: string; currencyType?: string; } |
Returns: Promise<{ value: string; }>
logAchievementUnlocked(...)
logAchievementUnlocked(options: { achievementId?: string; }) => Promise<{ value: string; }>Log this event when a user unlocks an achievement in the app
| Param | Type |
|---|---|
options | { achievementId?: string; } |
Returns: Promise<{ value: string; }>
logLevelCompleted(...)
logLevelCompleted(options: { levelNumber: number; levelName?: string; }) => Promise<{ value: string; }>Log this event when an App user completes a level
| Param | Type |
|---|---|
options | { levelNumber: number; levelName?: string; } |
Returns: Promise<{ value: string; }>
logLevelFailed(...)
logLevelFailed(options: { levelNumber: number; levelName?: string; }) => Promise<{ value: string; }>Log this event when an App user fails a level
| Param | Type |
|---|---|
options | { levelNumber: number; levelName?: string; } |
Returns: Promise<{ value: string; }>
logLevelUp(...)
logLevelUp(options: { levelNumber: number; levelName?: string; }) => Promise<{ value: string; }>Log this event when an App user levels up
| Param | Type |
|---|---|
options | { levelNumber: number; levelName?: string; } |
Returns: Promise<{ value: string; }>
logLevelStarted(...)
logLevelStarted(options: { levelNumber: number; levelName?: string; }) => Promise<{ value: string; }>Log this event when an App user starts a level
| Param | Type |
|---|---|
options | { levelNumber: number; levelName?: string; } |
Returns: Promise<{ value: string; }>
logLevelSkip(...)
logLevelSkip(options: { levelNumber: number; levelName?: string; }) => Promise<{ value: string; }>Log this event when an App user skips a level
| Param | Type |
|---|---|
options | { levelNumber: number; levelName?: string; } |
Returns: Promise<{ value: string; }>
logScorePosted(...)
logScorePosted(options: { levelNumber?: number; score: number; }) => Promise<{ value: string; }>Log this event when an App user posts his score
| Param | Type |
|---|---|
options | { levelNumber?: number; score: number; } |
Returns: Promise<{ value: string; }>
logAppActivated()
logAppActivated() => Promise<{ value: string; }>Log this event when the App is activated
Returns: Promise<{ value: string; }>
logApplicationSubmitted()
logApplicationSubmitted() => Promise<{ value: string; }>Log this event when a user submits an application through the App
Returns: Promise<{ value: string; }>
logAddItemToCart(...)
logAddItemToCart(options: { itemCount: number; price: number; itemId?: string; itemName?: string; itemType?: string; }) => Promise<{ value: string; }>Log this event when an item is added to the cart
| Param | Type |
|---|---|
options | { itemCount: number; price: number; itemId?: string; itemName?: string; itemType?: string; } |
Returns: Promise<{ value: string; }>
logAddItemToWishList(...)
logAddItemToWishList(options: { itemCount: number; price: number; itemId?: string; itemName?: string; itemType?: string; }) => Promise<{ value: string; }>Log this event when an item is added to the wish list
| Param | Type |
|---|---|
options | { itemCount: number; price: number; itemId?: string; itemName?: string; itemType?: string; } |
Returns: Promise<{ value: string; }>
logCompletedCheckout(...)
logCompletedCheckout(options: { itemCount: number; totalAmount: number; currencyType?: string; transactionId?: string; }) => Promise<{ value: string; }>Log this event when checkout is completed or transaction is successfully completed
| Param | Type |
|---|---|
options | { itemCount: number; totalAmount: number; currencyType?: string; transactionId?: string; } |
Returns: Promise<{ value: string; }>
logPaymentInfoAdded(...)
logPaymentInfoAdded(options: { success: boolean; paymentType: string; }) => Promise<{ value: string; }>Log this event when payment information is added during a checkout process
| Param | Type |
|---|---|
options | { success: boolean; paymentType: string; } |
Returns: Promise<{ value: string; }>
logItemViewed(...)
logItemViewed(options: { price?: number; itemId: string; itemName?: string; itemType?: string; }) => Promise<{ value: string; }>Log this event when an item is viewed
| Param | Type |
|---|---|
options | { price?: number; itemId: string; itemName?: string; itemType?: string; } |
Returns: Promise<{ value: string; }>
logItemListViewed(...)
logItemListViewed(options: { itemListType: string; }) => Promise<{ value: string; }>Log this event when a list of items is viewed
| Param | Type |
|---|---|
options | { itemListType: string; } |
Returns: Promise<{ value: string; }>
logPurchased(...)
logPurchased(options: { totalAmount: number; success: boolean; itemCount?: number; itemId?: string; itemName?: string; itemType?: string; currencyType?: string; transactionId?: string; }) => Promise<{ value: string; }>Log this event when a user does a purchase in the App
| Param | Type |
|---|---|
options | { totalAmount: number; success: boolean; itemCount?: number; itemId?: string; itemName?: string; itemType?: string; currencyType?: string; transactionId?: string; } |
Returns: Promise<{ value: string; }>
logPurchaseRefunded(...)
logPurchaseRefunded(options: { price: number; currencyType?: string; }) => Promise<{ value: string; }>Log this event when a purchase is refunded
| Param | Type |
|---|---|
options | { price: number; currencyType?: string; } |
Returns: Promise<{ value: string; }>
logRemoveItemFromCart(...)
logRemoveItemFromCart(options: { price?: number; itemId: string; itemName?: string; itemType?: string; }) => Promise<{ value: string; }>Log this event when a user removes an item from the cart
| Param | Type |
|---|---|
options | { price?: number; itemId: string; itemName?: string; itemType?: string; } |
Returns: Promise<{ value: string; }>
logCheckoutInitiated(...)
logCheckoutInitiated(options: { itemCount: number; totalAmount: number; }) => Promise<{ value: string; }>Log this event when a user starts checkout
| Param | Type |
|---|---|
options | { itemCount: number; totalAmount: number; } |
Returns: Promise<{ value: string; }>
logFundsDonated(...)
logFundsDonated(options: { price: number; currencyType?: string; }) => Promise<{ value: string; }>Log this event when a user donates fund to your App or through the App
| Param | Type |
|---|---|
options | { price: number; currencyType?: string; } |
Returns: Promise<{ value: string; }>
logUserScheduled()
logUserScheduled() => Promise<{ value: string; }>Log this event when user schedules an appointment using the App
Returns: Promise<{ value: string; }>
logOfferPresented(...)
logOfferPresented(options: { price: number; itemId: string; itemName?: string; itemCategory?: string; }) => Promise<{ value: string; }>Log this event when an offer is presented to the user
| Param | Type |
|---|---|
options | { price: number; itemId: string; itemName?: string; itemCategory?: string; } |
Returns: Promise<{ value: string; }>
logTutorialStarted(...)
logTutorialStarted(options: { tutorialName?: string; }) => Promise<{ value: string; }>Log this event when a user starts a tutorial
| Param | Type |
|---|---|
options | { tutorialName?: string; } |
Returns: Promise<{ value: string; }>
logTutorialCompleted(...)
logTutorialCompleted(options: { tutorialName?: string; }) => Promise<{ value: string; }>Log this event when a user completes a tutorial
| Param | Type |
|---|---|
options | { tutorialName?: string; } |
Returns: Promise<{ value: string; }>
logTutorialStepCompleted(...)
logTutorialStepCompleted(options: { stepNumber: number; tutorialName?: string; }) => Promise<{ value: string; }>Log this event when a specific tutorial step is completed
| Param | Type |
|---|---|
options | { stepNumber: number; tutorialName?: string; } |
Returns: Promise<{ value: string; }>
logTutorialSkipped(...)
logTutorialSkipped(options: { stepNumber: number; tutorialName?: string; }) => Promise<{ value: string; }>Log this event when user skips the tutorial
| Param | Type |
|---|---|
options | { stepNumber: number; tutorialName?: string; } |
Returns: Promise<{ value: string; }>
logPrivacyPromptDisplayed()
logPrivacyPromptDisplayed() => Promise<{ value: string; }>Log this event when a privacy prompt is displayed
Returns: Promise<{ value: string; }>
logPrivacyOptIn()
logPrivacyOptIn() => Promise<{ value: string; }>Log this event when a user opts in (on the privacy prompt)
Returns: Promise<{ value: string; }>
logPrivacyOptOut()
logPrivacyOptOut() => Promise<{ value: string; }>Log this event when a user opts out (on the privacy prompt)
Returns: Promise<{ value: string; }>
░░░░▒▒░░░░░░░░░░▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒░░░░░░░░░░▒▒░░ ░░░░▒▒░░░░░░░░░░▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒░░░░░░░░░░▒▒░░ ░░▒▒▒▒▒▒░░░░░░▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒░░░░░░▒▒▒▒▒▒ ░░▒▒▒▒▒▒▒▒░░▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒░░▒▒▒▒▒▒▒▒ ░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░████░░░░░░░░████░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░ ░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░████░░░░░░░░████░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░ ░░░░░░▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒░░░░ ░░░░░░░░▒▒▒▒▒▒░░░░░░░░░░░░▒▒▒▒░░░░░░░░▒▒▒▒░░░░░░░░░░░░▒▒▒▒▒▒░░░░░░ ░░░░░░░░▒▒▒▒▒▒░░░░░░░░░░░░▒▒▒▒░░░░░░░░▒▒▒▒░░░░░░░░░░░░▒▒▒▒▒▒░░░░░░ ░░░░░░░░▒▒▒▒▒▒░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░▒▒▒▒▒▒░░░░░░ ░░░░░░░░▒▒▒▒▒▒▒▒░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░▒▒▒▒▒▒▒▒░░░░░░ ░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░ ░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░ ░░░░░░▒▒▒▒░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░▒▒▒▒░░░░ ░░░░▒▒▒▒░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░▒▒▒▒░░ ░░░░▒▒▒▒░░░░░░░░▒▒▒▒░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░▒▒▒▒░░░░░░░░▒▒▒▒░░ ░░░░▒▒▒▒░░░░░░▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒░░░░░░▒▒▒▒░░ ░░░░░░▒▒░░░░░░▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒░░░░░░▒▒░░░░ ░░░░░░▒▒░░░░░░▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒░░░░░░▒▒░░░░ ░░░░░░░░░░░░░░░░▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░░░▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒░░░░░░░░░░░░░░