diff --git a/WPinternals/Models/NokiaFlashModel.cs b/WPinternals/Models/NokiaFlashModel.cs index b8c3561..48c53ed 100644 --- a/WPinternals/Models/NokiaFlashModel.cs +++ b/WPinternals/Models/NokiaFlashModel.cs @@ -952,8 +952,11 @@ namespace WPinternals case 0x01: Result.TransferSize = BigEndian.ToUInt32(Response, SubblockPayloadOffset); break; - case 0x1F: - Result.MmosOverUsbSupported = Response[SubblockPayloadOffset] == 1; + case 0x02: + Result.WriteBufferSize = BigEndian.ToUInt32(Response, SubblockPayloadOffset); + break; + case 0x03: + Result.EmmcSizeInSectors = BigEndian.ToUInt32(Response, SubblockPayloadOffset); break; case 0x04: if (Result.App == FlashAppType.BootManager) @@ -968,12 +971,6 @@ namespace WPinternals Result.SdCardSizeInSectors = BigEndian.ToUInt32(Response, SubblockPayloadOffset); } break; - case 0x02: - Result.WriteBufferSize = BigEndian.ToUInt32(Response, SubblockPayloadOffset); - break; - case 0x03: - Result.EmmcSizeInSectors = BigEndian.ToUInt32(Response, SubblockPayloadOffset); - break; case 0x05: Result.PlatformID = ByteOperations.ReadAsciiString(Response, (uint)SubblockPayloadOffset, SubblockLength).Trim([' ', '\0']); break; @@ -994,6 +991,9 @@ namespace WPinternals SubblockVersion = Response[SubblockPayloadOffset]; // 0x01 Result.SecureFfuSupportedProtocolMask = BigEndian.ToUInt16(Response, SubblockPayloadOffset + 0x01); break; + case 0x1F: + Result.MmosOverUsbSupported = Response[SubblockPayloadOffset] == 1; + break; case 0x20: // CRC header info break; @@ -1154,7 +1154,7 @@ namespace WPinternals //DisableRebootTimeOut(); Info.App = FlashAppType.PhoneInfoApp; - InterfaceChanged(PhoneInterfaces.Lumia_Bootloader); + InterfaceChanged(PhoneInterfaces.Lumia_PhoneInfo); } internal void SwitchAwayToPhoneInfoAppContextLegacy() @@ -1164,8 +1164,33 @@ namespace WPinternals ExecuteRawVoidMethod(Request); //DisableRebootTimeOut(); - //Info.App = FlashAppType.FlashApp; - //InterfaceChanged(PhoneInterfaces.Lumia_Flash); + Info.App = FlashAppType.FlashApp; + InterfaceChanged(PhoneInterfaces.Lumia_Flash); + } + + internal string GetPhoneInfo() + { + // NOKH = Get Phone Info (IMEI and info from Product.dat) - Not available on some phones, like Lumia 640. + // NOKV = Info Query + + if (Info.FlashAppProtocolVersionMajor >= 2) + { + return null; + } + + byte[] Request = new byte[4]; + ByteOperations.WriteAsciiString(Request, 0, "NOKH"); + byte[] Response = ExecuteRawMethod(Request); + if ((Response == null) || (ByteOperations.ReadAsciiString(Response, 0, 4) == "NOKU")) + { + throw new NotSupportedException(); + } + + UInt16 Length = BigEndian.ToUInt16(Response, 0x04); + + string PhoneInfoData = ByteOperations.ReadAsciiString(Response, 0x8, Length); + + return PhoneInfoData; } internal void SwitchToFlashAppContext() diff --git a/WPinternals/ViewModels/LumiaInfoViewModel.cs b/WPinternals/ViewModels/LumiaInfoViewModel.cs index b0db205..f3bf963 100644 --- a/WPinternals/ViewModels/LumiaInfoViewModel.cs +++ b/WPinternals/ViewModels/LumiaInfoViewModel.cs @@ -70,6 +70,9 @@ namespace WPinternals ActivateSubContext(null); //ActivateSubContext(new NokiaBootloaderViewModel((NokiaFlashModel)CurrentModel, ModeSwitchRequestCallback, SwitchToGettingStarted)); break; + case PhoneInterfaces.Lumia_PhoneInfo: + ActivateSubContext(new NokiaPhoneInfoViewModel((NokiaFlashModel)CurrentModel, ModeSwitchRequestCallback, SwitchToGettingStarted)); + break; case PhoneInterfaces.Lumia_Normal: ActivateSubContext(new NokiaNormalViewModel((NokiaPhoneModel)CurrentModel, ModeSwitchRequestCallback)); break; diff --git a/WPinternals/ViewModels/LumiaModeViewModel.cs b/WPinternals/ViewModels/LumiaModeViewModel.cs index fe4b035..8c0e69c 100644 --- a/WPinternals/ViewModels/LumiaModeViewModel.cs +++ b/WPinternals/ViewModels/LumiaModeViewModel.cs @@ -90,6 +90,9 @@ namespace WPinternals ActivateSubContext(null); //ActivateSubContext(new NokiaModeBootloaderViewModel((NokiaFlashModel)CurrentModel, OnModeSwitchRequested)); break; + case PhoneInterfaces.Lumia_PhoneInfo: + ActivateSubContext(new NokiaModePhoneInfoViewModel((NokiaFlashModel)CurrentModel, OnModeSwitchRequested)); + break; case PhoneInterfaces.Lumia_Normal: ActivateSubContext(new NokiaModeNormalViewModel((NokiaPhoneModel)CurrentModel, OnModeSwitchRequested)); break; diff --git a/WPinternals/ViewModels/MainViewModel.cs b/WPinternals/ViewModels/MainViewModel.cs index c643abf..9c8c1aa 100644 --- a/WPinternals/ViewModels/MainViewModel.cs +++ b/WPinternals/ViewModels/MainViewModel.cs @@ -45,7 +45,8 @@ namespace WPinternals Lumia_Bootloader, Qualcomm_Download, Qualcomm_Flash, - Lumia_BadMassStorage + Lumia_BadMassStorage, + Lumia_PhoneInfo }; // Create this class on the UI thread, after the main-window of the application is initialized. diff --git a/WPinternals/ViewModels/NokiaLabelViewModel.cs b/WPinternals/ViewModels/NokiaLabelViewModel.cs index 1a8a457..9fd127a 100644 --- a/WPinternals/ViewModels/NokiaLabelViewModel.cs +++ b/WPinternals/ViewModels/NokiaLabelViewModel.cs @@ -101,11 +101,20 @@ namespace WPinternals IMEI = CurrentModel.ExecuteJsonMethodAsString("ReadSerialNumber", "SerialNumber"); // IMEI LogFile.Log("IMEI: " + IMEI); BluetoothMac = CurrentModel.ExecuteJsonMethodAsBytes("ReadBtId", "BtId"); // 6 bytes: bc c6 ... - LogFile.Log("Bluetooth MAC: " + Converter.ConvertHexToString(BluetoothMac, " ")); - WlanMac = CurrentModel.ExecuteJsonMethodAsBytes("ReadWlanMacAddress", "WlanMacAddress1"); // 6 bytes - LogFile.Log("WLAN MAC: " + Converter.ConvertHexToString(WlanMac, " ")); - IsBootloaderSecurityEnabled = (bool)CurrentModel.ExecuteJsonMethodAsBoolean("ReadProductionDoneState", "ProductionDone"); + if (BluetoothMac != null) + { + LogFile.Log("Bluetooth MAC: " + Converter.ConvertHexToString(BluetoothMac, " ")); + } + + WlanMac = CurrentModel.ExecuteJsonMethodAsBytes("ReadWlanMacAddress", "WlanMacAddress1"); // 6 bytes + + if (WlanMac != null) + { + LogFile.Log("WLAN MAC: " + Converter.ConvertHexToString(WlanMac, " ")); + } + + IsBootloaderSecurityEnabled = CurrentModel.ExecuteJsonMethodAsBoolean("ReadProductionDoneState", "ProductionDone") ?? false; LogFile.Log("Bootloader Security: " + ((bool)IsBootloaderSecurityEnabled ? "Enabled" : "Disabled")); Params = new Dictionary diff --git a/WPinternals/ViewModels/NokiaModePhoneInfoViewModel.cs b/WPinternals/ViewModels/NokiaModePhoneInfoViewModel.cs new file mode 100644 index 0000000..fb44bb4 --- /dev/null +++ b/WPinternals/ViewModels/NokiaModePhoneInfoViewModel.cs @@ -0,0 +1,121 @@ +// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Threading; + +namespace WPinternals +{ + internal class NokiaModePhoneInfoViewModel : ContextViewModel + { + private readonly NokiaFlashModel CurrentModel; + private readonly Action RequestModeSwitch; + private readonly object LockDeviceInfo = new(); + private bool DeviceInfoLoaded = false; + + internal NokiaModePhoneInfoViewModel(NokiaPhoneModel CurrentModel, Action RequestModeSwitch) + : base() + { + this.CurrentModel = (NokiaFlashModel)CurrentModel; + this.RequestModeSwitch = RequestModeSwitch; + } + + internal override void EvaluateViewState() + { + if (IsActive) + { + new Thread(() => StartLoadDeviceInfo()).Start(); + } + } + + private bool? _EffectivePhoneInfoSecurityStatus = null; + public bool? EffectivePhoneInfoSecurityStatus + { + get + { + return _EffectivePhoneInfoSecurityStatus; + } + set + { + _EffectivePhoneInfoSecurityStatus = value; + OnPropertyChanged(nameof(EffectivePhoneInfoSecurityStatus)); + } + } + + internal void StartLoadDeviceInfo() + { + lock (LockDeviceInfo) + { + if (!DeviceInfoLoaded) + { + try + { + PhoneInfo Info = CurrentModel.ReadPhoneInfo(); + + if (Info.FlashAppProtocolVersionMajor < 2) + { + UefiSecurityStatusResponse SecurityStatus = CurrentModel.ReadSecurityStatus(); + + if (SecurityStatus != null) + { + EffectivePhoneInfoSecurityStatus = SecurityStatus.SecureFfuEfuseStatus && !SecurityStatus.AuthenticationStatus && !SecurityStatus.RdcStatus; + } + } + else + { + EffectivePhoneInfoSecurityStatus = Info.UefiSecureBootEnabled; + } + + LogFile.Log("Effective PhoneInfo Security Status: " + EffectivePhoneInfoSecurityStatus.ToString()); + } + catch + { + LogFile.Log("Reading status from Flash interface was aborted."); + } + DeviceInfoLoaded = true; + } + } + } + + internal void RebootTo(string Mode) + { + switch (Mode) + { + case "Normal": + RequestModeSwitch(PhoneInterfaces.Lumia_Normal); + break; + case "Flash": + RequestModeSwitch(PhoneInterfaces.Lumia_Flash); + break; + case "Label": + RequestModeSwitch(PhoneInterfaces.Lumia_Label); + break; + case "MassStorage": + RequestModeSwitch(PhoneInterfaces.Lumia_MassStorage); + break; + case "Shutdown": + RequestModeSwitch(null); + break; + default: + return; + } + } + } +} diff --git a/WPinternals/ViewModels/NokiaPhoneInfoViewModel.cs b/WPinternals/ViewModels/NokiaPhoneInfoViewModel.cs new file mode 100644 index 0000000..4c9a1a6 --- /dev/null +++ b/WPinternals/ViewModels/NokiaPhoneInfoViewModel.cs @@ -0,0 +1,206 @@ +// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +namespace WPinternals +{ + // Create this class on the UI thread, after the main-window of the application is initialized. + // It is necessary to create the object on the UI thread, because notification events to the View need to be fired on that thread. + // The Model for this ViewModel communicates over USB and for that it uses the hWnd of the main window. + // Therefore the main window must be created before the ViewModel is created. + + internal class NokiaPhoneInfoViewModel : ContextViewModel + { + private readonly NokiaFlashModel CurrentModel; + private readonly Action RequestModeSwitch; + internal Action SwitchToGettingStarted; + private readonly object LockDeviceInfo = new(); + private bool DeviceInfoLoaded = false; + + internal NokiaPhoneInfoViewModel(NokiaPhoneModel CurrentModel, Action RequestModeSwitch, Action SwitchToGettingStarted) + : base() + { + this.CurrentModel = (NokiaFlashModel)CurrentModel; + this.RequestModeSwitch = RequestModeSwitch; + this.SwitchToGettingStarted = SwitchToGettingStarted; + } + + // Device info should be loaded only one time and only when the ViewModel is active + internal override void EvaluateViewState() + { + if (IsActive) + { + new Thread(() => StartLoadDeviceInfo()).Start(); + } + } + + private void StartLoadDeviceInfo() + { + lock (LockDeviceInfo) + { + if (!DeviceInfoLoaded) + { + try + { + /* + * Version: 1.1.1.3 + * TYPE: RM-885 + * BTR: 059R0M0 + * LPSN: ... + * HWID: 1000 + * CTR: 059S4B1 + * MC: 0205354 + * IMEI: ... + */ + string PhoneInfoData = CurrentModel.GetPhoneInfo(); + if (!string.IsNullOrEmpty(PhoneInfoData)) + { + string[] Variables = PhoneInfoData.Split("\n"); + Dictionary FormattedVariables = []; + foreach (string Variable in Variables) + { + if (!Variable.Contains(":")) + { + continue; + } + + FormattedVariables.Add(Variable.Split(":")[0].Trim(), Variable.Split(":")[1].Trim()); + } + + HWID = FormattedVariables["HWID"]; + LogFile.Log("HWID: " + HWID); + } + + PhoneInfo Info = CurrentModel.ReadPhoneInfo(true); + BootloaderDescription = Info.FlashAppProtocolVersionMajor < 2 ? "Lumia Bootloader Spec A" : "Lumia Bootloader Spec B"; + + LogFile.Log("Bootloader: " + BootloaderDescription); + + ProductCode = Info.ProductCode; + LogFile.Log("ProductCode: " + ProductCode); + + ProductType = Info.Type; + LogFile.Log("ProductType: " + ProductType); + + IMEI = Info.Imei; + LogFile.Log("IMEI: " + ProductType); + } + catch + { + LogFile.Log("Reading status from Flash interface was aborted."); + } + DeviceInfoLoaded = true; + } + } + } + + private string _ProductType = null; + public string ProductType + { + get + { + return _ProductType; + } + set + { + _ProductType = value; + OnPropertyChanged(nameof(ProductType)); + } + } + + private string _ProductCode = null; + public string ProductCode + { + get + { + return _ProductCode; + } + set + { + _ProductCode = value; + OnPropertyChanged(nameof(ProductCode)); + } + } + + private string _BootloaderDescription = null; + public string BootloaderDescription + { + get + { + return _BootloaderDescription; + } + set + { + _BootloaderDescription = value; + OnPropertyChanged(nameof(BootloaderDescription)); + } + } + + private string _HWID = null; + public string HWID + { + get + { + return _HWID; + } + set + { + _HWID = value; + OnPropertyChanged(nameof(HWID)); + } + } + + private string _IMEI = null; + public string IMEI + { + get + { + return _IMEI; + } + set + { + _IMEI = value; + OnPropertyChanged(nameof(IMEI)); + } + } + + internal void RebootTo(string Mode) + { + switch (Mode) + { + case "Normal": + RequestModeSwitch(PhoneInterfaces.Lumia_Normal); + break; + case "Label": + RequestModeSwitch(PhoneInterfaces.Lumia_Label); + break; + case "MassStorage": + RequestModeSwitch(PhoneInterfaces.Lumia_MassStorage); + break; + default: + return; + } + } + } +} diff --git a/WPinternals/ViewModels/PhoneNotifierViewModel.cs b/WPinternals/ViewModels/PhoneNotifierViewModel.cs index d375744..906e92f 100644 --- a/WPinternals/ViewModels/PhoneNotifierViewModel.cs +++ b/WPinternals/ViewModels/PhoneNotifierViewModel.cs @@ -276,7 +276,8 @@ namespace WPinternals } case FlashAppType.PhoneInfoApp: { - CurrentInterface = PhoneInterfaces.Lumia_Bootloader; + ((NokiaFlashModel)CurrentModel).DisableRebootTimeOut(); + CurrentInterface = PhoneInterfaces.Lumia_PhoneInfo; LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly); LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly); LogFile.Log("Connected device: Lumia", LogType.FileAndConsole); diff --git a/WPinternals/Views/MainWindow.xaml b/WPinternals/Views/MainWindow.xaml index 9d7b499..298ecda 100644 --- a/WPinternals/Views/MainWindow.xaml +++ b/WPinternals/Views/MainWindow.xaml @@ -37,6 +37,9 @@ DEALINGS IN THE SOFTWARE. + + + @@ -58,6 +61,9 @@ DEALINGS IN THE SOFTWARE. + + + diff --git a/WPinternals/Views/NokiaModePhoneInfoView.xaml b/WPinternals/Views/NokiaModePhoneInfoView.xaml new file mode 100644 index 0000000..22714ce --- /dev/null +++ b/WPinternals/Views/NokiaModePhoneInfoView.xaml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Switch to Normal-mode + + + + + Switch to Label-mode + + + + + Switch to Mass-Storage-mode + + + + + + + Shutdown the phone + + + + + + + + + + + + + + + + + + + diff --git a/WPinternals/Views/NokiaModePhoneInfoView.xaml.cs b/WPinternals/Views/NokiaModePhoneInfoView.xaml.cs new file mode 100644 index 0000000..e4a5739 --- /dev/null +++ b/WPinternals/Views/NokiaModePhoneInfoView.xaml.cs @@ -0,0 +1,50 @@ +// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; + +namespace WPinternals +{ + /// + /// Interaction logic for NokiaModePhoneInfoView.xaml + /// + public partial class NokiaModePhoneInfoView : UserControl + { + public NokiaModePhoneInfoView() + { + InitializeComponent(); + } + + private void HandleHyperlinkClick(object sender, RoutedEventArgs args) + { + if (args.Source is Hyperlink link) + { + (this.DataContext as NokiaModePhoneInfoViewModel)?.RebootTo(link.NavigateUri.ToString()); + } + } + + private void Document_Loaded(object sender, RoutedEventArgs e) + { + (sender as FlowDocument)?.AddHandler(Hyperlink.ClickEvent, new RoutedEventHandler(HandleHyperlinkClick)); + } + } +} diff --git a/WPinternals/Views/NokiaPhoneInfoView.xaml b/WPinternals/Views/NokiaPhoneInfoView.xaml new file mode 100644 index 0000000..3d1506f --- /dev/null +++ b/WPinternals/Views/NokiaPhoneInfoView.xaml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Normal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WPinternals/Views/NokiaPhoneInfoView.xaml.cs b/WPinternals/Views/NokiaPhoneInfoView.xaml.cs new file mode 100644 index 0000000..aa9ea59 --- /dev/null +++ b/WPinternals/Views/NokiaPhoneInfoView.xaml.cs @@ -0,0 +1,58 @@ +// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; + +namespace WPinternals +{ + /// + /// Interaction logic for NokiaPhoneInfoView.xaml + /// + public partial class NokiaPhoneInfoView : UserControl + { + public NokiaPhoneInfoView() + { + InitializeComponent(); + + // Setting these properties in XAML results in an error. Why? + GifImage.GifSource = "/aerobusy.gif"; + GifImage.AutoStart = true; + } + + private void HandleHyperlinkClick(object sender, RoutedEventArgs args) + { + Hyperlink link = args.Source as Hyperlink; + if (link?.NavigateUri != null) + { + if (link.NavigateUri.ToString() == "GettingStarted") + { + (this.DataContext as NokiaPhoneInfoViewModel)?.SwitchToGettingStarted(); + } (this.DataContext as NokiaPhoneInfoViewModel)?.RebootTo(link.NavigateUri.ToString()); + } + } + + private void Document_Loaded(object sender, RoutedEventArgs e) + { + (sender as FlowDocument)?.AddHandler(Hyperlink.ClickEvent, new RoutedEventHandler(HandleHyperlinkClick)); + } + } +} diff --git a/WPinternals/WPinternals.csproj.user b/WPinternals/WPinternals.csproj.user index c130c5b..ecc3fb8 100644 --- a/WPinternals/WPinternals.csproj.user +++ b/WPinternals/WPinternals.csproj.user @@ -114,4 +114,9 @@ /Test + + + Designer + + \ No newline at end of file