Добавление в приложение компонентов демонстрации в розничных магазинах (RDX)

Включите режим демонстрации в розничных магазинах в свое приложение для Windows, чтобы клиенты, выбирая компьютеры и устройства в торговом зале, могли сразу же использовать ваше предложение.

Когда клиенты находятся в розничном магазине, они ожидают, что смогут попробовать демонстрации пк и устройств. Они часто тратят значительный фрагмент времени, играя вокруг с приложениями через розничный демонстрационный интерфейс (RDX).

Вы можете настроить приложение для предоставления различных возможностей в обычном или розничном режиме. Например, если приложение начинается с процесса установки, вы можете пропустить его в розничном режиме и предварительно заполнить приложение примерами данных и параметрами по умолчанию, чтобы они могли перейти прямо в.

С точки зрения наших клиентов, существует только одно приложение. Чтобы помочь клиентам различать два режима, рекомендуется, чтобы приложение было в розничном режиме, оно отображает слово "Розничная" видно в строке заголовка или в подходящем расположении.

Помимо требований Microsoft Store для приложений, приложения с поддержкой RDX также должны быть совместимы с настройкой, очисткой и обновлением RDX, чтобы гарантировать, что клиенты имеют согласованный положительный опыт в розничном магазине.

Принципы дизайна

  • Показать свое лучшее. Используйте демонстрационный интерфейс розничной торговли, чтобы продемонстрировать, почему ваше приложение скалы. Это, скорее всего, первый раз, когда ваш клиент увидит ваше приложение, так что показать им лучший фрагмент!

  • Показать его быстро. Клиенты могут быть нетерпеливыми. Чем быстрее пользователь может испытать реальную ценность вашего приложения, тем лучше.

  • Держите историю простой. Демонстрация розничной торговли — это лифт для ценности вашего приложения.

  • Сосредоточьтесь на опыте. Предоставьте пользователю время для дайджеста содержимого. Хотя получить их к лучшей части быстро важно, проектирование подходящих пауз может помочь им в полной мере наслаждаться опытом.

Технические требования

Так как приложения с поддержкой RDX предназначены для демонстрации лучших приложений для розничных клиентов, они должны соответствовать техническим требованиям и соблюдать правила конфиденциальности, которые Microsoft Store имеет для всех приложений для розничной демонстрации.

Это можно использовать в качестве контрольного списка, чтобы подготовиться к процессу проверки и обеспечить ясность в процессе тестирования. Обратите внимание, что эти требования должны поддерживаться не только для процесса проверки, но и в течение всего времени существования приложения для демонстрации розничной торговли; До тех пор, пока ваше приложение будет работать на розничных демонстрационных устройствах.

Критические требования

Приложения с поддержкой RDX, которые не соответствуют этим критически важным требованиям, будут удалены со всех демонстрационных устройств розничной торговли как можно скорее.

  • Не запрашивайте личную информацию (PII). Сюда входят сведения о входе, сведения об учетной записи Майкрософт или контактные данные.

  • Интерфейс без ошибок. Приложение должно выполняться без ошибок. Кроме того, всплывающие всплывающие окна или уведомления не должны отображаться клиентам с помощью демонстрационных устройств розничной торговли. Ошибки отражаются отрицательно на самом приложении, вашей фирме, бренде устройства, бренде устройства, бренде производителя устройства и бренде Майкрософт.

  • Платные приложения должны иметь пробный режим. Приложение должно быть бесплатным или включить пробный режим. Клиенты не хотят платить за опыт в розничном магазине.

Требования высокого приоритета

Приложения с поддержкой RDX, которые не соответствуют этим высоким приоритетам, необходимо немедленно изучить для исправления. Если немедленное исправление не найдено, это приложение может быть удалено со всех демонстрационных устройств розничной торговли.

  • Запоминающийся автономный интерфейс. Ваше приложение должно продемонстрировать отличный автономный интерфейс, так как около 50% устройств находятся в автономном режиме в розничных расположениях. Это гарантирует, что клиенты, взаимодействующие с приложением в автономном режиме, по-прежнему могут иметь значимый и положительный опыт.

  • Обновленный интерфейс содержимого. Ваше приложение никогда не должно запрашивать обновления при подключении к сети. Если требуется обновление, они должны выполняться автоматически.

  • Анонимный обмен данными не существует. Так как клиент, использующий демонстрационное устройство розничной торговли, является анонимным пользователем, он не сможет отправлять сообщения или предоставлять доступ к содержимому с устройства.

  • Доставить согласованные интерфейсы с помощью процесса очистки. Каждый клиент должен иметь тот же опыт, когда они идут на розничное демонстрационное устройство. Приложение должно использовать процесс очистки, чтобы вернуться к одному и тому же состоянию по умолчанию после каждого использования. Мы не хотим, чтобы следующий клиент видел, что последний клиент остался позади. Это включает в себя доски, достижения и разблокировки.

  • Возраст соответствующего содержимого. Все содержимое приложения должно быть назначено категории "Подросток" или "Более низкий рейтинг". Дополнительные сведения см. в статье "Получение оценки приложения по рейтингу IARC и ESRB".

Требования среднего приоритета

Команда Магазина Розничной торговли Windows может обратиться к разработчикам напрямую, чтобы настроить обсуждение по устранению этих проблем.

  • Возможность успешного выполнения на нескольких устройствах. Приложения должны работать хорошо на всех устройствах, включая устройства с низкими спецификациями. Если приложение установлено на устройствах, которые не соответствуют минимальным спецификациям, приложение должно четко сообщить пользователю об этом. Минимальные требования к устройству должны быть известны, чтобы приложение всегда могли работать с высокой производительностью.

  • Соответствие требованиям к размеру приложения розничного магазина. Приложение должно быть меньше 800 МБ. Обратитесь к группе Магазина Розничной торговли Windows напрямую для дальнейшего обсуждения, если ваше приложение с поддержкой удаленных рабочих столах не соответствует требованиям к размеру.

API RetailInfo: подготовка кода для демонстрационного режима

IsDemoModeEnabled

Свойство IsDemoModeEnabled в классе служебной программы RetailInfo, являющееся частью пространства имен Windows.System.Profile в пакете SDK для Windows 10 и Windows 11, используется в качестве логического индикатора, чтобы указать путь кода, на котором работает приложение в обычном режиме или в розничном режиме.

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

Когда IsDemoModeEnabled возвращает значение true, можно запросить набор свойств для устройства с помощью RetailInfo.Properties для создания более настраиваемого демонстрационного интерфейса розничной торговли. К этим свойствам относятся имя производителя, экранизация, память и т. д.

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

Процесс очистки

Очистка начинается через две минуты после того, как покупатель перестает взаимодействовать с устройством. Демонстрация розничной торговли воспроизводится, и Windows начинает сбрасывать все примеры данных в контактах, фотографиях и других приложениях. В зависимости от устройства это может занять от 1 до 5 минут, чтобы полностью сбросить все обратно в нормальное состояние. Это гарантирует, что каждый клиент в розничном магазине может пройти к устройству и иметь тот же интерфейс при взаимодействии с устройством.

Шаг 1. Очистка

  • Все приложения Win32 и магазин закрыты
  • Все файлы в известных папках, таких как изображения, видео, музыка, документы, сохраненныеPictures, CameraRoll, Desktop и Downloads, удаляются
  • Неструктурированные и структурированные перемещаемые состояния удаляются
  • Структурированные локальные состояния удаляются

Шаг 2. Настройка

  • Для автономных устройств: папки остаются пустыми
  • Для онлайн-устройств: демонстрационные ресурсы розничной торговли можно отправить на устройство из Microsoft Store

Хранение данных в сеансах пользователей

Для хранения данных в сеансах пользователей можно хранить сведения в ApplicationData.Current.TemporaryFolder , так как процесс очистки по умолчанию не удаляет данные в этой папке автоматически. Обратите внимание, что данные, хранящиеся с помощью LocalState , удаляются во время процесса очистки.

Настройка процесса очистки

Чтобы настроить процесс очистки, реализуйте Microsoft-RetailDemo-Cleanup службу приложений в приложении.

Сценарии, в которых требуется настраиваемая логика очистки, включает выполнение обширной настройки, скачивания и кэширования данных или удаления данных LocalState .

Шаг 1. Объявление службы Microsoft-RetailDemo-Cleanup в манифесте приложения.

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

Шаг 2. Реализация пользовательской логики очистки в функции дела AppdataCleanup с помощью примера шаблона ниже.

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