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

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

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

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

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

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

Принципы проектирования

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

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

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

  • Сосредоточьтесь на интерфейсе. Дайте покупателю время «переварить» то, что он видит в вашем приложении. Хотя ваша задача — как можно быстрее добраться до самого интересного в вашем приложении, возможно, имеет смысл предусмотреть логические паузы.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

IsDemoModeEnabled

Свойство IsDemoModeEnabled в служебном классе RetailInfo, которое является частью пространства имен Windows.System.Profile в Windows 10 и пакете SDK 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 набор свойств демонстрационного устройства для настройки приложения в соответствии с его характеристиками. К таким свойствам относятся ManufacturerName, Screensize, Memory и др.

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 и приложения Магазина закрываются
  • Все файлы в известных папках, таких как Изображения, Видео, Музыка, Документы, Сохраненные изображения, Альбом камеры, Рабочий стол и Загрузки удаляются
  • Неструктурированные и структурированные состояния перемещения удалены
  • Структурированные локальные состояния удалены

Шаг 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. Реализуйте пользовательскую логику очистки в функции case 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;
        }
    }
}