Hinzufügen von Einzelhandelsdemo-Features (RDX) zu Ihrer App

Fügen Sie einen Demomodus für den Einzelhandel in Ihre Windows-App ein, damit Kunden, die PCs und Geräte im Geschäft ausprobieren, direkt loslegen können.

Wenn Kunden in einem Einzelhandelsgeschäft sind, erwarten sie, Demos von PCs und Geräten ausprobieren zu können. Sie verbringen oft einen erheblichen Teil ihrer Zeit damit, mit Apps über die Einzelhandelsdemoerfahrung (RDX) herumzuspielen.

Sie können Ihre App so einrichten, dass sie im normalen Modus oder im Einzelhandelsmodus unterschiedliche Erfahrungen bietet. Wenn Ihre App beispielsweise mit einem Einrichtungsprozess beginnt, können Sie ihn im Einzelhandelsmodus überspringen und die App mit Beispieldaten und Standardeinstellungen vorfüllen, damit sie direkt einspringen kann.

Aus Sicht unserer Kunden gibt es nur eine App. Um Kunden bei der Unterscheidung zwischen den beiden Modi zu helfen, empfehlen wir, dass Ihre App sich im Einzelhandelsmodus befindet, das Wort "Einzelhandel" prominent in der Titelleiste oder an einem geeigneten Ort anzeigt.

Zusätzlich zu den Microsoft Store-Anforderungen für Apps müssen RDX-fähige Apps auch mit den RDX-Setup-, Bereinigungs- und Updateprozessen kompatibel sein, um sicherzustellen, dass Kunden eine durchweg positive Erfahrung im Einzelhandelsgeschäft haben.

Entwurfsprinzipien

  • Zeigen Sie Ihr Bestes. Verwenden Sie die Einzelhandelsdemo, um zu zeigen, warum Ihre App rockt. Dies ist wahrscheinlich das erste Mal, dass Ihre Kunden Ihre App sehen, also zeigen Sie ihnen das beste Stück!

  • Zeigen Sie es schnell an. Kunden können ungeduldig sein – je schneller ein Benutzer den Wert ihrer App erfasst, desto besser.

  • Halten Sie die Geschichte einfach. Die Einzelhandelsdemo ist ein Aufzugs-Pitch für den Wert Ihrer App.

  • Konzentrieren Sie sich auf die Erfahrung. Geben Sie den Benutzern die Zeit, um Ihre Inhalte zu verdauen. Es ist zwar wichtig, dass die Benutzer schnell zum wichtigsten Teil gelangen, aber nur mithilfe entsprechender Pausen können sie den Wert der App richtig erkennen.

Technische Anforderungen

Da RDX-fähige Apps den Einzelhandelskunden das Beste Ihrer App präsentieren sollen, müssen sie die technischen Anforderungen erfüllen und die Datenschutzbestimmungen des Microsoft Store für alle Apps für die Einzelhandelsdemoerfahrung einhalten.

Dies kann als Prüfliste verwendet werden, um Sie bei der Vorbereitung auf den Validierungsprozess zu unterstützen und Klarheit im Testprozess zu schaffen. Beachten Sie, dass diese Anforderungen nicht nur für den Prüfprozess, sondern für die gesamte Lebensdauer der Demo-App für den Einzelhandel (d. h. solange Ihre App auf den Vorführgeräten ausgeführt wird) eingehalten werden müssen.

Kritische Anforderungen

RDX-fähige Apps, die diese kritischen Anforderungen nicht erfüllen, werden so schnell wie möglich von allen Demogeräten im Einzelhandel entfernt.

  • Fordern Sie keine personenbezogenen Informationen (PII) an. Dies umfasst Anmeldeinformationen, Microsoft-Kontoinformationen oder Kontaktdaten.

  • Fehlerfreie Benutzeroberfläche. Ihr App muss fehlerfrei funktionieren. Außerdem dürfen keine Fehler-Pop-ups oder -Benachrichtigungen angezeigt werden, wenn Kunden die Vorführgeräte verwenden. Fehler wirken sich negativ auf die App selbst, Ihre Marke, die Marke des Geräts, die Herstellermarke des Geräts und die Marke von Microsoft aus.

  • Kostenpflichtige Apps müssen über einen Testmodus verfügen. Ihre App muss entweder kostenlos sein oder einen Testmodus enthalten. Kunden, die sich in einem Laden etwas ansehen, möchten dafür nicht zahlen.

Anforderungen mit hoher Priorität

RDX-fähige Apps, die diese hohen Anforderungen nicht erfüllen, müssen sofort auf eine Korrektur untersucht werden. Wenn eine umgehende Problembehebung nicht möglich ist, kann diese App von allen Vorführgeräten entfernt werden.

  • Unvergessliche Offline-Erfahrung. Ihre App muss eine hervorragende Offline-Erfahrung aufweisen, da etwa 50 % der Geräte an Einzelhandelsstandorten offline sind. Dadurch wird sichergestellt, dass das Erlebnis für die Kunden auch dann positiv ist, wenn sie offline mit Ihrer App interagieren.

  • Die Inhaltsumgebung wurde aktualisiert. Ihre App sollte nie online zur Aktualisierung aufgefordert werden. Wenn Updates erforderlich sind, sollten sie im Hintergrund ausgeführt werden.

  • Keine anonyme Kommunikation. Da ein Kunde, der ein Einzelhandelsdemogerät verwendet, ein anonymer Benutzer ist, sollte er keine Nachrichten senden oder Inhalte vom Gerät freigeben können.

  • Sorgen Sie mithilfe des Bereinigungsprozesses für konsistente Benutzeroberflächen. Die Verwendung eines Vorführgeräts sollte für alle Kunden gleich sein. Ihre App sollte sauber Up-Prozess verwenden, um nach jeder Verwendung in den gleichen Standardzustand zurückzukehren. Wir möchten nicht, dass der nächste Kunde sieht, was der letzte Kunde zurückgelassen hat. Dies umfasst z. B. Punktestände, Erfolge und aufgehobene Sperren.

  • Altersgemäße Inhalte. Allen App-Inhalten muss eine Teen- oder niedrigere Bewertungskategorie zugewiesen werden. Weitere Informationen finden Sie unter Abrufen der App-Bewertung durch IARC - und ESRB-Bewertungen.

Anforderungen mit mittlerer Priorität

Das Windows-Team für den Einzelhandel setzt sich unter Umständen direkt mit Entwicklern in Verbindung, um mit ihnen zu besprechen, wie diese Probleme behoben werden können.

  • Fähigkeit, eine Reihe von Geräten erfolgreich auszuführen. Apps müssen auf allen Geräten gut ausgeführt werden, einschließlich Geräten mit Low-End-Spezifikationen. Wenn die App auf Geräten installiert ist, die die Mindestanforderungen nicht erfüllten, muss die App den Benutzer darüber klar informieren. Die Mindestgeräteanforderungen müssen bekanntgegeben werden, damit die App immer mit höchster Leistung ausgeführt werden kann.

  • Erfüllen Sie die Größenanforderungen für Die App-Größe des Einzelhandelsgeschäfts. Die App darf eine Größe von 800 MB nicht übersteigen. Wenden Sie sich direkt an das Windows Retail Store-Team, wenn Ihre RDX-fähige App die Größenanforderungen nicht erfüllt.

RetailInfo-API: Vorbereiten ihres Codes für den Demomodus

IsDemoModeEnabled

Die IsDemoModeEnabled-Eigenschaft in der RetailInfo-Hilfsklasse, die Teil des Windows.System.Profile-Namespaces im Windows 10 und Windows 11 SDK ist, wird als boolescher Indikator verwendet, um anzugeben, unter welchem Codepfad Ihre App ausgeführt wird – im normalen Modus oder im Einzelhandelsmodus.

using Windows.Storage;

StorageFolder folder = ApplicationData.Current.LocalFolder;

if (Windows.System.Profile.RetailInfo.IsDemoModeEnabled) 
{
    // Use the demo specific directory
    folder = await folder.GetFolderAsync("demo");
}

StorageFile file = await folder.GetFileAsync("hello.txt");
// Now read from file
using namespace Windows::Storage;

StorageFolder^ localFolder = ApplicationData::Current->LocalFolder;

if (Windows::System::Profile::RetailInfo::IsDemoModeEnabled) 
{
    // Use the demo specific directory
    create_task(localFolder->GetFolderAsync("demo").then([this](StorageFolder^ demoFolder)
    {
        return demoFolder->GetFileAsync("hello.txt");
    }).then([this](task<StorageFile^> fileTask)
    {
        StorageFile^ file = fileTask.get();
    });
    // Do something with file
}
else
{
    create_task(localFolder->GetFileAsync("hello.txt").then([this](StorageFile^ file)
    {
        // Do something with file
    });
}
if (Windows.System.Profile.retailInfo.isDemoModeEnabled) {
    console.log("Retail mode is enabled.");
} else {
    Console.log("Retail mode is not enabled.");
}

RetailInfo.Properties

Wenn IsDemoModeEnabled true zurückgibt, können Sie mithilfe von RetailInfo.Properties eine Reihe von Eigenschaften für das Gerät abfragen, um eine benutzerdefiniertere Einzelhandelsdemoversion zu erstellen. Diese Eigenschaften umfassen ManufacturerName, Screensize, Memory usw.

using Windows.UI.Xaml.Controls;
using Windows.System.Profile

TextBlock priceText = new TextBlock();
priceText.Text = RetailInfo.Properties[KnownRetailInfo.Price];
// Assume infoPanel is a StackPanel declared in XAML
this.infoPanel.Children.Add(priceText);
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::System::Profile;

TextBlock ^manufacturerText = ref new TextBlock();
manufacturerText.set_Text(RetailInfo::Properties[KnownRetailInfoProperties::Price]);
// Assume infoPanel is a StackPanel declared in XAML
this->infoPanel->Children->Add(manufacturerText);
var pro = Windows.System.Profile;
console.log(pro.retailInfo.properties[pro.KnownRetailInfoProperties.price);

IDL

//  Copyright (c) Microsoft Corporation. All rights reserved.
//
//  WindowsRuntimeAPISet

import "oaidl.idl";
import "inspectable.idl";
import "Windows.Foundation.idl";
#include <sdkddkver.h>

namespace Windows.System.Profile
{
    runtimeclass RetailInfo;
    runtimeclass KnownRetailInfoProperties;

    [version(NTDDI_WINTHRESHOLD), uuid(0712C6B8-8B92-4F2A-8499-031F1798D6EF), exclusiveto(RetailInfo)]
    [version(NTDDI_WINTHRESHOLD, Platform.WindowsPhone)]
    interface IRetailInfoStatics : IInspectable
    {
        [propget] HRESULT IsDemoModeEnabled([out, retval] boolean *value);
        [propget] HRESULT Properties([out, retval, hasvariant] Windows.Foundation.Collections.IMapView<HSTRING, IInspectable *> **value);
    }

    [version(NTDDI_WINTHRESHOLD), uuid(50BA207B-33C4-4A5C-AD8A-CD39F0A9C2E9), exclusiveto(KnownRetailInfoProperties)]
    [version(NTDDI_WINTHRESHOLD, Platform.WindowsPhone)]
    interface IKnownRetailInfoPropertiesStatics : IInspectable
    {
        [propget] HRESULT RetailAccessCode([out, retval] HSTRING *value);
        [propget] HRESULT ManufacturerName([out, retval] HSTRING *value);
        [propget] HRESULT ModelName([out, retval] HSTRING *value);
        [propget] HRESULT DisplayModelName([out, retval] HSTRING *value);
        [propget] HRESULT Price([out, retval] HSTRING *value);
        [propget] HRESULT IsFeatured([out, retval] HSTRING *value);
        [propget] HRESULT FormFactor([out, retval] HSTRING *value);
        [propget] HRESULT ScreenSize([out, retval] HSTRING *value);
        [propget] HRESULT Weight([out, retval] HSTRING *value);
        [propget] HRESULT DisplayDescription([out, retval] HSTRING *value);
        [propget] HRESULT BatteryLifeDescription([out, retval] HSTRING *value);
        [propget] HRESULT ProcessorDescription([out, retval] HSTRING *value);
        [propget] HRESULT Memory([out, retval] HSTRING *value);
        [propget] HRESULT StorageDescription([out, retval] HSTRING *value);
        [propget] HRESULT GraphicsDescription([out, retval] HSTRING *value);
        [propget] HRESULT FrontCameraDescription([out, retval] HSTRING *value);
        [propget] HRESULT RearCameraDescription([out, retval] HSTRING *value);
        [propget] HRESULT HasNfc([out, retval] HSTRING *value);
        [propget] HRESULT HasSdSlot([out, retval] HSTRING *value);
        [propget] HRESULT HasOpticalDrive([out, retval] HSTRING *value);
        [propget] HRESULT IsOfficeInstalled([out, retval] HSTRING *value);
        [propget] HRESULT WindowsVersion([out, retval] HSTRING *value);
    }

    [version(NTDDI_WINTHRESHOLD), static(IRetailInfoStatics, NTDDI_WINTHRESHOLD)]
    [version(NTDDI_WINTHRESHOLD, Platform.WindowsPhone), static(IRetailInfoStatics, NTDDI_WINTHRESHOLD, Platform.WindowsPhone)]
    [threading(both)]
    [marshaling_behavior(agile)]
    runtimeclass RetailInfo
    {
    }

    [version(NTDDI_WINTHRESHOLD), static(IKnownRetailInfoPropertiesStatics, NTDDI_WINTHRESHOLD)]
    [version(NTDDI_WINTHRESHOLD, Platform.WindowsPhone), static(IKnownRetailInfoPropertiesStatics, NTDDI_WINTHRESHOLD, Platform.WindowsPhone)]
    [threading(both)]
    [marshaling_behavior(agile)]
    runtimeclass KnownRetailInfoProperties
    {
    }
}

Bereinigungsprozess

Die Bereinigung beginnt zwei Minuten, nachdem ein Käufer die Interaktion mit dem Gerät beendet hat. Die Einzelhandelsdemo wird wiedergegeben, und Windows beginnt mit dem Zurücksetzen aller Beispieldaten in den Kontakten, Fotos und anderen Apps. Je nach Gerät kann dies zwischen 1 und 5 Minuten dauern, bis alles wieder auf normal zurückgesetzt wurde. Dadurch wird sichergestellt, dass jeder Kunde im Einzelhandelsgeschäft zu einem Gerät aufsteigen kann und die gleiche Erfahrung bei der Interaktion mit dem Gerät hat.

Schritt 1: Bereinigen

  • Alle Win32- und Store-Apps werden geschlossen.
  • Alle Dateien in bekannten Ordnern wie Bilder, Videos, Musik, Dokumente, SavedPictures, CameraRoll, Desktop und Downloads Ordner werden gelöscht.
  • Unstrukturierte und strukturierte Roamingzustände werden gelöscht.
  • Strukturierte lokale Zustände werden gelöscht.

Schritt 2: Einrichten

  • Offlinegeräte: Ordner bleiben leer
  • Für Onlinegeräte: Demoobjekte für den Einzelhandel können aus dem Microsoft Store auf das Gerät gepusht werden.

Speichern von Daten über Benutzersitzungen hinweg

Um Daten über Benutzersitzungen hinweg zu speichern, können Sie Informationen in ApplicationData.Current.TemporaryFolder speichern, da der Standardbereinigungsprozess Daten in diesem Ordner nicht automatisch löscht. Beachten Sie, dass mithilfe von LocalState gespeicherte Informationen während des Bereinigungsprozesses gelöscht werden.

Anpassen des Bereinigungsprozesses

Um den Bereinigungsprozess anzupassen, implementieren Sie den Microsoft-RetailDemo-Cleanup App-Dienst in Ihre App.

Szenarien, in denen eine benutzerdefinierte Bereinigungslogik erforderlich ist, umfassen das Ausführen eines umfangreichen Setups, das Herunterladen und Zwischenspeichern von Daten oder das Nicht-Löschen von LocalState-Daten .

Schritt 1: Deklarieren Sie den Microsoft-RetailDemo-Cleanup-Dienst in Ihrem App-Manifest .

  <Applications>
      <Extensions>
        <uap:Extension Category="windows.appService" EntryPoint="MyCompany.MyApp.RDXCustomCleanupTask">
          <uap:AppService Name="Microsoft-RetailDemo-Cleanup" />
        </uap:Extension>
      </Extensions>
   </Application>
  </Applications>

Schritt 2: Implementieren Sie Ihre benutzerdefinierte Bereinigungslogik unter der AppdataCleanup-Fallfunktion mithilfe der folgenden Beispielvorlage.

using System;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Threading;
using System.Threading.Tasks;
using Windows.ApplicationModel.AppService;
using Windows.ApplicationModel.Background;
using Windows.Foundation.Collections;
using Windows.Storage;

namespace MyCompany.MyApp
{
    public sealed class RDXCustomCleanupTask : IBackgroundTask
    {
        BackgroundTaskCancellationReason _cancelReason = BackgroundTaskCancellationReason.Abort;
        BackgroundTaskDeferral _deferral = null;
        IBackgroundTaskInstance _taskInstance = null;
        AppServiceConnection _appServiceConnection = null;

        const string MessageCommand = "Command";

        public void Run(IBackgroundTaskInstance taskInstance)
        {
            // Get the deferral object from the task instance, and take a reference to the taskInstance;
            _deferral = taskInstance.GetDeferral();
            _taskInstance = taskInstance;
            _taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);

            AppServiceTriggerDetails appService = _taskInstance.TriggerDetails as AppServiceTriggerDetails;
            if ((appService != null) && (appService.Name == "Microsoft-RetailDemo-Cleanup"))
            {
                _appServiceConnection = appService.AppServiceConnection;
                _appServiceConnection.RequestReceived += _appServiceConnection_RequestReceived;
                _appServiceConnection.ServiceClosed += _appServiceConnection_ServiceClosed;
            }
            else
            {
                _deferral.Complete();
            }
        }

        void _appServiceConnection_ServiceClosed(AppServiceConnection sender, AppServiceClosedEventArgs args)
        {
        }

        async void _appServiceConnection_RequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
        {
            //Get a deferral because we will be calling async code
            AppServiceDeferral requestDeferral = args.GetDeferral();
            string command = null;
            var returnData = new ValueSet();

            try
            {
                ValueSet message = args.Request.Message;
                if (message.ContainsKey(MessageCommand))
                {
                    command = message[MessageCommand] as string;
                }

                if (command != null)
                {
                    switch (command)
                    {
                        case "AppdataCleanup":
                            {
                                // Do custom clean up logic here
                                break;
                            }
                    }
                }
            }
            catch (Exception e)
            {
            }
            finally
            {
                requestDeferral.Complete();
                // Also release the task deferral since we only process one request per instance.
                _deferral.Complete();
            }
        }

        private void OnCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
        {
            _cancelReason = reason;
        }
    }
}