using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Collections;
using System.Threading;
using System.IO;
using System.Reflection;
using Microsoft.Win32;
namespace abPlayer
{
public partial class frmPlayer : Form
{
// API used
[DllImport("coredll.dll")]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("coredll.dll")]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
// skin related constants
private const String SKIN_DIR = "\\skins";
private const String NO_SKIN = "None";
private String SkinDir;
// default backlight timeout
private const int BACKLIGHT_TIMEOUT = 10;
// key press constants
const int TIME_CHANGE = 10;
const int VOLUME_CHANGE = 5;
// class that encapsulates various calls to win API
private cASUS639DeviceSpecifics DeviceSpecifics = null;
// form used to manage and maintain books (playlists)
private frmLibrary Library = null;
// form used to manage book properties
private frmProps Props = null;
// the class that manages the windows media player access
private cPlayer Player = null;
// In order to prevent auto power off
// a set of API needs to be called periodically (less than the auto
// power off timeout set in the device. So -
// we keep the interval desired (Threshold) and a time stamp indicating
// when the reset was last performed
private long KeepPowerThreshold = 30;
private DateTime KeepPowerTimeStamp = DateTime.Now;
// text color of the display and the forecolor of the controls change
// to reflect battery state: charging = white, good = yellow
// medium = orange, critical = red.
private Color TextColor = Color.White;
// since volume is saved as a book property and is set as a book is being
// played, we keep the volume level at entry time and resores it on exit.
private int SaveEntryVolume = 0;
// same for backlight timeout
private int SaveEntryBacklight = 0;
// sleep function data
private DateTime SleepTime = DateTime.Now;
private bool SleepActive = false;
private int[] SleepIntervals = { 15, 30, 60, 90, 120 };
// media transition goes through several statuses
// to avoid intermediate status displays, status paint is not done
// in real time but rather on the next timer tick
private bool NeedStatusPaint = false;
// if no media is being played timeline location does't change
// to avoid excessive and unnecessary screen paint and persistancy saves
// we only do it if location actually changed
private int LastLocationSaved = -1;
public frmPlayer()
{
InitializeComponent();
// this will catch any object and subsequent object creation error (as
// all 7 major objects - 4 forms and 3 classes are instantiated here).
// most common reasons: low memory and low battery
try
{
// various non managed API calls
DeviceSpecifics = new cASUS639DeviceSpecifics();
// forms used to manage and maintain books & book properties
Library = new frmLibrary();
// the class that manages the windows media player access
Player = new cPlayer();
// form to manage book properties
Props = new frmProps(Library, Player, DeviceSpecifics);
}
catch (Exception e)
{
MessageBox.Show("Failed to Initialize Objects - " +
e.Message, "Fatal Error");
Close();
return;
}
// align buttons
picDark.Left = picLight.Left = 2;
// prepare paint resources
apiDC = GetDC(this.Handle);
if (apiDC == IntPtr.Zero)
{
MessageBox.Show("Failed to Initialize GDI Resource", "Fatal Error");
Close();
return;
}
try
{
screenGraphics = Graphics.FromHdc(apiDC);
fgBMP = new Bitmap(Width, pnlBottom.Top - pnlTop.Height);
fgGraphics = Graphics.FromImage(fgBMP);
bgBMP = new Bitmap(Width, pnlBottom.Top - pnlTop.Height);
bgGraphics = Graphics.FromImage(bgBMP);
}
catch
{
MessageBox.Show("Failed to Initialize Graphics Resource", "Fatal Error");
Close();
return;
}
todFont = new Font(this.Font.Name, TOD_FONT_SIZE, FontStyle.Bold);
// enumerate existing skins and mark active one
SkinDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase) +
SKIN_DIR;
String sk = Library.BookStore.Skin;
try
{
foreach (DirectoryInfo dir in new DirectoryInfo(SkinDir).GetDirectories())
{
MenuItem mnu = new MenuItem();
mnu.Text = dir.Name;
mnu.Checked = (mnu.Text.ToUpper() == sk.ToUpper());
mnu.Click += new EventHandler(mnuSkin_Click);
mnuSkins.MenuItems.Add(mnu);
}
}
catch
{
MessageBox.Show("Failed to Find Skins Dir", "Error");
}
MenuItem mni = new MenuItem();
mni.Text = "-";
mnuSkins.MenuItems.Add(mni);
mni = new MenuItem();
mni.Text = NO_SKIN;
mni.Checked = (mni.Text.ToUpper() == sk.ToUpper());
mni.Click += new EventHandler(mnuSkin_Click);
mnuSkins.MenuItems.Add(mni);
//Load Active skin
LoadSkin(sk);
// ser print related sizes
textHeight = (int)fgGraphics.MeasureString("Z", this.Font).Height;
// time till sleep display
lblSleep.Text = "";
// set backlight timeout
SaveEntryBacklight = DeviceSpecifics.BacklightTimeout;
if (BACKLIGHT_TIMEOUT != SaveEntryBacklight)
DeviceSpecifics.BacklightTimeout = BACKLIGHT_TIMEOUT;
// initialize event handlers
Player.PlayItemChanged += new PlayItemChangedEventHandler(Player_PlayItemChanged);
Player.StatusChanged += new StatusChangedEventHandler(Player_StatusChanged);
SaveEntryVolume = DeviceSpecifics.Volume;
// get device screensave timeout
KeepPowerThreshold = DeviceSpecifics.PowerOffThreshold;
// if we have an active book - go load it
if ((Library.BookStore.ActiveBookName.Length > 0) &&
(Library.BookStore.GetBookFileCount("") > 0))
{
// set volume
DeviceSpecifics.Volume = Library.BookStore.GetBookProperty(eBookProperty.VOLUME, "");
// Load playlist, restore bookmark and pause
Player.Playlist(Library.BookStore.GetBookFiles(""),
Library.BookStore.GetBookProperty(eBookProperty.BOOKMARK_ENTRY, ""),
Library.BookStore.GetBookProperty(eBookProperty.BOOKMARK_LOCATION, ""),
(Library.BookStore.GetBookProperty(eBookProperty.SHUFFLE, "") != 0));
// take stock of current location
PaintLocation();
// force garbage collection here to dispose of all obsolete objects
// to prevent gc to kick in when it may disturb the playing
GC.Collect();
// signal ready to play
DeviceSpecifics.Beep();
}
tmr.Enabled = true;
}
// skin menu clicked
void mnuSkin_Click(object sender, EventArgs e)
{
if (((MenuItem)sender).Checked) return;
foreach (MenuItem mnu in mnuSkins.MenuItems)
mnu.Checked = (((MenuItem)sender) == mnu);
Library.BookStore.Skin = ((MenuItem)sender).Text;
LoadSkin(((MenuItem)sender).Text);
PaintScreen();
}
// load 5 forms skins
void LoadSkin(String skin)
{
ApplySkin(skin, this);
ApplySkin(skin, Library);
ApplySkin(skin, Library.FileSelector);
ApplySkin(skin, Props);
ApplySkin(skin, Props.MediaProps);
// set text color of sleep label to picture color
if (skin == NO_SKIN)
lblSleep.ForeColor = TextColor;
else
{
try
{
Bitmap bmp = new Bitmap(picSleep.Image);
for (int i = 0; i < picSleep.Width; i++)
for (int j = 0; j < picSleep.Height; j++)
{
int c = bmp.GetPixel(i, j).ToArgb();
if (((c & 0x00FF0000) > (0x00FF0000 / 2)) ||
((c & 0x0000FF00) > (0x0000FF00 / 2)) ||
((c & 0x000000FF) > (0x000000FF / 2)))
{
lblSleep.ForeColor = bmp.GetPixel(i, j);
bmp.Dispose();
return;
}
}
}
catch { }
}
}
// find all picturebox controls and load image
void ApplySkin(String skin, Control ctrl)
{
foreach (Control item in ctrl.Controls)
if (item.GetType() == typeof(PictureBox))
{
if (((PictureBox)item).Tag != null)
{
((PictureBox)item).Paint += Library.FileSelector.Pic_Paint;
((PictureBox)item).Image = null;
// load a picture
if (skin != NO_SKIN)
{
try
{
String[] file = Directory.GetFiles(SkinDir + "\\" + skin, (String)((PictureBox)item).Tag + ".*");
if (file.Length > 0)
((PictureBox)item).Image = new Bitmap(file[0]);
}
catch { }
}
}
}
else
ApplySkin(skin, item);
}
// monitor status change and show it
// do it on next timer 1 second tick so if status is changed rapidly
// (as in playlist loading) I don't show all intermediate statuses
void Player_StatusChanged(object sender, StatusChangedEventArgs e)
{
NeedStatusPaint = true;
}
// player or user changed the active file. Commit it to persistant storage
void Player_PlayItemChanged(object sender, PlayItemChangedEventArgs e)
{
if (e.ItemIndex > -1)
Library.BookStore.SetBookProperty(eBookProperty.BOOKMARK_ENTRY, e.ItemIndex, "");
PaintScreen();
}
// this is a timer (1 second) based event so we can reflect
// the new timeline marker visually and commit it to persistant storage
// we also do all time based housekeeping
private void tmr_Tick(object sender, EventArgs e)
{
if ((Player.Duration != -1) &&
(Player.Location != -1) &&
(LastLocationSaved != Player.Location))
{
LastLocationSaved = Player.Location;
PaintLocation();
Library.BookStore.SetBookProperty(eBookProperty.BOOKMARK_LOCATION,
Player.Location, "");
}
if (NeedStatusPaint)
{
PaintStatus(Player.StatusString);
NeedStatusPaint = false;
}
DateTime now = DateTime.Now;
// check if time since last reset has reached the threshold
if ((now - KeepPowerTimeStamp).TotalSeconds >= KeepPowerThreshold)
{
// yes - rewind timestamp and paint time and battery state
KeepPowerTimeStamp = now;
DeviceSpecifics.PreventAutoPowerOff();
}
// update tod display
if(now.Second == 0)
PaintBatteryVolAndTOD();
// flying text
if ((Player.PlaylistCount > 0) &&
(flyingTextCount > 0))
PaintFlyingText();
// handle sleep
if (SleepActive)
{
DrawSleepTime();
if (SleepTime <= DateTime.Now)
{
ReleaseResources();
// shutdown
DeviceSpecifics.PowerOff();
Close();
return;
}
}
}
// click
private void frmPlayer_Click(object sender, EventArgs e)
{
SwitchState();
}
// exit program after stopping media player
private void picExit_Click(object sender, EventArgs e)
{
Library.FileSelector.FlashControl((Control)sender);
ReleaseResources();
Close();
}
// release resources. close all
void ReleaseResources()
{
try
{
tmr.Enabled = false;
Player.StopAndClear();
if (fgBMP != null)
fgBMP.Dispose();
if (fgGraphics != null)
fgGraphics.Dispose();
if (bgBMP != null)
bgBMP.Dispose();
if (bgGraphics != null)
bgGraphics.Dispose();
if (screenGraphics != null)
screenGraphics.Dispose();
if (apiDC != IntPtr.Zero)
ReleaseDC(this.Handle, apiDC);
Player.Dispose();
}
catch { }
finally
{
DeviceSpecifics.Volume = SaveEntryVolume;
DeviceSpecifics.BacklightTimeout = SaveEntryBacklight;
}
}
// light on
private void picLight_Click(object sender, EventArgs e)
{
Library.FileSelector.FlashControl((Control)sender);
picLight.Visible = !picLight.Visible;
picDark.Visible = !picDark.Visible;
DeviceSpecifics.BacklightTimeout = BACKLIGHT_TIMEOUT;
}
// light off
private void picDark_Click(object sender, EventArgs e)
{
Library.FileSelector.FlashControl((Control)sender);
picLight.Visible = !picLight.Visible;
picDark.Visible = !picDark.Visible;
DeviceSpecifics.BacklightTimeout = 0;
}
// play button pressed
private void picPlay_Click(object sender, EventArgs e)
{
// if we are not already playing and there's a playlist - do it
Library.FileSelector.FlashControl((Control)sender);
if((Player.Status != ePlayerStatus.PLAYING) && (Player.PlaylistCount > 0))
Player.Play();
}
// stop button pressed
private void picStop_Click(object sender, EventArgs e)
{
Library.FileSelector.FlashControl((Control)sender);
Player.Stop();
}
public delegate bool PlayerAction();
// next button pressed
private void picNext_Click(object sender, EventArgs e)
{
Library.FileSelector.FlashControl((Control)sender);
circumventTransitioningHang(new PlayerAction(Player.Next));
}
// prev button pressed
private void picPrev_Click(object sender, EventArgs e)
{
Library.FileSelector.FlashControl((Control)sender);
circumventTransitioningHang(new PlayerAction(Player.Previous));
}
// sometimes the player gets stuck in transitioning state
// the only thing that gets it going again is play()
// this doesn't happen if player is already in play state (only paused/stopped)
void circumventTransitioningHang(PlayerAction action)
{
//bool needStop = false;
//try
//{
// if (Player.Status != ePlayerStatus.PLAYING)
// {
// needStop = true;
// Player.Play();
// Player.WaitForPlayerStatus(ePlayerStatus.PLAYING);
// }
action();
// if (needStop)
// {
// Player.WaitForPlayerStatus(ePlayerStatus.PLAYING);
// Player.Stop();
// }
//}
//catch { }
}
// sleep presed
private void picSleep_Click(object sender, EventArgs e)
{
// convert sleeptime to minutes from now
foreach (int minutesInterval in SleepIntervals)
{
// allow 5 secs to jump between intervals
if ((SleepTime.AddSeconds(5).Subtract(DateTime.Now)).TotalMinutes <
minutesInterval)
{
SleepTime = DateTime.Now.AddMinutes(minutesInterval);
SleepActive = true;
lblSleep.Visible = true;
DrawSleepTime();
return;
}
}
SleepTime = DateTime.Now;
SleepActive = false;
lblSleep.Visible = false;
DrawSleepTime();
}
// display sleep time
void DrawSleepTime()
{
if (SleepActive)
lblSleep.Text = "Sleep in: " +
(SleepTime.Subtract(DateTime.Now)).ToString();
else
lblSleep.Text = "";
}
// try to pause before popup
private void cnm_Popup(object sender, EventArgs e)
{
if (Player.Status == ePlayerStatus.PLAYING)
{
Player.Pause();
Player.WaitForPlayerStatus(ePlayerStatus.PAUSED);
}
}
// paint screen
private void abPlayer_Paint(object sender, PaintEventArgs e)
{
PaintScreen(e.Graphics);
}
// properties button clicked
private void picProps_Click(object sender, EventArgs e)
{
Library.FileSelector.FlashControl((Control)sender);
// only if we have a playlist
if (Player.PlaylistCount <= 0) return;
// if we are in a play state - try to pause
if (Player.Status == ePlayerStatus.PLAYING)
{
Player.Pause();
Player.WaitForPlayerStatus(ePlayerStatus.PAUSED);
}
// if we failed (or state is something else - deny access)
if ((Player.Status != ePlayerStatus.PAUSED) &&
(Player.Status != ePlayerStatus.STOPPED)) return;
// set all property values to their current state
// make properties form accessible
DeviceSpecifics.BacklightTimeout = 0;
if (Props.ShowDialog() == DialogResult.OK)
{
if ((Props.Dirty) && (Player.Status == ePlayerStatus.PAUSED))
{
if (Props.hscVol.Value != Library.BookStore.GetBookProperty(eBookProperty.VOLUME, ""))
{
Library.BookStore.SetBookProperty(eBookProperty.VOLUME, Props.hscVol.Value, "");
DeviceSpecifics.Volume = Props.hscVol.Value;
}
// shuffle option changed
if (Props.chkShuffle.Checked != (Library.BookStore.GetBookProperty(eBookProperty.SHUFFLE, "") != 0))
{
Library.BookStore.SetBookProperty(eBookProperty.SHUFFLE, (Props.chkShuffle.Checked ? 1 : 0), "");
Player.Shuffle = Props.chkShuffle.Checked;
}
// playlist entry
if ((Props.hscEntry.Value - 1) != Library.BookStore.GetBookProperty(eBookProperty.BOOKMARK_ENTRY, ""))
{
Library.BookStore.SetBookProperty(eBookProperty.BOOKMARK_ENTRY, Props.hscEntry.Value - 1, "");
Library.BookStore.SetBookProperty(eBookProperty.BOOKMARK_LOCATION, Props.hscTime.Value, "");
Player.Playlist(null,
Props.hscEntry.Value - 1,
Props.hscTime.Value,
Props.chkShuffle.Checked);
PaintScreen();
}
else
// timeline marker changed
if (Props.hscTime.Value != Library.BookStore.GetBookProperty(eBookProperty.BOOKMARK_LOCATION, ""))
{
Library.BookStore.SetBookProperty(eBookProperty.BOOKMARK_LOCATION, Props.hscTime.Value, "");
Player.Location = Props.hscTime.Value;
}
//indicate change done & we are ready to play
DeviceSpecifics.Beep();
}
}
if(picDark.Visible)
DeviceSpecifics.BacklightTimeout = BACKLIGHT_TIMEOUT;
}
// library button clicked
private void picLibrary_Click(object sender, EventArgs e)
{
Library.FileSelector.FlashControl((Control)sender);
// if we are in a playing state - try to pause
if (Player.Status == ePlayerStatus.PLAYING)
{
Player.Pause();
if (!Player.WaitForPlayerStatus(ePlayerStatus.PAUSED))
return;
}
// show library form and:
DeviceSpecifics.BacklightTimeout = 0;
if ((Library.ShowDialog() == DialogResult.OK) ||
((Player.PlaylistCount > 0) &&
(Player.PlaylistCount != Library.BookStore.GetBookFileCount(""))))
{
// we came back after user pressed play
tmr.Enabled = false;
PaintScreen();
Application.DoEvents();
// reset current playlist
Player.StopAndClear();
// set volume
Props.hscVol.Value = Library.BookStore.GetBookProperty(eBookProperty.VOLUME, "");
Props.lblVol.Text = "Volume: " + Props.hscVol.Value.ToString();
DeviceSpecifics.Volume = Props.hscVol.Value;
// load playlist and start playing it from the last position
Player.Playlist(Library.BookStore.GetBookFiles(""),
Library.BookStore.GetBookProperty(eBookProperty.BOOKMARK_ENTRY, ""),
Library.BookStore.GetBookProperty(eBookProperty.BOOKMARK_LOCATION, ""),
(Library.BookStore.GetBookProperty(eBookProperty.SHUFFLE, "") != 0));
GC.Collect();
DeviceSpecifics.Beep();
tmr.Enabled = true;
PaintScreen();
}
else
{
// we came back after user pressed the cancel button
// if active book was deleted - reset player
if (Library.BookStore.ActiveBookName == "")
Player.StopAndClear();
PaintScreen();
}
if (picDark.Visible)
DeviceSpecifics.BacklightTimeout = BACKLIGHT_TIMEOUT;
}
// get battery life percent and according to its level set the
// display color: charging=white, good=yellow, medium=orange, low=red
private int GetBatteryLifePercentAndSetTextColor()
{
PowerState state = DeviceSpecifics.PowerAndBatteryState;
Color c;
if (state.ACLineConnected)
c = Color.White;
else
c = (state.BatteryLifePercent <= 35) ? Color.Red :
(state.BatteryLifePercent <= 70) ? Color.Orange : Color.Yellow;
if (c != TextColor)
{
TextColor = c;
PaintScreen();
}
return state.BatteryLifePercent;
}
// key press function
private void frmPlayer_KeyDown(object sender, KeyEventArgs e)
{
//if (Player.PlaylistCount <= 0) return;
switch (e.KeyCode)
{
case Keys.Right:
AdjustLocation(TIME_CHANGE);
break;
case Keys.Left:
AdjustLocation(-TIME_CHANGE);
break;
case Keys.Up:
AdjustVolume(-VOLUME_CHANGE);
break;
case Keys.Down:
AdjustVolume(VOLUME_CHANGE);
break;
case Keys.Enter:
SwitchState();
break;
}
e.Handled = true;
}
// volume manipulation function
private void AdjustVolume(int delta)
{
int vol = DeviceSpecifics.Volume;
int newVol = ((vol + delta) < 0) ? 0 :
((vol + delta) > 100) ? 100 :
(vol + delta);
if (vol != newVol)
{
DeviceSpecifics.Volume = newVol;
Library.BookStore.SetBookProperty(eBookProperty.VOLUME, newVol, "");
PaintBatteryVolAndTOD();
}
}
// location manipulation function
private void AdjustLocation(int delta)
{
int loc = Player.Location;
int dur = Player.Duration;
if ((loc != -1) &&
(dur != -1))
{
int newLoc = ((loc + delta) < 0) ? 0 :
((loc + delta) > dur) ? dur :
(loc + delta);
if (newLoc != loc)
{
Player.Location = newLoc;
Library.BookStore.SetBookProperty(eBookProperty.BOOKMARK_LOCATION,
newLoc, "");
PaintLocation();
}
}
}
// switch between play and pause
private void SwitchState()
{
if (Player.PlaylistCount > 0)
{
if (Player.Status == ePlayerStatus.PLAYING)
Player.Pause();
else
Player.Play();
}
}
// show about dialog
private void mnuAbout_Click(object sender, EventArgs e)
{
DeviceSpecifics.BacklightTimeout = 0;
frmAbout frm = new frmAbout();
frm.Left = Width / 2 - frm.Width / 2;
frm.Top = Height / 2 - frm.Height / 2;
frm.ShowDialog();
if (picDark.Visible)
DeviceSpecifics.BacklightTimeout = BACKLIGHT_TIMEOUT;
PaintScreen();
}
}
}