QwenCloudBRASIL
Tradução de apoio para consulta. Código e identificadores técnicos preservados. Consultar referência original ↗

HappyOyster Guia de integração do Android SDK

Ao integrar o HappyOyster Android SDK, seu aplicativo pode entrar em mundos gerados por IA em tempo real, proporcionando uma experiência de vídeo interativa em tempo real em três modos: Aventura (exploração do mundo), Direção (direção em tempo real) e Atuação (encenação).

Happy Oyster é um produto de exploração mundial de IA. Ao integrar o Happy Oyster Android SDK, seu aplicativo pode entrar em "mundos" gerados pela IA em tempo real, proporcionando uma experiência de vídeo interativa em tempo real em aventura, dirigir, ou Atuando.

Este documento é para desenvolvedores Android do lado da integração. Abrange instalação, autenticação, fluxo de integração completo, tratamento de eventos e práticas recomendadas, explicando como integrar de forma confiável. Para APIs específicas (assinaturas, parâmetros, valores de retorno, códigos de erro, modelos de dados), consulte a Referência da API do SDK Android do Happy Oyster.

1. O que você pode fazer

2. Instalação

O Happy Oyster SDK (cn.happyoyster:opensdk) é publicado em Maven Central; seu mecanismo de comunicação em tempo real subjacente (Alibaba Cloud ARTC) é publicado em Alibaba Cloud Maven. Ambos os repositórios devem ser declarados.

Requisitos ambientais

<table><colgroup><col style={{ width: "30%" }} /><col style={{ width: "70%" }} /></colgroup><thead><tr><th> Artigo </th><th> Requisito </th></tr></thead><tbody><tr><td> minSdk </td><td> 24 (Android 7.0) e superior </td></tr><tr><td> compileSdk </td><td> 36 </td></tr><tr><td> JDK </td><td> Destino de bytecode JDK 11 (conjunto de ferramentas de host: JDK 11 ou superior recomendado) </td></tr><tr><td> Idioma </td><td> Kotlin (co-rotina <code>suspender</code> API) </td></tr><tr><td> ABI </td><td> <code>arm64-v8a</code> / <code>armeabi-v7a</code> (o mecanismo de comunicação em tempo real contém bibliotecas nativas) </td></tr><tr><td> Rede </td><td> É necessário acesso público à Internet </td></tr></tbody></table>

Adicione os repositórios na raiz do projeto settings.gradle.kts:

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()                                       // Happy Oyster SDK (cn.happyoyster:opensdk)
        maven("https://maven.aliyun.com/repository/public")  // real-time communication engine (Alibaba Cloud ARTC)
    }
}

Encontre o último lançamento publicado em Maven Central, substitua <version> abaixo com o número exato da versão e adicione a dependência no módulo build.gradle.kts:

dependencies {
    implementation("cn.happyoyster:opensdk:<version>")
}

Fixe a dependência em uma versão exata. Revise as notas de versão antes de atualizar e recompile o código do host após a atualização.

O SDK é publicado como um AAR fino e não incorpora dependências de terceiros; dependências transitivas, como o mecanismo de comunicação em tempo real, são extraídas automaticamente dos repositórios acima durante a resolução, de modo que o O repositório Alibaba Cloud Maven é indispensável—sua ausência causará com.aliyun.aio:AliVCSDK_ARTC falhar na resolução.

Permissões

A própria biblioteca SDK declara apenas INTERNET. O mecanismo de comunicação em tempo real mescla automaticamente um pequeno número de permissões do tipo rede/Bluetooth/configurações de áudio; o manifesto da biblioteca faz não inclua permissões de microfone ou câmera. O stream de vídeo é reproduzido somente por assinatura e o SDK não envia áudio real para o lado remoto.

O anfitrião deve declarar por conta própria: Quando seu targetSdk é 33 ou superior, você deve declarar a permissão de notificação no host AndroidManifest.xml (o mecanismo de comunicação em tempo real inclui um serviço em primeiro plano):

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<strong>Modo aventura (sendCommand) — é recomendado declarar a permissão do microfone</strong>: Os comandos de controle em tempo real do modo aventura utilizam um uplink transportado por um fluxo de áudio silencioso que estabeleça a “identidade do editor” local; o SDK não grava ou carrega seu áudio real. RECORD_AUDIO é não é um pré-requisito difícil para o DataChannel- mesmo sem ele, o fluxo silencioso ainda permanece como editor e o uplink está normalmente disponível. Ainda assim, para estabilidade entre dispositivos, recomendar que, se seu aplicativo usa sendCommand, você declara a permissão no host AndroidManifest.xml e solicite-o em tempo de execução antes de ligar.

<uses-permission android:name="android.permission.RECORD_AUDIO" />

<strong>Sobre o código de erro 105004</strong>: 105004 significa que o canal em tempo real não está pronto/um envio falhou (por exemplo, uma interrupção do DataChannel ou uma anomalia no canal em tempo real). É não é uma consequência necessária de uma permissão ausente—não é acionado diretamente por RECORD_AUDIO sendo não concedido. Modo de direção (sendInstruct) e a reprodução de vídeo não são afetadas. Se o seu aplicativo não usa o modo aventura, essa permissão não é necessária.

Para cortar permissões mescladas, use tools:node="remove".

3. Modelo de autenticação

O SDK não obtém nem atualiza tokens, mantendo-o leve. A autenticação tem duas camadas:

  1. QwenCloud chave de API do gateway: injetado como um token de portador pelo seu aplicativo via updateToken(token). O SDK mantém apenas o mais recente; ele não persiste nem o atualiza e, após a alteração da chave, você o injeta novamente. O gateway exige que todas as solicitações do SDK, incluindo startTravel, use este portador. Quando a chave do portador expira ou é inválida, o SDK lança SDKError(101002); neste caso você deve obter novamente e reinjetar a Chave do Portador (updateToken) em vez de trocar por um novo bilhete. Durante o desenvolvimento/demonstração, você pode usar seu principal QwenCloud Chave de API diretamente como portador (updateToken) para fazer o fluxo funcionar. Em produção, sempre mude para um token de curta duração cunhado pelo seu servidor e nunca agrupe uma chave de API de longa duração em um aplicativo distribuído.
  2. <strong>Credencial de experiência única ticket</strong>: Trocado pelo seu servidor através da plataforma aberta e entregue ao cliente; usado para um único startTravel apenas. Torna-se inválido após o término da experiência (normal ou anormal) e não pode ser reutilizado. Erros de credenciais no nível do ticket são identificados por códigos de servidor de seis dígitos (por exemplo, 401010 = bilhete inválido/expirado, 401011 = ticket já usado), que o SDK passa literalmente.

Seu servidor é responsável por criar/gerenciar o Mundo, trocar pelo bilhete de viagem e entregá-lo ao cliente; o Android SDK consome apenas tokens e tickets e não fornece interface de gerenciamento mundial. Durante a inicialização, você deve passar o código da sua conta QwenCloud Anfitrião de API através de SDKConfig.apiHost (da forma maas.qwencloudapi.com, copiado do campo "API Host" na página API Key do QwenCloud console) e passe o necessário e sem padrão SDKConfig.model (happyoyster-1.0-directing / happyoyster-1.0-acting / happyoyster-1.0-adventure, correspondendo à entrada Open API). O SDK completa o URL da solicitação como https://{apiHost}/api/v2/apps/{model}/openapi/v1/{endpoint}, então você não monta você mesmo.

Omitindo model ao construir SDKConfig é um erro em tempo de compilação. Se model está em branco, initialize lança sincronizadamente SDKError(100002) (raw = "SDKConfig.model must not be blank"), não cria ou substitui nenhum tempo de execução e não faz nenhuma solicitação de rede. O host da API, o modelo e a chave de API injetada devem corresponder à conta necessária e à autorização do modelo, caso contrário, o gateway normalmente retornará AccessDenied (em tempo de execução, isso é lançado como SDKError(106003), com o original AccessDenied corpo disponível em SDKError.raw; veja o 106003 linha da tabela de códigos de erro de referência da API para obter uma lista de verificação de solução de problemas). Por segurança, recomendamos fortemente que o cliente injete um token de curta duração cunhado pelo seu servidor como portador, em vez de agrupar uma chave de API de longa duração no aplicativo ou enviá-la para um repositório de código.

4. Início rápido

import cn.happyoyster.opensdk.*   // All entry-point classes live in this package: HappyOyster, SDKConfig, TravelStatusValue, ModeValue, SDKError, etc.

// Track the current experience status and this Travel's metadata; use them to decide whether interaction can be sent.
@Volatile private var currentStatus: TravelStatusValue? = null
@Volatile private var currentTravel: StartTravelData? = null

// 1) Initialize (recommended in Application.onCreate)
//    apiHost is required: your account's QwenCloud API Host (copied from the API Key page in the QwenCloud console)
//    model is required with no default: the complete model name and version; refer to the official QwenCloud HappyOyster model documentation for available values
HappyOyster.initialize(
    applicationContext,
    SDKConfig(
        apiHost = "maas.qwencloudapi.com",
        model = "happyoyster-1.0",
    ),
)

// 2) Inject the QwenCloud gateway API Key (as the Bearer token)
HappyOyster.updateToken(qwenApiKey)

// 3) Listen to SDK events: use onStatusChanged to drive the host state machine and gate interaction capabilities
HappyOyster.addListener(object : HappyOysterListener {
    override fun onStatusChanged(status: TravelStatusValue) {
        currentStatus = status
        when (status) {
            // Interaction is allowed only in running; adventure-mode sendCommand is valid only in running.
            TravelStatusValue.Running -> markInteractionAllowed()
            // Paused is the signal that pauseTravel has actually taken effect (asynchronous, see below); sendInstruct is still allowed here.
            TravelStatusValue.Paused -> markTravelPaused()
            // Terminal states: the SDK has already ended and released resources; clean up the host-side experience state.
            TravelStatusValue.Completed, TravelStatusValue.Failed -> clearActiveTravel()
            // init / pending: not yet ready, interaction unavailable.
            else -> markInteractionBlocked()
        }
    }
    override fun onError(error: SDKError) {
        // Unified error callback; see the error-codes section of the API Reference
    }
})

// 4) Start the experience (the ticket is delivered by your server)
lifecycleScope.launch {
    try {
        // Only one concurrent Travel is allowed per App at a time (alirtc limitation: even with a different ticket
        // you cannot start a second Travel concurrently within the same App). Calling again while an experience is in progress throws
        // SDKError(103004), whose raw carries only a redacted summary of the ticket currently playing, never the full ticket.
        val travel: StartTravelData = HappyOyster.startTravel(ticket)
        currentTravel = travel

        // Inspect the returned Travel metadata first, then decide how to interact:
        //   travel.mode          —— directing / adventure / acting
        //   travel.creationModel —— Simple / ScriptList
        //   travel.aspectRatio   —— Acting only; use to set player orientation
        val canSendInstruct = (travel.mode == ModeValue.Directing || travel.mode == ModeValue.Acting) &&
            travel.creationModel != CreationModelValue.ScriptList
        // Note: calling sendInstruct in ScriptList mode throws SDKError(103002); script content is not managed through this SDK.

        // 5) Mount the video View (the SDK returns the view; you add it to the layout)
        //    Acting: size the container from travel.aspectRatio first, then attachVideo() (see §13 Mode Adaptation) —
        //    the remote view is bound clip-to-fill, so a mismatched container orientation crops the picture.
        val videoView = HappyOyster.attachVideo()
        binding.videoContainer.addView(videoView)

        // 6) In-run interaction — you must wait for running before sending:
        //    - sendCommand (adventure): valid only in running; calling in init/pending/paused returns 103002.
        //    - sendInstruct (directing / Acting): valid in running or paused; calling in init/pending returns 103002.
        if (canSendInstruct && currentStatus == TravelStatusValue.Running) {
            HappyOyster.sendInstruct("It suddenly starts to rain heavily")
        }
    } catch (e: SDKError) {
        // Handle start failure (e.g., 103004: an experience is already playing)
    }
}

// 7) Pause / Resume (directing and Acting; rewind is directing only)
//    pauseTravel is asynchronous: the method's return only means "accepted"; the actual pause is determined by onStatusChanged(paused).
//    Be sure to wait for the paused callback before allowing resumeTravel.
lifecycleScope.launch {
    if ((currentTravel?.mode == ModeValue.Directing || currentTravel?.mode == ModeValue.Acting) &&
        currentStatus == TravelStatusValue.Running
    ) {
        HappyOyster.pauseTravel()        // accepted; the host marks pausing and waits for the status callback
        // …after receiving onStatusChanged(Paused)…
    }
}
lifecycleScope.launch {
    if (currentStatus == TravelStatusValue.Paused) {
        HappyOyster.resumeTravel()       // call only after paused
    }
}

// 8) End the experience (the SDK automatically disconnects the real-time connection and releases resources)
lifecycleScope.launch { HappyOyster.endTravel() }

5. Assinatura de eventos e tratamento de erros

Usar onStatusChanged para conduzir a máquina de estado host e para controlar os recursos de interação; usar onError para receber erros de tempo de execução uniformemente. Para obter a interface do evento e os códigos de erro completos, consulte a Referência da API do SDK Android do Happy Oyster.

Deve

Recomendado

Evite

6. Instruções de envio: sendInstruct e sendCommand

sendInstruct (direção/atuação)

sendInstruct é usado na direção e atuação para enviar instruções de texto que conduzem a imagem; ele pode ser chamado tanto no running ou paused estado. No estado pausado, o SDK não retoma automaticamente – o host decide se deseja chamar resumeTravel primeiro e depois envie a instrução (para detalhes do contrato, como limitação e validação de estado, consulte a Referência da API). Um mundo de atuação creationModel é sempre Simple, portanto, a restrição ScriptList se aplica apenas ao direcionamento.

sendCommand (modo aventura)

sendCommand é usado no modo aventura (aventura) para enviar comandos de controle de direção/ponto de vista/ação; é válido apenas em running. O SDK possui um acelerador de últimas vitórias integrado de 42 ms (24 fps), para que o host possa chamá-lo na taxa de quadros do jogo e o SDK se funde automaticamente; nenhuma limitação de taxa manual é necessária. <strong>Ligue uma vez para uma ação única. Para uma ação retida, continue chamando cada quadro enquanto estiver retido e envie explicitamente um None redefinir quando liberado</strong> (para obter o contrato detalhado de limitação e detecção de falhas, consulte a Referência da API).

Prática recomendada: na interface de interação do modo aventura do host, forneça três pontos de entrada de comando independentes (direção do movimento + direção do ponto de vista + interação da ação), mantenha o valor atualmente mantido para cada grupo e envie um instantâneo completo de três campos em cada chamada. Usar "None" somente para dimensões inativas. Quando entradas de dimensões diferentes são mantidas simultaneamente, preserve e envie todos os valores ativos em vez de redefinir as outras dimensões para "None".

7. Pausar / Retomar / Retroceder

Modos aplicáveis: pausar/resumir aplica-se a dirigir e Atuandoe se comportam de forma idêntica em ambos (incluindo a barreira de 3 segundos abaixo); retroceder é direcionar apenas. Chamando pauseTravel / resumeTravel / rewindTravel no modo aventura é rejeitado pelo SDK com 103003. Além disso, o version relatado pelo servidor deve ser o token v2 que seu modo requer (storyV2 para dirigir, actingV2 para Atuação); caso contrário, pausar/retomar retornar 103002.

A pausa é assíncrona: O retorno do pauseTravel método significa apenas que foi aceito; a pausa real é determinada por onStatusChanged(Paused). Entre "chamar pauseTravel" e "recebendo o Paused retorno de chamada", o host pode marcar seu estado de experiência local como pausing; só depois de receber Paused você deve permitir chamadas resumeTravel ou o dependente do estado pausado rewindTravel.

Pausa interna do SDK→reabrir barreira: uma vez Paused é recebido, o host pode ligar resumeTravel / rewindTravel de acordo com o contrato. Se a chamada cair dentro da janela de liquidação de 3 segundos após a confirmação da pausa, o método de suspensão do SDK aguardará o tempo restante antes de enviar a solicitação que reabre a sala em tempo real. O host não precisa de um atraso extra de pausa→retomada e nenhum atraso é adicionado depois que 3 segundos tiverem decorrido naturalmente.

Tempo de espera da chamada do host (recomendado): É recomendado que o host adie a emissão de outro pauseTravel dentro de 3 s depois resumeTravel retorna com sucesso, para evitar alternar com muita frequência. O próprio SDK não impõe esse resfriamento.

Retroceder: rewindTravel está disponível apenas no paused estado; após retroceder, o servidor retoma automaticamente e o SDK reconecta automaticamente o RTC, sem necessidade de intervenção do host. Sequência típica: pause → wait:paused → rewindTravel(sec). Os segundos de retrocesso rewindToSec deve ser múltiplo de 4 (por exemplo, 4, 8, 12); não múltiplos são definidos pelo servidor (por exemplo, 7 → 4), e os segundos efetivos reais são determinados pelo retornado resumedAtSec. Retroceder é apenas dirigir: ligando rewindTravel no modo Atuação ou aventura é rejeitado pelo SDK localmente com 103003, sem emitir nenhuma solicitação HTTP; o anfitrião deve esconder a entrada de retrocesso em vez de apenas esmaecer o botão.

Para obter detalhes do contrato, como semântica assíncrona e espera de 3x novas tentativas (resistência de currículoTravel de 1 s/2 s/3 s), consulte a Referência da API do SDK Android do Happy Oyster.

8. Registro

Etiqueta Logcat do SDK: HappyOysterSDK. O SDK fornece dois caminhos de saída de log independentes:

HappyOyster.initialize(
    context,
    SDKConfig(
        apiHost = "maas.qwencloudapi.com",  // required: your account's QwenCloud API Host
        model = "happyoyster-1.0",                          // required with no default: complete model name and version
        logcatEnabled = true,        // enable built-in Logcat (default false)
        logLevel = LogLevel.DEBUG,   // affects only the built-in Logcat
        logHandler = { record ->     // optional: forward to the host's own Logcat tag
            android.util.Log.d("MyApp/SDK", record.message, record.throwable)
        },
    ),
)
adb logcat -s HappyOysterSDK          # output only when built-in Logcat is enabled
adb logcat -s HappyOysterSDK MyApp    # also view the host App logs (replace MyApp with your tag)

Correlação de sessão e solicitação: o travelId (i.e. encryptedTravelId) nos logs do SDK é emitido em cheio e é a chave da sessão enviada ao servidor — inclua-a ao investigar uma sessão ou preencher um ticket para alinhar os logs do cliente e do servidor. Cada linha de resposta HTTP também carrega o nome do servidor requestId (o reqId= campo, mostrado como - quando ausente) para identificar uma única solicitação; esse ID aparece apenas nos logs e nunca aparece em nenhum valor de retorno público.

9. Ciclo de vida e memória

10. Corrotinas e Threading

11. Gerenciamento de tokens

12. Recuperação de erros

(Para a classificação fatal/não fatal, consulte a seção de códigos de erro da Referência da API do SDK Android do Happy Oyster.)

13. Adaptação de modo

Atuação: Definindo a Orientação do Jogador a partir aspectRatio

StartTravelData.aspectRatio é não nulo apenas para atuação: "9:16" (retrato, o padrão do lado do servidor quando o mundo é criado) ou "16:9" (paisagem); é null para aventura e direção. A tela é fixa quando o mundo for criado (especificado pelo seu servidor através da Open API), portanto, para o cliente, é um resultado somente leitura. O SDK preserva valores não reconhecidos literalmente, então o host deve tratar qualquer valor que não reconheça como null e voltar à sua própria orientação padrão.

Tempo: decida a orientação do contêiner de reprodução após startTravel() retorna e antes ligando attachVideo() e montando o devolvido SurfaceView em seu layout. O SDK só começa a vincular e renderizar o fluxo remoto quando a visualização do host é anexada, portanto, decidir a orientação nesse ponto ainda precede o primeiro quadro renderizado. O valor é entregue com o startTravel() valor de retorno e é não garantido chegar antes que o SDK entre no canal de comunicação em tempo real - ele só precisa ser aplicado antes attachVideo().

Consequência: a visualização remota está vinculada a um clipe para preencher modo de renderização - um contêiner cuja orientação discorda aspectRatio recorta a imagem (por exemplo, um 9:16 o fluxo de retrato colocado em um contêiner 16:9 perde a maior parte de sua parte superior e inferior) em vez de colocá-lo em caixa de correio.

// After startTravel() returns and before attachVideo(): size the container from aspectRatio first
val ratio: Float = when (travel.aspectRatio) {   // width / height
    "9:16" -> 9f / 16f                           // portrait (the Acting server-side default)
    "16:9" -> 16f / 9f                           // landscape
    else -> HOST_DEFAULT_RATIO                   // null or unrecognized: fall back to your own default orientation
}
// Apply `ratio` to the container, for example:
//   - Compose: Modifier.fillMaxWidth().aspectRatio(ratio)
//   - Views: put the container in a ConstraintLayout, full width, height driven by the ratio (height=0dp in XML)
binding.videoContainer.updateLayoutParams<ConstraintLayout.LayoutParams> {
    dimensionRatio = ratio.toString()
}

// Only once the container orientation is settled, mount the video View returned by the SDK
val videoView = HappyOyster.attachVideo()
binding.videoContainer.addView(videoView)

Quando a ação não está habilitada para a conta (ou a especificação está desativada), tanto a criação quanto a adesão ao mundo são rejeitadas pelo servidor: startTravel falha com 403007 (rejeitado antes da viagem ser criada), que o SDK passa literalmente. Faça não tente novamente este código como capacidade total; solicitará a ativação.

14. Exemplo completo (ViewModel + snippet de atividade)

class TravelViewModel : ViewModel() {

    private val listener = object : HappyOysterListener {
        override fun onStatusChanged(status: TravelStatusValue) {
            _status.value = status
        }
        override fun onError(error: SDKError) {
            _error.value = error
        }
    }

    init { HappyOyster.addListener(listener) }

    fun start(ticket: String) = viewModelScope.launch {
        try {
            val travel = HappyOyster.startTravel(ticket)
            _travel.value = travel
        } catch (e: SDKError) {
            _error.value = e
        }
    }

    fun send(text: String) = viewModelScope.launch {
        runCatching { HappyOyster.sendInstruct(text) }
    }

    fun stop() = viewModelScope.launch { runCatching { HappyOyster.endTravel() } }

    override fun onCleared() {
        HappyOyster.removeListener(listener)
        viewModelScope.launch { runCatching { HappyOyster.endTravel() } }
    }
}

// Mount the video in the Activity
val videoView = HappyOyster.attachVideo()
binding.videoContainer.addView(videoView)
// On end
binding.videoContainer.removeAllViews()

15. Guia de solução de problemas de DevOps

Quando algo dá errado durante a integração (não é possível entrar em uma viagem, tela preta sem vídeo, queda de fluxo no meio da sessão, anomalias de pausa/retomada, ...), abrir os logs do SDK é a maneira mais rápida de localizar o problema. Este capítulo fornece um fluxo padrão de solução de problemas – útil tanto para sua autoverificação quanto para nos fornecer informações suficientes em uma única rodada.

15.1 Habilitando registros

Escolha uma saída de registro conforme necessário durante a solução de problemas (consulte §8 Registro): para uma autoverificação rápida, defina logcatEnabled = true e leia com adb logcat, levantando logLevel para DEBUG ou VERBOSE ao diagnosticar (o padrão INFO já carrega o cronograma completo do ciclo de vida; cair para WARN apenas para erros); para alimentar seu próprio sistema, use logHandler para receber cada LogRecord (não afetado por logLevel) e grave-o em seu arquivo ou plataforma de travamento.

15.2 ID de correlação de sessão: travelId

O travelId (i.e. StartTravelData.encryptedTravelId) nos logs do SDK é emitido em cheio. É o identificador único executado em uma sessão e a chave da sessão enviada ao servidor. É a chave para alinhar o registros do cliente com o registros de sessão do lado do servidor - inclua sempre o travelId da sessão com falha ao relatar um problema.

15.3 Coletando registros

Ao reproduzir o problema, persista os logs do SDK em um arquivo:

# Clear history, reproduce the issue, then capture SDK logs to a file
adb logcat -c
adb logcat -s HappyOysterSDK > happyoyster-sdk.log
# To also see your own app logs (replace MyApp with your tag):
adb logcat -s HappyOysterSDK MyApp > happyoyster-sdk.log

15.4 O que incluir ao relatar um problema

Para evitar múltiplas viagens de ida e volta, anexe o seguinte:

<table><colgroup><col style={{ width: "30%" }} /><col style={{ width: "70%" }} /></colgroup><thead><tr><th> Artigo </th><th> Notas </th></tr></thead><tbody><tr><td> Versão do SDK </td><td> <code>HappyOyster.VERSION</code> </td></tr><tr><td> Sessão <code>ID de viagem</code> </td><td> o <code>ID de viagem criptografado</code> da sessão com falha (ver 15.2) </td></tr><tr><td> Hora da ocorrência </td><td> o tempo aproximado (o nível de minuto está bom) </td></tr><tr><td> Código de erro </td><td> o capturado <code>SDKError.code</code> (significados na tabela de códigos de erro de referência da API) </td></tr><tr><td> Etapas de reprodução </td><td> caminho de ação + resultado esperado + resultado real </td></tr><tr><td> Meio Ambiente </td><td> modelo do dispositivo, versão do Android, rede (WiFi/celular) </td></tr><tr><td> Arquivo de registro </td><td> o <code>HappyOysterSDK</code> registro de 15.3 (<code>DEBUGAR</code>/<code>VERBOSO</code> recomendado) </td></tr></tbody></table>

15.5 Sobre redação e compartilhamento de log

Os logs do SDK são projetados para serem seguro para compartilhar: credenciais (token do portador, bilhete de viagem, token RTC), identificadores internos RTC e URLs de mídia são editados antes de serem escritos, para que nunca apareçam em texto simples; travelId é um identificador de sessão (não uma credencial) e é mantido por completo apenas para correlação. Mesmo assim, recomendamos transferir os arquivos de log por meio de um canal confiável, em vez de colá-los publicamente em plataformas não controladas.