Skip to content
This repository has been archived by the owner on Feb 2, 2023. It is now read-only.

Commit

Permalink
Merge branch 'restore-toast'
Browse files Browse the repository at this point in the history
Temporary workaround proposed by @bartuszekj for issue #86
  • Loading branch information
aleab committed Aug 30, 2018
2 parents 7483e72 + a7e08e1 commit 983f2d0
Show file tree
Hide file tree
Showing 19 changed files with 489 additions and 2,159 deletions.
4 changes: 3 additions & 1 deletion Toastify.Test/Model/HotkeyTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ public void OneTimeSetUp()
this.action = A.Fake<IAction>();
A.CallTo(() => this.action.Name).Returns("Fake Action");
A.CallTo(() => this.action.PerformAction()).DoesNothing();
A.CallTo(() => this.action.Equals(this.action)).Returns(true);

this.actionClone = A.Fake<IAction>();
A.CallTo(() => this.actionClone.Name).Returns("Fake Action");
A.CallTo(() => this.actionClone.PerformAction()).DoesNothing();
A.CallTo(() => this.actionClone.Equals(this.actionClone)).Returns(true);

A.CallTo(() => this.action.Equals(this.actionClone)).Returns(true);
A.CallTo(() => this.actionClone.Equals(this.action)).Returns(true);
Expand Down Expand Up @@ -125,7 +127,7 @@ public void TestClone()
Assert.That(hotkeyClone.Active, Is.False);
if (hotkeyClone.Action != null)
Assert.That(hotkeyClone.Action, Is.Not.SameAs(this.fakeHotkey.Action));
Assert.That(hotkeyClone.Action, Is.SameAs(this.fakeHotkey.Action)); // Actions should be the same
});
}

Expand Down
10 changes: 10 additions & 0 deletions Toastify/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Toastify/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,10 @@ The host address "open.spotify.com" might have been redirected by the client. Ch
</data>
<data name="ERROR_STARTUP_SPOTIFY_NOT_FOUND" xml:space="preserve">
<value>Couldn't find Spotify executable.
Try to start Spotify manually before launching Toastify.</value>
</data>
<data name="ERROR_STARTUP_SPOTIFY_WINDOW_NOT_FOUND" xml:space="preserve">
<value>Couldn't find Spotify main window.
Try to start Spotify manually before launching Toastify.</value>
</data>
</root>
1 change: 1 addition & 0 deletions Toastify/Toastify.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@
<Compile Include="src\Core\HotkeyType.cs" />
<Compile Include="src\Core\ProxyConfigAdapter.cs" />
<Compile Include="src\Core\SecureProxyConfigJsonConverter.cs" />
<Compile Include="src\Core\SpotifyWindow.cs" />
<Compile Include="src\Core\UpdateDeliveryMode.cs" />
<Compile Include="src\Core\VersionCheckFrequency.cs" />
<Compile Include="src\CustomMessageBox.cs" />
Expand Down
219 changes: 103 additions & 116 deletions Toastify/src/Core/Spotify.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
using Toastify.Helpers;
using Toastify.Model;
using Toastify.Services;
using ToastifyAPI.Events;
using ToastifyAPI.Helpers;
using ToastifyAPI.Native;
using ToastifyAPI.Native.Enums;
using ToastifyAPI.Native.Structs;
using SpotifyTrackChangedEventArgs = Toastify.Events.SpotifyTrackChangedEventArgs;

namespace Toastify.Core
{
Expand Down Expand Up @@ -51,6 +53,8 @@ public static Spotify Instance

#endregion Spotify Launcher

private SpotifyWindow spotifyWindow;

private SpotifyLocalAPI localAPI;

private SpotifyLocalAPIConfig localAPIConfig;
Expand All @@ -67,32 +71,9 @@ public static Spotify Instance

#region Public Properties

public bool IsRunning { get { return this.GetMainWindowHandle() != IntPtr.Zero; } }

public bool IsMinimized
public bool IsRunning
{
get
{
if (!this.IsRunning)
return false;

var hWnd = this.GetMainWindowHandle();
WindowStylesFlags windowStyles = (WindowStylesFlags)Windows.GetWindowLongPtr(hWnd, GWL.GWL_STYLE);
return (windowStyles & WindowStylesFlags.WS_MINIMIZE) != 0L || this.IsMinimizedToTray;
}
}

public bool IsMinimizedToTray
{
get
{
if (!this.IsRunning)
return false;

var hWnd = this.GetMainWindowHandle();
WindowStylesFlags windowStyles = (WindowStylesFlags)Windows.GetWindowLongPtr(hWnd, GWL.GWL_STYLE);
return (windowStyles & WindowStylesFlags.WS_MINIMIZE) == 0L && (windowStyles & WindowStylesFlags.WS_VISIBLE) == 0L;
}
get { return this.spotifyWindow?.IsValid ?? false; }
}

public StatusResponse Status { get { return this.localAPI?.GetStatus(); } }
Expand Down Expand Up @@ -175,7 +156,9 @@ private void SpotifyLauncherTimeoutTimer_Elapsed(object sender, System.Timers.El

private void StartSpotify_WorkerTask(object sender, DoWorkEventArgs e)
{
this.spotifyProcess = !this.IsRunning ? this.LaunchSpotifyAndWaitForInputIdle(e) : ToastifyAPI.Spotify.FindSpotifyProcess();
var process = ToastifyAPI.Spotify.FindSpotifyProcess();
this.spotifyProcess = !this.IsRunning && process == null ? this.LaunchSpotifyAndWaitForInputIdle(e) : process;

if (e.Cancel)
return;
if (this.spotifyProcess == null)
Expand All @@ -184,6 +167,9 @@ private void StartSpotify_WorkerTask(object sender, DoWorkEventArgs e)
this.spotifyProcess.EnableRaisingEvents = true;
this.spotifyProcess.Exited += this.Spotify_Exited;

this.spotifyWindow = new SpotifyWindow(this.spotifyProcess);
this.spotifyWindow.InitializationFinished += this.SpotifyWindow_InitializationFinished;

//this.ConnectWithSpotify(e);
}

Expand Down Expand Up @@ -249,8 +235,8 @@ private void StartSpotify_WorkerTaskCompleted(object sender, RunWorkerCompletedE
// Terminate Toastify
App.Terminate();
}
else
this.Spotify_Connected(this, new SpotifyStateEventArgs(this.Status));
//else
// this.Spotify_Connected(this, new SpotifyStateEventArgs(this.Status));

this.DisposeSpotifyLauncher();
this.DisposeSpotifyLauncherTimeoutTimer();
Expand Down Expand Up @@ -578,38 +564,35 @@ private void ChangeProxySettings()

#endregion Spotify Launcher background worker

public Task Minimize(int delay = 0)
#region SpotifyWindow wrapper methods/properties

public bool IsMinimized
{
return Task.Run(async () =>
{
int timeout = 2000;
IntPtr hWnd;
get { return this.spotifyWindow?.IsMinimized ?? false; }
}

do
{
hWnd = this.GetMainWindowHandle();
public bool IsMinimizedToTray
{
get { return this.spotifyWindow?.IsMinimizedToTray ?? false; }
}

if (hWnd == IntPtr.Zero)
{
timeout -= 100;
await Task.Delay(100);
}
} while (hWnd == IntPtr.Zero && timeout > 0);
public Task Minimize(int delay = 0)
{
return this.spotifyWindow?.Minimize(delay);
}

return hWnd;
}).ContinueWith(async hWndTask =>
{
IntPtr hWnd = await hWndTask;
if (hWnd != IntPtr.Zero)
{
// We also need to wait a little more before minimizing the window;
// if we don't, the toast will not show the current track until 'something' happens (track change, play state change...).
await Task.Delay(delay);
User32.ShowWindow(hWnd, ShowWindowCmd.SW_SHOWMINIMIZED);
}
});
public void ShowSpotify()
{
this.spotifyWindow?.Show();
}

private IntPtr GetMainWindowHandle()
{
return this.spotifyWindow?.Handle ?? IntPtr.Zero;
}

#endregion

public void Kill()
{
//Can't kindly close Spotify this way anymore since Spotify version 1.0.75.483.g7ff4a0dc due to issue #31
Expand All @@ -629,68 +612,6 @@ public void Kill()
catch { /* ignore */ }
}

public void ShowSpotify()
{
if (this.IsRunning)
{
var hWnd = this.GetMainWindowHandle();

// check Spotify's current window state
var placement = new WindowPlacement();
User32.GetWindowPlacement(hWnd, ref placement);

var showCommand = ShowWindowCmd.SW_SHOW;
if (placement.showCmd == ShowWindowCmd.SW_SHOWMINIMIZED || placement.showCmd == ShowWindowCmd.SW_HIDE)
showCommand = ShowWindowCmd.SW_RESTORE;

if (this.IsMinimizedToTray)
{
// TODO: Restore Spotify if minimized to the tray.

return;

//IntPtr renderWindowHandle = Win32API.GetProcessWindows((uint)this.spotifyProcess.Id, "Chrome_WidgetWin_0")
// .Select(Win32API.GetChildWindows)
// .SingleOrDefault(children => children != null && children.Any(h => Win32API.GetClassName(h) == "Chrome_RenderWidgetHostHWND"))
// ?.SingleOrDefault() ?? IntPtr.Zero;

//Win32API.ShowWindow(hWnd, showCommand);
//if (renderWindowHandle != IntPtr.Zero)
//{
// IntPtr parent = Win32API.GetParent(renderWindowHandle);
// if (parent != hWnd)
// {
// Win32API.SetParent(renderWindowHandle, hWnd);
// Win32API.SendWindowMessage(renderWindowHandle, Win32API.WindowsMessagesFlags.WM_CHILDACTIVATE, IntPtr.Zero, IntPtr.Zero);
// Win32API.ShowWindow(renderWindowHandle, Win32API.ShowWindowCmd.SW_SHOW);
// Win32API.ShowWindow(renderWindowHandle, Win32API.ShowWindowCmd.SW_RESTORE);

// IntPtr hDC = Win32API.GetDC(renderWindowHandle);
// Win32API.SendWindowMessage(renderWindowHandle, Win32API.WindowsMessagesFlags.WM_ERASEBKGND, hDC, IntPtr.Zero);
// Win32API.ReleaseDC(renderWindowHandle, hDC);

// Win32API.UpdateWindow(renderWindowHandle);
// }
// else
// Win32API.AddVisibleWindowStyle(renderWindowHandle);
//}
}
else
User32.ShowWindow(hWnd, showCommand);

User32.SetForegroundWindow(hWnd);
User32.SetFocus(hWnd);
}
}

private IntPtr GetMainWindowHandle()
{
if (this.spotifyProcess == null)
this.spotifyProcess = ToastifyAPI.Spotify.FindSpotifyProcess();

return this.spotifyProcess == null ? IntPtr.Zero : ToastifyAPI.Spotify.GetMainWindowHandle(unchecked((uint)this.spotifyProcess.Id));
}

public void SendAction(ToastifyActionEnum action)
{
if (!this.IsRunning)
Expand Down Expand Up @@ -905,6 +826,72 @@ private void Spotify_Connected(object sender, SpotifyStateEventArgs e)
this.Connected?.Invoke(sender, e);
}

private void SpotifyWindow_InitializationFinished(object sender, EventArgs e)
{
this.spotifyWindow.InitializationFinished -= this.SpotifyWindow_InitializationFinished;

if (this.spotifyWindow.IsValid)
{
this.spotifyWindow.TitleWatcher.TitleChanged += this.SpotifyWindowTitleWatcher_TitleChanged;

// Fake the Connected event
string currentTitle = this.spotifyWindow.Title;
SpotifyStateEventArgs spotifyStateEventArgs = null;

if (string.Equals(currentTitle, SpotifyWindow.PAUSED_TITLE, StringComparison.InvariantCulture))
spotifyStateEventArgs = new SpotifyStateEventArgs(null, false, 1.0, 1.0);
else
{
string[] titleElements = currentTitle.Split('-');
if (titleElements.Length != 2)
{
// TODO: Handle unexpected title format
}
else
{
this.CurrentSong = new Song(titleElements[0].Trim(), titleElements[1].Trim(), 1, SpotifyTrackType.NORMAL, "Unknown Album");
spotifyStateEventArgs = new SpotifyStateEventArgs(this.CurrentSong, true, 1.0, 1.0);
}
}

if (spotifyStateEventArgs != null)
this.Connected?.Invoke(this, spotifyStateEventArgs);
}
else
{
string logError = this.spotifyProcess.HasExited ? "process has been terminated" : "null handle";
logger.Error($"Couldn't find Spotify's window: {logError}");

string errorMsg = Properties.Resources.ERROR_STARTUP_SPOTIFY_WINDOW_NOT_FOUND;
MessageBox.Show($"{errorMsg}", "Toastify", MessageBoxButton.OK, MessageBoxImage.Error);

App.Terminate();
}
}

private void SpotifyWindowTitleWatcher_TitleChanged(object sender, WindowTitleChangedEventArgs e)
{
if (string.Equals(e.NewTitle, SpotifyWindow.PAUSED_TITLE, StringComparison.InvariantCulture))
this.SpotifyLocalAPI_OnPlayStateChange(this, new PlayStateEventArgs { Playing = false });
else if (string.Equals(e.OldTitle, SpotifyWindow.PAUSED_TITLE, StringComparison.InvariantCulture))
this.SpotifyLocalAPI_OnPlayStateChange(this, new PlayStateEventArgs { Playing = true });
else
{
string[] oldTitleElements = e.OldTitle.Split('-');
string[] newTitleElements = e.NewTitle.Split('-');
if (oldTitleElements.Length != 2 || newTitleElements.Length != 2)
{
// TODO: Handle unexpected title format
}
else
{
Song oldSong = this.CurrentSong;
this.CurrentSong = new Song(newTitleElements[0].Trim(), newTitleElements[1].Trim(), 1, SpotifyTrackType.NORMAL, "Unknown Album");
this.SongChanged?.Invoke(this, new SpotifyTrackChangedEventArgs(oldSong, this.CurrentSong));
}
}
}

private void SpotifyLocalAPI_OnTrackChange(object sender, TrackChangeEventArgs e)
{
this.CurrentSong = e.NewTrack;
Expand Down

0 comments on commit 983f2d0

Please sign in to comment.