diff options
Diffstat (limited to '')
-rw-r--r-- | src/WixToolset.WixBA/UpdateViewModel.cs | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/src/WixToolset.WixBA/UpdateViewModel.cs b/src/WixToolset.WixBA/UpdateViewModel.cs new file mode 100644 index 00000000..6b60112c --- /dev/null +++ b/src/WixToolset.WixBA/UpdateViewModel.cs | |||
@@ -0,0 +1,207 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolset.UX | ||
4 | { | ||
5 | using System; | ||
6 | using System.ComponentModel; | ||
7 | using System.Windows.Input; | ||
8 | using WixToolset.Bootstrapper; | ||
9 | |||
10 | /// <summary> | ||
11 | /// The states of the update view model. | ||
12 | /// </summary> | ||
13 | public enum UpdateState | ||
14 | { | ||
15 | Unknown, | ||
16 | Initializing, | ||
17 | Checking, | ||
18 | Current, | ||
19 | Available, | ||
20 | Failed, | ||
21 | } | ||
22 | |||
23 | /// <summary> | ||
24 | /// The model of the update view. | ||
25 | /// </summary> | ||
26 | public class UpdateViewModel : PropertyNotifyBase | ||
27 | { | ||
28 | private RootViewModel root; | ||
29 | private UpdateState state; | ||
30 | private ICommand updateCommand; | ||
31 | private string updateVersion; | ||
32 | private string updateChanges; | ||
33 | |||
34 | |||
35 | public UpdateViewModel(RootViewModel root) | ||
36 | { | ||
37 | this.root = root; | ||
38 | WixBA.Model.Bootstrapper.DetectUpdateBegin += this.DetectUpdateBegin; | ||
39 | WixBA.Model.Bootstrapper.DetectUpdate += this.DetectUpdate; | ||
40 | WixBA.Model.Bootstrapper.DetectUpdateComplete += this.DetectUpdateComplete; | ||
41 | |||
42 | this.root.PropertyChanged += new PropertyChangedEventHandler(this.RootPropertyChanged); | ||
43 | |||
44 | this.State = UpdateState.Initializing; | ||
45 | } | ||
46 | |||
47 | void RootPropertyChanged(object sender, PropertyChangedEventArgs e) | ||
48 | { | ||
49 | if ("InstallState" == e.PropertyName) | ||
50 | { | ||
51 | base.OnPropertyChanged("CanUpdate"); | ||
52 | } | ||
53 | } | ||
54 | |||
55 | public bool CheckingEnabled | ||
56 | { | ||
57 | get { return this.State == UpdateState.Initializing || this.State == UpdateState.Checking; } | ||
58 | } | ||
59 | |||
60 | public bool CanUpdate | ||
61 | { | ||
62 | get | ||
63 | { | ||
64 | switch(this.root.InstallState) | ||
65 | { | ||
66 | case InstallationState.Waiting: | ||
67 | case InstallationState.Applied: | ||
68 | case InstallationState.Failed: | ||
69 | return this.IsUpdateAvailable; | ||
70 | default: | ||
71 | return false; | ||
72 | } | ||
73 | } | ||
74 | } | ||
75 | |||
76 | public ICommand UpdateCommand | ||
77 | { | ||
78 | get | ||
79 | { | ||
80 | if (this.updateCommand == null) | ||
81 | { | ||
82 | this.updateCommand = new RelayCommand(param => WixBA.Plan(LaunchAction.UpdateReplace), param => this.CanUpdate); | ||
83 | } | ||
84 | |||
85 | return this.updateCommand; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | public bool IsUpdateAvailable | ||
90 | { | ||
91 | get { return this.State == UpdateState.Available; } | ||
92 | } | ||
93 | |||
94 | /// <summary> | ||
95 | /// Gets and sets the state of the update view model. | ||
96 | /// </summary> | ||
97 | public UpdateState State | ||
98 | { | ||
99 | get | ||
100 | { | ||
101 | return this.state; | ||
102 | } | ||
103 | |||
104 | set | ||
105 | { | ||
106 | if (this.state != value) | ||
107 | { | ||
108 | this.state = value; | ||
109 | base.OnPropertyChanged("State"); | ||
110 | base.OnPropertyChanged("CanUpdate"); | ||
111 | base.OnPropertyChanged("CheckingEnabled"); | ||
112 | base.OnPropertyChanged("IsUpdateAvailable"); | ||
113 | } | ||
114 | } | ||
115 | } | ||
116 | /// <summary> | ||
117 | /// The version of an available update. | ||
118 | /// </summary> | ||
119 | public string UpdateVersion | ||
120 | { | ||
121 | get | ||
122 | { | ||
123 | return updateVersion; | ||
124 | } | ||
125 | set | ||
126 | { | ||
127 | if (this.updateVersion != value) | ||
128 | { | ||
129 | this.updateVersion = value; | ||
130 | base.OnPropertyChanged("UpdateVersion"); | ||
131 | } | ||
132 | } | ||
133 | } | ||
134 | |||
135 | /// <summary> | ||
136 | /// The changes in the available update. | ||
137 | /// </summary> | ||
138 | public string UpdateChanges | ||
139 | { | ||
140 | get | ||
141 | { | ||
142 | return updateChanges; | ||
143 | } | ||
144 | set | ||
145 | { | ||
146 | if (this.updateChanges != value) | ||
147 | { | ||
148 | this.updateChanges = value; | ||
149 | base.OnPropertyChanged("UpdateChanges"); | ||
150 | } | ||
151 | } | ||
152 | } | ||
153 | |||
154 | private void DetectUpdateBegin(object sender, Bootstrapper.DetectUpdateBeginEventArgs e) | ||
155 | { | ||
156 | // Don't check for updates if: | ||
157 | // the first check failed (no retry) | ||
158 | // if we are being run as an uninstall | ||
159 | // if we are not under a full UI. | ||
160 | if ((UpdateState.Failed != this.State) && (LaunchAction.Uninstall != WixBA.Model.Command.Action) && (Display.Full == WixBA.Model.Command.Display)) | ||
161 | { | ||
162 | this.State = UpdateState.Checking; | ||
163 | e.Skip = false; | ||
164 | } | ||
165 | } | ||
166 | |||
167 | private void DetectUpdate(object sender, Bootstrapper.DetectUpdateEventArgs e) | ||
168 | { | ||
169 | // The list of updates is sorted in descending version, so the first callback should be the largest update available. | ||
170 | // This update should be either larger than ours (so we are out of date), the same as ours (so we are current) | ||
171 | // or smaller than ours (we have a private build). If we really wanted to, we could leave the e.StopProcessingUpdates alone and | ||
172 | // enumerate all of the updates. | ||
173 | WixBA.Model.Engine.Log(LogLevel.Verbose, String.Format("Potential update v{0} from '{1}'; current version: v{2}", e.Version, e.UpdateLocation, WixBA.Model.Version)); | ||
174 | if (e.Version > WixBA.Model.Version) | ||
175 | { | ||
176 | WixBA.Model.Engine.SetUpdate(null, e.UpdateLocation, e.Size, UpdateHashType.None, null); | ||
177 | this.UpdateVersion = String.Concat("v", e.Version.ToString()); | ||
178 | string changesFormat = @"<body style='overflow: auto;'>{0}</body>"; | ||
179 | this.UpdateChanges = String.Format(changesFormat, e.Content); | ||
180 | this.State = UpdateState.Available; | ||
181 | } | ||
182 | else | ||
183 | { | ||
184 | this.State = UpdateState.Current; | ||
185 | } | ||
186 | e.StopProcessingUpdates = true; | ||
187 | } | ||
188 | |||
189 | private void DetectUpdateComplete(object sender, Bootstrapper.DetectUpdateCompleteEventArgs e) | ||
190 | { | ||
191 | // Failed to process an update, allow the existing bundle to still install. | ||
192 | if ((UpdateState.Failed != this.State) && !Hresult.Succeeded(e.Status)) | ||
193 | { | ||
194 | this.State = UpdateState.Failed; | ||
195 | WixBA.Model.Engine.Log(LogLevel.Verbose, String.Format("Failed to locate an update, status of 0x{0:X8}, updates disabled.", e.Status)); | ||
196 | e.IgnoreError = true; | ||
197 | } | ||
198 | // If we are uninstalling, we don't want to check or show an update | ||
199 | // If we are checking, then the feed didn't find any valid enclosures | ||
200 | // If we are initializing, we're either uninstalling or not a full UI | ||
201 | else if ((LaunchAction.Uninstall == WixBA.Model.Command.Action) || (UpdateState.Initializing == this.State) || (UpdateState.Checking == this.State)) | ||
202 | { | ||
203 | this.State = UpdateState.Unknown; | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | } | ||