Merge branch 'config' into develop

This commit is contained in:
mgroves 2011-02-05 20:41:13 -05:00
commit 3f1189254a
37 changed files with 902 additions and 3186 deletions

View file

@ -0,0 +1,46 @@
using Android.Content;
using Android.Database.Sqlite;
using Android.Util;
namespace MonoStockPortfolio.Core
{
public abstract class AndroidSqliteBase : SQLiteOpenHelper
{
public const string PORTFOLIO_TABLE_NAME = "Portfolios";
public const string POSITION_TABLE_NAME = "Positions";
public const string CONFIG_TABLE_NAME = "Config";
public const string DATABASE_NAME = "stockportfolio.db";
public const int DATABASE_VERSION = 1;
protected AndroidSqliteBase(Context context)
: base(context, DATABASE_NAME, null, DATABASE_VERSION)
{
}
protected SQLiteDatabase Db { get { return WritableDatabase; } }
public override void OnCreate(SQLiteDatabase db)
{
db.ExecSQL("CREATE TABLE " + PORTFOLIO_TABLE_NAME + " (id INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT)");
db.ExecSQL("CREATE TABLE " + POSITION_TABLE_NAME + " (id INTEGER PRIMARY KEY AUTOINCREMENT, Ticker TEXT, Shares REAL, PricePerShare REAL, ContainingPortfolioID INT)");
db.ExecSQL("CREATE TABLE " + CONFIG_TABLE_NAME + " (StockItems TEXT)");
db.ExecSQL("INSERT INTO " + CONFIG_TABLE_NAME + " (StockItems) VALUES ('2,0,1,3')");
db.ExecSQL("INSERT INTO " + PORTFOLIO_TABLE_NAME + " (Name) VALUES ('Sample portfolio')");
db.ExecSQL("INSERT INTO " + POSITION_TABLE_NAME + " (Ticker, Shares, PricePerShare, ContainingPortfolioID) VALUES ('GOOG', '500', '593.97', 1)");
db.ExecSQL("INSERT INTO " + POSITION_TABLE_NAME + " (Ticker, Shares, PricePerShare, ContainingPortfolioID) VALUES ('AMZN', '500', '180.00', 1)");
db.ExecSQL("INSERT INTO " + POSITION_TABLE_NAME + " (Ticker, Shares, PricePerShare, ContainingPortfolioID) VALUES ('AAPL', '500', '322.56', 1)");
db.ExecSQL("INSERT INTO " + POSITION_TABLE_NAME + " (Ticker, Shares, PricePerShare, ContainingPortfolioID) VALUES ('MSFT', '500', '27.91', 1)");
db.ExecSQL("INSERT INTO " + POSITION_TABLE_NAME + " (Ticker, Shares, PricePerShare, ContainingPortfolioID) VALUES ('NOVL', '500', '5.92', 1)");
db.ExecSQL("INSERT INTO " + POSITION_TABLE_NAME + " (Ticker, Shares, PricePerShare, ContainingPortfolioID) VALUES ('S', '500', '4.23', 1)");
db.ExecSQL("INSERT INTO " + POSITION_TABLE_NAME + " (Ticker, Shares, PricePerShare, ContainingPortfolioID) VALUES ('VZ', '500', '35.78', 1)");
db.ExecSQL("INSERT INTO " + POSITION_TABLE_NAME + " (Ticker, Shares, PricePerShare, ContainingPortfolioID) VALUES ('T', '500', '29.38', 1)");
}
public override void OnUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
Log.Warn("Upgrade", "Nothing to upgrade");
}
}
}

View file

@ -1,11 +1,44 @@
using System.Collections.Generic;
using System.Linq;
using Android.Content;
using MonoStockPortfolio.Entities;
namespace MonoStockPortfolio.Core.Config
{
public class AndroidSqliteConfigRepository : IConfigRepository
public class AndroidSqliteConfigRepository : AndroidSqliteBase, IConfigRepository
{
public AndroidSqliteConfigRepository(Context context) : base(context)
{ }
public IEnumerable<StockDataItem> GetStockItems()
{
var cursor = Db.Query(CONFIG_TABLE_NAME, new[] { "StockItems" }, null, null, null, null, null);
string stockItemsCsv = null;
if (cursor.Count > 0)
{
cursor.MoveToNext();
stockItemsCsv = cursor.GetString(0);
if (!cursor.IsClosed) cursor.Close();
}
if (string.IsNullOrEmpty(stockItemsCsv))
{
return DefaultItems();
}
return stockItemsCsv.Split(',').Select(i => (StockDataItem)int.Parse(i));
}
public void UpdateStockItems(List<StockDataItem> stockDataItems)
{
var stockItemsCsv = string.Join(",", stockDataItems.Select(i => ((int) i).ToString()).ToArray());
var contentValues = new ContentValues();
contentValues.Put("StockItems", stockItemsCsv);
Db.Update(CONFIG_TABLE_NAME, contentValues, null, null);
}
// this should never be called, but it's here anyway in case of some catastrophe
private static IEnumerable<StockDataItem> DefaultItems()
{
var items = new List<StockDataItem>();
items.Add(StockDataItem.Ticker);

View file

@ -6,5 +6,6 @@ namespace MonoStockPortfolio.Core.Config
public interface IConfigRepository
{
IEnumerable<StockDataItem> GetStockItems();
void UpdateStockItems(List<StockDataItem> stockDataItems);
}
}

View file

@ -1,4 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using MonoStockPortfolio.Entities;
namespace MonoStockPortfolio.Core
@ -21,5 +24,16 @@ namespace MonoStockPortfolio.Core
}
return string.Empty;
}
public static IEnumerable<T> GetValues<T>(this Enum value)
{
var enumerations = new List<Enum>();
var fields = value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public);
foreach (var fieldInfo in fields)
{
enumerations.Add((Enum)fieldInfo.GetValue(value));
}
return enumerations.Cast<T>();
}
}
}

View file

@ -31,11 +31,7 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="FileHelpers">
<HintPath>..\libs\FileHelpers.dll</HintPath>
</Reference>
<Reference Include="Mono.Android" />
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
@ -46,14 +42,15 @@
<Compile Include="Config\IConfigRepository.cs" />
<Compile Include="DictionaryExtensions.cs" />
<Compile Include="EnumExtensions.cs" />
<Compile Include="AndroidSqliteBase.cs" />
<Compile Include="PortfolioRepositories\AndroidSqlitePortfolioRepository.cs" />
<Compile Include="PortfolioRepositories\IPortfolioRepository.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\IPortfolioService.cs" />
<Compile Include="Services\PortfolioService.cs" />
<Compile Include="StockData\CsvParser.cs" />
<Compile Include="StockData\GoogleStockDataProvider.cs" />
<Compile Include="StockData\IStockDataProvider.cs" />
<Compile Include="StockData\YahooFinanceStockData.cs" />
<Compile Include="StockData\YahooStockDataProvider.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MonoStockPortfolio.Entities\MonoStockPortfolio.Entities.csproj">

View file

@ -7,41 +7,32 @@ using MonoStockPortfolio.Entities;
namespace MonoStockPortfolio.Core.PortfolioRepositories
{
public class AndroidSqlitePortfolioRepository : IPortfolioRepository
public class AndroidSqlitePortfolioRepository : AndroidSqliteBase, IPortfolioRepository
{
private OpenHelper _dbHelper;
private SQLiteDatabase _db;
private const string PORTFOLIO_TABLE_NAME = "Portfolios";
private const string DATABASE_NAME = "stockportfolio.db";
private const int DATABASE_VERSION = 1;
private const string POSITION_TABLE_NAME = "Positions";
public AndroidSqlitePortfolioRepository(Context context)
{
_dbHelper = new OpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
_db = _dbHelper.WritableDatabase;
}
: base(context)
{ }
public IList<Portfolio> GetAllPortfolios()
{
var list = new List<Portfolio>();
var cursor = _db.Query(PORTFOLIO_TABLE_NAME, new[] {"id", "Name"}, null, null, null, null, null);
if(cursor.Count > 0)
var cursor = Db.Query(PORTFOLIO_TABLE_NAME, new[] { "id", "Name" }, null, null, null, null, null);
if (cursor.Count > 0)
{
while(cursor.MoveToNext())
while (cursor.MoveToNext())
{
var portfolio = new Portfolio(cursor.GetInt(0));
portfolio.Name = cursor.GetString(1);
list.Add(portfolio);
}
}
if(!cursor.IsClosed) cursor.Close();
if (!cursor.IsClosed) cursor.Close();
return list;
}
public Portfolio GetPortfolioById(long portfolioId)
{
var cursor = _db.Query(PORTFOLIO_TABLE_NAME, new[] { "id", "Name" }, " ID = " + portfolioId, null, null, null, null);
var cursor = Db.Query(PORTFOLIO_TABLE_NAME, new[] { "id", "Name" }, " ID = " + portfolioId, null, null, null, null);
if (cursor.Count > 0)
{
cursor.MoveToNext();
@ -67,26 +58,26 @@ namespace MonoStockPortfolio.Core.PortfolioRepositories
public void DeletePortfolioById(int portfolioId)
{
_db.BeginTransaction();
Db.BeginTransaction();
try
{
_db.Delete(PORTFOLIO_TABLE_NAME, "id = " + portfolioId, null);
_db.Delete(POSITION_TABLE_NAME, "ContainingPortfolioID = " + portfolioId, null);
_db.SetTransactionSuccessful();
Db.Delete(PORTFOLIO_TABLE_NAME, "id = " + portfolioId, null);
Db.Delete(POSITION_TABLE_NAME, "ContainingPortfolioID = " + portfolioId, null);
Db.SetTransactionSuccessful();
}
catch (SQLiteException)
{
Log.E("DeletePortfolio", "SQLiteException => Id = " + portfolioId);
Log.Error("DeletePortfolio", "SQLiteException => Id = " + portfolioId);
}
finally
{
_db.EndTransaction();
Db.EndTransaction();
}
}
public Portfolio GetPortfolioByName(string portfolioName)
{
var cursor = _db.Query(PORTFOLIO_TABLE_NAME, new[] { "id", "Name" }, " Name = '" + portfolioName + "'", null, null, null, null);
var cursor = Db.Query(PORTFOLIO_TABLE_NAME, new[] { "id", "Name" }, " Name = '" + portfolioName + "'", null, null, null, null);
if (cursor.Count > 0)
{
cursor.MoveToNext();
@ -100,19 +91,19 @@ namespace MonoStockPortfolio.Core.PortfolioRepositories
public void DeletePositionById(long positionId)
{
_db.Delete(POSITION_TABLE_NAME, "id = " + positionId, null);
Db.Delete(POSITION_TABLE_NAME, "id = " + positionId, null);
}
public Position GetPositionById(long positionId)
{
Position position = null;
var cursor = _db.Query(POSITION_TABLE_NAME, new[] { "id", "Ticker", "Shares", "PricePerShare" }, " id = " + positionId, null, null, null, null);
var cursor = Db.Query(POSITION_TABLE_NAME, new[] { "id", "Ticker", "Shares", "PricePerShare" }, " id = " + positionId, null, null, null, null);
if (cursor.Count > 0)
{
while (cursor.MoveToNext())
{
position= new Position(cursor.GetInt(0));
position = new Position(cursor.GetInt(0));
position.Ticker = cursor.GetString(1);
position.Shares = Convert.ToDecimal(cursor.GetFloat(2));
position.PricePerShare = Convert.ToDecimal(cursor.GetFloat(3));
@ -126,7 +117,7 @@ namespace MonoStockPortfolio.Core.PortfolioRepositories
{
var list = new List<Position>();
var cursor = _db.Query(POSITION_TABLE_NAME, new[] { "id", "Ticker", "Shares", "PricePerShare" }, " ContainingPortfolioID = " + portfolioId, null, null, null, null);
var cursor = Db.Query(POSITION_TABLE_NAME, new[] { "id", "Ticker", "Shares", "PricePerShare" }, " ContainingPortfolioID = " + portfolioId, null, null, null, null);
if (cursor.Count > 0)
{
while (cursor.MoveToNext())
@ -157,23 +148,23 @@ namespace MonoStockPortfolio.Core.PortfolioRepositories
private void UpdateExistingPortfolio(Portfolio portfolio)
{
var portfolioID = portfolio.ID ?? -1;
Log.E("UpdateExistingPortfolio", "Portfolios updated: " + _db.Update(PORTFOLIO_TABLE_NAME, GetPortfolioContentValues(portfolio), "id = " + portfolioID, null));
Log.Error("UpdateExistingPortfolio", "Portfolios updated: " + Db.Update(PORTFOLIO_TABLE_NAME, GetPortfolioContentValues(portfolio), "id = " + portfolioID, null));
}
private void InsertNewPortfolio(Portfolio portfolio)
{
Log.E("InsertNewPortfolio", "Portfolios inserted: " + _db.Insert(PORTFOLIO_TABLE_NAME, null, GetPortfolioContentValues(portfolio)));
Log.Error("InsertNewPortfolio", "Portfolios inserted: " + Db.Insert(PORTFOLIO_TABLE_NAME, null, GetPortfolioContentValues(portfolio)));
}
private void UpdateExistingPosition(Position position)
{
var positionID = position.ID ?? -1;
Log.E("UpdateExistingPosition", "Positions updated: " + _db.Update(POSITION_TABLE_NAME, GetPositionContentValues(position), "id = " + positionID, null));
Log.Error("UpdateExistingPosition", "Positions updated: " + Db.Update(POSITION_TABLE_NAME, GetPositionContentValues(position), "id = " + positionID, null));
}
private void InsertNewPosition(Position position)
{
Log.E("InsertNewPosition", "Positions inserted: " + _db.Insert(POSITION_TABLE_NAME, null, GetPositionContentValues(position)));
Log.Error("InsertNewPosition", "Positions inserted: " + Db.Insert(POSITION_TABLE_NAME, null, GetPositionContentValues(position)));
}
private static ContentValues GetPortfolioContentValues(Portfolio portfolio)
@ -192,41 +183,5 @@ namespace MonoStockPortfolio.Core.PortfolioRepositories
positionValues.Put("ContainingPortfolioID", position.ContainingPortfolioID);
return positionValues;
}
private class OpenHelper : SQLiteOpenHelper
{
public OpenHelper(Context context, string name, SQLiteDatabase.ICursorFactory factory, int version)
: base(context, name, factory, version)
{
}
public override void OnCreate(SQLiteDatabase db)
{
db.ExecSQL("CREATE TABLE " + PORTFOLIO_TABLE_NAME + " (id INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT)");
db.ExecSQL("CREATE TABLE " + POSITION_TABLE_NAME + " (id INTEGER PRIMARY KEY AUTOINCREMENT, Ticker TEXT, Shares REAL, PricePerShare REAL, ContainingPortfolioID INT)");
db.ExecSQL("INSERT INTO " + PORTFOLIO_TABLE_NAME + " (Name) VALUES ('Sample portfolio')");
db.ExecSQL("INSERT INTO " + POSITION_TABLE_NAME + " (Ticker, Shares, PricePerShare, ContainingPortfolioID) VALUES ('GOOG', '500', '593.97', 1)");
db.ExecSQL("INSERT INTO " + POSITION_TABLE_NAME + " (Ticker, Shares, PricePerShare, ContainingPortfolioID) VALUES ('AMZN', '500', '180.00', 1)");
db.ExecSQL("INSERT INTO " + POSITION_TABLE_NAME + " (Ticker, Shares, PricePerShare, ContainingPortfolioID) VALUES ('AAPL', '500', '322.56', 1)");
db.ExecSQL("INSERT INTO " + POSITION_TABLE_NAME + " (Ticker, Shares, PricePerShare, ContainingPortfolioID) VALUES ('MSFT', '500', '27.91', 1)");
db.ExecSQL("INSERT INTO " + POSITION_TABLE_NAME + " (Ticker, Shares, PricePerShare, ContainingPortfolioID) VALUES ('NOVL', '500', '5.92', 1)");
db.ExecSQL("INSERT INTO " + POSITION_TABLE_NAME + " (Ticker, Shares, PricePerShare, ContainingPortfolioID) VALUES ('S', '500', '4.23', 1)");
db.ExecSQL("INSERT INTO " + POSITION_TABLE_NAME + " (Ticker, Shares, PricePerShare, ContainingPortfolioID) VALUES ('VZ', '500', '35.78', 1)");
db.ExecSQL("INSERT INTO " + POSITION_TABLE_NAME + " (Ticker, Shares, PricePerShare, ContainingPortfolioID) VALUES ('T', '500', '29.38', 1)");
}
public override void OnUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
Log.W("Upgrade", "Nothing to upgrade");
}
}
}
}

View file

@ -50,7 +50,7 @@ namespace MonoStockPortfolio.Core.Services
}
catch (Exception ex)
{
Log.E("GetDetailedItems", ex.ToString());
Log.Error("GetDetailedItems", ex.ToString());
throw;
}
}

View file

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.IO;
using Android.Util;
namespace MonoStockPortfolio.Core.StockData
{
public class CsvParser
{
public static IEnumerable<string[]> ParseCsvIntoStockQuotes(string csvText)
{
using (var sr = new StringReader(csvText))
{
var lines = new List<string[]>();
try
{
string line;
while ((line = sr.ReadLine()) != null)
{
var tokens = line.Split(',');
for (int i = 0; i < tokens.Length; i++)
{
tokens[i] = tokens[i].Trim('\"');
}
lines.Add(tokens);
}
}
catch (Exception)
{
Log.Error("ParseCSV", "Error in retrieving/parsing stock information");
}
return lines;
}
}
}
}

View file

@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using MonoStockPortfolio.Entities;
namespace MonoStockPortfolio.Core.StockData
{
public class GoogleStockDataProvider : IStockDataProvider
{
private const string BASE_URL = "http://www.google.com/finance/info?infotype=infoquoteall&q=";
/*
http://code.google.com/p/qsb-mac-plugins/source/browse/trunk/stock-quoter/trunk/StockQuoter.py?r=4
The Google Finance feed can return some or all of the following keys:
avvo * Average volume (float with multiplier, like '3.54M')
beta * Beta (float)
c * Amount of change while open (float)
ccol * (unknown) (chars)
cl Last perc. change
cp * Change perc. while open (float)
e * Exchange (text, like 'NASDAQ')
ec * After hours last change from close (float)
eccol * (unknown) (chars)
ecp * After hours last chage perc. from close (float)
el * After. hours last quote (float)
el_cur * (unknown) (float)
elt After hours last quote time (unknown)
eo * Exchange Open (0 or 1)
eps * Earnings per share (float)
fwpe Forward PE ratio (float)
hi * Price high (float)
hi52 * 52 weeks high (float)
id * Company id (identifying number)
l * Last value while open (float)
l_cur * Last value at close (like 'l')
lo * Price low (float)
lo52 * 52 weeks low (float)
lt Last value date/time
ltt Last trade time (Same as "lt" without the data)
mc * Market cap. (float with multiplier, like '123.45B')
name * Company name (text)
op * Open price (float)
pe * PE ratio (float)
t * Ticker (text)
type * Type (i.e. 'Company')
vo * Volume (float with multiplier, like '3.54M')
*/
public IEnumerable<StockQuote> GetStockQuotes(IEnumerable<string> tickers)
{
var tickerCsv = string.Join(",", tickers.ToArray());
var url = BASE_URL + tickerCsv;
var jsonResults = ScrapeUrl(url).Split('}');
return jsonResults.Select(MapJsonToStockitems);
}
protected StockQuote MapJsonToStockitems(string jsonResults)
{
using(var sr = new StringReader(jsonResults))
{
var sq = new StockQuote();
string line;
while((line = sr.ReadLine()) != null)
{
if(line.StartsWith(",\"t\""))
{
sq.Ticker = line.Replace(",\"t\" : ", "").Trim().Trim('"');
continue;
}
if(line.StartsWith(",\"c\""))
{
sq.Change = decimal.Parse(line.Replace(",\"c\" : ", "").Trim().Trim('"'));
continue;
}
if(line.StartsWith(",\"l\""))
{
sq.LastTradePrice = decimal.Parse(line.Replace(",\"l\" : ", "").Trim().Trim('"'));
continue;
}
if(line.StartsWith(",\"ltt\""))
{
sq.LastTradeTime = line.Replace(",\"ltt\":", "").Trim().Trim('"').Replace("EST","").Trim();
continue;
}
}
return sq;
}
}
private static string ScrapeUrl(string url)
{
string resultCsv;
var req = WebRequest.Create(url);
var resp = req.GetResponse();
using (var sr = new StreamReader(resp.GetResponseStream()))
{
resultCsv = sr.ReadToEnd();
sr.Close();
}
return resultCsv;
}
}
}

View file

@ -1,29 +0,0 @@
using FileHelpers;
namespace MonoStockPortfolio.Core.StockData
{
[DelimitedRecord(",")]
public class YahooFinanceStockData
{
[FieldQuoted(QuoteMode.OptionalForBoth)]
public string Ticker;
public decimal LastTradePrice;
[FieldQuoted(QuoteMode.OptionalForBoth)]
public string Name;
public string Volume;
public decimal Change;
[FieldQuoted(QuoteMode.OptionalForBoth)]
public string LastTradeTime;
[FieldQuoted(QuoteMode.OptionalForBoth)]
public string RealTimeLastTradeWithTime;
[FieldQuoted(QuoteMode.OptionalForBoth)]
public string ChangeRealTime;
}
}

View file

@ -1,105 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using Android.Util;
using FileHelpers;
using MonoStockPortfolio.Entities;
namespace MonoStockPortfolio.Core.StockData
{
public class YahooStockDataProvider : IStockDataProvider
{
private const string LAST_TRADE_PRICE_ONLY = "l1";
private const string NAME = "n";
private const string VOLUME = "v";
private const string TICKER_SYMBOL = "s";
private const string CHANGE = "c1";
private const string LAST_TRADE_TIME = "t1";
private const string REAL_TIME_LAST_TRADE_WITH_TIME = "k1";
private const string REAL_TIME_CHANGE = "c6";
// http://www.gummy-stuff.org/Yahoo-data.htm
// http://finance.yahoo.com/d/quotes.csv?s= a BUNCH of
// STOCK SYMBOLS separated by "+" &f=a bunch of special tags
public IEnumerable<StockQuote> GetStockQuotes(IEnumerable<string> tickers)
{
string url = "http://finance.yahoo.com/d/quotes.csv?s=";
url += string.Join("+", tickers.ToArray());
url += "&f=";
url += TICKER_SYMBOL;
url += LAST_TRADE_PRICE_ONLY;
url += NAME;
url += VOLUME;
url += CHANGE;
url += LAST_TRADE_TIME;
url += REAL_TIME_LAST_TRADE_WITH_TIME;
url += REAL_TIME_CHANGE;
string resultCsv = ScrapeUrl(url);
var yahooQuoteData = ParseCsvIntoStockQuotes(resultCsv);
foreach (var quote in yahooQuoteData)
{
yield return MapYahooData(quote);
}
}
private static StockQuote MapYahooData(YahooFinanceStockData data)
{
if(data == null)
{
return null;
}
var stock = new StockQuote();
stock.Name = data.Name;
stock.LastTradePrice = data.LastTradePrice;
stock.Ticker = data.Ticker;
stock.Volume = data.Volume;
stock.Change = data.Change;
stock.LastTradeTime = data.LastTradeTime;
stock.RealTimeLastTradePrice = decimal.Parse(data.RealTimeLastTradeWithTime
.Replace("<b>", "")
.Replace("</b>", "")
.Replace("N/A -", "")
.Trim()
);
stock.ChangeRealTime = data.ChangeRealTime;
return stock;
}
private static IList<YahooFinanceStockData> ParseCsvIntoStockQuotes(string csv)
{
var engine = new FileHelperEngine(typeof(YahooFinanceStockData));
var stockQuotes = engine.ReadString(csv) as YahooFinanceStockData[];
if(stockQuotes == null)
{
throw new ArgumentException("Could not parse CSV input");
}
return stockQuotes;
}
private static string ScrapeUrl(string url)
{
try
{
string resultCsv;
var req = WebRequest.Create(url);
var resp = req.GetResponse();
using (var sr = new StreamReader(resp.GetResponseStream()))
{
resultCsv = sr.ReadToEnd();
sr.Close();
}
return resultCsv;
}
catch (Exception ex)
{
Log.E("ScrapeUrlException", ex.ToString());
throw;
}
}
}
}

View file

@ -31,7 +31,6 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />

View file

@ -3,22 +3,22 @@ namespace MonoStockPortfolio.Entities
public enum StockDataItem
{
[StringValue("Change")]
Change,
Change = 0,
[StringValue("Gain/Loss")]
GainLoss,
GainLoss = 1,
[StringValue("Ticker")]
Ticker,
Ticker = 2,
[StringValue("Time")]
Time,
Time = 3,
[StringValue("Volume")]
Volume,
Volume = 4,
[StringValue("Price")]
LastTradePrice,
LastTradePrice = 5,
[StringValue("Price-RT")]
RealTimeLastTradeWithTime,
RealTimeLastTradeWithTime = 6,
[StringValue("Change-RT")]
ChangeRealTime,
ChangeRealTime = 7,
[StringValue("Gain/Loss-RT")]
GainLossRealTime
GainLossRealTime = 8
}
}

View file

@ -0,0 +1,47 @@
using System.Linq;
using MonoStockPortfolio.Core.StockData;
using Xunit;
namespace MonoStockPortfolio.Tests
{
public class CsvParserTests
{
[Fact]
public void Can_parse_a_single_line_with_a_single_token()
{
var line = "XIN";
var result = CsvParser.ParseCsvIntoStockQuotes(line);
Assert.Equal(result.Count(), 1);
Assert.Equal(result.First()[0], "XIN");
}
[Fact]
public void Can_parse_two_lines_with_a_single_token_each()
{
var line = "XIN\nMSFT";
var result = CsvParser.ParseCsvIntoStockQuotes(line);
Assert.Equal(result.Count(), 2);
Assert.Equal(result.First()[0], "XIN");
Assert.Equal(result.ElementAt(1)[0], "MSFT");
}
[Fact]
public void Can_parse_a_more_complex_set()
{
var line = @"""XIN"",2.41,""Xinyuan Real Esta"",269244,-0.03,""4:00pm"",""N/A - <b>2.41</b>"",""-0.03""";
var result = CsvParser.ParseCsvIntoStockQuotes(line);
Assert.Equal(result.Count(), 1);
Assert.Equal(result.First()[0], "XIN");
Assert.Equal(result.First()[1], "2.41");
Assert.Equal(result.First()[2], "Xinyuan Real Esta");
Assert.Equal(result.First()[3], "269244");
Assert.Equal(result.First()[4], "-0.03");
Assert.Equal(result.First()[5], "4:00pm");
Assert.Equal(result.First()[6], "N/A - <b>2.41</b>");
Assert.Equal(result.First()[7], "-0.03");
}
}
}

View file

@ -0,0 +1,170 @@
using MonoStockPortfolio.Core.StockData;
using Xunit;
namespace MonoStockPortfolio.Tests
{
public class GoogleStockQuoteTests : GoogleStockDataProvider
{
#region ExampleJson : Example Json Result
private string ExampleJson =
@"
// [ {
""id"": ""720780""
,""t"" : ""XIN""
,""e"" : ""NYSE""
,""l"" : ""2.41""
,""l_cur"" : ""2.41""
,""s"": ""0""
,""ltt"":""4:00PM EST""
,""lt"" : ""Feb 4, 4:00PM EST""
,""c"" : ""-0.03""
,""cp"" : ""-1.23""
,""ccol"" : ""chr""
,""eo"" : """"
,""delay"": """"
,""op"" : ""2.45""
,""hi"" : ""2.45""
,""lo"" : ""2.39""
,""vo"" : ""269,244.00""
,""avvo"" : ""352,270.00""
,""hi52"" : ""4.30""
,""lo52"" : ""2.20""
,""mc"" : ""182.78M""
,""pe"" : ""3.47""
,""fwpe"" : """"
,""beta"" : ""1.30""
,""eps"" : ""0.69""
,""name"" : ""Xinyuan Real Estate Co., Ltd. (ADR)""
,""type"" : ""Company""
}
,{
""id"": ""358464""
,""t"" : ""MSFT""
,""e"" : ""NASDAQ""
,""l"" : ""27.77""
,""l_cur"" : ""27.77""
,""s"": ""2""
,""ltt"":""4:01PM EST""
,""lt"" : ""Feb 4, 4:01PM EST""
,""c"" : ""+0.12""
,""cp"" : ""0.43""
,""ccol"" : ""chg""
,""el"": ""27.72""
,""el_cur"": ""27.72""
,""elt"" : ""Feb 4, 7:39PM EST""
,""ec"" : ""-0.05""
,""ecp"" : ""-0.18""
,""eccol"" : ""chr""
,""div"" : ""0.16""
,""yld"" : ""2.30""
,""eo"" : """"
,""delay"": """"
,""op"" : ""27.73""
,""hi"" : ""27.84""
,""lo"" : ""27.51""
,""vo"" : ""40.42M""
,""avvo"" : ""55.50M""
,""hi52"" : ""31.58""
,""lo52"" : ""22.73""
,""mc"" : ""233.33B""
,""pe"" : ""11.77""
,""fwpe"" : """"
,""beta"" : ""1.06""
,""eps"" : ""2.36""
,""name"" : ""Microsoft Corporation""
,""type"" : ""Company""
}
,{
""id"": ""22144""
,""t"" : ""AAPL""
,""e"" : ""NASDAQ""
,""l"" : ""346.50""
,""l_cur"" : ""346.50""
,""s"": ""2""
,""ltt"":""4:02PM EST""
,""lt"" : ""Feb 4, 4:02PM EST""
,""c"" : ""+3.06""
,""cp"" : ""0.89""
,""ccol"" : ""chg""
,""el"": ""346.48""
,""el_cur"": ""346.48""
,""elt"" : ""Feb 4, 7:59PM EST""
,""ec"" : ""-0.02""
,""ecp"" : ""-0.01""
,""eccol"" : ""chr""
,""div"" : """"
,""yld"" : """"
,""eo"" : """"
,""delay"": """"
,""op"" : ""343.76""
,""hi"" : ""346.70""
,""lo"" : ""343.51""
,""vo"" : ""11.49M""
,""avvo"" : ""15.58M""
,""hi52"" : ""348.60""
,""lo52"" : ""190.85""
,""mc"" : ""319.22B""
,""pe"" : ""19.35""
,""fwpe"" : """"
,""beta"" : ""1.38""
,""eps"" : ""17.91""
,""name"" : ""Apple Inc.""
,""type"" : ""Company""
}
]
";
#endregion
private string[] SplitResults
{
get
{
return ExampleJson.Split('}');
}
}
[Fact]
public void Test_ticker()
{
var results = base.MapJsonToStockitems(SplitResults[0]);
Assert.Equal(results.Ticker,"XIN");
results = base.MapJsonToStockitems(SplitResults[1]);
Assert.Equal(results.Ticker,"MSFT");
results = base.MapJsonToStockitems(SplitResults[2]);
Assert.Equal(results.Ticker,"AAPL");
}
[Fact]
public void Test_change()
{
var results = base.MapJsonToStockitems(SplitResults[0]);
Assert.Equal(results.Change, -0.03M);
results = base.MapJsonToStockitems(SplitResults[1]);
Assert.Equal(results.Change, 0.12M);
results = base.MapJsonToStockitems(SplitResults[2]);
Assert.Equal(results.Change, 3.06M);
}
[Fact]
public void Test_last_price()
{
var results = base.MapJsonToStockitems(SplitResults[0]);
Assert.Equal(results.LastTradePrice, 2.41M);
results = base.MapJsonToStockitems(SplitResults[1]);
Assert.Equal(results.LastTradePrice, 27.77M);
results = base.MapJsonToStockitems(SplitResults[2]);
Assert.Equal(results.LastTradePrice, 346.50M);
}
[Fact]
public void Test_time()
{
var results = base.MapJsonToStockitems(SplitResults[0]);
Assert.Equal(results.LastTradeTime, "4:00PM");
results = base.MapJsonToStockitems(SplitResults[1]);
Assert.Equal(results.LastTradeTime, "4:01PM");
results = base.MapJsonToStockitems(SplitResults[2]);
Assert.Equal(results.LastTradeTime, "4:02PM");
}
}
}

View file

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{C2797FAB-AFAB-49F6-9131-FC9BF03CAB9D}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MonoStockPortfolio.Tests</RootNamespace>
<AssemblyName>MonoStockPortfolio.Tests</AssemblyName>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Android">
<Private>True</Private>
</Reference>
<Reference Include="mscorlib">
<Private>True</Private>
</Reference>
<Reference Include="System">
<Private>True</Private>
</Reference>
<Reference Include="System.Core">
<Private>True</Private>
</Reference>
<Reference Include="System.Xml.Linq">
<Private>True</Private>
</Reference>
<Reference Include="System.Xml">
<Private>True</Private>
</Reference>
<Reference Include="xunit">
<HintPath>..\libs\xunit.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CsvParserTests.cs" />
<Compile Include="GoogleStockQuoteTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MonoStockPortfolio.Core\MonoStockPortfolio.Core.csproj">
<Project>{251E7BB4-CFE2-4DE4-9E2A-AAE1AF41C8CB}</Project>
<Name>MonoStockPortfolio.Core</Name>
</ProjectReference>
<ProjectReference Include="..\MonoStockPortfolio.Entities\MonoStockPortfolio.Entities.csproj">
<Project>{05A57650-3B41-46FF-9EAD-9112B5EFBEED}</Project>
<Name>MonoStockPortfolio.Entities</Name>
</ProjectReference>
<ProjectReference Include="..\MonoStockPortfolio\MonoStockPortfolio.csproj">
<Project>{E23D8575-CE4E-4716-B9C7-70115D23ADBB}</Project>
<Name>MonoStockPortfolio</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="PostSharp.Custom.targets" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Novell\Novell.MonoDroid.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PostSharpAssemblyBindingPolicySet>Silverlight20</PostSharpAssemblyBindingPolicySet>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,29 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("MonoStockPortfolio.Tests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("MonoStockPortfolio.Tests")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2011")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -8,15 +8,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "libs", "libs", "{643BA3D4-E
libs\FileHelpers.dll = libs\FileHelpers.dll
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{4EE7F6AD-B8A9-4402-800E-E4C8AE0FF8FB}"
ProjectSection(SolutionItems) = preProject
assets\UInotes.txt = assets\UInotes.txt
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoStockPortfolio.Core", "MonoStockPortfolio.Core\MonoStockPortfolio.Core.csproj", "{251E7BB4-CFE2-4DE4-9E2A-AAE1AF41C8CB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoStockPortfolio.Entities", "MonoStockPortfolio.Entities\MonoStockPortfolio.Entities.csproj", "{05A57650-3B41-46FF-9EAD-9112B5EFBEED}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoStockPortfolio.Tests", "MonoStockPortfolio.Tests\MonoStockPortfolio.Tests.csproj", "{C2797FAB-AFAB-49F6-9131-FC9BF03CAB9D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -35,6 +32,10 @@ Global
{05A57650-3B41-46FF-9EAD-9112B5EFBEED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{05A57650-3B41-46FF-9EAD-9112B5EFBEED}.Release|Any CPU.ActiveCfg = Release|Any CPU
{05A57650-3B41-46FF-9EAD-9112B5EFBEED}.Release|Any CPU.Build.0 = Release|Any CPU
{C2797FAB-AFAB-49F6-9131-FC9BF03CAB9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C2797FAB-AFAB-49F6-9131-FC9BF03CAB9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C2797FAB-AFAB-49F6-9131-FC9BF03CAB9D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C2797FAB-AFAB-49F6-9131-FC9BF03CAB9D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

@ -0,0 +1,70 @@
using System.Collections.Generic;
using System.Linq;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Preferences;
using MonoStockPortfolio.Core.Config;
using MonoStockPortfolio.Entities;
using MonoStockPortfolio.Framework;
using MonoStockPortfolio.Core;
namespace MonoStockPortfolio.Activites
{
[Activity(Label = "Config")]
public class ConfigActivity : PreferenceActivity
{
[IoC] private IConfigRepository _repo;
private StockItemPreference[] _stockItemsConfig;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
AddPreferencesFromResource(Resource.Layout.config);
_stockItemsConfig = StockItemPreference.BuildList(_repo.GetStockItems()).ToArray();
var customPref = FindPreference("customStockItems");
customPref.PreferenceClick += customPref_PreferenceClick;
}
bool customPref_PreferenceClick(Preference preference)
{
IEnumerable<char>[] stockItemsDisplay = _stockItemsConfig.OrderBy(i => i.StockDataItem).Select(i => i.StockDataItem.GetStringValue()).ToArray();
bool[] allitemschecked = _stockItemsConfig.OrderBy(i => i.StockDataItem).Select(i => i.IsChecked).ToArray();
var dialog = new AlertDialog.Builder(this);
dialog.SetMultiChoiceItems(stockItemsDisplay, allitemschecked, clickCallback);
dialog.SetTitle("Select columns");
dialog.SetPositiveButton("Save", okCallback);
dialog.Create().Show();
return true;
}
private void okCallback(object sender, DialogClickEventArgs e)
{
var list = _stockItemsConfig.Where(i => i.IsChecked).Select(i => i.StockDataItem).ToList();
_repo.UpdateStockItems(list);
}
private void clickCallback(object sender, DialogMultiChoiceClickEventArgs e)
{
_stockItemsConfig[e.Which].IsChecked = e.IsChecked;
}
public static string ClassName { get { return "monostockportfolio.activites.ConfigActivity"; } }
private class StockItemPreference
{
public static IEnumerable<StockItemPreference> BuildList(IEnumerable<StockDataItem> checkedItems)
{
var allitems = StockDataItem.Change.GetValues<StockDataItem>();
return allitems.Select(item => new StockItemPreference {StockDataItem = item, IsChecked = checkedItems.Contains(item)});
}
public StockDataItem StockDataItem { get; private set; }
public bool IsChecked { get; set; }
}
}
}

View file

@ -16,7 +16,7 @@ namespace MonoStockPortfolio.Activites
{
base.OnCreate(bundle);
SetContentView(Resource.layout.addportfolio);
SetContentView(Resource.Layout.addportfolio);
WireUpEvents();

View file

@ -8,7 +8,7 @@ namespace MonoStockPortfolio.Activites
public static string Extra_PortfolioID { get { return "monoStockPortfolio.EditPortfolioActivity.PortfolioID"; } }
public long ThisPortfolioId { get { return Intent.GetLongExtra(Extra_PortfolioID, -1); } }
protected Button SaveButton { get { return FindViewById<Button>(Resource.id.btnSave); } }
protected EditText PortfolioName { get { return FindViewById<EditText>(Resource.id.portfolioName); } }
protected Button SaveButton { get { return FindViewById<Button>(Resource.Id.btnSave); } }
protected EditText PortfolioName { get { return FindViewById<EditText>(Resource.Id.portfolioName); } }
}
}

View file

@ -16,7 +16,7 @@ namespace MonoStockPortfolio.Activites
{
base.OnCreate(bundle);
SetContentView(Resource.layout.addposition);
SetContentView(Resource.Layout.addposition);
var positionId = ThisPositionId;
if (positionId != -1)

View file

@ -10,9 +10,9 @@ namespace MonoStockPortfolio.Activites
public static string Extra_PositionID { get { return "monoStockPortfolio.EditPositionActivity.PositionID"; } }
public long ThisPositionId { get { return Intent.GetLongExtra(Extra_PositionID, -1); } }
protected EditText TickerTextBox { get { return FindViewById<EditText>(Resource.id.addPositionTicker); } }
protected EditText PriceTextBox { get { return FindViewById<EditText>(Resource.id.addPositionPrice); } }
protected EditText SharesTextBox { get { return FindViewById<EditText>(Resource.id.addPositionShares); } }
protected Button SaveButton { get { return FindViewById<Button>(Resource.id.addPositionSaveButton); } }
protected EditText TickerTextBox { get { return FindViewById<EditText>(Resource.Id.addPositionTicker); } }
protected EditText PriceTextBox { get { return FindViewById<EditText>(Resource.Id.addPositionPrice); } }
protected EditText SharesTextBox { get { return FindViewById<EditText>(Resource.Id.addPositionShares); } }
protected Button SaveButton { get { return FindViewById<Button>(Resource.Id.addPositionSaveButton); } }
}
}

View file

@ -4,6 +4,7 @@ using System.Linq;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Views;
using Android.Widget;
using MonoStockPortfolio.Core.PortfolioRepositories;
using MonoStockPortfolio.Entities;
@ -22,7 +23,7 @@ namespace MonoStockPortfolio.Activites
{
base.OnCreate(bundle);
SetContentView(Resource.layout.main);
SetContentView(Resource.Layout.main);
RefreshList();
@ -33,7 +34,7 @@ namespace MonoStockPortfolio.Activites
{
_portfolios = _repo.GetAllPortfolios();
var listAdapter = new ArrayAdapter<string>(this, Android.R.Layout.SimpleListItem1, _portfolios.Select(p => p.Name).ToList());
var listAdapter = new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItem1, _portfolios.Select(p => p.Name).ToList());
PortfolioListView.Adapter = listAdapter;
}
@ -79,6 +80,32 @@ namespace MonoStockPortfolio.Activites
return base.OnContextItemSelected(item);
}
public override bool OnCreateOptionsMenu(IMenu menu)
{
var configItem = menu.Add(0, 1, 1, "Config");
configItem.SetIcon(Android.Resource.Drawable.IcMenuPreferences);
var exitItem = menu.Add(0, 1, 1, "Exit");
exitItem.SetIcon(Android.Resource.Drawable.IcMenuCloseClearCancel);
return true;
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
switch (item.Title.ToS())
{
case "Config":
var intent = new Intent();
intent.SetClassName(this, ConfigActivity.ClassName);
StartActivityForResult(intent, 0);
return true;
case "Exit":
Finish();
return true;
default:
return base.OnOptionsItemSelected(item);
}
}
private void listView_ItemClick(object sender, ItemEventArgs e)
{
var intent = new Intent();

View file

@ -6,7 +6,7 @@ namespace MonoStockPortfolio.Activites
{
public static string ClassName { get { return "monostockportfolio.activites.MainActivity"; } }
protected Button AddPortfolioButton { get { return FindViewById<Button>(Resource.id.btnAddPortfolio); } }
protected ListView PortfolioListView { get { return FindViewById<ListView>(Resource.id.portfolioList); } }
protected Button AddPortfolioButton { get { return FindViewById<Button>(Resource.Id.btnAddPortfolio); } }
protected ListView PortfolioListView { get { return FindViewById<ListView>(Resource.Id.portfolioList); } }
}
}

View file

@ -27,7 +27,7 @@ namespace MonoStockPortfolio.Activites
{
base.OnCreate(bundle);
SetContentView(Resource.layout.portfolio);
SetContentView(Resource.Layout.portfolio);
Refresh();
@ -39,7 +39,7 @@ namespace MonoStockPortfolio.Activites
public override bool OnCreateOptionsMenu(IMenu menu)
{
var item = menu.Add(0,1,1,"Refresh");
item.SetIcon(Resource.drawable.ic_menu_refresh);
item.SetIcon(Resource.Drawable.ic_menu_refresh);
return true;
}
@ -115,7 +115,7 @@ namespace MonoStockPortfolio.Activites
private void ShowMessage(string message)
{
var listAdapter = new ArrayAdapter<string>(this, Android.R.Layout.SimpleListItem1, new[] {message});
var listAdapter = new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItem1, new[] { message });
QuoteListview.Adapter = listAdapter;
}
@ -134,10 +134,10 @@ namespace MonoStockPortfolio.Activites
var textItem = new TextView(this);
textItem.Text = stockDataItem.GetStringValue();
textItem.SetWidth(cellwidth);
textItem.SetTextColor(Resources.GetColor(Android.R.Color.Black));
textItem.SetTextColor(Resources.GetColor(Android.Resource.Color.Black));
QuoteListviewHeader.AddView(textItem);
}
QuoteListviewHeader.SetBackgroundResource(Android.R.Color.BackgroundLight);
QuoteListviewHeader.SetBackgroundResource(Android.Resource.Color.BackgroundLight);
}
public class PositionArrayAdapter : GenericArrayAdapter<PositionResultsViewModel>

View file

@ -9,8 +9,8 @@ namespace MonoStockPortfolio.Activites
public static string Extra_PortfolioID { get { return "monoStockPortfolio.PortfolioActivity.PortfolioID"; } }
public long ThisPortofolioId { get { return Intent.GetLongExtra(Extra_PortfolioID, -1); } }
protected ListView QuoteListview { get { return FindViewById<ListView>(Resource.id.quoteListview); } }
protected Button AddPositionButton { get { return FindViewById<Button>(Resource.id.btnAddPosition); } }
protected LinearLayout QuoteListviewHeader { get { return FindViewById<LinearLayout>(Resource.id.quoteHeaderLayout); } }
protected ListView QuoteListview { get { return FindViewById<ListView>(Resource.Id.quoteListview); } }
protected Button AddPositionButton { get { return FindViewById<Button>(Resource.Id.btnAddPosition); } }
protected LinearLayout QuoteListviewHeader { get { return FindViewById<LinearLayout>(Resource.Id.quoteHeaderLayout); } }
}
}

View file

@ -5,18 +5,18 @@ namespace MonoStockPortfolio.Framework
{
public class IoCAttribute : LocationInterceptionAspect
{
public override void OnGetValue(LocationInterceptionArgs args)
public sealed override void OnGetValue(LocationInterceptionArgs args)
{
if (ServiceLocator.Context == null)
{
var activityContext= (Context)args.Instance;
var activityContext = (Context)args.Instance;
ServiceLocator.Context = activityContext.ApplicationContext.ApplicationContext;
}
var locationType = args.Location.LocationType;
var instantiation = ServiceLocator.Get(locationType);
if(instantiation != null)
if (instantiation != null)
{
args.SetNewValue(instantiation);
}

View file

@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace MonoStockPortfolio.Framework
{
public static class IttyBittyIoC
{
[AttributeUsage(AttributeTargets.Constructor)]
public class InjectionConstructorAttribute : Attribute
{ }
private enum DependencyType
{
None = 0, // Type is unset
Delegate, // A builder function
Instance, // A specific instance
Singleton, // Dynamically created singleton
Transient // Dynamically created transient object
}
private class DependencyInfo
{
public object Dependency { get; private set; }
public DependencyType DependencyType { get; private set; }
public DependencyInfo(DependencyType dependencyType, object dependency)
{
DependencyType = dependencyType;
Dependency = dependency;
}
}
private readonly static IDictionary<Type, DependencyInfo> dependencies = new Dictionary<Type, DependencyInfo>();
private readonly static IDictionary<Type, object> instances = new Dictionary<Type, object>();
public static void Register<TContract>(TContract instance)
{
dependencies[typeof(TContract)] = new DependencyInfo(DependencyType.Instance, instance);
instances[typeof(TContract)] = instance;
}
public static void Register<TContract, TImplementation>()
{
Register<TContract, TImplementation>(false);
}
public static void Register<TContract, TImplementation>(bool isSingleton)
{
DependencyType dependencyType = isSingleton ? DependencyType.Singleton : DependencyType.Transient;
dependencies[typeof(TContract)] = new DependencyInfo(dependencyType, typeof(TImplementation));
}
public static void Register<TContract>(Func<TContract> builder)
{
dependencies[typeof(TContract)] = new DependencyInfo(DependencyType.Delegate, builder);
}
public static TContract Resolve<TContract>()
{
return (TContract)Resolve(typeof(TContract));
}
public static object Resolve(Type contract)
{
if (!dependencies.ContainsKey(contract))
throw new InvalidOperationException(string.Format("Unable to resolve type '{0}'.", contract));
if (instances.ContainsKey(contract))
return instances[contract];
var dependency = dependencies[contract];
if (dependency.DependencyType == DependencyType.Delegate)
return ((Delegate)dependency.Dependency).DynamicInvoke();
var constructorInfo = ((Type)dependency.Dependency).GetConstructors()
.OrderByDescending(o => (o.GetCustomAttributes(typeof(InjectionConstructorAttribute), false).Count()))
.ThenByDescending(o => (o.GetParameters().Length))
.First();
var parameterInfos = constructorInfo.GetParameters();
object instance;
if (parameterInfos.Length == 0)
{
instance = Activator.CreateInstance((Type)dependency.Dependency);
}
else
{
var parameters = new List<object>(parameterInfos.Length);
foreach (ParameterInfo parameterInfo in parameterInfos)
{
parameters.Add(Resolve(parameterInfo.ParameterType));
}
instance = constructorInfo.Invoke(parameters.ToArray());
}
if (dependency.DependencyType == DependencyType.Singleton)
{
instances[contract] = instance;
}
return instance;
}
}
}

View file

@ -4,35 +4,25 @@ using MonoStockPortfolio.Core.Config;
using MonoStockPortfolio.Core.PortfolioRepositories;
using MonoStockPortfolio.Core.Services;
using MonoStockPortfolio.Core.StockData;
using TinyIoC;
namespace MonoStockPortfolio.Framework
{
public static class ServiceLocator
{
public static Context Context { get; set; }
private static TinyIoCContainer _container;
static ServiceLocator()
{
//IttyBittyIoC.Register(Context);
IttyBittyIoC.Register<IStockDataProvider>(() => new GoogleStockDataProvider());
IttyBittyIoC.Register<IPortfolioService>(() => new PortfolioService(new AndroidSqlitePortfolioRepository(Context), new GoogleStockDataProvider()));
IttyBittyIoC.Register<IPortfolioRepository>(() => new AndroidSqlitePortfolioRepository(Context));
IttyBittyIoC.Register<IConfigRepository>(() => new AndroidSqliteConfigRepository(Context));
}
public static object Get(Type serviceType)
{
if (_container == null)
{
_container = RegisterTypes();
}
return _container.Resolve(serviceType);
}
private static TinyIoCContainer RegisterTypes()
{
var container = TinyIoCContainer.Current;
container.Register<Context>(Context);
container.Register<IStockDataProvider, YahooStockDataProvider>().AsMultiInstance();
container.Register<IPortfolioService, PortfolioService>().AsMultiInstance();
container.Register<IPortfolioRepository, AndroidSqlitePortfolioRepository>().AsMultiInstance();
container.Register<IConfigRepository, AndroidSqliteConfigRepository>().AsMultiInstance();
return container;
return IttyBittyIoC.Resolve(serviceType);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -40,7 +40,7 @@
<Reference Include="mscorlib" />
<Reference Include="PostSharp.SL, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b13fd38b8f9c99d7, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\libs\PostSharp.SL.dll</HintPath>
<HintPath>..\..\..\Program Files (x86)\PostSharp 2.0\Reference Assemblies\Silverlight 3.0\PostSharp.SL.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
@ -48,6 +48,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Activites\ConfigActivity.cs" />
<Compile Include="Activites\EditPositionActivity.designer.cs">
<DependentUpon>EditPositionActivity.cs</DependentUpon>
</Compile>
@ -58,7 +59,6 @@
<Compile Include="Framework\ActivityExtensions.cs" />
<Compile Include="Framework\ContextExtensions.cs" />
<Compile Include="Framework\GenericArrayAdapter.cs" />
<Compile Include="Framework\IoCAttribute.cs" />
<Compile Include="Activites\MainActivity.designer.cs">
<DependentUpon>MainActivity.cs</DependentUpon>
</Compile>
@ -68,17 +68,16 @@
<Compile Include="Activites\PortfolioActivity.designer.cs">
<DependentUpon>PortfolioActivity.cs</DependentUpon>
</Compile>
<Compile Include="Framework\IoCAttribute.cs" />
<Compile Include="Framework\IttyBittyIoC.cs" />
<Compile Include="Framework\ServiceLocator.cs" />
<Compile Include="Framework\TinyIoC.cs" />
<Compile Include="Resources\Resource.Designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Framework\StringExtensions.cs" />
<Compile Include="Framework\FormValidator.cs" />
</ItemGroup>
<ItemGroup>
<None Include="PostSharp.Custom.targets">
<SubType>Designer</SubType>
</None>
<None Include="PostSharp.Custom.targets" />
<None Include="Resources\AboutResources.txt" />
</ItemGroup>
<ItemGroup>
@ -117,15 +116,27 @@
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\layout\config.xml" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\ic_menu_refresh.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\icon.png" />
<AndroidResource Include="Resources\drawable-ldpi\icon.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\ic_menu_refresh.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\icon.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\ic_menu_refresh.png" />
<AndroidResource Include="Resources\drawable-ldpi\ic_menu_refresh.png" />
<AndroidResource Include="Resources\drawable-mdpi\ic_menu_refresh.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-ldpi\icon.png" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Novell\Novell.MonoDroid.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View file

@ -15,15 +15,15 @@ namespace MonoStockPortfolio
public partial class Resource
{
public partial class attr
public partial class Attribute
{
private attr()
private Attribute()
{
}
}
public partial class drawable
public partial class Drawable
{
// aapt resource value: 0x7f020000
@ -32,12 +32,12 @@ namespace MonoStockPortfolio
// aapt resource value: 0x7f020001
public const int icon = 2130837505;
private drawable()
private Drawable()
{
}
}
public partial class id
public partial class Id
{
// aapt resource value: 0x7f050004
@ -73,12 +73,12 @@ namespace MonoStockPortfolio
// aapt resource value: 0x7f05000a
public const int quoteListview = 2131034122;
private id()
private Id()
{
}
}
public partial class layout
public partial class Layout
{
// aapt resource value: 0x7f030000
@ -88,17 +88,20 @@ namespace MonoStockPortfolio
public const int addposition = 2130903041;
// aapt resource value: 0x7f030002
public const int main = 2130903042;
public const int config = 2130903042;
// aapt resource value: 0x7f030003
public const int portfolio = 2130903043;
public const int main = 2130903043;
private layout()
// aapt resource value: 0x7f030004
public const int portfolio = 2130903044;
private Layout()
{
}
}
public partial class @string
public partial class String
{
// aapt resource value: 0x7f040001
@ -107,7 +110,7 @@ namespace MonoStockPortfolio
// aapt resource value: 0x7f040000
public const int hello = 2130968576;
private @string()
private String()
{
}
}

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="General Settings">
<Preference
android:title="Stock Items"
android:summary="Which columns do you want to see?"
android:key="customStockItems" />
</PreferenceCategory>
</PreferenceScreen>

View file

@ -4,16 +4,11 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout android:layout_width="fill_parent"
android:layout_height="fill_parent" android:orientation="vertical">
<Button android:text="Add New Portfolio" android:layout_height="wrap_content"
android:layout_width="fill_parent" android:id="@+id/btnAddPortfolio" />
<Button android:text="Add New Portfolio" android:layout_height="wrap_content"
android:layout_width="fill_parent" android:id="@+id/btnAddPortfolio" />
<ListView android:id="@+id/portfolioList"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<ListView android:id="@+id/portfolioList"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>

BIN
libs/xunit.dll Normal file

Binary file not shown.