New App: Chrync – syncs Chrome bookmarks and more to Windows Phone
Chrync syncs your Chrome bookmarks, recently viewed pages and open tabs down to your Windows Phone.
Click here for a short video of it in action.
You don’t have to install any special Chrome add-in for it to work, and the only servers Chrync talks to are Google’s own servers, to authenticate and then to sync.
The way I’ve implemented Chrync is that it talks to Google’s servers using the native Chrome sync protocol, exactly like Chrome itself does.
I’m charging US$0.99, but there is an unlimited trial – I’m fine for people to use it on a trial basis for as long as they want … The app is available from the marketplace here.
Give it a go and let me know what you think at this User Voice site.
Nokia showcasing Evernote for Windows Phone
I'll not be giving up my day job, but it was fun doing these interviews ...
Presentation at the Evernote Trunk Conference on developing the Evernote Windows Phone client
Elephants love mangoes – a look behind our Windows Phone 7 client
I wrote this article for the Evernote Tech Blog last month, summarizing my experience developing the Windows Phone Evernote client: http://blog.evernote.com/tech/2011/06/28/look-behind-evernote-wp7/
GoBackAction–a Windows Phone Behavior to navigate back
Behaviors are a great way to encapsulate functionality that can be used in a drag-and-drop environment such as Expression Blend.
Out of the box you get a NavigateToPageAction, but I couldn’t see an easy way to navigate backwards to the previous page using any of the built-in behaviors. I wanted to let the user click on an item in a list, and then have the Phone automatically navigate to the previous page.
So here it is, the world’s simplest behavior:
[DefaultTrigger(typeof (UIElement), typeof (System.Windows.Interactivity.EventTrigger), "MouseLeftButtonDown")]
public class GoBackAction : TriggerAction<FrameworkElement>
{
protected override void Invoke(object parameter)
{
var rootFrame = Application.Current.RootVisual as PhoneApplicationFrame;
if(rootFrame !=null)
{
rootFrame.GoBack();
}
}
}
Once you’ve built this class in your project, if you open your project in Expression Blend you should see the GoBackAction listed in the Behaviors section of the Assets tab. You can then drag the behavior onto a UI element, define the event that should trigger the action, and then when the event occurs you should be automatically navigated backwards.
Adapting Fluid List Animation to work with the LongListSelector
Colin Eberhardt at ScottLogic has been doing some excellent work showing how we can make our own UIs animate in a fluid manner similar to the built in Microsoft UIs on Windows Phone.
His Fluid List Animation is particularly neat, and I thought I’d apply it to the UI I’m developing, specifically on a couple of LongListSelectors that I embed in Pivots.
Unfortunately his code didn’t work on the LongListSelector out of the box because although Colin went out of his way to make it work with any ItemsControl, the LongListSelector is not an ItemsControl. Fortunately his code was well commented, and it was trivial to modify it to work with the LongListSelector.
All the changes are in the OnIsPivotAnimatedChanged method in the ListAnimation class.
{
// ItemsControl list = d as ItemsControl;
LongListSelector list = d as LongListSelector;
Next find the section that starts with the comment “locate the stack panel that hosts the items” and replace the code that follows it (up to and including the for loop) with:
foreach (ContentPresenter item in itemsInView)
{
var localItem = item;
list.Dispatcher.BeginInvoke(() =>
{
var animationTargets = localItem.Descendants().Where(p => GetAnimationLevel(p) > -1);
foreach (FrameworkElement target in animationTargets)
{
// trigger the required animation
GetAnimation(target, fromRight).Begin();
}
});
}
The key is the call to GetItemsWithContainers – the first parameter indicates we only want items that are in view, and the second indicates we want the containers (hence the ContentPresenter type in the foreach).
That’s it – Colin did all the hard work – I just adapted it – all kudos to him.
So you think you’ve finished implementing that new Windows Phone 7 page?
So you think you’ve finished implementing that new Windows Phone 7 page?
Have you checked that you:
- are using resources for all user-visible strings?
- have made sure the page handles rotation properly?
- handle the Enter key on each TextBox to move to the next TextBox, or submit the page?
- have set the InputScope on each TextBox?
- handle tombstoning properly?
- have set up Navigation transitions?
- have tested with both the Light and Dark Themes?
- are using the built-in resources for text styles?
- override the Back button press if you are displaying popups?
What have I missed?
Storing WP7 recorded audio as WAV format streams
The Microsoft audio recording example shows how to record audio, but it just gets a PCM encoded stream of samples – if you want to send this audio stream off somewhere else, to be played later, it needs a proper header.
Below I show you how to use a couple of methods I’ve written (WriteWavHeader and UpdateWavHeader), and I include their implementation.
Assuming you start with the Microsoft sample, when you initiate the audio recording you initialize a memory stream into which the samples will be written:
{
_microphone.BufferDuration = TimeSpan.FromMilliseconds(500);
// Allocate memory to hold the audio data
_recordingBuffer = new byte[_microphone.GetSampleSizeInBytes(_microphone.BufferDuration)];
_recordingStream.SetLength(0);
WriteWavHeader(_recordingStream, _microphone.SampleRate);
_microphone.Start();
}
Note the call to WriteWavHeader to write the wav header to the start of the audio stream.
When the recording finishes, you update the header (because we need to fill in fields based on the data length):
{
_microphone.Stop();
UpdateWavHeader(_recordingStream);
}
Here are the implementations of the WriteWavHeader and UpdateWavHeader methods. The comments come from this web page describing the wav header file format.
{
const int bitsPerSample = 16;
const int bytesPerSample = bitsPerSample / 8;
var encoding = System.Text.Encoding.UTF8;
// ChunkID Contains the letters "RIFF" in ASCII form (0x52494646 big-endian form).
stream.Write(encoding.GetBytes("RIFF"), 0, 4);
// NOTE this will be filled in later
stream.Write(BitConverter.GetBytes(0), 0, 4);
// Format Contains the letters "WAVE"(0x57415645 big-endian form).
stream.Write(encoding.GetBytes("WAVE"), 0, 4);
// Subchunk1ID Contains the letters "fmt " (0x666d7420 big-endian form).
stream.Write(encoding.GetBytes("fmt "), 0, 4);
// Subchunk1Size 16 for PCM. This is the size of therest of the Subchunk which follows this number.
stream.Write(BitConverter.GetBytes(16), 0, 4);
// AudioFormat PCM = 1 (i.e. Linear quantization) Values other than 1 indicate some form of compression.
stream.Write(BitConverter.GetBytes((short)1), 0, 2);
// NumChannels Mono = 1, Stereo = 2, etc.
stream.Write(BitConverter.GetBytes((short)1), 0, 2);
// SampleRate 8000, 44100, etc.
stream.Write(BitConverter.GetBytes(sampleRate), 0, 4);
// ByteRate = SampleRate * NumChannels * BitsPerSample/8
stream.Write(BitConverter.GetBytes(sampleRate * bytesPerSample), 0, 4);
// BlockAlign NumChannels * BitsPerSample/8 The number of bytes for one sample including all channels.
stream.Write(BitConverter.GetBytes((short)(bytesPerSample)), 0, 2);
// BitsPerSample 8 bits = 8, 16 bits = 16, etc.
stream.Write(BitConverter.GetBytes((short)(bitsPerSample)), 0, 2);
// Subchunk2ID Contains the letters "data" (0x64617461 big-endian form).
stream.Write(encoding.GetBytes("data"), 0, 4);
// NOTE to be filled in later
stream.Write(BitConverter.GetBytes(0), 0, 4);
}
public void UpdateWavHeader(Stream stream)
{
if (!stream.CanSeek) throw new Exception("Can't seek stream to update wav header");
var oldPos = stream.Position;
// ChunkSize 36 + SubChunk2Size
stream.Seek(4, SeekOrigin.Begin);
stream.Write(BitConverter.GetBytes((int)stream.Length - 8), 0, 4);
// Subchunk2Size == NumSamples * NumChannels * BitsPerSample/8 This is the number of bytes in the data.
stream.Seek(40, SeekOrigin.Begin);
stream.Write(BitConverter.GetBytes((int)stream.Length - 44), 0, 4);
stream.Seek(oldPos, SeekOrigin.Begin);
}
Windows Phone 7 Watermark on PasswordBox
For my textboxes I’ve been using a modified version Johan Danforth’s Watermark behaviour (binding the watermark text to a ViewModel property instead of hardcoding the text), but when I needed to do something similar for a PasswordBox I thought I was stuck.
I’ve come up with an incredibly basic, simple solution that seems to work well. Instead of just having a PasswordBox, I also have a TextBox behind it which I make invisible when the PasswordBox has content, or has focus. Because the watermark TextBox is behind the PasswordBox, it never gets focus: it is purely visual.
<TextBox x:Name="PasswordWatermark" TextWrapping="Wrap" Text="{Binding Labels.password, Mode=OneTime}" Foreground="{StaticResource PhoneTextBoxReadOnlyBrush}" IsHitTestVisible="False"/>
<PasswordBox x:Name="Password" LostFocus="PasswordLostFocus" Opacity="0" GotFocus="PasswordGotFocus" Password="{Binding Password, Mode=TwoWay}"/>
</Grid>
In the code-behind I hide/show the watermark TextBox and the PasswordBox as appropriate. Note that I show and hide them by changing their opacity, not by setting their visibility.
Because the PasswordBox is in front of the the watermark Textbox, when the user clicks in that area he always clicks on the PasswordBox, even if it is invisible.
- private void PasswordLostFocus(object sender, RoutedEventArgs e)
- {
- CheckPasswordWatermark();
- }
- public void CheckPasswordWatermark()
- {
- var passwordEmpty = string.IsNullOrEmpty(Password.Password);
- PasswordWatermark.Opacity = passwordEmpty ? 100 : 0;
- Password.Opacity = passwordEmpty ? 0 : 100;
- }
- private void PasswordGotFocus(object sender, RoutedEventArgs e)
- {
- PasswordWatermark.Opacity = 0;
- Password.Opacity = 100;
- }
When populating the PasswordBox, you can call CheckPasswordWatermark() to show/hide the watermark as appropriate.