hotfy_sdk 2.2.0 copy "hotfy_sdk: ^2.2.0" to clipboard
hotfy_sdk: ^2.2.0 copied to clipboard

PlatformAndroid

SDK da Hotfy para Flutter — wrapper (orquestração de ads AdMob/GAM), CDP (eventos, atribuição, ad revenue/ILAR, push) e device token (sync FCM/APNS no Console + CDP).

Changelog #

Todas as mudanças notáveis deste pacote são documentadas aqui.

O formato segue Keep a Changelog e o versionamento segue Semantic Versioning.

2.2.0 #

Added #

  • collectAdvertisingId no AnalyticsConfig (default true). Optar por não coletar o GAID passa a ser configuração explícita, em vez de efeito colateral de a origem do dado estar indisponível. Paridade com o SDK React Native.
  • adTrackingLimited no contexto do evento (ad_tracking_limited no JSON), para que o servidor consiga separar advertising_id vazio por escolha do usuário de vazio por falha. Sem esse sinal os dois são indistinguíveis e a saúde do campo não é monitorável.

Fixed #

  • O nativo colapsava três desfechos em success(null). HotfySdkPlugin.getAdvertisingId devolvia null tanto quando o usuário limitava o rastreamento (comportamento correto, a respeitar) quanto quando o Play Services falhava (bug). Agora devolve { id, isAdTrackingLimited } e reporta exceção por result.error, não por success(null).
  • Erro na leitura do advertising ID passa a avisar, fora do debug-gate — mesma razão do descarte por fila cheia (invariante I2 do Contrato): degradação de dado nunca acontece em silêncio.

Changed #

  • A checagem de plataforma usa defaultTargetPlatform no lugar de Platform.isAndroid do dart:io. Em teste o dart:io reporta o host, o que tornava esse caminho inexercitável — e caminho não exercitado foi exatamente como o gap equivalente do RN passou meses sem ninguém ver. Em release o override de debug não existe, então o valor é sempre a plataforma real.

Contrato #

Novo test/cdp_device_context_test.dart implementando §9.4, com os mesmos nomes do context.test.ts do RN: context_advertising_id_present_when_available, context_limit_ad_tracking_is_empty_and_silent, context_collect_advertising_id_false_skips_collection, context_missing_advertising_source_warns_once.

2.1.3 #

Fixed #

  • Remoção do lote virou seguro por padrão (contrato §4.2). O lote era retirado da fila com removeRange ANTES do envio, então cada ramo do flush() precisava lembrar de devolvê-lo em caso de falha. O default de qualquer caminho novo era PERDER — e foi exatamente assim que o bug do 2.1.2 nasceu: alguém adicionou um catch e esqueceu. Agora o lote é lido com take e só sai da fila quando confirmado que saiu (entregue ou descartado). Perder passa a exigir ação explícita.
  • Remoção por eventId, nunca por posição. O descarte por estouro (D7) remove do início da fila; se ocorrer durante um envio, os índices deslocam e um removeRange(0, N) removeria eventos que não são do lote — perda silenciosa por outro caminho.

2.1.2 #

Fixed #

  • Perda silenciosa de eventos quando o envio lança (contrato §5 D6). O flush() retirava o lote da fila antes de enviar; se o post lançasse — rede, DNS, timeout de 10s, cliente parado — o catch apenas logava, e só em debug. O lote não voltava e o espelho não era atualizado; o próximo enqueue gravava a fila truncada por cima, tornando a perda definitiva. Como o lifecycle força flush ao ir para background, isso acontecia justamente quando o usuário sai do app depois do anúncio. A cobertura medida caiu de ~91% para ~86% em produção por causa disso.
  • 400 e demais 4xx permanentes passam a descartar o lote (D2). Antes caíam no ramo genérico e eram re-enfileirados: um 400 girava em laço apertado e, por ficar na frente da fila, prendia todos os eventos atrás dele.
  • Regra anti-laço na continuação do dreno (§4.1). A recursão do fim do flush() só continua se o lote saiu. Esse laço já existia no caminho de 5xx; não aparecia porque o lote era perdido e a fila encolhia sozinha — um bug mascarava o outro.
  • shutdown() parava o transporte antes do flush final (§7.3), destruindo tudo que estivesse pendente. Ordem invertida.
  • Estouro de maxQueueSize agora avisa ao descartar (I2). Antes sumia sem log.
  • sdkVersion dos eventos estava congelado em 1.0.0. Existiam duas constantes homônimas: a gerada do pubspec.yaml (usada por Hotfy.version, correta) e um literal em cdp/config.dart, que era o carimbado em todo evento. O console de debug mostrava a versão certa enquanto os dados diziam 1.0.0.

Added #

  • Suíte do Contrato de Entrega de Eventos (test/cdp_event_delivery_contract_test.dart), com nomes de caso idênticos aos do SDK React Native. Se um SDK for corrigido e o outro não, falta um teste de nome conhecido — em vez de a divergência ficar semanas escondida.

2.1.1 #

Fixed #

  • winningFloorTtlDays agora vale pra TODOS os formatos. Só o interstitial passava o ttlDays pro CascadeRunnerrewarded, banner (×2) e boot/app-open (×2) nunca liam a config. Como getWinningFloor trata ttlDays null como "nunca expira", o winning floor cacheado desses formatos ficava cravado pra sempre no último floor que vendeu, e o TTL configurado no console não tinha efeito nenhum. Na prática o TTL era feature exclusiva do interstitial desde que foi criado — 5 dos 7 call sites ignoravam. O mesmo bug existia no SDK React Native (corrigido lá na 2.1.1/2.1.2).

  • showRewarded agora usa o rewarded_fallback. O rewarded era o único formato que não caía pro unit de fallback quando a cascata do primary esgotava — interstitial, banner, native e app_open já usavam o seu. O slot é configurável no console e era silenciosamente ignorado. Agora espelha o showInterstitial: cascata do primary → se tudo der no-fill, cascata do fallback → senão skip(slot_not_ready). Só no-fill desce pro fallback; erro não-de-demanda (network/internal) continua abortando, e um ad já mostrado não dispara outro. O evento show do rewarded agora emite isFallback.

  • O log do customTargeting agora mostra a key real. Ele hardcodava price_floor= no texto enquanto a key enviada vinha do priceFloorTargetingKey da config (default hotfy_price_floor). Quando um app configura key própria, o log seguia dizendo price_floor= e não havia como saber o que foi enviado — e key que não casa com a UPR do GAM dá no-fill com exatamente o mesmo sintoma de floor errado ou rule ausente. Os três casos eram indistinguíveis em campo. Mesmo bug do SDK RN.

Changed #

  • CascadeRunnerInit.ttlDays virou obrigatório (aceitando null). Como null significa "nunca expira", um call site que esquecesse o campo desligava o TTL sem erro do analyzer, sem warning e sem teste pegando — foi exatamente assim que os 5 call sites acima quebraram, em Flutter e RN, sem ninguém notar por versões. Agora esquecer é erro de compilação. Mudança interna: CascadeRunner não é API pública.

2.1.0 #

Added #

  • Atribuição para o CDP agora é automática. No Hotfy.init, os sinais que o wrapper captura do Install Referrer (Android) / deep link (iOS) — gclid, gbraid/wbraid, dados do Meta e UTMs — passam a ser repassados sozinhos pro POST /v1/attribution, sem o app precisar chamar captureAttribution(...). Antes, esquecer essa chamada (ou chamá-la sem os sinais) fazia o CDP classificar os installs como source="unknown". O repasse:
    • só roda com adsEnabled (sem wrapper não há o que repassar);
    • só dispara quando há sinal de origem real — instalação orgânica sem referrer não gera linha unknown;
    • é idempotente (envia 1x por install), então coexiste com um captureAttribution(...) manual sem duplicar envio.
  • AttributionParams ganhou os campos gbraid, wbraid e metaCampaignGroupId (mapeados pro corpo do /v1/attribution), pra não perder sinais no repasse.

Notas #

  • Hotfy.captureAttribution(...) continua público como escape hatch — apps não precisam mais chamá-lo, mas quem chama segue funcionando (a guarda de idempotência evita envio duplicado).

2.0.1 #

Fixed #

  • Analytics: fim do retry storm em falha de auth. Uma key inválida/expirada fazia o flush de eventos (POST /v1/batch) retornar 401/403 e o EventQueue re-enfileirava o batch e re-chamava flush() imediatamente, num loop apertado (~1 req/s por device) que nunca convergia — com potencial de sobrecarregar o backend em escala. Agora 401/403 são tratados como falha permanente da key atual: o flush pausa (os eventos continuam na fila e persistidos) até um novo Hotfy.init com uma key válida. Erros transitórios (5xx/429) seguem com o backoff habitual.

2.0.0 #

⚠️ Breaking — key única + nomes genéricos (unificação CDP ↔ Console) #

O SDK passa a usar uma única API key (a App.apiKey do Hotfy Console) pros três módulos, e os nomes públicos com jargão interno (wrapper/cdp) viraram genéricos (ads/analytics).

Config do init()apiKey no topo; cdp/wrapper viram ads/analytics (overrides opcionais):

// antes
await Hotfy.init(SdkConfig(
  cdp: CdpConfig(apiKey: cdpKey),
  wrapper: WrapperInitConfig(apiKey: consoleKey),
));
// agora
await Hotfy.init(SdkConfig(apiKey: consoleKey));

O formato antigo continua acessível via SdkConfig.legacy(cdp:, wrapper:) (compat) — migre quando puder.

Tipos renomeados (sem alias — atualize os imports):

Antes Agora
CdpConfig AnalyticsConfig
CdpEvent AnalyticsEvent
WrapperInitConfig AdsConfig
WrapperConfig AdsResolvedConfig
WrapperBanner / WrapperBannerSize AdBanner / AdBannerSize
WrapperNativeAd AdNativeAd
WrapperAdEvent / WrapperEventName / WrapperEventListener AdEvent / AdEventName / AdEventListener
WrapperAdUnits ResolvedAdUnits
WrapperRoute AdsRoute

Changed #

  • Tags de log padronizadas: [Hotfy:cdp][Hotfy:analytics], [Hotfy:wrapper][Hotfy:ads].
  • README reescrito pro formato de key única (push token vira opt-in via deviceToken: DeviceTokenConfig()).

1.1.4 #

Nota: a 1.1.3 foi pulada — a tag foi criada mas o publish falhou (drift de versão); estas mudanças saem na 1.1.4.

Added #

  • Lock de coordenação de fullscreen. Garante que dois ads fullscreen (interstitial / app open / rewarded) nunca apareçam ao mesmo tempo nem em sequência imediata. canShowFullscreen() gateia todos os show(); o flag de "ad na tela" passa a contar só formatos fullscreen (banner/native não travam) e ganha janela anti-empilhamento de 1.5s pós-close. Resolve o "ad surgindo do nada" quando o app open de foreground colide com o gate de ad de uma tela (ex: chat). Novo motivo de skip: ad_already_showing.
  • Pré-carga do App Open de foreground (warm return). Na ida pro background o SDK pré-carrega o app open — escalando primary → fallback (esquenta o unit que realmente enche) — e, no retorno (≥30s, fora de cooldown), mostra instantâneo, sem a latência do load on-demand. Guard de frescor de 3.5h + gate de cooldown; fallback transparente pro on-demand quando não há warm pronto.
  • Open-tracking de push no SDK. Hotfy.trackConsolePushOpened(campaignId) reporta o open ao console (/v1/push-events, api_key-only — sem app_id; resolve o anonymousId do CDP automaticamente). Hotfy.parsePushData(data) extrai campaignId / targetScreen / targetParams / deeplink / isTest do payload do push. Substitui o fetch hand-rolled no handler de notificação; CDP segue com o tracking próprio (Hotfy.trackPushOpened).

1.1.2 #

Fixed #

  • Hotfy.version reportava a versão errada. A const sdkVersion em src/version.dart era mantida à mão e ficou em 1.1.0 na release 1.1.1 (drift). Agora src/version.dart é gerado de pubspec.yaml por tool/gen_version.dart (equivalente Dart do gen-version.mjs do SDK React Native) e um check no CI (PR Checks) falha se o arquivo gerado divergir do pubspec — Hotfy.version não sai mais de sync com a versão publicada.

1.1.1 #

Added #

  • Hotfy.version. Versão do SDK exposta em runtime (de src/version.dart), útil pra exibir no debug console e confirmar qual build está rodando.
  • Override de segmento (debug/QA). Hotfy.setSegmentOverride(slug), Hotfy.getSegmentOverride(), Hotfy.getSegment() e a constante availableSegments. Força qualquer Wrapper-Segment mandando um body limpo (source_type + days_since_install) no /v1/wrapper/config — sem mudança de backend. O slug persiste (SharedPreferences) e a troca refaz o fetch + reseta o ad pool. Passar null volta à resolução natural.
  • TTL do winning floor. WrapperConfig.winningFloorTtlDays (por ad unit, vindo da coluna winning_floor_ttl_days da pricing rule). Depois de N dias o cache do floor vencedor expira e a cascata reprova do topo. Janela conta desde que o floor virou vencedor (não reseta a cada venda). Ausente/0 = nunca expira (comportamento legado). Aplicado ao interstitial.
  • Custom Targeting Key dinâmica. WrapperConfig.priceFloorTargetingKey — o SDK injeta essa key no customTargeting em vez da fixa, com fallback hotfy_price_floor. Permite cada app usar a própria key sem rebuild.

Changed #

  • Observabilidade da cascata nos logs. O log de erro do preload agora inclui floor= (identifica qual variante deu no-fill), e o show emite CASCADE_NEXT ao descer de variante — deixando a cascata legível linha-a-linha.
  • Formato do winning floor no storage. Agora JSON {floor, savedAt} (era a string crua do floor) pra carregar o timestamp do TTL. Back-compat: entries legadas são lidas normalmente.

1.0.7 #

Changed #

  • Price floor só vai na request quando configurado no console. Removido o fallback hardcoded (1.86) que era aplicado a ad units GAM sem rule de price_floor. Na prática esse piso forçado não tinha UPR correspondente no GAM para units não configurados, garantindo No Fill. Agora, sem rule ativa, o customTargeting.hotfy_price_floor não é enviado — o GAM decide o fill sem piso imposto pelo cliente. Ad units com rule no console seguem inalterados.

    Internamente, units sem piso usam o sentinel noFloor como chave de slot/ cascata (mantém preload e show consistentes); o log passa a mostrar price_floor=none source=unconfigured. Afeta todos os formatos (interstitial, banner, rewarded, app open/boot).

1.0.6 #

Fixed #

  • hotfy_price_floor não chegava no GAM (todos os formatos). Os ad units GAM (/network/...) eram carregados pelas classes AdMob (InterstitialAd.load, AppOpenAd.load, RewardedAd.load, BannerAd), que convertem o request via asAdRequest() na camada nativa e descartam o customTargeting. Resultado: o piso era montado e logado, mas nunca enviado no request — as UPRs do GAM não casavam e o resultado era No Fill (só backfill AdX a $0).

    Agora cada loader ramifica por rede (detectNetwork): unit GAM usa o loader AdManager (AdManagerInterstitialAd.load, AppOpenAd.loadWithAdManagerAdRequest, RewardedAd.loadWithAdManagerAdRequest, AdManagerBannerAd), que preserva o customTargeting. Unit AdMob (ca-app-pub-...) segue pelo loader AdMob, sem targeting (não suportado).

1.0.5 #

Fixed #

  • Logs de ad ([Hotfy:ads:*] PRELOAD/SHOW/PAID/SKIP/CLOSE/LOAD via adLog) não apareciam mesmo com debug: truesetWrapperDebug() estava definido mas nunca era chamado, deixando _wrapperDebug sempre false. Agora o init do wrapper chama setWrapperDebug(config.debug).

1.0.4 #

Changed #

  • Prefixos de log alinhados 1:1 com o SDK React Native (@hotfyllc/sdk): [HotfyCdp][Hotfy:cdp], [HotfyWrapper][Hotfy:wrapper]. Logs de ads passam de Ad [INTERSTITIAL] para [Hotfy:ads:interstitial] (sufixo :test quando useTestAds). [Hotfy:device-token] já estava no padrão. Casa com o filtro ad do debug console ([hotfy:ads).

1.0.3 #

Alterado #

  • Ajustes internos de documentação e comentários (sem mudança de API).
  • CI: actions/checkout atualizado para v5 (Node 24).

1.0.0 #

Primeira versão do hotfy_sdk — SDK da Hotfy para Flutter.

Adicionado #

  • Módulo wrapper — orquestração remota de ads (interstitial, app open, rewarded, banner, native) com AdMob/GAM, configurada via Hotfy App Console.
    • Fetch de config via POST /v1/wrapper/config (header x-api-key).
    • Captura automática de atribuição: Install Referrer (Android) e deep link inicial (iOS), com cálculo de daysSinceInstall.
    • Price floors + cascade (pré-carrega instâncias por floor, avança em no-fill, cacheia o floor vencedor por ad unit).
    • Sistema de eventos (load, show, impression, click, close, error, skip) via Hotfy.on(...).
    • isAdShowing global pra suprimir reações a background falso.
  • Módulo CDP (Customer Data Platform) — eventos custom, screen views, identify, atribuição, ad revenue (ILAR), push events e offline queue persistida com retry.
  • Módulo device token — registro automático do FCM/APNS no Console E no CDP, com change-detection, retry e safety re-sync de 30 dias.
  • Fachada Hotfy — API flat única (Hotfy.init(...) e métodos dos três módulos).
1
likes
130
points
290
downloads

Documentation

API reference

Publisher

verified publisherhotfy.com

Weekly Downloads

SDK da Hotfy para Flutter — wrapper (orquestração de ads AdMob/GAM), CDP (eventos, atribuição, ad revenue/ILAR, push) e device token (sync FCM/APNS no Console + CDP).

Homepage
Repository (GitHub)

License

unknown (license)

Dependencies

android_play_install_referrer, app_links, device_info_plus, flutter, google_mobile_ads, http, package_info_plus, shared_preferences

More

Packages that depend on hotfy_sdk

Packages that implement hotfy_sdk