Condividi tramite


Raccolta di commenti e suggerimenti degli utenti nella libreria dell'interfaccia utente ACS

Introduzione

Questa guida completa è progettata per aiutare gli sviluppatori a integrare il supporto avanzato nella libreria dell'interfaccia utente di ACS, usando i servizi di Azure per l'elaborazione back-end. La guida è suddivisa in passaggi lato client e lato server per maggiore chiarezza e facilità di implementazione.

Prerequisiti

  • Sottoscrizione di Azure: è necessaria una sottoscrizione di Azure attiva. Se non è disponibile, è possibile creare un account gratuito in Account gratuito di Azure.
  • risorsa Servizi di comunicazione di Azure: Per usare le funzionalità di chiamata e chat, è necessaria una risorsa ACS. È possibile crearne uno nel portale di Azure.
  • Configurazione dell'ambiente di sviluppo: assicurarsi che l'ambiente di sviluppo sia configurato per una o più piattaforme di destinazione: Android, iOS o Web.
  • account Archiviazione di Azure: Per archiviare i commenti e suggerimenti degli utenti e i dati correlati in modo sicuro, è necessario un account Archiviazione di Azure.
  • Node.js e Express.js: la conoscenza di base di Node.js e Express.js è utile per configurare l'applicazione lato server per ricevere ed elaborare le richieste di supporto.
  • Conoscenza delle API RESTful: informazioni su come creare e usare LE API RESTful facilitano la distribuzione e la configurazione di client e server.
  • Competenze di sviluppo client: competenza nello sviluppo di applicazioni Android o iOS.

La presenza di questi prerequisiti garantisce un inizio semplice per l'integrazione di un sistema di feedback degli utenti completo usando Servizi di comunicazione di Azure e altre risorse di Azure.

Contenuto dell'esercitazione

In questa guida si ottengono informazioni dettagliate complete sull'integrazione dei meccanismi di feedback degli utenti all'interno delle applicazioni Servizi di comunicazione di Azure (ACS). L'obiettivo è migliorare il supporto clienti tramite la libreria dell'interfaccia utente ACS, usando i servizi back-end di Azure per l'elaborazione. Seguendo questa guida, gli sviluppatori apprendono a:

  • Implementare l'acquisizione di feedback sul lato client: informazioni su come acquisire commenti e suggerimenti degli utenti, log degli errori e supportare le richieste direttamente dalle applicazioni Android e iOS usando la libreria dell'interfaccia utente ACS.
  • Configurare un'applicazione lato server: istruzioni dettagliate sulla configurazione di un'applicazione Node.js usando Express.js per ricevere, elaborare e archiviare le richieste di supporto in Archiviazione BLOB di Azure. Questo server include la gestione di dati multipart/form-data per i caricamenti di file e la gestione sicura dei dati utente.
  • Creare ticket di supporto: informazioni su come generare numeri di ticket di supporto univoci, archiviare il feedback degli utenti insieme ai dati dell'applicazione pertinenti.
  • Usare Archiviazione BLOB di Azure: informazioni su come usare Archiviazione BLOB di Azure per archiviare commenti e suggerimenti e supportare i dati delle richieste, garantendo una gestione sicura e strutturata dei dati che supporti un recupero e un'analisi efficienti.
  • Migliorare l'affidabilità delle applicazioni e la soddisfazione degli utenti: gli sviluppatori possono risolvere rapidamente i problemi degli utenti implementando le strategie descritte in questa guida.

Installazione lato server

Configurazione di un'applicazione Node.js per gestire le richieste di supporto

Obiettivo della sezione: l'obiettivo è creare un'applicazione Node.js usando Express.js che funge da back-end per ricevere richieste di supporto dagli utenti. Queste richieste possono includere commenti e suggerimenti testuali, log degli errori, screenshot e altre informazioni rilevanti che consentono di diagnosticare e risolvere i problemi degli utenti. L'applicazione archivia questi dati in Archiviazione BLOB di Azure per l'accesso organizzato e sicuro.

Framework & Tools

  • Express.js: framework di Node.js per la creazione di applicazioni Web e API. Funge da base per la configurazione del server e la gestione delle richieste.
  • Formidable: una libreria per l'analisi dei dati dei moduli, progettata in particolare per la gestione di dati multipart/form-data, spesso usata per i caricamenti di file.
  • Archiviazione BLOB di Azure: Un servizio di Microsoft Azure per l'archiviazione di grandi quantità di dati non strutturati.

Passaggio 1: Configurazione dell'ambiente

Prima di iniziare, assicurarsi che l'ambiente di sviluppo sia pronto con Node.js installato. È anche necessario accedere a un account Archiviazione di Azure per archiviare i dati inviati.

  1. Installare Node.js: assicurarsi che Node.js sia installato nel sistema. È possibile scaricarlo da Node.js.

  2. Creare un account Archiviazione BLOB di Azure: se non è già stato fatto, creare un account Archiviazione di Azure tramite il portale di Azure. Questo account viene usato per archiviare i dati della richiesta di supporto.

  3. Raccogliere le credenziali necessarie: assicurarsi di avere il stringa di connessione per l'account Archiviazione BLOB di Azure.

Passaggio 2: Configurazione dell'applicazione

  1. Inizializzare un nuovo progetto Node.js:

    • Creare una nuova directory per il progetto e inizializzarla con npm init per creare un package.json file.

    • Installare Express.js, Formidable, Archiviazione di Azure BLOB SDK e altre librerie necessarie usando npm.

      npm install express formidable @azure/storage-blob uuid
      
  2. Implementazione del server:

    • Usare Express.js per configurare un server Web di base in ascolto delle richieste POST in un endpoint specifico.
    • Usare Formidable per analizzare i dati del modulo in ingresso, gestendo il contenuto multipart/form-data.
    • Generare un numero di ticket univoco per ogni richiesta di supporto, che può essere usato per organizzare i dati in Archiviazione BLOB di Azure e fornire un riferimento per gli utenti.
    • Archiviare dati strutturati, ad esempio i messaggi utente e i metadati del file di log, in un file JSON all'interno dell'Archiviazione BLOB. Archiviare i file di log effettivi e gli eventuali screenshot o allegati in BLOB separati all'interno della stessa directory del ticket.
    • Fornire un endpoint per recuperare i dettagli del supporto, che comportano il recupero e la visualizzazione dei dati da Archiviazione BLOB di Azure.
  3. Considerazioni sulla sicurezza:

    • Assicurarsi che l'applicazione convalide i dati in ingresso per la protezione da payload dannosi.
    • Usare le variabili di ambiente per archiviare in modo sicuro informazioni riservate, ad esempio il Archiviazione di Azure stringa di connessione.

Passaggio 3: Esecuzione e test dell'applicazione

  1. Variabili di ambiente:

    • Configurare le variabili di ambiente per il Archiviazione BLOB di Azure stringa di connessione e qualsiasi altra informazione sensibile. Ad esempio, è possibile usare un .env file e il dotenv pacchetto npm per il caricamento di queste variabili.
  2. Esecuzione del server:

    • Avviare l'applicazione Node.js eseguendo node <filename>.js, dove <filename> è il nome del file del server principale.
    • Convalidare il server con uno strumento appropriato per lo sviluppo Web.

Codice server:

Viene fornita un'implementazione funzionante con cui iniziare. Questo codice è un'implementazione di base personalizzata per illustrare la creazione di ticket dalle applicazioni di esempio dell'interfaccia utente ACS.

const express = require('express');
const formidable = require('formidable');
const fs = require('fs').promises
const { BlobServiceClient } = require('@azure/storage-blob');
const { v4: uuidv4 } = require('uuid');
const app = express();
const connectionString = process.env.SupportTicketStorageConnectionString
const port = process.env.PORT || 3000;
const portPostfix = (!process.env.PORT || port === 3000 || port === 80 || port === 443) ? '' : `:${port}`;

app.use(express.json());

app.all('/receiveEvent', async (req, res) => {
    try {
        const form = new formidable.IncomingForm();
        form.parse(req, async (err, fields, files) => {
            if (err) {
                return res.status(500).send("Error processing request: " + err.message);
            }
            // Generate a unique ticket number
            const ticketNumber = uuidv4();
            const blobServiceClient = BlobServiceClient.fromConnectionString(connectionString);
            const containerClient = blobServiceClient.getContainerClient('supporttickets');
            await containerClient.createIfNotExists();

            // Prepare and upload support data
            const supportData = {
                userMessage: fields.user_message,
                uiVersion: fields.ui_version,
                sdkVersion: fields.sdk_version,
                callHistory: fields.call_history
            };
            const supportDataBlobClient = containerClient.getBlockBlobClient(`${ticketNumber}/supportdata.json`);
            await supportDataBlobClient.upload(JSON.stringify(supportData), Buffer.byteLength(JSON.stringify(supportData)));

            // Upload log files
            Object.values(files).forEach(async (fileOrFiles) => {
                // Check if the fileOrFiles is an array (multiple files) or a single file object
                const fileList = Array.isArray(fileOrFiles) ? fileOrFiles : [fileOrFiles];
            
                for (let file of fileList) {
                    const blobClient = containerClient.getBlockBlobClient(`${ticketNumber}/logs/${file.originalFilename}`);
                    
                    // Read the file content into a buffer
                    const fileContent = await fs.readFile(file.filepath);
                    
                    // Now upload the buffer
                    await blobClient.uploadData(fileContent); // Upload the buffer instead of the file path
                }
            });
            // Return the ticket URL
            const endpointUrl = `${req.protocol}://${req.headers.host}${portPostfix}/ticketDetails?id=${ticketNumber}`;
            res.send(endpointUrl);
        });
    } catch (err) {
        res.status(500).send("Error processing request: " + err.message);
    }
});

// ticketDetails endpoint to serve details page
app.get('/ticketDetails', async (req, res) => {
    const ticketNumber = req.query.id;
    if (!ticketNumber) {
        return res.status(400).send("Ticket number is required");
    }

    // Fetch the support data JSON blob to display its contents
    try {
        const blobServiceClient = BlobServiceClient.fromConnectionString(connectionString);
        const containerClient = blobServiceClient.getContainerClient('supporttickets');
        const blobClient = containerClient.getBlobClient(`${ticketNumber}/supportdata.json`);
        const downloadBlockBlobResponse = await blobClient.download(0);
        const downloadedContent = (await streamToBuffer(downloadBlockBlobResponse.readableStreamBody)).toString();
        const supportData = JSON.parse(downloadedContent);

        // Generate links for log files
        let logFileLinks = `<h3>Log Files:</h3>`;
        const listBlobs = containerClient.listBlobsFlat({ prefix: `${ticketNumber}/logs/` });
        for await (const blob of listBlobs) {
            logFileLinks += `<a href="/getLogFile?id=${ticketNumber}&file=${encodeURIComponent(blob.name.split('/')[2])}">${blob.name.split('/')[2]}</a><br>`;
        }

        // Send a simple HTML page with support data and links to log files
        res.send(`
            <h1>Ticket Details</h1>
            <p><strong>User Message:</strong> ${supportData.userMessage}</p>
            <p><strong>UI Version:</strong> ${supportData.uiVersion}</p>
            <p><strong>SDK Version:</strong> ${supportData.sdkVersion}</p>
            <p><strong>Call History:</strong> </p> <pre>${supportData.callHistory}</pre>
            ${logFileLinks}
        `);
    } catch (err) {
        res.status(500).send("Error fetching ticket details: " + err.message);
    }
});

// getLogFile endpoint to allow downloading of log files
app.get('/getLogFile', async (req, res) => {
    const { id: ticketNumber, file } = req.query;
    if (!ticketNumber || !file) {
        return res.status(400).send("Ticket number and file name are required");
    }

    try {
        const blobServiceClient = BlobServiceClient.fromConnectionString(connectionString);
        const containerClient = blobServiceClient.getContainerClient('supporttickets');
        const blobClient = containerClient.getBlobClient(`${ticketNumber}/logs/${file}`);

        // Stream the blob to the response
        const downloadBlockBlobResponse = await blobClient.download(0);
        res.setHeader('Content-Type', 'application/octet-stream');
        res.setHeader('Content-Disposition', `attachment; filename=${file}`);
        downloadBlockBlobResponse.readableStreamBody.pipe(res);
    } catch (err) {
        res.status(500).send("Error downloading file: " + err.message);
    }
});

// Helper function to stream blob content to a buffer
async function streamToBuffer(stream) {
    const chunks = [];
    return new Promise((resolve, reject) => {
        stream.on('data', (chunk) => chunks.push(chunk));
        stream.on('end', () => resolve(Buffer.concat(chunks)));
        stream.on('error', reject);
    });
}


app.listen(port, () => {
    console.log(`Server running on port ${port}`);
});

Configurazione lato client

Questa sezione illustra la configurazione lato client e come raggiungere gli obiettivi seguenti:

  1. Registrarsi per i problemi segnalati dall'utente.
  2. Serializzare i dati.
  3. Inoltrarlo al server.
  4. Ricevere una risposta.
  5. Presentare la risposta all'utente.

L'abilitazione del feedback degli utenti all'interno della libreria dell'interfaccia utente di Servizi di comunicazione di Azure (ACS) richiede un'azione nella parte degli sviluppatori. onUserReportedIssueEventHandler Usando nell'integrazione della libreria, gli sviluppatori possono abilitare il modulo di supporto predefinito, consentendo agli utenti di segnalare direttamente i problemi. Questa sezione illustra come configurare il modulo di feedback sul lato client.

Implementazione dell'acquisizione di feedback sul lato client in Android

Abilitazione del modulo di supporto

  1. Registrazione del gestore eventi:

    • Per attivare il modulo di supporto all'interno dell'applicazione Android, registrare in onUserReportedIssueEventHandler un punto appropriato nel ciclo di vita dell'applicazione. Questa registrazione non solo abilita il modulo, ma garantisce anche che diventi visibile e accessibile agli utenti.
  2. Visibilità e accessibilità dei moduli:

    • La presenza dell'oggetto registrato onUserReportedIssueEventHandler influisce direttamente sulla visibilità del modulo di supporto. Senza questo gestore, il modulo rimane nascosto dall'interfaccia utente, rendendolo inaccessibile per la segnalazione dei problemi.

Acquisizione ed elaborazione di eventi di supporto

  1. Emissione di eventi in caso di segnalazione dei problemi:

    • Quando gli utenti segnalano problemi tramite il modulo di supporto abilitato, acquisisce onUserReportedIssueEventHandler gli eventi generati. Questi eventi incapsulano tutti i dettagli necessari relativi al problema segnalato dall'utente, ad esempio descrizioni, log degli errori e potenzialmente screenshot.
  2. Preparazione dei dati per l'invio:

    • Dopo che un utente segnala un problema, il passaggio successivo prevede la preparazione dei dati del problema segnalati per l'invio del server. Questa preparazione include la strutturazione delle informazioni acquisite in un formato adatto per la trasmissione HTTP, rispettando le aspettative del server.

Invio dei dati del problema al server

  1. Trasmissione asincrona dei dati:

    • Utilizzare meccanismi asincroni per trasmettere i dati preparati all'endpoint server designato. Questo approccio garantisce che l'applicazione rimanga reattiva, offrendo un'esperienza utente uniforme mentre i dati vengono inviati in background.
  2. Gestione delle risposte del server:

    • Al momento dell'invio dei dati, è fondamentale gestire le risposte del server in modo flessibile. Questa gestione potrebbe comportare l'analisi del feedback del server per confermare la corretta trasmissione dei dati ed eventualmente estrarre un riferimento al problema inviato (ad esempio un numero di ticket o un URL) che può essere comunicato all'utente.

Fornire commenti e suggerimenti degli utenti e notifiche

  1. Commenti e suggerimenti degli utenti immediati:

    • Notificare immediatamente agli utenti lo stato dell'invio del report sul problema tramite l'interfaccia utente dell'applicazione. Per gli invii con esito positivo, è consigliabile fornire un riferimento al problema inviato che consente agli utenti di tenere traccia dello stato di avanzamento del report.
  2. Strategia di notifica per Android O e versioni successive:

    • Per i dispositivi che eseguono Android O (livello API 26) e versioni successive, assicurarsi che l'implementazione di un canale di notifica specifico per gli invii di report. Questa configurazione è essenziale per la distribuzione efficace delle notifiche ed è un requisito per queste versioni di Android.

Seguendo questa procedura, gli sviluppatori possono integrare un solido meccanismo di feedback degli utenti nelle applicazioni Android, usando per la onUserReportedIssueEventHandler creazione efficiente di report e rilevamento dei problemi. Questo processo non solo facilita la risoluzione tempestiva dei problemi degli utenti, ma contribuisce in modo significativo a migliorare l'esperienza utente complessiva e la soddisfazione con l'applicazione.

Esempio di codice Android

Il frammento di codice Kotlin illustra il processo di integrazione di un sistema per la gestione dei problemi segnalati dall'utente all'interno di un'applicazione Android tramite Servizi di comunicazione di Azure. Questa integrazione mira a semplificare il processo di supporto abilitando la comunicazione diretta tra utenti e team di supporto. Ecco una panoramica dei passaggi coinvolti:

  1. Acquisizione eventi: il sistema resta in ascolto dei problemi segnalati dall'utente tramite la libreria dell'interfaccia utente ACS. Usa per onUserReportedIssueEventHandler acquisire feedback dall'interfaccia utente dell'applicazione, inclusi gli errori e le preoccupazioni degli utenti.

  2. Trasmissione dei dati al server: quando viene segnalato un problema, il sistema inserisce i dati pertinenti, inclusi i messaggi utente, i log degli errori, le versioni e le informazioni di diagnostica. Questi dati vengono quindi inviati a un endpoint server usando una richiesta POST asincrona, assicurando che il processo non impedisca le prestazioni dell'app.

  3. Feedback e notifica dell'utente: dopo l'invio, gli utenti vengono immediatamente informati sullo stato del report tramite notifiche in-app. Per gli invii riusciti, una notifica include un collegamento o un riferimento al ticket inviato, consentendo agli utenti di tenere traccia dello stato di avanzamento della risoluzione.

Questa configurazione non solo aiuta a risolvere rapidamente i problemi degli utenti, ma contribuisce anche significativamente a migliorare la soddisfazione degli utenti e l'affidabilità delle app fornendo un canale chiaro per il supporto e il feedback.

package com.azure.android.communication.ui.callingcompositedemoapp

import android.app.Application
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Intent
import android.net.Uri
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.azure.android.communication.ui.calling.CallCompositeEventHandler
import com.azure.android.communication.ui.calling.models.CallCompositeCallHistoryRecord
import com.azure.android.communication.ui.calling.models.CallCompositeUserReportedIssueEvent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.asRequestBody
import org.threeten.bp.format.DateTimeFormatter
import java.io.File
import java.io.IOException

/**
 * This class is responsible for handling user-reported issues within the Azure Communication Services Calling UI composite.
 * It implements the CallCompositeEventHandler interface to listen for CallCompositeUserReportedIssueEvents.
 * The class demonstrates how to send diagnostic information to a server endpoint for support purposes and
 * how to provide user feedback through notifications.
 */
class UserReportedIssueHandler : CallCompositeEventHandler<CallCompositeUserReportedIssueEvent> {
    // Flow to observe user reported issues.
    val userIssuesFlow = MutableStateFlow<CallCompositeUserReportedIssueEvent?>(null)

    // Reference to the application context, used to display notifications.
    lateinit var context: Application

    // Lazy initialization of the NotificationManagerCompat for managing notifications.
    private val notificationManager by lazy { NotificationManagerCompat.from(context) }

    /**
     * Handles the event when a user reports an issue.
     * - Creates a notification channel for Android O and above.
     * - Updates the userIssuesFlow with the new event data.
     * - Sends the event data including user message, app and SDK versions, call history, and log files to a server.
     */
    override fun handle(eventData: CallCompositeUserReportedIssueEvent?) {
        createNotificationChannel()
        userIssuesFlow.value = eventData
        eventData?.apply {
            sendToServer(
                userMessage,
                debugInfo.versions.azureCallingUILibrary,
                debugInfo.versions.azureCallingLibrary,
                debugInfo.callHistoryRecords,
                debugInfo.logFiles
            )
        }
    }

    /**
     * Prepares and sends a POST request to a server with the user-reported issue data.
     * Constructs a multipart request body containing the user message, app versions, call history, and log files.
     */
    private fun sendToServer(
        userMessage: String?,
        callingUIVersion: String?,
        callingSDKVersion: String?,
        callHistoryRecords: List<CallCompositeCallHistoryRecord>,
        logFiles: List<File>
    ) {
        if (SERVER_URL.isBlank()) { // Check if the server URL is configured.
            return
        }
        showProgressNotification()
        CoroutineScope(Dispatchers.IO).launch {
            val client = OkHttpClient()
            val requestBody = MultipartBody.Builder().setType(MultipartBody.FORM).apply {
                userMessage?.let { addFormDataPart("user_message", it) }
                callingUIVersion?.let { addFormDataPart("ui_version", it) }
                callingSDKVersion?.let { addFormDataPart("sdk_version", it) }
                addFormDataPart(
                    "call_history",
                    callHistoryRecords.map { "\n\n${it.callStartedOn.format(DateTimeFormatter.BASIC_ISO_DATE)}\n${it.callIds.joinToString("\n")}" }
                        .joinToString("\n"))
                logFiles.filter { it.length() > 0 }.forEach { file ->
                    val mediaType = "application/octet-stream".toMediaTypeOrNull()
                    addFormDataPart("log_files", file.name, file.asRequestBody(mediaType))
                }
            }.build()

            val request = Request.Builder()
                .url("$SERVER_URL/receiveEvent")
                .post(requestBody)
                .build()

            client.newCall(request).enqueue(object : Callback {
                override fun onFailure(call: Call, e: IOException) {
                    CoroutineScope(Dispatchers.Main).launch {
                        onTicketFailed(e.message ?: "Unknown error")
                    }
                }

                override fun onResponse(call: Call, response: Response) {
                    CoroutineScope(Dispatchers.Main).launch {
                        if (response.isSuccessful) {
                            onTicketCreated(response.body?.string() ?: "No URL provided")
                        } else {
                            onTicketFailed("Server error: ${response.message}")
                        }
                    }
                }
            })
        }
    }

    /**
     * Displays a notification indicating that the issue ticket has been created successfully.
     * The notification includes a URL to view the ticket status, provided by the server response.
     */
    private fun onTicketCreated(url: String) {
        showCompletionNotification(url)
    }

    /**
     * Displays a notification indicating that the submission of the issue ticket failed.
     * The notification includes the error reason.
     */
    private fun onTicketFailed(error: String) {
        showErrorNotification(error)
    }

    companion object {
        // The server URL to which the user-reported issues will be sent. Must be configured.
        private const val SERVER_URL = "${INSERT_YOUR_SERVER_ENDPOINT_HERE}"
    }

    /**
     * Creates a notification channel for Android O and above.
     * This is necessary to display notifications on these versions of Android.
     */
    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val name = "Report Submission"
            val descriptionText = "Notifications for report submission status"
            val importance = NotificationManager.IMPORTANCE_DEFAULT
            val channel = NotificationChannel("report_submission_channel", name, importance).apply {
                description = descriptionText
            }
            notificationManager.createNotificationChannel(channel)
        }
    }

    /**
     * Shows a notification indicating that the report submission is in progress.
     * This uses an indeterminate progress indicator to signify ongoing activity.
     */
    private fun showProgressNotification() {
        val notification = NotificationCompat.Builder(context, "report_submission_channel")
            .setContentTitle("Submitting Report")
            .setContentText("Your report is being submitted...")
            .setSmallIcon(R.drawable.image_monkey) // Replace with an appropriate icon for your app
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
            .setProgress(0, 0, true) // Indeterminate progress
            .build()

        notificationManager.notify(1, notification)
    }

    /**
     * Shows a notification indicating that the report has been successfully submitted.
     * The notification includes an action to view the report status via a provided URL.
     */
    private fun showCompletionNotification(url: String) {
        val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
        val pendingIntent = PendingIntent.getActivity(
            context,
            0,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )

        val notification = NotificationCompat.Builder(context, "report_submission_channel")
            .setContentTitle("Report Submitted")
            .setContentText("Tap to view")
            .setSmallIcon(R.drawable.image_monkey) // Replace with an appropriate icon for your app
            .setContentIntent(pendingIntent)
            .setAutoCancel(true) // Removes notification after tap
            .build()

        notificationManager.notify(1, notification)
    }

    /**
     * Shows a notification indicating an error in submitting the report.
     * The notification includes the reason for the submission failure.
     */
    private fun showErrorNotification(error: String) {
        val notification = NotificationCompat.Builder(context, "report_submission_channel")
            .setContentTitle("Submission Error")
            .setContentText("Error submitting report\nReason: $error")
            .setSmallIcon(R.drawable.image_monkey) // Replace with an appropriate icon for your app
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
            .build()

        notificationManager.notify(1, notification)
    }
}

Dopo aver creato un gestore per l'evento, è possibile registrarlo durante la creazione della chiamata composita.

            callComposite.addOnUserReportedEventHandler(userReportedIssueEventHandler)

Panoramica del supporto per iOS

Per integrare la raccolta di commenti e suggerimenti degli utenti nelle applicazioni iOS usando la libreria dell'interfaccia utente di Servizi di comunicazione di Azure (ACS), gli sviluppatori devono seguire un approccio strutturato. Questo processo implica l'acquisizione di commenti e suggerimenti degli utenti, inclusi i log degli errori e le informazioni utente. Al termine, queste informazioni vengono inviate a un server per l'elaborazione. In questa sezione vengono descritti in dettaglio i passaggi necessari per eseguire questa attività.

In questo esempio viene usata la libreria Alamofire per gestire l'invio di un modulo in più parti, inclusi i file di log, al server.

Implementazione del modulo di supporto

  1. Registrazione del gestore eventi: iniziare registrando un gestore eventi in ascolto dei problemi segnalati dall'utente. Questo gestore è fondamentale per acquisire feedback direttamente dall'interfaccia dell'applicazione iOS, usando le funzionalità della libreria dell'interfaccia utente di ACS.

  2. Visibilità e accessibilità del modulo: assicurarsi che il modulo di supporto sia facilmente accessibile e visibile agli utenti all'interno dell'applicazione. L'attivazione del modulo è direttamente collegata all'implementazione del gestore eventi, che attiva l'aspetto all'interno dell'interfaccia utente, consentendo agli utenti di segnalare i problemi.

Acquisizione ed elaborazione delle richieste di supporto

  1. Emissione di eventi sull'azione dell'utente: quando un utente segnala un problema tramite il modulo di supporto, il gestore eventi acquisisce questa azione. Le informazioni, ad esempio la descrizione dell'utente, il problema, i log degli errori e gli ID chiamata devono essere pronti per l'invio al server.

  2. Strutturazione dei dati per l'invio: organizzare le informazioni acquisite in un formato strutturato adatto per la trasmissione. Preparare i dati in modo che siano allineati al formato previsto dell'endpoint server che riceve ed elabora la richiesta di supporto.

Invio di dati al server

  1. Invio asincrono: usare chiamate di rete asincrone per inviare i dati strutturati al server. Questo approccio garantisce che l'applicazione rimanga reattiva, offrendo un'esperienza senza interruzioni per l'utente mentre i dati vengono trasmessi in background.

  2. Gestione delle risposte del server: al momento dell'invio, elaborare in modo efficiente le risposte del server. Ricevere e analizzare la risposta per confermare la ricezione corretta dei dati. Estrarre il collegamento del ticket di supporto dalla risposta analizzata, che può essere comunicata all'utente per il completamento.

Commenti e notifiche agli utenti

  1. Riconoscimento immediato: confermare immediatamente l'invio di una richiesta di supporto all'interno dell'applicazione, fornendo agli utenti la conferma della ricezione del report.

  2. Strategia di notifica: implementare una strategia per la distribuzione di notifiche agli utenti, in particolare nei dispositivi che eseguono versioni iOS che supportano framework di notifica specifici. È possibile usare le notifiche locali per informare gli utenti sullo stato del report o fornire aggiornamenti durante la risoluzione del problema.

Esempio di codice iOS

Questo esempio di codice Swift descrive un'implementazione di base per l'acquisizione di problemi segnalati dall'utente e l'invio a un server per l'elaborazione. In questo esempio viene illustrato come costruire un gestore eventi di supporto, inclusi i commenti e suggerimenti degli utenti e le informazioni di diagnostica dell'applicazione e il recapito al server. Il codice include anche strategie di gestione degli errori e notifiche utente per garantire un'esperienza utente uniforme.

L'esempio seguente è progettato per essere un hook da installare all'interno del gestore eventi.

Installazione

let onUserReportedIssueHandler: (CallCompositeUserReportedIssue) -> Void = { issue in
    // Add a hook to this method, and provide it the Server endpoint + a result callback
    sendSupportEventToServer(server: self.issueUrl, event: issue) { success, result in
        if success {
            // Success: Convey the result link back to the user
        } else {
            // Error: Let the user know something has happened
        }
    }
}

Hook di rete

import Foundation
import UIKit
import Combine
import AzureCommunicationUICalling
import Alamofire

/// Sends a support event to a server with details from a `CallCompositeUserReportedIssue`.
/// - Parameters:
///   - server: The URL of the server where the event will be sent.
///   - event: The `CallCompositeUserReportedIssue` containing details about the issue reported by the user.
///   - callback: A closure that is called when the operation is complete.
///               It provides a `Bool` indicating success or failure, and a `String`
///               containing the server's response or an error message.
func sendSupportEventToServer(server: String,
                              event: CallCompositeUserReportedIssue,
                              callback: @escaping (Bool, String) -> Void) {
    // Construct the URL for the endpoint.
    let url = "\(server)/receiveEvent" // Ensure this is replaced with the actual server URL.

    // Extract debugging information from the event.
    let debugInfo = event.debugInfo

    // Prepare the data to be sent as key-value pairs.
    let parameters: [String: String] = [
        "user_message": event.userMessage, // User's message about the issue.
        "ui_version": debugInfo.versions.callingUIVersion, // Version of the calling UI.
        "call_history": debugInfo.callHistoryRecords
            .map { $0.callIds.joined(separator: ",") }
            .joined(separator: "\n") // Call history, formatted.
    ]

    // Define the headers for the HTTP request.
    let headers: HTTPHeaders = [
        .contentType("multipart/form-data")
    ]

    // Perform the multipart/form-data upload.
    AF.upload(multipartFormData: { multipartFormData in
        // Append each parameter as a part of the form data.
        for (key, value) in parameters {
            if let data = value.data(using: .utf8) {
                multipartFormData.append(data, withName: key)
            }
        }

        // Append log files.
        debugInfo.logFiles.forEach { fileURL in
            do {
                let fileData = try Data(contentsOf: fileURL)
                multipartFormData.append(fileData,
                                         withName: "log_files",
                                         fileName: fileURL.lastPathComponent,
                                         mimeType: "application/octet-stream")
            } catch {
                print("Error reading file data: \(error)")
            }
        }
    }, to: url, method: .post, headers: headers).response { response in
        // Handle the response from the server.
        switch response.result {
        case .success(let responseData):
            // Attempt to decode the response.
            if let data = responseData, let responseString = String(data: data, encoding: .utf8) {
                callback(true, responseString) // Success case.
            } else {
                callback(false, "Failed to decode response.") // Failed to decode.
            }
        case .failure(let error):
            // Handle any errors that occurred during the request.
            print("Error sending support event: \(error)")
            callback(false, "Error sending support event: \(error.localizedDescription)")
        }
    }
}

Questo codice Swift illustra il processo di invio di problemi segnalati dall'utente da un'applicazione iOS usando Servizi di comunicazione di Azure. Gestisce la raccolta di feedback degli utenti, la creazione di pacchetti di informazioni di diagnostica e l'invio asincrono a un endpoint server. Fornisce inoltre le basi per implementare meccanismi di feedback, assicurandosi che gli utenti siano informati sullo stato dei report e migliorando l'affidabilità complessiva delle applicazioni e la soddisfazione degli utenti.

Conclusione

L'integrazione di meccanismi di feedback degli utenti nelle applicazioni che usano Servizi di comunicazione di Azure (ACS) è fondamentale per lo sviluppo di app reattive e incentrate sull'utente. Questa guida fornisce un percorso chiaro per configurare l'elaborazione lato server con Node.js e l'acquisizione di feedback sul lato client per le applicazioni Android e iOS. Grazie a tale integrazione, gli sviluppatori possono migliorare l'affidabilità delle applicazioni e la soddisfazione degli utenti usando i servizi cloud di Azure per una gestione efficiente dei dati.

La guida illustra i passaggi pratici per acquisire commenti e suggerimenti degli utenti, log degli errori e richieste di supporto direttamente dalle applicazioni. L'integrazione degli eventi di supporto garantisce un modo sicuro e organizzato per gestire il feedback, consentendo agli sviluppatori di risolvere rapidamente e risolvere i problemi degli utenti, con conseguente miglioramento dell'esperienza utente complessiva.

Seguendo le istruzioni descritte in questa guida, gli sviluppatori possono migliorare la velocità di risposta delle applicazioni e soddisfare meglio le esigenze degli utenti. Queste integrazioni non solo aiutano a comprendere il feedback degli utenti in modo più efficace, ma usano anche i servizi cloud per garantire un meccanismo di elaborazione e raccolta di feedback uniforme ed efficace. In definitiva, l'integrazione dei meccanismi di feedback degli utenti è essenziale per la creazione di applicazioni coinvolgenti e affidabili che assegnano priorità alla soddisfazione degli utenti.