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 Sich Kunden in einem Einzelhandelsgeschäft befinden, erwarten sie, dass sie demoos von PCs und Geräten ausprobieren können. Sie verbringen häufig einen erheblichen Teil ihrer Zeit mit Apps durch die Demo-Erfahrung (RDX) im Einzelhandel.

Sie können Ihre App so einrichten, dass sie unterschiedliche Oberflächen in normalen oder Einzelhandelsmodi bereitstellt. Wenn Ihre App z. B. mit einem Setupprozess beginnt, können Sie sie im Einzelhandelsmodus überspringen und die App mit Beispieldaten und Standardeinstellungen vorab auffüllen, damit sie direkt einsteigen können.

Aus Sicht unserer Kunden gibt es nur eine App. Um Kunden dabei zu helfen, zwischen den beiden Modi zu unterscheiden, empfiehlt es sich, während sich Ihre App im Einzelhandelsmodus befindet, das Wort "Einzelhandel" in der Titelleiste oder an einem geeigneten Ort anzuzeigen.

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 konsistente positive Erfahrung im Einzelhandelsgeschäft haben.

Entwurfsprinzipien

  • Zeigen Sie Ihr Bestes. Verwenden Sie die Demo-Erfahrung für den Einzelhandel, um zu zeigen, warum Ihre App rockt. Dies ist wahrscheinlich das erste Mal, wenn Ihr Kunde Ihre App sehen wird, also zeigen Sie ihnen das beste Stück!

  • Zeigen Sie es schnell an. Kunden können ungeduldig sein – Je schneller ein Benutzer den tatsächlichen Wert Ihrer App erleben kann, desto besser.

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

  • Konzentrieren Sie sich auf die Erfahrung. Geben Sie dem Benutzer Zeit, Ihre Inhalte zu digestieren. Während sie schnell zum besten Teil gelangen, ist es wichtig, geeignete Pausen zu entwerfen, die ihnen helfen, die Erfahrung vollständig zu genießen.

Technische Anforderungen

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

Dies kann als Prüfliste verwendet werden, um Sie bei der Vorbereitung auf den Validierungsprozess zu unterstützen und Klarheit im Testprozess zu bieten. Beachten Sie, dass diese Anforderungen nicht nur für den Validierungsprozess, sondern für die gesamte Lebensdauer der Demo-App für den Einzelhandel beibehalten werden müssen; solange Ihre App auf den Demogeräten für den Einzelhandel ausgeführt wird.

Kritische Anforderungen

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

  • Fordern Sie keine personenbezogenen Informationen (PII) an. Dazu gehören Anmeldeinformationen, Microsoft-Kontoinformationen oder Kontaktdetails.

  • Fehlerfreie Erfahrung. Ihre App muss ohne Fehler ausgeführt werden. Darüber hinaus sollten keine Fehler-Popups oder Benachrichtigungen für Kunden angezeigt werden, die die Demogeräte für den Einzelhandel verwenden. Fehler spiegeln sich negativ auf die App selbst, Ihre Marke, die Marke des Geräts, die Marke des Geräts, die Marke des Geräts und die Marke von Microsoft wider.

  • Kostenpflichtige Apps müssen über einen Testmodus verfügen. Ihre App muss entweder kostenlos sein oder einen Testmodus enthalten. Kunden suchen keine Erfahrung in einem Einzelhandelsgeschäft.

Anforderungen mit hoher Priorität

RDX-fähige Apps, die diese Anforderungen mit hoher Priorität nicht erfüllen, müssen sofort nach einer Lösung untersucht werden. Wenn keine sofortige Lösung gefunden wird, wird diese App möglicherweise von allen Demogeräten für den Einzelhandel entfernt.

  • Einprägsame Offlineerfahrung. Ihre App muss eine großartige Offlineerfahrung demonstrieren, da etwa 50 % der Geräte an Einzelhandelsstandorten offline sind. Dadurch wird sichergestellt, dass Kunden, die offline mit Ihrer App interagieren, weiterhin eine sinnvolle und positive Erfahrung haben können.

  • Aktualisierte Inhaltsoberfläche. Ihre App sollte niemals beim Onlinemodus zur Aktualisierung aufgefordert werden. Wenn Updates erforderlich sind, sollten sie im Hintergrund ausgeführt werden.

  • Keine anonyme Kommunikation. Da ein Kunde, der ein Demogerät für den Einzelhandel verwendet, ein anonymer Benutzer ist, sollte er nicht in der Lage sein, Inhalte vom Gerät zu senden oder zu teilen.

  • Sorgen Sie für konsistente Oberflächen, indem Sie den Bereinigungsprozess verwenden. Jeder Kunde sollte die gleiche Erfahrung haben, wenn er zu einem Demogerät für den Einzelhandel führt. Ihre App sollte den Bereinigungsprozess verwenden, um nach jeder Verwendung zum gleichen Standardzustand zurückzukehren. Wir möchten nicht, dass der nächste Kunde sieht, was der letzte Kunde hinter sich gelassen hat. Dazu gehören Scoreboards, Erfolge und Entsperrungen.

  • Altersgerechter Inhalt. Allen App-Inhalten muss eine Teen- oder niedrigere Bewertungskategorie zugewiesen werden. Weitere Informationen finden Sie unter "Bewerten Ihrer App durch IARC - und ESRB-Bewertungen".

Anforderungen für mittlere Priorität

Das Windows Retail Store-Team kann entwickler direkt kontaktieren, um eine Diskussion zur Behebung dieser Probleme einzurichten.

  • Die Möglichkeit, erfolgreich über eine Reihe von Geräten ausgeführt zu werden. 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 Mindestspezifikationen nicht erfüllten, muss die App den Benutzer eindeutig darüber informieren. Die Mindestgeräteanforderungen müssen bekannt gemacht werden, damit die App immer mit hoher Leistung ausgeführt werden kann.

  • Erfüllen der Größenanforderungen für Einzelhandels-Store-Apps. Die App muss kleiner als 800 MB sein. Wenden Sie sich direkt an das Windows Retail Store-Team, um weitere Diskussionen zu erhalten, wenn Ihre RDX-fähige App die Größenanforderungen nicht erfüllt.

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

IsDemoModeEnabled

Die IsDemoModeEnabled-Eigenschaft in der RetailInfo-Hilfsklasse, die Teil des Windows.System.Profile-Namespace im Windows 10- und Windows 11 SDK ist, wird als boolescher Indikator verwendet, um anzugeben, auf welchem Codepfad Ihre App ausgeführt wird – den normalen Modus oder den 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 zu dem Gerät abfragen, um eine benutzerdefiniertere Demoumgebung für den Einzelhandel zu erstellen. Zu diesen Eigenschaften gehören 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 nicht mehr mit dem Gerät interagiert. Die Demo für den Einzelhandel wird wiedergegeben, und Windows beginnt, alle Beispieldaten in den Kontakten, Fotos und anderen Apps zurückzusetzen. 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 gelangen kann und bei der Interaktion mit dem Gerät die gleiche Erfahrung 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" werden gelöscht.
  • Unstrukturierte und strukturierte Roamingzustände werden gelöscht.
  • Strukturierte lokale Zustände werden gelöscht.

Schritt 2: Einrichten

  • Für Offlinegeräte: Ordner bleiben leer
  • Für Onlinegeräte: Demoressourcen für den Einzelhandel können über den Microsoft Store an das Gerät übertragen werden.

Speichern von Daten in Benutzersitzungen

Um Daten in Benutzersitzungen 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, umfasst das Ausführen eines umfangreichen Setups, das Herunterladen und Zwischenspeichern von Daten oder das 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;
        }
    }
}