locked
Privacy Policy - acceptable ways to add it to your store app? RRS feed

  • Question

  • Ok, I'm new here so forgive me if I make break any rules!!

    My app got rejected because I didn't include a privacy policy IN the app (I provided a URL link fine) and I got the 4.1 message saying I needed to add it to the Windows Charm, but my question is:

    Does it HAVE to be in the Charm pane? Could I add a form that shows up when the browser is opened first time, and add a check box that will stop it showing up in future? Could I just add a button that says "Privacy Policy" and opens a new form that has all the relevant information on?

    Oh, and for reference, it's a simple web browser done in C# & XAML and created in Visual Studio 2012.

    Any non-judgemental help is appreciated because I've looked at code templates to add the Policy and they all look complicated (but one of my peers told me it can be done in 'about' 4 lines of code - which for the life of me I can't find)

    Pete (Newbie developer)

    Wednesday, November 27, 2013 3:25 PM

Answers

  • Hi,

    You have to put it on your windows charm.

    The easiest is to create a SettingsFlyout with your privacy policiy in xaml; 

    on Onlaunched of app.xaml.cs

    SettingsPane.GetForCurrentView().CommandsRequested += onCommandsRequested;
    with 
    void onCommandsRequested(SettingsPane settingsPane, SettingsPaneCommandsRequestedEventArgs e)
            {
                SettingsCommand PPCommand = new SettingsCommand("PP", "Privacy Policy",
                    (handler) =>
                    {
                        SettingsFlyoutPP sf = new SettingsFlyoutPP();
                        sf.Show();
                    });
                e.Request.ApplicationCommands.Add(defaultsCommand);
    
            }
    can't be more easy ;) 
    Wednesday, November 27, 2013 5:11 PM

All replies

  • Hi,

    You have to put it on your windows charm.

    The easiest is to create a SettingsFlyout with your privacy policiy in xaml; 

    on Onlaunched of app.xaml.cs

    SettingsPane.GetForCurrentView().CommandsRequested += onCommandsRequested;
    with 
    void onCommandsRequested(SettingsPane settingsPane, SettingsPaneCommandsRequestedEventArgs e)
            {
                SettingsCommand PPCommand = new SettingsCommand("PP", "Privacy Policy",
                    (handler) =>
                    {
                        SettingsFlyoutPP sf = new SettingsFlyoutPP();
                        sf.Show();
                    });
                e.Request.ApplicationCommands.Add(defaultsCommand);
    
            }
    can't be more easy ;) 
    Wednesday, November 27, 2013 5:11 PM
  • I let it generate the 'stubs' it needed but it's throwing up the following errors:

    Error 1 Type 'WindowsBrowser.MainPage' already defines a member called 'onCommandsRequested' with the same parameter types C:\Users\Pete\Downloads\WindowsBrowser\WindowsBrowser\WindowsBrowser\WindowsBrowser\MainPage.xaml.cs 64 14 WindowsBrowser

    Error 2 Type 'WindowsBrowser.MainPage' already defines a member called 'SettingCharmManager_CommandsRequested' with the same parameter types C:\Users\Pete\Downloads\WindowsBrowser\WindowsBrowser\WindowsBrowser\WindowsBrowser\MainPage.xaml.cs 78 21 WindowsBrowser


    Any help appreciated, pointing out my errors and mistakes helps me learn :)

    Wednesday, November 27, 2013 8:58 PM
  • Did you copy/paste an existing method and scrub out the implementation? This error suggests you've got the same name twice. You should put the code above into the method that was already existing. It will add a link onto the settings charm below whatever else you are adding.

    KeepMyIdentities, Your Key to Password Security. Available now on the Windows Store: http://apps.microsoft.com/webpdp/en-US/app/keepmyidentities/61a9f340-97ac-4666-beab-39f9246cb6fa

    Thursday, November 28, 2013 4:40 AM
  • Confirm my answer as correct to close the thread ;) 
    Thursday, November 28, 2013 4:20 PM
  • You were right, I think I managed to fix it - I got rid of the naming errors but now my app breaks when it tries to open the settings charm.

    For some reason I can't post code blocks or links because my account "hasn't been verified yet" despite verifying it about 5 days ago.

    This is what it says:

    "A debugger is attached to WindowsBrowser.exe but not configured to debug this unhandled exception. To debug this exception, detach the current debugger."

    I've tried to handle the errors but nothing seems to work. The browser runs fine except for when the settings charm opens.

    Saturday, November 30, 2013 11:52 PM
  • Here is my c# privacy code embedded in other code.

    // Copyright (c) Microsoft. All rights reserved.
    
    
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using Windows.Foundation;
    using Windows.Foundation.Collections;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Data;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Navigation;
    using Windows.UI.Xaml.Documents;
    
    
    
    using Windows.UI.ApplicationSettings;
    using Windows.UI.Popups;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Navigation;
    using System;
    
    
    
    /*
      
      
      
                
                MessageDialog dlg = new MessageDialog("Parts list file saved as " + fname);
                await dlg.ShowAsync();
     
     */
    
    using System;
    using System.IO;
    using System.Threading.Tasks;
    using System.Xml.Serialization;
    using Windows.Storage;
    using Windows.Storage.Streams;
    
    
    
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using Windows.Foundation;
    using Windows.Foundation.Collections;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Data;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Navigation;
    
    using Windows.UI.Popups;
    using Windows.Storage.Pickers;
    using Windows.Storage;
    using Windows.Storage.Streams;
    using System.Threading.Tasks;
    using System.Runtime.InteropServices.WindowsRuntime;
    using Windows.System;
    
    
    //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    using System;
    using Windows.UI.ViewManagement;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Markup;
    using Windows.UI.Xaml.Navigation;
    
    /////////////////////////
    
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using Windows.Foundation;
    using Windows.Foundation.Collections;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Data;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Navigation;
    using Windows.UI.Xaml.Documents;
    
    
    
    using Windows.UI.ApplicationSettings;
    using Windows.UI.Popups;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Navigation;
    using System;
    
    namespace SDKTemplate
    {
        /// <summary>
        /// An empty page that can be used on its own or navigated to within a Frame.
        /// </summary>
        public sealed partial class MainPage : SDKTemplate.Common.LayoutAwarePage
        {
            public event System.EventHandler ScenarioLoaded;
            public event EventHandler<MainPageSizeChangedEventArgs> MainPageResized;
    
            public static MainPage Current;
    
            private Frame HiddenFrame = null;
    
            public MainPage()
            {
                this.InitializeComponent();
                SettingsPane.GetForCurrentView().CommandsRequested += onCommandsRequested;
    
                // This is a static public property that will allow downstream pages to get 
                // a handle to the MainPage instance in order to call methods that are in this class.
                Current = this;
    
                // This frame is hidden, meaning it is never shown.  It is simply used to load
                // each scenario page and then pluck out the input and output sections and
                // place them into the UserControls on the main page.
                HiddenFrame = new Windows.UI.Xaml.Controls.Frame();
                HiddenFrame.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
                ContentRoot.Children.Add(HiddenFrame);
    
                // Populate the sample title from the constant in the Constants.cs file.
                SetFeatureName(FEATURE_NAME);
                Scenarios.SelectionChanged += Scenarios_SelectionChanged;
                SizeChanged += MainPage_SizeChanged;
            }
    
    
            void onCommandsRequested(
                SettingsPane settingsPane,
                SettingsPaneCommandsRequestedEventArgs eventArgs)
            {
                UICommandInvokedHandler handler = new UICommandInvokedHandler(onSettingsCommand);
    
                SettingsCommand generalCommand = new SettingsCommand(
                    "generalSettings", "Privacy statement", handler);
                eventArgs.Request.ApplicationCommands.Add(generalCommand);
    
    
            }
    
    
            void onSettingsCommand(IUICommand command)
            {
                SettingsCommand settingsCommand = (SettingsCommand)command;
    
    
                ps();
    
    
    
                //            rootPage.NotifyUser(
                //              "You selected the " + settingsCommand.Label + " settings command from the " +
                //            SettingsPane.Edge.ToString(), NotifyType.StatusMessage);
    
            }
    
            public async Task ps()
            {
    
    
                MessageDialog dlg = new MessageDialog("Murton-Pike Systems Privacy Statement. \n Murton-Pike Systems Ltd. apps do not use any of your personal information.\n Murton-Pike Systems Ltd. apps do not connect to the internet to send or receive any personal information.");
                await dlg.ShowAsync();
    
            }
    
            void MainPage_SizeChanged(object sender, SizeChangedEventArgs e)
            {
                InvalidateSize();
                if (MainPageResized != null)
                {
                    MainPageSizeChangedEventArgs args = new MainPageSizeChangedEventArgs();
                    args.Width = this.ActualWidth;
                    MainPageResized(this, args);
                }
            }
    
            /// <summary>
            /// Invoked when this page is about to be displayed in a Frame.
            /// </summary>
            /// <param name="e">Event data that describes how this page was reached.  The Parameter
            /// property is typically used to configure the page.</param>
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
                PopulateScenarios();
                InvalidateLayout();
            }
    
            private void InvalidateSize()
            {
                // Get the window width
                double windowWidth = this.ActualWidth;
                if (windowWidth != 0.0)
                {
                    // Get the width of the ListBox.
                    double listBoxWidth = Scenarios.ActualWidth;
    
                    // Is the ListBox using any margins that we need to consider?
                    double listBoxMarginLeft = Scenarios.Margin.Left;
                    double listBoxMarginRight = Scenarios.Margin.Right;
    
                    // Figure out how much room is left after considering the list box width
                    double availableWidth = windowWidth - listBoxWidth;
    
                    // Is the top most child using margins?
                    double layoutRootMarginLeft = ContentRoot.Margin.Left;
                    double layoutRootMarginRight = ContentRoot.Margin.Right;
    
                    // We have different widths to use depending on the view state
                    if (this.ActualWidth >= 768)
                    {
                        // Make us as big as the the left over space, factoring in the ListBox width, the ListBox margins.
                        // and the LayoutRoot's margins
                        InputSection.Width = availableWidth - (layoutRootMarginLeft + layoutRootMarginRight + listBoxMarginLeft + listBoxMarginRight);
                    }
                    else
                    {
                        // Make us as big as the left over space, factoring in just the LayoutRoot's margins.
                            InputSection.Width = windowWidth - (layoutRootMarginLeft + layoutRootMarginRight);
                        
                    }
                }
                InvalidateLayout();
            }
    
            private void InvalidateLayout()
            {
                if (this.ActualWidth < 768)
                {
                    
    
                    Grid.SetRow(InputSection, 4);
                    Grid.SetColumn(InputSection, 0);
    
                    Grid.SetRow(FooterPanel, 2);
                    Grid.SetColumn(FooterPanel, 0);
                }
                else
                {
                   
    
                    Grid.SetRow(InputSection, 2);
                    Grid.SetColumn(InputSection, 1);
    
                    Grid.SetRow(FooterPanel, 1);
                    Grid.SetColumn(FooterPanel, 1);
                }
    
                // Since we don't load the scenario page in the traditional manner (we just pluck out the
                // input and output sections from the page) we need to ensure that any VSM code used
                // by the scenario's input and output sections is fired.
                VisualStateManager.GoToState((Control)InputSection, "Input" + DetermineVisualState(this.ActualWidth), false);
                VisualStateManager.GoToState((Control)OutputSection, "Output" + DetermineVisualState(this.ActualWidth), false);
            }
    
            private void PopulateScenarios()
            {
    
    
    
                System.Collections.ObjectModel.ObservableCollection<object> ScenarioList = new System.Collections.ObjectModel.ObservableCollection<object>();
                int i = 0;
    
                // Populate the ListBox with the list of scenarios as defined in Constants.cs.
                foreach (Scenario s in scenarios)
                {
                    ListBoxItem item = new ListBoxItem();
                    s.Title = (++i).ToString() + ") " + s.Title;
                    item.Content = s;
                    item.Name = s.ClassType.FullName;
                    ScenarioList.Add(item);
                }
    
                // Bind the ListBox to the scenario list.
                Scenarios.ItemsSource = ScenarioList;
    
                // Starting scenario is the first or based upon a previous selection.
                int startingScenarioIndex = -1;
                
                if (SuspensionManager.SessionState.ContainsKey("SelectedScenarioIndex"))
                {
                    int selectedScenarioIndex = Convert.ToInt32(SuspensionManager.SessionState["SelectedScenarioIndex"]);
                    startingScenarioIndex = selectedScenarioIndex;
                }
                Scenarios.SelectedIndex = startingScenarioIndex != -1 ? startingScenarioIndex : 0;
                Scenarios.ScrollIntoView(Scenarios.SelectedItem);
            }
    
            /// <summary>
            /// This method is responsible for loading the individual input and output sections for each scenario.  This 
            /// is based on navigating a hidden Frame to the ScenarioX.xaml page and then extracting out the input
            /// and output sections into the respective UserControl on the main page.
            /// </summary>
            /// <param name="scenarioName"></param>
            public void LoadScenario(Type scenarioClass)
            {
    
                // Load the ScenarioX.xaml file into the Frame.
                HiddenFrame.Navigate(scenarioClass, this);
    
                // Get the top element, the Page, so we can look up the elements
                // that represent the input and output sections of the ScenarioX file.
                Page hiddenPage = HiddenFrame.Content as Page;
    
                // Get each element.
                UIElement input = hiddenPage.FindName("Input") as UIElement;
                UIElement output = hiddenPage.FindName("Output") as UIElement;
    
                if (input == null)
                {
                    // Malformed input section.
                    NotifyUser(String.Format(
                        "Cannot load scenario input section for {0}.  Make sure root of input section markup has x:Name of 'Input'",
                        scenarioClass.Name), NotifyType.ErrorMessage);
                    return;
                }
    
                if (output == null)
                {
                    // Malformed output section.
                    NotifyUser(String.Format(
                        "Cannot load scenario output section for {0}.  Make sure root of output section markup has x:Name of 'Output'",
                        scenarioClass.Name), NotifyType.ErrorMessage);
                    return;
                }
    
                // Find the LayoutRoot which parents the input and output sections in the main page.
                Panel panel = hiddenPage.FindName("LayoutRoot") as Panel;
    
                if (panel != null)
                {
                    // Get rid of the content that is currently in the intput and output sections.
                    panel.Children.Remove(input);
                    panel.Children.Remove(output);
    
                    // Populate the input and output sections with the newly loaded content.
                    InputSection.Content = input;
                    OutputSection.Content = output;
                }
                else
                {
                    // Malformed Scenario file.
                    NotifyUser(String.Format(
                        "Cannot load scenario: '{0}'.  Make sure root tag in the '{0}' file has an x:Name of 'LayoutRoot'",
                        scenarioClass.Name), NotifyType.ErrorMessage);
                }
            }
    
            void Scenarios_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                if (Scenarios.SelectedItem != null)
                {
                    NotifyUser("", NotifyType.StatusMessage);
    
                    ListBoxItem selectedListBoxItem = Scenarios.SelectedItem as ListBoxItem;
                    SuspensionManager.SessionState["SelectedScenarioIndex"] = Scenarios.SelectedIndex;
    
                    Scenario scenario = selectedListBoxItem.Content as Scenario;
                    LoadScenario(scenario.ClassType);
                    InvalidateSize();
    
                    // Fire the ScenarioLoaded event since we know that everything is loaded now.
                    if (ScenarioLoaded != null)
                    {
                        ScenarioLoaded(this, new EventArgs());
                    }
                }
            }
    
            public void NotifyUser(string strMessage, NotifyType type)
            {
                switch (type)
                {
                    // Use the status message style.
                    case NotifyType.StatusMessage:
                        StatusBlock.Style = Resources["StatusStyle"] as Style;
                        break;
                    // Use the error message style.
                    case NotifyType.ErrorMessage:
                        StatusBlock.Style = Resources["ErrorStyle"] as Style;
                        break;
                }
                StatusBlock.Text = strMessage;
    
                // Collapse the StatusBlock if it has no text to conserve real estate.
                if (StatusBlock.Text != String.Empty)
                {
                    StatusBlock.Visibility = Windows.UI.Xaml.Visibility.Visible;
                }
                else
                {
                    StatusBlock.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
                }
            }
    
            async void Footer_Click(object sender, RoutedEventArgs e)
            {
                await Windows.System.Launcher.LaunchUriAsync(new Uri(((HyperlinkButton)sender).Tag.ToString()));
            }
    
            private void SetFeatureName(string str)
            {
                //FeatureName.Text = str;
            }
        }
    
        public class MainPageSizeChangedEventArgs : EventArgs
        {
            private double width;
    
            public double Width
            {
                get { return width; }
                set { width = value; }
            }
        }
    
        public enum NotifyType
        {
            StatusMessage,
            ErrorMessage
        };
    
    
    
    
    }


    n.Wright

    Sunday, December 1, 2013 7:03 PM