Solving 3 problems with the ShellTileSchedule

Are the problems with the Shell Tile Schedule? Yes there are, at least I’m in the impression that there are some problems. Although we have those problems I really like the ShellTileSchedule because it enables an app to have an updated tile without the requirement to write server-side code to notify the client for a new tile. The smallest schedule that’s supported is 1-hour. In that situation every hour the tile will be updated with a tile located on the web (static url, which can return a dynamic image of course).

There are three problems I identified so far.

1. You can’t get the status of the ShellTileSchedule. Worse, although you started the schedule it might be stopped because for whatever reason (ex. phone is on Airplane mode) the downloading of the tile failed.

2. You have to wait at least 1 hour before the tile is updated for the first time.

3. After you stopped the ShellTileSchedule, the tile will be the last downloaded tile forever. It would be better if automatically the original tile (from the .xap package) is put back.

Combined a diagram to show the problems.

image

Solution 1 for problem 1

Alright, we can’t get the status. So what do we do? We store it locally, in the Isolated Storage. I really like the approach that’s explained by Joost van Schaik who created some extension methods for storing the settings in the Isolated Storage. He’s storing and retrieving to the Phone State on the Tomb Stoning events: Activated and Deactivated. I did store and retrieve from Isolated on all events: Activated, Deactivated, Launching and Closing.

That’s all about making sure we have a status. But still this status won’t be updated when the schedule stopped. The Windows Phone team suggests to start the schedule on every application start. The code of the Application_Launching event in the App.xaml.cs could look like this.

private void Application_Launching(object sender, LaunchingEventArgs e)
{
    Settings = this.RetrieveFromIsolatedStorage<SettingsViewModel>() ?? new SettingsViewModel();
    if(Settings.TileUpdatesEnabled)
    {
        new ShellTileSchedule
        {
            Interval = UpdateInterval.EveryHour,
            MaxUpdateCount = 0,
            Recurrence = UpdateRecurrence.Interval,
            RemoteImageUri = new Uri(@"http://mark.mymonster.nl/Uploads/2010/12/servertile.png"),
            StartTime = DateTime.Now
        }.Start();
    }
}

Joost mentions that the ViewModelBase of MVVM Light isn’t serializable. So I created a basic ViewModelBase that has the functionality that I’m always using in ViewModelBase (RaisePropertyChanged) and decorated it with the DataContract attribute.

[DataContract]
public class ViewModelBase : INotifyPropertyChanged
{
    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion

    protected void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Solution 2 for problem 2

Not many people know it’s possible, but Matthijs Hoekstra from Microsoft helped me in the direction to solve this problem.

Solution: Send a Push Notification from the phone itself. And afterwards close the channel and start the ShellTileSchedule.

It’s important that you send the Push Notification before the actual call to the start the ShellTileSchedule because the ShellTileSchedule is a kind of NotificationChannel of which only one can exist.

A lot of articles have been written already about how to do Push Notifications. After opening a NotificationChannel you will get a url. After that you send some xml to that url. The xml that has to be send looks like this.

?<?xml version="1.0" encoding="utf-8"?>
<wp:Notification xmlns:wp="WPNotification">
  <wp:Tile>
    <wp:BackgroundImage>http://someserver/servertile.png</wp:BackgroundImage>
    <wp:Count>0</wp:Count>
  </wp:Tile>
</wp:Notification>

The setup of the xml in code:

public void SendTile(Uri notificationUrl, string tileUri, int? count, string title, Action onComplete)
{
    var stream = new MemoryStream();
    var settings = new XmlWriterSettings {Indent = true, Encoding = Encoding.UTF8};
    XmlWriter writer = XmlWriter.Create(stream, settings);
    writer.WriteStartDocument();
    writer.WriteStartElement("wp", "Notification", "WPNotification");
    writer.WriteStartElement("wp", "Tile", "WPNotification");
    if (!string.IsNullOrEmpty(tileUri))
    {
        writer.WriteStartElement("wp", "BackgroundImage", "WPNotification");
        writer.WriteValue(tileUri);
        writer.WriteEndElement();
    }
    if (count.HasValue)
    {
        writer.WriteStartElement("wp", "Count", "WPNotification");
        writer.WriteValue(count.ToString());
        writer.WriteEndElement();
    }
    if (!string.IsNullOrEmpty(title))
    {
        writer.WriteStartElement("wp", "Title", "WPNotification");
        writer.WriteValue(title);
        writer.WriteEndElement();
    }
    writer.WriteEndElement();
    writer.Close();
    byte[] payload = stream.ToArray();

	...
}

If you take a look at the Windows Phone 7 Training Kit you will see an example on how to do a Push Notification. It can be used almost one on one in the Windows Phone application itself. I removed some of the code (for the Raw and Toast Notifications) and refactored it to fit my needs.

So after the setup of the xml, the sending of the xml and setting of the HTTP headers looks like this:

public void SendTile(Uri notificationUrl, string tileUri, int? count, string title, Action onComplete)
{
	...
	
	byte[] payload = stream.ToArray();

    //Check the length of the payload and reject it if too long
    if (payload.Length > MaxPayloadLength)
        throw new ArgumentOutOfRangeException(
            string.Format("Payload is too long. Maximum payload size shouldn't exceed {0} bytes",
                            MaxPayloadLength));

    //Create and initialize the request object
    var request = (HttpWebRequest) WebRequest.Create(notificationUrl);
    request.Method = "POST";
    request.ContentType = "text/xml; charset=utf-8";
    //request.ContentLength = payload.Length;
    request.Headers["X-MessageID"] = Guid.NewGuid().ToString();
    request.Headers["X-NotificationClass"] = 1.ToString();
    request.Headers["X-WindowsPhone-Target"] = "token";

    request.BeginGetRequestStream(
        ar =>
            {
                //Once async call returns get the Stream object
                Stream requestStream = request.EndGetRequestStream(ar);

                //and start to write the payload to the stream asynchronously
                requestStream.BeginWrite(
                    payload, 0, payload.Length,
                    iar =>
                        {
                            //When the writing is done, close the stream
                            requestStream.EndWrite(iar);
                            requestStream.Close();

                            //and switch to receiving the response from MPNS
                            request.BeginGetResponse(
                                iarr =>
                                    {
                                        if (onComplete != null)
                                            onComplete();
                                    },
                                null);
                        },
                    null);
            },
        null);
}

Of course we shouldn’t forget the important part: Subscribing to the Notification channel. Take special notice to the Thread.Sleep in line 21. This is to make sure that the Tile update is completed before the unbinding, and more starting the ShellTileSchedule.

private void UpdateTileBeforeOperation(Uri imageUri, Action onComplete)
{
    HttpNotificationChannel channel = HttpNotificationChannel.Find("OneTime");
    if (channel != null)
        channel.Close();
    else
    {
        channel = new HttpNotificationChannel("OneTime");
        channel.ChannelUriUpdated +=
            (s, e) =>
                {
                    if (imageUri.IsAbsoluteUri)
                        channel.BindToShellTile(new Collection<Uri> {imageUri});
                    else
                        channel.BindToShellTile();

                    SendTile(e.ChannelUri, imageUri.ToString(), 0, "",
                                () =>
                                    {
                                        //Give it some time to let the update propagate
                                        Thread.Sleep(
                                            TimeSpan.FromSeconds(1));

                                        channel.UnbindToShellTile();
                                        channel.Close();
                                        //Do the operation
                                        if (onComplete != null)
                                            onComplete();
                                    }
                        );
                };
        channel.Open();
    }
}

Solution 3 for problem 3

The solution for problem 3 is similar to solution 2. But instead of a remote url the url is an relative url, local to the .xap file.

public void Stop(Action onComplete)
{
    UpdateTileBeforeOperation(new Uri("/Background.png", UriKind.Relative),
                                () => { if (onComplete != null) onComplete(); });
}

Again it allows you to include an action that will be called upon completion of the Stop method.

 

Full solution diagram

Alright, all problems solved. The diagram now looks like this.

image

Of course you want to have the full code for the SmartShellTileSchedule.

public class SmartShellTileSchedule
{
    private const int MaxPayloadLength = 1024;

    public UpdateRecurrence Recurrence { get; set; }

    public int MaxUpdateCount { get; set; }

    public DateTime StartTime { get; set; }

    public UpdateInterval Interval { get; set; }

    public Uri RemoteImageUri { get; set; }

    /// <summary>
    /// If the schedule is enabled (store this in application settings) this operation should be 
    /// called upon each application start.
    /// </summary>
    public void CheckForStart()
    {
        DelegateSchedule().Start();
    }

    /// <summary>
    /// This will enable the schedule and make sure the tile is updated immediately. Don't call 
    /// this operation on each application start.
    /// </summary>
    public void Start()
    {
        Start(null);
    }

    /// <summary>
    /// This will enable the schedule and make sure the tile is updated immediately. Don't call 
    /// this operation on each application start.
    /// </summary>
    /// <param name="onComplete">will be called upon completion</param>
    public void Start(Action onComplete)
    {
        UpdateTileBeforeOperation(RemoteImageUri, () =>
                                                        {
                                                            CheckForStart();
                                                            if (onComplete != null) onComplete();
                                                        });
    }

    /// <summary>
    /// This will stop the schedule and make sure the tile is replaced with the original logo-tile.
    /// Assumption is that the logo-tile is called "Background.png"
    /// </summary>
    public void Stop()
    {
        Stop(null);
    }

    /// <summary>
    /// This will stop the schedule and make sure the tile is replaced with the original logo-tile.
    /// Assumption is that the logo-tile is called "Background.png"
    /// </summary>
    /// <param name="onComplete">will be called upon completion</param>
    public void Stop(Action onComplete)
    {
        UpdateTileBeforeOperation(new Uri("/Background.png", UriKind.Relative),
                                    () => { if (onComplete != null) onComplete(); });
    }

    private void UpdateTileBeforeOperation(Uri imageUri, Action onComplete)
    {
        HttpNotificationChannel channel = HttpNotificationChannel.Find("OneTime");
        if (channel != null)
            channel.Close();
        else
        {
            channel = new HttpNotificationChannel("OneTime");
            channel.ChannelUriUpdated +=
                (s, e) =>
                    {
                        if (imageUri.IsAbsoluteUri)
                            channel.BindToShellTile(new Collection<Uri> {imageUri});
                        else
                            channel.BindToShellTile();

                        SendTile(e.ChannelUri, imageUri.ToString(), 0, "",
                                    () =>
                                        {
                                            //Give it some time to let the update propagate
                                            Thread.Sleep(
                                                TimeSpan.FromSeconds(1));

                                            channel.UnbindToShellTile();
                                            channel.Close();
                                            //Do the operation
                                            if (onComplete != null)
                                                onComplete();
                                        }
                            );
                    };
            channel.Open();
        }
    }

    private ShellTileSchedule DelegateSchedule()
    {
        return new ShellTileSchedule
                    {
                        Interval = Interval,
                        MaxUpdateCount = MaxUpdateCount,
                        Recurrence = Recurrence,
                        RemoteImageUri = RemoteImageUri,
                        StartTime = StartTime
                    };
    }

    public void SendTile(Uri notificationUrl, string tileUri, int? count, string title, Action onComplete)
    {
        var stream = new MemoryStream();
        var settings = new XmlWriterSettings {Indent = true, Encoding = Encoding.UTF8};
        XmlWriter writer = XmlWriter.Create(stream, settings);
        writer.WriteStartDocument();
        writer.WriteStartElement("wp", "Notification", "WPNotification");
        writer.WriteStartElement("wp", "Tile", "WPNotification");
        if (!string.IsNullOrEmpty(tileUri))
        {
            writer.WriteStartElement("wp", "BackgroundImage", "WPNotification");
            writer.WriteValue(tileUri);
            writer.WriteEndElement();
        }
        if (count.HasValue)
        {
            writer.WriteStartElement("wp", "Count", "WPNotification");
            writer.WriteValue(count.ToString());
            writer.WriteEndElement();
        }
        if (!string.IsNullOrEmpty(title))
        {
            writer.WriteStartElement("wp", "Title", "WPNotification");
            writer.WriteValue(title);
            writer.WriteEndElement();
        }
        writer.WriteEndElement();
        writer.Close();
        byte[] payload = stream.ToArray();

        //Check the length of the payload and reject it if too long
        if (payload.Length > MaxPayloadLength)
            throw new ArgumentOutOfRangeException(
                string.Format("Payload is too long. Maximum payload size shouldn't exceed {0} bytes",
                                MaxPayloadLength));

        //Create and initialize the request object
        var request = (HttpWebRequest) WebRequest.Create(notificationUrl);
        request.Method = "POST";
        request.ContentType = "text/xml; charset=utf-8";
        //request.ContentLength = payload.Length;
        request.Headers["X-MessageID"] = Guid.NewGuid().ToString();
        request.Headers["X-NotificationClass"] = 1.ToString();
        request.Headers["X-WindowsPhone-Target"] = "token";

        request.BeginGetRequestStream(
            ar =>
                {
                    //Once async call returns get the Stream object
                    Stream requestStream = request.EndGetRequestStream(ar);

                    //and start to write the payload to the stream asynchronously
                    requestStream.BeginWrite(
                        payload, 0, payload.Length,
                        iar =>
                            {
                                //When the writing is done, close the stream
                                requestStream.EndWrite(iar);
                                requestStream.Close();

                                //and switch to receiving the response from MPNS
                                request.BeginGetResponse(
                                    iarr =>
                                        {
                                            if (onComplete != null)
                                                onComplete();
                                        },
                                    null);
                            },
                        null);
                },
            null);
    }
}

Additional I also included my SettingsViewModel which is fully bindable.

[DataContract]
public class SettingsViewModel : ViewModelBase
{
    private ICommand _enforceTileUpdatesState;
    private bool _executing;
    private ICommand _setScheduleIfEnabled;
    private bool _tileUpdatesEnabled;

    [DataMember]
    public bool TileUpdatesEnabled
    {
        get { return _tileUpdatesEnabled; }
        set
        {
            if (value != _tileUpdatesEnabled)
            {
                _tileUpdatesEnabled = value;
                RaisePropertyChanged("TileUpdatesEnabled");
            }
        }
    }

    public bool Executing
    {
        get { return _executing; }
        set
        {
            if (value != _executing)
            {
                _executing = value;
                RaisePropertyChanged("Executing");
            }
        }
    }

    public ICommand SetScheduleIfEnabled
    {
        get
        {
            if (_setScheduleIfEnabled == null)
            {
                _setScheduleIfEnabled = new RelayCommand(
                    () =>
                        {
                            if (TileUpdatesEnabled)
                            {
                                Executing = true;
                                GetSchedule().CheckForStart();
                                Executing = false;
                            }
                        });
            }
            return _setScheduleIfEnabled;
        }
    }

    public ICommand EnforceTileUpdatesState
    {
        get
        {
            if (_enforceTileUpdatesState == null)
            {
                _enforceTileUpdatesState = new RelayCommand(
                    () =>
                        {
                            if (TileUpdatesEnabled)
                            {
                                Executing = true;
                                GetSchedule().Start(
                                    () =>
                                    Deployment.Current.Dispatcher.
                                        BeginInvoke(() => Executing = false));
                            }
                            else
                            {
                                Executing = true;
                                GetSchedule().Stop(
                                    () =>
                                    Deployment.Current.Dispatcher.
                                        BeginInvoke(() => Executing = false));
                            }
                        });
            }
            return _enforceTileUpdatesState;
        }
    }


    private SmartShellTileSchedule GetSchedule()
    {
        return new SmartShellTileSchedule
                    {
                        Interval = UpdateInterval.EveryHour,
                        RemoteImageUri =
                            new Uri(@"http://someserver/servertile.png"),
                        StartTime = DateTime.Now,
                        Recurrence = UpdateRecurrence.Interval
                    };
    }
}
  • Gravatar Axel December 19th, 2010 at 23:39
    Just curious: what makes you think 1 second is enough for the push server to process your request and send the notification back? In my experience (I've been running a live service using tile notifications for the last couple of weeks) the notification takes anywhere from 1 second to several HOURS to actually reach the phone. For some reason things tends to work much better in the emulator but your mileage may vary widely on an actual device. Be also prepared for ChannelUriUpdated never firing, or for the push server telling you that you are "temporarily disconnected" even if it's clearly not the case :-) Just my $0.002
  • Gravatar Arktronic January 4th, 2011 at 20:26
    FYI, this code seems to cause some odd issues in live Marketplace apps - http://forums.create.msdn.com/forums/t/71361.aspx
  • Gravatar Chris Hardy January 6th, 2011 at 21:56
    Hey Mark,

    For the first solution, having the tile schedule start like that means that if someone opens the app up every 50 minute or so, they will never get a live tile. Though this is not realistic, this coupled with the fact that when the phone is idle, it won't be checking for updates and if you're on WiFi only then it might not even have a connection when the phone comes out of idle then you're going to run into some very intermittent "live tiles".

    For the second solution, if a user wanted to turn off live tiles, you could always do a one time shell tile update to get the original live tile from the server, this seems to work pretty, it just won't be as instant as doing a push notification to get it.

    Hope this helps,

    ChrisNTR
  • Gravatar Joost van Schaik January 11th, 2011 at 15:16
    Hi Mark,

    Thanks for noticing my solution for tombstoning. Be aware that I more or less abandoned the approach using an XMLSerializer - I know use SilverligthSerializer by Mike Talbot which - binary - serializes about everything. Very cool. I'd recommend using it.
  • Gravatar Edo February 3rd, 2011 at 13:26
    Thanks Mark, really neat solution!

    I had only one problem (really hard to find out): when calling
    channel.BindToShellTile(new Collection { imageUri });
    it threw InvalidOperationException(Notification server temporary unavailable)
    and subsequent calls to
    HttpNotificationChannel channel = HttpNotificationChannel.Find("ChannelName");
    always threw InvalidOperationException(Notification server temporary unavailable)

    After a lot of debug, I found out that the problem was the length of Uri (including http://... ).
    If it's shorter than 130 chars all is ok, if longer it keeps on throwing exceptions.

    For what I've seen, it's enough to use the host in the Uri, so replacing
    channel.BindToShellTile(new Collection { imageUri });
    with
    channel.BindToShellTile(new Collection { (new UriBuilder(imageUri.Scheme, imageUri.Host, imageUri.Port)).Uri });
    solved the problem.

    Thanks again!
  • Gravatar Doug Rathbone March 4th, 2011 at 08:34
    I am having the same problem as Edo,

    except mine is actually failing all the way through as my Uri is too long for the tile. i don't have a way around this as i'm actually passing token values in the Uri and they are around 200 chars long - unless i store these for my users (bad security model) it won't work

    anyone else run into this?

    cheers
    Doug
  • Gravatar Manfred Dalmeijer June 13th, 2011 at 16:18
    This solution works very well in my Windows Phone 7 apps.
    There is one issue though: HttpNotificationChannel methods and the ShellTileSchedule methods can raise an InvalidOperationException (see http://msdn.microsoft.com/en-us/library/microsoft.phone.shell.shelltileschedule.start (VS.92).aspx).
    Since these exceptions occur very infrequently and my apps set the schedule every time they run, I chose a lazy solution: catch them with an empty (apart from logging) handler.
  • Gravatar abc June 27th, 2011 at 07:41
    can i get the code sample please
  • Gravatar jeremy scott May 13th, 2013 at 16:05
    Hello, I think your site might be having browser compatibility issues. When I look at your blog site in Ie, it looks fine but when opening in Internet Explorer, it has some overlapping. I just wanted to give you a quick heads up! Other then that, superb blog! jeremy scott http://www.adidasyasuyi.com/jeremy-scott【ジェレミースコット】-c-11.html
  • Gravatar アディダス ランニング May 13th, 2013 at 16:05
    Hello! Do you know if they make any plugins to help with SEO? I'm trying to get my blog to rank for some targeted keywords but I'm not seeing very good results. If you know of any please share. Thank you! アディダス ランニング http://www.adidasyasuyi.com/adidas-アディダス-ランニングシューズ-c-3.html
  • Gravatar アディダス スニーカー May 13th, 2013 at 16:05
    Heya! I'm at work browsing your blog from my new iphone! Just wanted to say I love reading through your blog and look forward to all your posts! Keep up the fantastic work! アディダス スニーカー http://www.adidasyasuyi.com/adidas-アディダス-スニーカー-c-7.html
  • Gravatar ミズノ グローブ May 13th, 2013 at 17:27
    Do you have a spam problem on this blog; I also am a blogger, and I was wondering your situation; we have developed some nice practices and we are looking to exchange methods with others, please shoot me an e-mail if interested. ミズノ グローブ http://www.mizunogoruhujp.biz/ミズノ-グローブ-c-15.html
  • Gravatar ジェレミースコット May 13th, 2013 at 18:20
    Hey there! I know this is kinda off topic but I was wondering which blog platform are you using for this website? I'm getting tired of Wordpress because I've had problems with hackers and I'm looking at options for another platform. I would be great if you could point me in the direction of a good platform. ジェレミースコット http://www.adidasyasui.com/jeremy-scott【ジェレミースコット】-c-31.html
  • Gravatar mcm リュック May 16th, 2013 at 05:04
    Currently it looks like BlogEngine is the best blogging platform available right now. (from what I've read) Is that what you are using on your blog? mcm リュック http://www.mcmjp.net/mcm-リュック-c-248.html
  • Gravatar mcm 通販 May 16th, 2013 at 05:04
    Wonderful blog! I found it while surfing around on Yahoo News. Do you have any tips on how to get listed in Yahoo News? I've been trying for a while but I never seem to get there! Many thanks mcm 通販 http://www.mcmjp.net/mcm-バッグ5月入荷-c-255.html
  • Gravatar mcm 激安 May 16th, 2013 at 05:04
    Howdy! Someone in my Myspace group shared this site with us so I came to take a look. I'm definitely enjoying the information. I'm book-marking and will be tweeting this to my followers! Exceptional blog and outstanding design and style. mcm 激安 http://www.mcmjp.net/mcm-財布5月入荷-c-257.html
  • Gravatar リュック 通販 May 16th, 2013 at 05:47
    Hello there! This is kind of off topic but I need some advice from an established blog. Is it hard to set up your own blog? I'm not very techincal but I can figure things out pretty fast. I'm thinking about creating my own but I'm not sure where to start. Do you have any points or suggestions? Many thanks リュック 通販 http://www.mcm-shop.net/mcm-リュック-c-253.html
  • Gravatar MCM手帳 May 16th, 2013 at 05:47
    I love your blog.. very nice colors & theme. Did you design this website yourself or did you hire someone to do it for you? Plz answer back as I'm looking to construct my own blog and would like to know where u got this from. cheers MCM手帳 http://www.mcm-shop.net/mcm-その他-c-248.html
  • Gravatar MCM バッグ通贩 May 16th, 2013 at 05:47
    Hi there! Do you use Twitter? I'd like to follow you if that would be ok. I'm definitely enjoying your blog and look forward to new updates. MCM バッグ通贩 http://www.mcm-shop.net/mcm-バッグ-c-251.html
  • Gravatar MCM リュック May 16th, 2013 at 05:47
    Great post however , I was wondering if you could write a litte more on this subject? I'd be very thankful if you could elaborate a little bit more. Thanks! MCM リュック http://www.mcm-shop.net/mcm-リュック-c-253.html
  • Gravatar キーリング メンズ May 16th, 2013 at 05:47
    Hey there! I've been reading your blog for a while now and finally got the bravery to go ahead and give you a shout out from Dallas Texas! Just wanted to say keep up the great job! キーリング メンズ http://www.mcm-shop.net/mcm-キーリング-c-249.html
  • Gravatar 財布 人気 May 16th, 2013 at 05:47
    Good day! I could have sworn I've been to this site before but after checking through some of the post I realized it's new to me. Anyways, I'm definitely glad I found it and I'll be bookmarking and checking back often! 財布 人気 http://www.mcm-shop.net/mcm-財布-c-246.html
  • Gravatar MCM キーケース メンズ May 16th, 2013 at 05:48
    This design is spectacular! You obviously know how to keep a reader amused. Between your wit and your videos, I was almost moved to start my own blog (well, almost...HaHa!) Fantastic job. I really loved what you had to say, and more than that, how you presented it. Too cool! MCM キーケース メンズ http://www.mcm-shop.net/mcm-キーケース-c-245.html
  • Gravatar コンバース 靴 May 16th, 2013 at 20:46
    Hi there would you mind sharing which blog platform you're using? I'm going to start my own blog soon but I'm having a tough time deciding between BlogEngine/Wordpress/B2evolution and Drupal. The reason I ask is because your layout seems different then most blogs and I'm looking for something unique. P.S My apologies for being off-topic but I had to ask! コンバース 靴 http://www.kutuyasuyi.com/converseコンバース-c-23.html
  • Gravatar プーマ 靴 May 16th, 2013 at 20:46
    Hmm is anyone else encountering problems with the pictures on this blog loading? I'm trying to find out if its a problem on my end or if it's the blog. Any feedback would be greatly appreciated. プーマ 靴 http://www.kutuyasuyi.com/pumaプーマ-c-19.html
  • Gravatar puma ランニング May 16th, 2013 at 20:46
    Have you ever considered publishing an e-book or guest authoring on other sites? I have a blog based upon on the same ideas you discuss and would really like to have you share some stories/information. I know my readers would enjoy your work. If you're even remotely interested, feel free to send me an e-mail. puma ランニング http://www.kutuyasuyi.com/pumaプーマ-c-19.html
  • Gravatar ナイキ フリー May 16th, 2013 at 20:47
    Hi there just wanted to give you a quick heads up. The text in your content seem to be running off the screen in Chrome. I'm not sure if this is a format issue or something to do with browser compatibility but I figured I'd post to let you know. The design and style look great though! Hope you get the problem fixed soon. Thanks ナイキ フリー http://www.kutuyasuyi.com/nikeナイキ-c-9.html
  • Gravatar コンバース 靴 May 16th, 2013 at 20:47
    Today, I went to the beach with my kids. I found a sea shell and gave it to my 4 year old daughter and said "You can hear the ocean if you put this to your ear." She placed the shell to her ear and screamed. There was a hermit crab inside and it pinched her ear. She never wants to go back! LoL I know this is totally off topic but I had to tell someone! コンバース 靴 http://www.kutuyasuyi.com/converseコンバース-c-23.html
  • Gravatar ブライトリング May 16th, 2013 at 21:09
    When I initially commented I clicked the "Notify me when new comments are added" checkbox and now each time a comment is added I get several emails with the same comment. Is there any way you can remove people from that service? Thank you! ブライトリング http://www.watchesjapan.biz/メンズ-腕時計-c-3.html
  • Gravatar 時計 レディース May 16th, 2013 at 21:09
    I am curious to find out what blog system you are working with? I'm experiencing some small security problems with my latest blog and I'd like to find something more risk-free. Do you have any recommendations? 時計 レディース http://www.watchesjapan.biz/レディース-腕時計-c-5.html
  • Gravatar シチズン May 16th, 2013 at 21:09
    Woah! I'm really loving the template/theme of this blog. It's simple, yet effective. A lot of times it's very hard to get that "perfect balance" between superb usability and appearance. I must say that you've done a great job with this. Additionally, the blog loads very quick for me on Internet explorer. Outstanding Blog! シチズン http://www.watchesjapan.biz/キッズ-腕時計-c-279.html
Gravatar