Freigeben über


Sammeln von Benutzerfeedback in der ACS-Benutzeroberflächenbibliothek

Einführung

Dieses umfassende Handbuch wurde entwickelt, um Entwicklern bei der Integration erweiterter Unterstützung in die ACS UI Library unter Verwendung von Azure-Diensten für die Back-End-Verarbeitung zu helfen. Das Handbuch ist in clientseitige und serverseitige Schritte unterteilt, um Klarheit und einfache Implementierung zu bieten.

Voraussetzungen

  • Azure-Abonnement: Sie benötigen ein aktives Azure-Abonnement. Wenn Sie kein Konto haben, können Sie ein kostenloses Konto unter Azure Free Account erstellen.
  • Azure Communication Services-Ressource: Für die Verwendung von Anruf- und Chatfunktionen ist eine ACS-Ressource erforderlich. Sie können einen Cluster im Azure-Portal erstellen.
  • Setup der Entwicklungsumgebung: Stellen Sie sicher, dass Ihre Entwicklungsumgebung für eine oder mehrere Zielplattformen eingerichtet ist – Android, iOS oder Web.
  • Azure Storage-Konto: Zum sicheren Speichern von Benutzerfeedback und verwandten Daten ist ein Azure Storage-Konto erforderlich.
  • Node.js und Express.js: Grundlegende Kenntnisse über Node.js und Express.js sind hilfreich beim Einrichten der serverseitigen Anwendung zum Empfangen und Verarbeiten von Supportanfragen.
  • Kenntnisse über RESTful-APIs: Grundlegendes Verständnis von der Erstellung und Nutzung von RESTful-APIs bei der Bereitstellung und Konfiguration von Client und Server.
  • Fähigkeiten zur Cliententwicklung: Kenntnisse in der Entwicklung von Android- oder iOS-Anwendungen.

Die Bereitstellung dieser Voraussetzungen gewährleistet einen reibungslosen Einstieg in die Integration eines umfassenden Benutzerfeedbacksystems mithilfe von Azure Communication Services und anderen Azure-Ressourcen.

Lerninhalt

In diesem Leitfaden erhalten Sie umfassende Einblicke in die Integration von Benutzerfeedbackmechanismen in Ihre Azure Communication Services (ACS)-Anwendungen. Der Fokus liegt auf der Verbesserung des Kundensupports über die ACS UI Library mit Azure-Back-End-Diensten zur Verarbeitung. Anhand dieses Leitfadens lernen Entwickler Folgendes:

  • Implementieren der clientseitigen Feedbackerfassung: Erfahren Sie, wie Sie Benutzerfeedback, Fehlerprotokolle und Supportanfragen direkt aus Android- und iOS-Anwendungen mithilfe der ACS UI Library erfassen.
  • Einrichten einer serverseitigen Anwendung: Schrittweise Anleitungen zum Einrichten einer Node.js Anwendung mithilfe von Express.js zum Empfangen, Verarbeiten und Speichern von Supportanfragen in Azure Blob Storage. Dieser Server umfasst die Verarbeitung von mehrteiligen/Formulardaten für Dateiuploads und die sichere Verwaltung von Benutzerdaten.
  • Erstellen von Supporttickets: Erfahren Sie, wie Sie eindeutige Supportticketnummern generieren und Benutzerfeedback zusammen mit relevanten Anwendungsdaten speichern.
  • Nutzen von Azure Blob Storage: Erfahren Sie, wie Sie Azure Blob Storage zum Speichern von Feedback- und Supportanforderungsdaten verwenden, um eine sichere und strukturierte Datenverwaltung sicherzustellen, die effizientes Abrufen und Analysieren unterstützt.
  • Verbessern der Zuverlässigkeit von Anwendungen und der Benutzerzufriedenheit: Entwickler können Benutzerprobleme schnell beheben, indem Sie die in diesem Handbuch beschriebenen Strategien umsetzen.

Serverseitiges Setup

Einrichten einer Node.js-Anwendung zur Behandlung von Supportanfragen

Abschnittsziel: Es ist das Ziel eine Node.js-Anwendung mithilfe von Express.js zu erstellen, die als Back-End dient, um Supportanfragen von Benutzern zu erhalten. Diese Anforderungen können textbezogenes Feedback, Fehlerprotokolle, Screenshots und andere relevante Informationen enthalten, die bei der Diagnose und Behebung von Benutzerproblemen helfen können. Die Anwendung speichert diese Daten in Azure Blob Storage für den organisierten und sicheren Zugriff.

Framework & Tools

  • Express.js: Ein Node.js-Framework zum Erstellen von Webanwendungen und APIs. Es dient als Grundlage für unsere Servereinrichtung und Anforderungsverarbeitung.
  • Formidable: Eine Bibliothek zum Analysieren von Formulardaten, insbesondere für die Verarbeitung von mehrteiligen/Formulardaten, die häufig für Dateiuploads verwendet wird.
  • Azure Blob Storage: Ein Microsoft Azure-Dienst für die Speicherung großer Mengen unstrukturierter Daten.

Schritt 1: Umgebungseinrichtung

Bevor Sie beginnen, stellen Sie sicher, dass Ihre Entwicklungsumgebung bereit und Node.js installiert ist. Sie benötigen auch Zugriff auf ein Azure Storage-Konto, um die übermittelten Daten zu speichern.

  1. Installieren Sie Node.js: Stellen Sie sicher, dass Node.js auf Ihrem System installiert ist. Sie können sie von Node.js herunterladen.

  2. Erstellen Sie ein Azure Blob Storage-Konto: Falls Sie dies noch nicht getan haben, erstellen Sie über das Azure-Portal ein Azure Storage-Konto. Dieses Konto wird verwendet, um die Supportanfragedaten zu speichern.

  3. Sammeln erforderlicher Anmeldeinformationen: Stellen Sie sicher, dass Sie über die Verbindungszeichenfolge für Ihr Azure Blob Storage-Konto verfügen.

Schritt 2: Anwendungssetup

  1. Initialisieren eines neuen Node.js-Projekts:

    • Erstellen Sie ein neues Verzeichnis für Ihr Projekt und initialisieren Sie es mit npm init, um eine package.json-Datei zu erstellen.

    • Installieren Sie mithilfe von npm Express.js, Formidable, das Azure Storage Blob SDK und andere erforderliche Bibliotheken.

      npm install express formidable @azure/storage-blob uuid
      
  2. Serverimplementierung:

    • Verwenden Sie Express.js, um einen einfachen Webserver einzurichten, der POST-Anforderungen auf einen bestimmten Endpunkt überwacht.
    • Verwenden Sie Formidable zum Analysieren eingehender Formulardaten, behandeln Sie mehrteiligen/Formulardateninhalt.
    • Generieren Sie eine eindeutige Ticketnummer für jede Supportanfrage, die zur Organisation von Daten in Azure Blob Storage verwendet werden kann, und stellen Sie einen Verweis für Benutzer bereit.
    • Speichern Sie strukturierte Daten, wie Benutzernachrichten und Protokolldateimetadaten, in einer JSON-Datei im Blob Storage. Speichern Sie die eigentlichen Protokolldateien und alle Screenshots oder Anlagen in separaten Blobs im Verzeichnis desselben Tickets.
    • Stellen Sie einen Endpunkt zum Abrufen von Supportdetails bereit, was das Abrufen und Anzeigen von Daten aus Azure Blob Storage umfasst.
  3. Sicherheitsüberlegungen:

    • Stellen Sie sicher, dass Ihre Anwendung die eingehenden Daten überprüft, um vor schädlichen Nutzlasten zu schützen.
    • Verwenden Sie Umgebungsvariablen, um vertrauliche Informationen wie Ihre Azure Storage-Verbindungszeichenfolge sicher zu speichern.

Schritt 3: Ausführen und Testen der Anwendung

  1. Umgebungsvariablen:

    • Richten Sie Umgebungsvariablen für Ihre Azure Blob Storage-Verbindungszeichenfolge und alle anderen vertraulichen Informationen ein. Sie können beispielsweise eine .env-Datei (und das dotenv-npm-Paket zum Laden dieser Variablen) verwenden.
  2. Ausführen des Servers:

    • Starten Sie Ihre Node.js Anwendung, indem Sie node <filename>.js ausführen, wobei <filename> der Name der Hauptserverdatei ist.
    • Überprüfen Sie den Server mit einem geeigneten Tool für die Webentwicklung.

Servercode:

Im Folgenden finden Sie eine funktionierende Implementierung, mit der Sie beginnen können. Dieser Code ist eine grundlegende Implementierung zum Veranschaulichen der Ticketerstellung aus den ACS UI-Beispielanwendungen.

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}`);
});

Clientseitige Einrichtung

In diesem Abschnitt werden die clientseitige Einrichtung und das Erreichen der folgenden Ziele behandelt:

  1. Registrieren Sie sich für Benutzermeldungen von Problemen.
  2. Serialisieren Sie die Daten.
  3. Leiten Sie sie an den Server weiter.
  4. Empfangen Sie eine Antwort.
  5. Präsentieren Sie die Antwort an den Benutzer.

Das Aktivieren von Benutzerfeedback in der Azure Communication Services (ACS) UI Library erfordert Aktionen auf Entwicklerseite. Durch die Verwendung der onUserReportedIssueEventHandler in der Integration der Bibliothek können Entwickler das integrierte Supportformular aktivieren, sodass Benutzer Probleme direkt melden können. Dieser Abschnitt führt Sie durch das Einrichten des clientseitigen Feedbackformulars.

Implementieren der clientseitigen Feedbackerfassung in Android

Aktivieren des Supportformulars

  1. Registrierung des Ereignishandlers:

    • Um das Supportformular in Ihrer Android-Anwendung zu aktivieren, registrieren Sie das onUserReportedIssueEventHandler an einer passenden Stelle im Lebenszyklus Ihrer Anwendung. Diese Registrierung aktiviert nicht nur das Formular, sondern stellt auch sicher, dass es für die Benutzer sichtbar und zugänglich wird.
  2. Formularsichtbarkeit und Barrierefreiheit:

    • Das Vorhandensein des registrierten onUserReportedIssueEventHandler wirkt sich direkt auf die Sichtbarkeit des Supportformulars aus. Ohne diesen Handler bleibt das Formular auf der Benutzeroberfläche ausgeblendet, sodass es für die Problemmeldung nicht zugänglich ist.

Erfassen und Verarbeiten von Supportereignissen

  1. Ereignisemissionen bei Problemmeldung:

    • Wenn Benutzer Probleme über das aktivierte Supportformular melden, erfasst die onUserReportedIssueEventHandler ausgesendete Ereignisse. Diese Ereignisse beinhalten alle erforderlichen Details im Zusammenhang mit dem vom Benutzer gemeldeten Problem, z. B. Beschreibungen, Fehlerprotokolle und potenziell Screenshots.
  2. Datenvorbereitung für die Übermittlung:

    • Wenn ein Benutzer ein Problem meldet, ist der nächste Schritt die Vorbereitung der gemeldeten Problemdaten für die Serverübermittlung. Diese Vorbereitung umfasst die Strukturierung der erfassten Informationen in ein Format, das für die HTTP-Übertragung geeignet ist, wobei die Servererwartungen eingehalten werden müssen.

Übermitteln von Problemdaten an den Server

  1. Asynchrone Datenübertragung:

    • Verwenden Sie asynchrone Mechanismen, um die vorbereiteten Daten an den angegebenen Serverendpunkt zu übertragen. Mit diesem Ansatz wird sichergestellt, dass die Anwendung reaktionsfähig bleibt und eine reibungslose Benutzererfahrung bietet, während die Daten im Hintergrund gesendet werden.
  2. Serverantwortbehandlung:

    • Bei der Datenübermittlung ist es wichtig, Serverantworten ordnungsgemäß zu verarbeiten. Diese Behandlung kann das Analysieren von Serverfeedback umfassen, um eine erfolgreiche Datenübertragung zu bestätigen und möglicherweise einen Verweis auf das übermittelte Problem (z. B. eine Ticketnummer oder URL) zu extrahieren, die dem Benutzer mitgeteilt werden kann.

Bereitstellen von Benutzerfeedback und Benachrichtigungen

  1. Sofortiges Benutzerfeedback:

    • Benachrichtigen Sie Benutzer sofort über den Status der Übermittlung von Problemberichten über die Benutzeroberfläche der Anwendung. Bei erfolgreichen Übermittlungen sollten Sie eine Referenz auf das übermittelte Problem bereitstellen, mit dem Benutzer den Fortschritt ihrer Meldung nachverfolgen können.
  2. Benachrichtigungsstrategie für Android O und höher:

    • Stellen Sie für Geräte mit Android O (API-Ebene 26) und höher die Implementierung eines Benachrichtigungskanals spezifisch für die Meldungsübermittlung sicher. Diese Einrichtung ist wichtig für die effektive Bereitstellung von Benachrichtigungen und ist eine Anforderung für diese Android-Versionen.

Mithilfe dieser Schritte können Entwickler einen robusten Benutzerfeedbackmechanismus in ihre Android-Anwendungen integrieren, indem sie die onUserReportedIssueEventHandler für effiziente Problemmeldung und -verfolgung nutzen. Dieser Prozess erleichtert nicht nur die zeitnahe Lösung von Benutzerproblemen, sondern trägt auch wesentlich zur Verbesserung der Benutzerfreundlichkeit und Zufriedenheit mit der Anwendung bei.

Android-Codebeispiel

Der Kotlin-Codeausschnitt veranschaulicht den Prozess der Integration eines Systems zur Behandlung von Problemmeldungen von Benutzern in einer Android-Anwendung mit Azure Communication Services. Diese Integration zielt darauf ab, den Supportprozess zu optimieren, indem die direkte Kommunikation zwischen Benutzern und Supportteams ermöglicht wird. Hier finden Sie eine Übersicht über die erforderlichen Schritte:

  1. Ereigniserfassung: Das System lauscht auf vom Benutzer gemeldete Probleme über die ACS UI Library. Es verwendet die onUserReportedIssueEventHandler, um Feedback aus der Benutzeroberfläche der Anwendung zu erfassen, einschließlich Fehlern und Anliegen von Benutzern.

  2. Datenübertragung an Server: Wenn ein Problem gemeldet wird, erstellt das System ein Paket mit den relevanten Daten, einschließlich Benutzermeldungen, Fehlerprotokollen, Versionen und Diagnoseinformationen. Diese Daten werden dann mithilfe einer asynchronen POST-Anforderung an einen Serverendpunkt gesendet, sodass sichergestellt wird, dass der Prozess die Leistung der App nicht beeinträchtigt.

  3. Benutzerfeedback und Benachrichtigung: Nach der Übermittlung werden Benutzer sofort über den Status ihrer Meldung über In-App-Benachrichtigungen informiert. Bei erfolgreichen Übermittlungen enthält eine Benachrichtigung einen Link oder einen Verweis auf das übermittelte Ticket, sodass Benutzer den Lösungsfortschritt nachverfolgen können.

Diese Einrichtung hilft nicht nur bei der schnellen Behandlung von Benutzerproblemen, sondern trägt auch wesentlich zur Verbesserung der Benutzerzufriedenheit und App-Zuverlässigkeit bei, indem ein klarer Kanal für Support und Feedback bereitgestellt wird.

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)
    }
}

Sobald Sie über einen Handler für Ihr Ereignis verfügen, können Sie ihn beim Erstellen des zusammengesetzten Aufrufs registrieren.

            callComposite.addOnUserReportedEventHandler(userReportedIssueEventHandler)

Übersicht über den iOS-Support

Um die Sammlung von Benutzerfeedback in iOS-Anwendungen mithilfe der UI Library für Azure Communication Services (ACS) zu integrieren, müssen Entwickler einen strukturierten Ansatz befolgen. Dieser Prozess umfasst das Erfassen von Benutzerfeedback, einschließlich Fehlerprotokollen und Benutzerinformationen. Nach Abschluss werden diese Informationen zur Verarbeitung an einen Server übermittelt. In diesem Abschnitt werden die Schritte beschrieben, die zum Ausführen dieser Aufgabe erforderlich sind.

In diesem Beispiel verwenden wir die Alamofire-Bibliothek, um das Senden eines mehrteiligen Formulars einschließlich Protokolldateien an den Server zu verarbeiten.

Implementieren des Supportformulars

  1. Ereignishandlerregistrierung: Beginnen Sie mit der Registrierung eines Ereignishandlers, der auf vom Benutzer gemeldete Probleme lauscht. Dieser Handler ist entscheidend für die direkte Erfassung von Feedback über die Benutzeroberfläche Ihrer iOS-Anwendung mithilfe der Funktionen der ACS UI Library.

  2. Sichtbarkeit und Barrierefreiheit des Formulars: Stellen Sie sicher, dass das Supportformular für Benutzer innerhalb der Anwendung leicht zugänglich und sichtbar ist. Die Aktivierung des Formulars ist direkt mit der Implementierung des Ereignishandlers verknüpft, wodurch die Funktion in der Benutzeroberfläche erscheint, damit Benutzer Probleme melden können.

Erfassen und Verarbeiten von Supportanfragen

  1. Ereignisemissionen bei Benutzeraktion: Wenn ein Benutzer ein Problem über das Supportformular meldet, erfasst der Ereignishandler diese Aktion. Die Informationen wie die Beschreibung des Problems, Fehlerprotokolle und alle Anruf-IDs des Benutzers sollten für das Senden an den Server vorbereitet werden.

  2. Datenstrukturierung für die Übermittlung: Organisieren Sie die erfassten Informationen in einem strukturierten Format, das für die Übertragung geeignet ist. Bereiten Sie die Daten auf eine Weise vor, die mit dem erwarteten Format des Serverendpunkts übereinstimmt, der die Supportanfrage empfängt und verarbeitet.

Senden von Daten an den Server

  1. Asynchrone Übermittlung: Verwenden sie asynchrone Netzwerkaufrufe, um die strukturierten Daten an den Server zu senden. Mit diesem Ansatz wird sichergestellt, dass die Anwendung reaktionsfähig bleibt und dem Benutzer eine nahtlose Benutzererfahrung bietet, während die Daten im Hintergrund übertragen werden.

  2. Behandeln von Serverantworten: Bei Übermittlung werden Serverantworten effizient verarbeitet. Empfangen und analysieren Sie die Antwort, um den erfolgreichen Empfang der Daten zu bestätigen. Extrahieren Sie den Link zum Supportticket aus der analysierten Antwort, die dem Benutzer zur Nachverfolgung mitgeteilt werden kann.

Feedback und Benachrichtigungen an Benutzer

  1. Sofortige Bestätigung: Bestätigen Sie die Übermittlung einer Supportanfrage sofort innerhalb der Anwendung und geben den Benutzern die Bestätigung, dass ihr Bericht empfangen wurde.

  2. Benachrichtigungsstrategie: Implementieren Sie eine Strategie für die Bereitstellung von Benachrichtigungen an Benutzer, insbesondere auf Geräten mit iOS-Versionen, die bestimmte Benachrichtigungsframeworks unterstützen. Sie können lokale Benachrichtigungen verwenden, um Benutzer über den Status ihrer Meldung zu informieren oder Updates bereitzustellen, wenn ihr Problem behoben wurde.

iOS-Codebeispiel

In diesem Swift-Codebeispiel wird eine grundlegende Implementierung zum Erfassen von Problemen über Benutzermeldungen und zum Übermitteln an einen Server zur Verarbeitung beschrieben. In diesem Beispiel wird gezeigt, wie Sie einen Supportereignishandler erstellen, einschließlich Benutzerfeedback und Anwendungsdiagnoseinformationen und Übermittlung an den Server. Der Code enthält auch Fehlerbehandlungs- und Benutzerbenachrichtigungsstrategien, um eine reibungslose Benutzererfahrung sicherzustellen.

Das folgende Beispiel ist so konzipiert, dass es sich um einen Hook handelt, der innerhalb des Ereignishandlers installiert werden soll.

Installation

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
        }
    }
}

Netzwerk-Hook

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)")
        }
    }
}

Dieser Swift-Code veranschaulicht den Prozess der Übermittlung von vom Benutzer gemeldeten Problemen aus einer iOS-Anwendung mit Azure Communication Services. Er behandelt die Sammlung von Benutzerfeedback, das Verpacken von Diagnoseinformationen und die asynchrone Übermittlung an einen Serverendpunkt. Darüber hinaus bietet er die Grundlage für die Implementierung von Feedbackmechanismen, um sicherzustellen, dass die Benutzer über den Status ihrer Berichte informiert werden und die Gesamtsicherheit der Anwendung und die Benutzerzufriedenheit verbessert wird.

Zusammenfassung

Die Integration von Benutzerfeedbackmechanismen in Anwendungen mit Azure Communication Services (ACS) ist entscheidend für die Entwicklung reaktionsfähiger und benutzerorientierter Apps. Dieses Handbuch bietet einen klaren Weg zum Einrichten der serverseitigen Verarbeitung mit Node.js und der clientseitigen Feedbackerfassung für Android- und iOS-Anwendungen. Durch eine solche Integration können Entwickler die Zuverlässigkeit der Anwendung und die Benutzerzufriedenheit verbessern und gleichzeitig die Clouddienste von Azure für eine effiziente Datenverwaltung nutzen.

In diesem Leitfaden werden praktische Schritte zum Erfassen von Benutzerfeedback, Fehlerprotokollen und Supportanfragen direkt aus Anwendungen beschrieben. Die Integration von Supportereignissen stellt eine sichere und organisierte Möglichkeit zur Behandlung von Feedback sicher und ermöglicht Entwicklern Benutzerprobleme schnell zu beheben, was generelle zu einer verbesserten Benutzererfahrung führt.

Indem Sie die in diesem Leitfaden beschriebenen Anweisungen befolgen, können Entwickler die Reaktionsfähigkeit ihrer Anwendungen verbessern und die Benutzeranforderungen besser erfüllen. Diese Integrationen helfen nicht nur beim effektiveren Verständnis von Benutzerfeedback, sondern auch bei der Nutzung von Clouddiensten, um einen reibungslosen und effektiven Feedbacksammlungs- und Verarbeitungsmechanismus sicherzustellen. Letztendlich ist die Integration von Benutzerfeedbackmechanismen für die Erstellung ansprechender und zuverlässiger Anwendungen, welche die Benutzerzufriedenheit priorisieren, unerlässlich.