Bagikan melalui


Menambahkan fitur demo ritel (RDX) ke aplikasi Anda

Sertakan mode demo ritel di aplikasi Windows Anda sehingga pelanggan yang mencoba PC dan perangkat di lantai penjualan dapat langsung masuk.

Ketika pelanggan berada di toko ritel, mereka berharap dapat mencoba demo PC dan perangkat. Mereka sering menghabiskan sebagian besar waktu mereka bermain-main dengan aplikasi melalui pengalaman demo ritel (RDX).

Anda dapat menyiapkan aplikasi untuk memberikan pengalaman yang berbeda saat berada dalam mode normal atau ritel . Misalnya, jika aplikasi Anda dimulai dengan proses penyiapan, Anda mungkin melewatinya dalam mode ritel, dan mengisi aplikasi terlebih dahulu dengan data sampel dan pengaturan default sehingga aplikasi dapat langsung masuk.

Dari perspektif pelanggan kami, hanya ada satu aplikasi. Untuk membantu pelanggan membedakan antara dua mode, sebaiknya saat aplikasi Anda berada dalam mode ritel, ini menunjukkan kata "Retail" secara menonjol di bilah judul atau di lokasi yang sesuai.

Selain persyaratan Microsoft Store untuk aplikasi, aplikasi yang sadar RDX juga harus kompatibel dengan proses penyiapan, pembersihan, dan pembaruan RDX untuk memastikan bahwa pelanggan memiliki pengalaman positif secara konsisten di toko ritel.

Prinsip desain

  • Tunjukkan yang terbaik. Gunakan pengalaman demo ritel untuk menampilkan alasan aplikasi Anda berbatu. Ini kemungkinan pertama kalinya pelanggan Anda akan melihat aplikasi Anda, jadi tunjukkan kepada mereka bagian terbaik!

  • Tunjukkan dengan cepat. Pelanggan tidak sabar - Semakin cepat pengguna dapat mengalami nilai nyata aplikasi Anda, semakin baik.

  • Sederhanakan ceritanya. Pengalaman demo ritel adalah pitch lift untuk nilai aplikasi Anda.

  • Fokus pada pengalaman. Beri pengguna waktu untuk mencerna konten Anda. Meskipun membawa mereka ke bagian terbaik dengan cepat itu penting, merancang jeda yang sesuai dapat membantu mereka untuk sepenuhnya menikmati pengalaman.

Persyaratan Teknis

Karena aplikasi sadar RDX dimaksudkan untuk menampilkan aplikasi terbaik Anda kepada pelanggan ritel, mereka harus memenuhi persyaratan teknis dan mematuhi peraturan privasi yang dimiliki Microsoft Store untuk semua aplikasi pengalaman demo ritel.

Ini dapat digunakan sebagai daftar periksa untuk membantu Anda mempersiapkan proses validasi dan untuk memberikan kejelasan dalam proses pengujian. Perhatikan bahwa persyaratan ini harus dipertahankan, bukan hanya untuk proses validasi, tetapi untuk seluruh masa pakai aplikasi pengalaman demo ritel; selama aplikasi Anda tetap berjalan di perangkat demo ritel.

Persyaratan penting

Aplikasi sadar RDX yang tidak memenuhi persyaratan penting ini akan dihapus dari semua perangkat demo ritel sesegera mungkin.

  • Jangan meminta informasi identitas pribadi (PII). Ini termasuk info masuk, info akun Microsoft, atau detail kontak.

  • Pengalaman bebas kesalahan. Aplikasi Anda harus berjalan tanpa kesalahan. Selain itu, tidak ada pop-up kesalahan atau pemberitahuan yang harus ditampilkan kepada pelanggan yang menggunakan perangkat demo ritel. Kesalahan mencerminkan secara negatif pada aplikasi itu sendiri, merek Anda, merek perangkat, merek produsen perangkat, dan merek Microsoft.

  • Aplikasi berbayar harus memiliki mode uji coba. Aplikasi Anda harus gratis atau menyertakan mode uji coba. Pelanggan tidak ingin membayar pengalaman di toko ritel.

Persyaratan prioritas tinggi

Aplikasi sadar RDX yang tidak memenuhi persyaratan prioritas tinggi ini perlu segera diselidiki untuk perbaikan. Jika tidak ada perbaikan langsung yang ditemukan, aplikasi ini dapat dihapus dari semua perangkat demo ritel.

  • Pengalaman offline yang berkesan. Aplikasi Anda perlu menunjukkan pengalaman offline yang hebat karena sekitar 50% perangkat offline di lokasi ritel. Hal ini untuk memastikan bahwa pelanggan yang berinteraksi dengan aplikasi Anda secara offline masih dapat memiliki pengalaman yang bermakna dan positif.

  • Pengalaman konten yang diperbarui. Aplikasi Anda tidak boleh dimintai pembaruan saat online. Jika pembaruan diperlukan, pembaruan harus dilakukan secara diam-diam.

  • Tidak ada komunikasi anonim. Karena pelanggan yang menggunakan perangkat demo ritel adalah pengguna anonim, mereka seharusnya tidak dapat mengirim pesan atau berbagi konten dari perangkat.

  • Memberikan pengalaman yang konsisten dengan menggunakan proses pembersihan. Setiap pelanggan harus memiliki pengalaman yang sama ketika mereka berjalan ke perangkat demo ritel. Aplikasi Anda harus menggunakan proses pembersihan untuk kembali ke status default yang sama setelah setiap penggunaan. Kami tidak ingin pelanggan berikutnya melihat apa yang terakhir ditinggalkan pelanggan. Ini termasuk papan skor, prestasi, dan buka kunci.

  • Konten yang sesuai usia. Semua konten aplikasi harus diberi kategori Remaja atau peringkat yang lebih rendah. Untuk mempelajari lebih lanjut, lihat Mendapatkan aplikasi Anda dengan peringkat IARC dan ESRB.

Persyaratan prioritas sedang

Tim Windows Retail Store dapat menghubungi pengembang secara langsung untuk menyiapkan diskusi tentang cara memperbaiki masalah ini.

  • Kemampuan untuk berjalan dengan sukses di berbagai perangkat. Aplikasi harus berjalan dengan baik di semua perangkat, termasuk perangkat dengan spesifikasi low-end. Jika aplikasi diinstal pada perangkat yang tidak memenuhi spesifikasi minimum, aplikasi perlu memberi tahu pengguna dengan jelas tentang hal ini. Persyaratan perangkat minimum harus diketahui sehingga aplikasi selalu dapat berjalan dengan performa tinggi.

  • Memenuhi persyaratan ukuran aplikasi toko ritel. Aplikasi harus lebih kecil dari 800MB. Hubungi tim Windows Retail Store secara langsung untuk diskusi lebih lanjut jika aplikasi sadar RDX Anda tidak memenuhi persyaratan ukuran.

RetailInfo API: Menyiapkan kode Anda untuk mode demo

IsDemoModeEnabled

Properti IsDemoModeEnabled di kelas utilitas RetailInfo, yang merupakan bagian dari namespace Layanan Windows.System.Profile di SDK Windows 10 dan Windows 11, digunakan sebagai indikator Boolean untuk menentukan jalur kode mana yang dijalankan aplikasi Anda - mode normal atau mode ritel.

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

Saat IsDemoModeEnabled mengembalikan true, Anda dapat meminta sekumpulan properti tentang perangkat menggunakan RetailInfo.Properties untuk membangun pengalaman demo ritel yang lebih disesuaikan. Properti ini termasuk ManufacturerName, Screensize, Memory dan sebagainya.

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

Proses pembersihan

Pembersihan dimulai dua menit setelah pembeli berhenti berinteraksi dengan perangkat. Demo ritel diputar, dan Windows mulai mengatur ulang data sampel apa pun di kontak, foto, dan aplikasi lainnya. Tergantung pada perangkat, ini bisa memakan waktu antara 1-5 menit untuk sepenuhnya mengatur ulang semuanya kembali ke normal. Ini memastikan bahwa setiap pelanggan di toko ritel dapat berjalan ke perangkat dan memiliki pengalaman yang sama saat berinteraksi dengan perangkat.

Langkah 1: Pembersihan

  • Semua Win32 dan aplikasi toko ditutup
  • Semua file dalam folder yang diketahui seperti folder Gambar, Video, Musik, Dokumen, Gambar Tersimpan, CameraRoll, Desktop , dan Unduhan dihapus
  • Status roaming yang tidak terstruktur dan terstruktur dihapus
  • Status lokal terstruktur dihapus

Langkah 2: Penyetelan

  • Untuk perangkat offline: Folder tetap kosong
  • Untuk perangkat online: Aset demo ritel dapat didorong ke perangkat dari Microsoft Store

Menyimpan data di seluruh sesi pengguna

Untuk menyimpan data di seluruh sesi pengguna, Anda dapat menyimpan informasi di ApplicationData.Current.TemporaryFolder karena proses pembersihan default tidak secara otomatis menghapus data dalam folder ini. Perhatikan bahwa informasi yang disimpan menggunakan LocalState dihapus selama proses pembersihan.

Menyesuaikan proses pembersihan

Untuk menyesuaikan proses pembersihan, terapkan Microsoft-RetailDemo-Cleanup layanan aplikasi ke dalam aplikasi Anda.

Skenario di mana logika pembersihan kustom diperlukan termasuk menjalankan penyiapan yang luas, mengunduh dan menyimpan data cache, atau tidak ingin data LocalState dihapus.

Langkah 1: Deklarasikan layanan Microsoft-RetailDemo-Cleanup di manifes aplikasi Anda.

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

Langkah 2: Terapkan logika pembersihan kustom Anda di bawah fungsi kasus AppdataCleanup menggunakan templat sampel di bawah ini.

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