piątek, 20 kwietnia 2012

Notatki o Windows 8 Consumer Preview - odc. 12

Tym razem na temat zarządzania danymi użytkowników oraz o cyklu życia aplikacji.

W nowej wersji uwagę zwraca udokumentowany sposób korzystania z klasy ContactPicker, który powala wybrać jednego lub więcej użytkowników. Lista kontaktów może zostać zawężona do zbioru zależnego od podanego przez nas parametru (np. adresu e-mail).

W przypadku cyklu życia aplikacji zwracają uwagę nieco inne sposoby obsługi w niektórych przypadkach (np. zdarzenie checkpoint w JS).

Godny odnotowania jest fakt, że w Windows 8 możemy zarządzać rozszerzeniami plików obsługiwanymi przez poszczególne aplikacje za pomocą nowego modułu Set Default Programs w panelu sterowania.

JS

Managing user info

Apps can also work with user contacts using the Windows.ApplicationModel.Contacts and Windows.ApplicationModel.Contacts.Provider namespaces. When working with contacts, apps can choose to get contact information from other apps, such as Live, or to provide contacts to other apps.

Setting up single sign-on (SSO) enables users to switch apps without having to provide their credentials to authenticate. An app can choose to use the web authentication broker to sign in to OAuth or OpenID protocol-based web services, such as many social network and picture-sharing websites, provided that the particular service provider has made the changes listed in the following topics.

Developers

Step 1: Use the operation that doesn't require the callbackUri parameter

Use the authenticateAsync function that doesn't expose the callbackUri parameter. Behind the scenes, the web authentication broker constructs the value of the callbackUri parameter itself by querying the app's package SID and appending it to the "ms-app://" string.

Step 2: Get your app's SID

Call the getCurrentApplicationCallbackUri method to get your app's SID for SSO. This is useful when building a parameter list to be sent to an online provider through the query string.

Step 3: Register your app with your online provider

You must register your "ms-app://" URI with your online provider.

Step 4: Prevent a dialog box from appearing to your user

When the app doesn't expect the provider to show any UI, use the WebAuthenticationOptions.silentMode flag to avoid a dialog box.

You can find out what your "ms-app://" URI is by going to the Developer portal. Click the Manage your cloud services setting, and then find your app's SID under the Application Authentication tab of the Advanced Features.

Managing user contacts

How to select a single contact

function selectContact() {
    var picker = Windows.ApplicationModel.Contacts.ContactPicker();
    picker.commitButtonText = "Select";
    picker.pickSingleContactAsync().then(function (contact) {
        var contactElement = document.createElement("div");
        contactElement.innerText = contact.name + " " + contact.emails[0].value;
        document.body.appendChild(contactElement);
    });
}

How to select multiple contacts

function selectContacts() {
    var picker = Windows.ApplicationModel.Contacts.ContactPicker();
    picker.commitButtonText = "Select";
    picker.pickMultipleContactsAsync().then(function (contacts) {
        contacts.forEach(function (contact) {
            var contactElement = document.createElement("div");
            contactElement.innerText = contact.name;
        });
    });
}

How to select specific contact data

function selectContact() {
    var picker = Windows.ApplicationModel.Contacts.ContactPicker();
    picker.commitButtonText = "Select";
    picker.selectionMode = Windows.ApplicationModel.Contacts.ContactSelectionMode.fields;
    picker.desiredFields.append(Windows.ApplicationModel.Contacts.KnownContactField.email);
    picker.pickSingleContactAsync().then(function (contact) {
        var contactElement = document.createElement("div");
        contactElement.innerText = contact.name + " " + contact.emails[0].value;
        document.body.appendChild(contactElement);
    });
}

Launching and resuming apps

Register for the DOMContentLoaded event, which occurs when all Document Object Model (DOM) and script content has been loaded, but not necessarily the images and content. You should set up any custom loading UI for the app in domcontentloadedHandler.

document.addEventListener("DOMContentLoaded", domcontentloadedHandler, false);

Register for the activated event in the global scope.

var app = WinJS.Application;

app.addEventListener("activated", activatedHandler, false);
app.start();

When the user switches to your terminated app, the system sends the activated event. Check the sessionState object to see whether your session state variable is defined. If so, the app should load its saved application data and refresh its displayed content.

function activatedHandler(eventArgs) {
   if (eventArgs.detail.kind == Windows.ApplicationModel.Activation.ActivationKind.launch)
   {
      // Check whether my session state variables are valid.
      // If so, retrieve the application data saved in the checkpoint handler
      if (app.sessionState)
      {
         // TODO: Populate the UI with the previously saved application data           
      }
      else
      {
         // TODO: Populate the UI with defaults            
      }

      // Initialize the WinJS controls
      WinJS.UI.processAll();
   }
}

When the system launches your app, there may be additional context for the activation:

function activatedHandler(eventArgs) {
    if (eventArgs.detail.kind == Windows.ApplicationModel.Activation.ActivationKind.launch)
    {
        if (eventArgs.detail.arguments !== '')
        {
            // TODO: Parse the arguments string
        }
    }
}

How to suspend an app

Register for the checkpoint event in the global scope. This event indicates that your app should save its application data in case it is suspended and then terminated.

var app = WinJS.Application;

app.addEventListener("checkpoint", checkpointHandler);

The app can use the sessionState object to save simple application data synchronously.

function checkpointHandler(eventArgs)
{
    var stateObject = new Object();

    // TODO: Populate the state object with app data

    // Save the state object to the session object
    app.sessionState.stateObject = stateObject;
}

Auto-launching with file and protocol associations

How to handle file activation

function onActivatedHandler(eventArgs) {
   if (eventArgs.detail.kind == Windows.ApplicationModel.Activation.ActivationKind.file)
   {
       // TODO: Handle file activation.

       // The number of files received is eventArgs.detail.files.size
       // The first file is eventArgs.detail.files[0].name
   }
}

The files that you receive could come from an untrusted source. You should validate the content of a file before taking action on it.

How to handle protocol activation

function onActivatedHandler(eventArgs) {
   if (eventArgs.detail.kind == Windows.ApplicationModel.Activation.ActivationKind.protocol)
   {
       // TODO: Handle protocol activation.

       // The received URI is eventArgs.detail.uri.rawUri
   }
}

Any app or website can use your protocol, including malicious ones. So any data that you get over the protocol could come from an untrusted source. You should never perform a permanent action based on the parameters that you receive over a protocol. For example, protocol parameters could be used to launch the app to a user's account page, but should never be used to directly modify the user's account.

How to launch the default app for a file

Alternatively, call the Windows.System.Launcher.launchFileAsync(IStorageFile, LauncherOptions) method with LauncherOptions.displayApplicationPicker set to true to launch the app that the user selects from the Open With dialog box.

Your app cannot select the app that is launched. The user determines which app is launched. The user can select either a Metro style app or a desktop app. If there is no app installed that can handle the file type, Windows helps the user acquire an app from the Windows Store.

How to launch the default app for a protocol

// Launch the URI
Windows.System.Launcher.launchUriAsync(uri).then(  
   function (success) {
      if (success) {
        // URI launched
      } else {
        // URI launch failed
      }
   });

Alternatively, call launchUriAsync(Uri, LauncherOptions) to launch the URI created in step 1 with a warning. The treatAsUntrusted property indicates that the system should display a warning.

// Launch the URI with a warning prompt
var options = new Windows.System.LauncherOptions();
options.treatAsUntrusted = true;

Windows.System.Launcher.launchUriAsync(uri, options).then(
   function (success) {
      if (success) {
        // URI launched
      } else {
        // URI launch failed
      }
   });

Alternatively, call launchUriAsync(Uri, LauncherOptions) to launch the URI using the app that the user selects from the Open With dialog box.

// Launch the URI using the selected app
var options = new Windows.System.LauncherOptions();
options.displayApplicationPicker = true;

Windows.System.Launcher.launchUriAsync(uri, options).then(
   function (success) {
      if (success) {
        // Uri launched
      } else {
        // Uri launch failed
      }
   });

Your app cannot select the app that is launched.

If there is no app installed that can handle the protocol, Windows helps the user acquire an app from the Windows Store.

Guidelines and checklist for file types and protocols

When opening a file or protocol, the user may need to use the Open With list to select which app to use as the default. Windows 8 implements this list as a Flyout. Although you can't customize the contents of the Open WithFlyout, you can control its position in your app.

Remember, Metro style apps can't set, change, or query default apps for file types and protocols, so you shouldn't try to add that functionality to your app.

Desktop apps

In Windows 8, apps no longer have the ability to set, change, or query the default handlers for file types and protocols. Instead, we recommend that you link to Set Default Programs in Control Panel.

Example of Set Default Programs in Control Panel

Auto-launching with AutoPlay

When a user connects a device, AutoPlay determines the type of device and raises either a device event for non-volume devices, or a content event for volume devices.

  • Open the Package.appxmanifest file and select the Capabilities tab. Select the Removable Storage and Pictures Library capabilities. This gives the app access to removable storage devices for camera memory, and access to the local Pictures library.
  • In the manifest file, select the Declarations tab. In the Available Declarations drop-down list, select AutoPlay Content and click Add. Select the new AutoPlay Content item that was added to the Supported Declarations list.
  • An AutoPlay Content declaration identifies your app as an option when AutoPlay raises an event. The event is based on the content of a volume device such as a DVD or a thumb drive. AutoPlay examines the content of the volume device and determines which content event to raise. In the case where the volume contains image files, AutoPlay raises the ShowPicturesOnArrival event.

In the Launch Actions section, enter the following values for launch actions:

  • Verb - show / copy
  • Action Display Name - Show Pictures / Copy Pictures Into Library,
  • Content Event - ShowPicturesOnArrival

The Action Display Name setting identifies the string that AutoPlay displays for your app. The Verb setting identifies a value that is passed to your app for the selected option. You can specify multiple launch actions for an AutoPlay event and use the Verb setting to determine which option a user has selected for your app. You can tell which option the user selected by checking the verb property of the startup event arguments passed to your app.

  • In the Available Declarations drop-down list, select File Type Associations and click Add. In the Properties of the new File Type Associations declaration, set the Display Name field to AutoPlay Copy or Show Images and the Name field to image_association1. In the Supported File Types section, click Add New. Set the File Type field to .jpg. In the Supported File Types section, click Add New again. Set the File Type field of the new file association to .png.

var filesDiv;

// This function responds to all application activations.
app.onactivated = function (eventObject) {
    if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.file) {
        filesDiv = document.getElementById("files");

        if (eventObject.detail.verb === "show") {
            // Call displayImages with root folder from camera storage.
            displayImages(eventObject.detail.files[0]);
        }
        else if (eventObject.detail.verb === "copy") {
            // Call copyImagesToLibrary with root folder from camera storage.
            copyImagesToLibrary(eventObject.detail.files[0]);
        }
    }

    WinJS.UI.processAll();
};

C#

Launching and resuming apps

How to activate an app

The Windows.UI.Xaml.Application class defines methods you can override to handle the various activation types. Several of the activation types have a specific method that you can override. For the other activation types, override the OnActivated method.

Override the OnLaunched method. This method is called whenever the user launches the app. The LaunchActivatedEventArgs parameter contains the previous state of your app and the activation arguments.

   public partial class App
   {
      async protected override void OnLaunched(LaunchActivatedEventArgs args)
      {
         EnsurePageCreatedAndActivate();
      }

      // Creates the MainPage if it isn't already created.  Also activates
      // the window so it takes foreground and input focus.
      private MainPage EnsurePageCreatedAndActivate()
      {
         if (Window.Current.Content == null)
         {
             Window.Current.Content = new MainPage();
         }

         Window.Current.Activate();
         return Window.Current.Content as MainPage;
      }
   }

When the user switches to your terminated app, the system sends the Activated event, with Kind set to Launch and PreviousExecutionState set to Terminated or ClosedByUser.

async protected override void OnLaunched(LaunchActivatedEventArgs args)
{
   if (args.PreviousExecutionState == ApplicationExecutionState.Terminated ||
       args.PreviousExecutionState == ApplicationExecutionState.ClosedByUser)
   {
      // TODO: Populate the UI with the previously saved application data
   }
   else
   {
      // TODO: Populate the UI with defaults
   }

   EnsurePageCreatedAndActivate();
}

If the value of PreviousExecutionState is NotRunning, the app failed to save its application data successfully and the app should start over as if it were being initially launched.

How to suspend an app

partial class MainPage
{
   public MainPage()
   {
      InitializeComponent();
      App.Current.Suspending += new SuspendingEventHandler(App_Suspending);
   }
}

The app should use the LocalSettings storage API to save simple application data synchronously.

CoreDispatcher dispatcher = Window.Current.Dispatcher;

private void App_Suspending(object sender, object e)
{
    // This is a good time to save app data in case the process gets terminated.
    IPropertySet settingsValues = ApplicationData.Current.LocalSettings.Values;

    // TODO: Save the app data
}

When the user switches back to a suspended app that has been terminated, the system sends an Activated event and should restore its application data in its OnLaunched method.

How to resume an app

partial class MainPage
{
   public MainPage()
   {
      InitializeComponent();
      App.Current.Resuming += new Windows.UI.Xaml.EventHandler(App_Resuming);
   }
}

When your app handles the Resuming event, it has the opportunity to refresh its displayed content. Because this event is not raised in the UI thread, a dispatcher must be used inject an update to the UI.

CoreDispatcher dispatcher = Window.Current.Dispatcher;

private void App_Resuming(object sender, object e)
{
    // There are no special arguments for the resuming event
    dispatcher.Invoke(CoreDispatcherPriority.Normal,
      (object invokedSender, InvokedHandlerArgs invokedArgs) =>
    {
        // TODO: Refresh network data
    }, this, null);
}

Auto-launching with AutoPlay

protected override void OnFileActivated(FileActivatedEventArgs args)
{
    if (args.Verb == "show")
    {
        Frame rootFrame = (Frame)Window.Current.Content;
        BlankPage page = (BlankPage)rootFrame.Content;

        // Call DisplayImages with root folder from camera storage.
        page.DisplayImages((Windows.Storage.StorageFolder)args.Files[0]);
    }

    if (args.Verb == "copy")
    {
        Frame rootFrame = (Frame)Window.Current.Content;
        BlankPage page = (BlankPage)rootFrame.Content;

        // Call CopyImages with root folder from camera storage.
        page.CopyImages((Windows.Storage.StorageFolder)args.Files[0]);
    }

    base.OnFileActivated(args);
}

Brak komentarzy: