diff --git a/.gitignore b/.gitignore index 9e9b2a7..b86dd92 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,7 @@ */*/*/bin/* */*/*/obj/* *_ReSharper.*/ + +*/*/Resource.designer.cs +*/*/*/Resource.designer.cs +*/*/*/*/Resource.designer.cs \ No newline at end of file diff --git a/TaskyProCloned/Dependencies/Community.CsharpSqlite/Community.CsharpSqlite.WP7.dll b/TaskyProCloned/Dependencies/Community.CsharpSqlite/Community.CsharpSqlite.WP7.dll new file mode 100644 index 0000000..80a162f Binary files /dev/null and b/TaskyProCloned/Dependencies/Community.CsharpSqlite/Community.CsharpSqlite.WP7.dll differ diff --git a/TaskyProCloned/Dependencies/Community.CsharpSqlite/Community.CsharpSqlite.WP7.pdb b/TaskyProCloned/Dependencies/Community.CsharpSqlite/Community.CsharpSqlite.WP7.pdb new file mode 100644 index 0000000..2575615 Binary files /dev/null and b/TaskyProCloned/Dependencies/Community.CsharpSqlite/Community.CsharpSqlite.WP7.pdb differ diff --git a/TaskyProCloned/Dependencies/Microsoft.Phone.Controls.Toolkit/Microsoft.Phone.Controls.Toolkit.dll b/TaskyProCloned/Dependencies/Microsoft.Phone.Controls.Toolkit/Microsoft.Phone.Controls.Toolkit.dll new file mode 100644 index 0000000..44d59b1 Binary files /dev/null and b/TaskyProCloned/Dependencies/Microsoft.Phone.Controls.Toolkit/Microsoft.Phone.Controls.Toolkit.dll differ diff --git a/TaskyProCloned/Dependencies/Microsoft.Phone.Controls.Toolkit/Microsoft.Phone.Controls.Toolkit.pdb b/TaskyProCloned/Dependencies/Microsoft.Phone.Controls.Toolkit/Microsoft.Phone.Controls.Toolkit.pdb new file mode 100644 index 0000000..ac620fd Binary files /dev/null and b/TaskyProCloned/Dependencies/Microsoft.Phone.Controls.Toolkit/Microsoft.Phone.Controls.Toolkit.pdb differ diff --git a/TaskyProCloned/Tasky.Core.Cloned/About.txt b/TaskyProCloned/Tasky.Core.Cloned/About.txt new file mode 100644 index 0000000..205d076 --- /dev/null +++ b/TaskyProCloned/Tasky.Core.Cloned/About.txt @@ -0,0 +1,3 @@ +The Tasky.Core library is the reusable portion of the Tasky application. It contains the Business +Layer (BL), Data Access Layer (DAL), and Data Layer (DL). All of the Tasky.Core code is shared between +the iOS and Android versions of the application without modification. \ No newline at end of file diff --git a/TaskyProCloned/Tasky.Core.Cloned/BL/About.txt b/TaskyProCloned/Tasky.Core.Cloned/BL/About.txt new file mode 100644 index 0000000..1b5507d --- /dev/null +++ b/TaskyProCloned/Tasky.Core.Cloned/BL/About.txt @@ -0,0 +1,15 @@ +Business Layer (BL) +=================== +Sometimes also called the Business Logic Layer (BLL), the BL contains entitiy definitions and +business logic. + +Business Entities +----------------- +Business entites are classes that represent real-world objects. They're the core of +Object-Oriented-Programming (OOP). + +Manager Classes +--------------- +In this particular architecture, we're using the façade pattern (like we did with the DAL) +which is represented by static Manager classes. The manager classes are an abstraction on +the DAL and SAL layers and perform any business logic. \ No newline at end of file diff --git a/TaskyProCloned/Tasky.Core.Cloned/BL/Contracts/BusinessEntityBase.cs b/TaskyProCloned/Tasky.Core.Cloned/BL/Contracts/BusinessEntityBase.cs new file mode 100644 index 0000000..0a0a2b7 --- /dev/null +++ b/TaskyProCloned/Tasky.Core.Cloned/BL/Contracts/BusinessEntityBase.cs @@ -0,0 +1,20 @@ +using System; +using Tasky.DL.SQLite; + +namespace Tasky.BL.Contracts { + /// + /// Business entity base class. Provides the ID property. + /// + public abstract class BusinessEntityBase : IBusinessEntity { + public BusinessEntityBase () + { + } + + /// + /// Gets or sets the Database ID. + /// + [PrimaryKey, AutoIncrement] + public int ID { get; set; } + } +} + diff --git a/TaskyProCloned/Tasky.Core.Cloned/BL/Contracts/IBusinessEntity.cs b/TaskyProCloned/Tasky.Core.Cloned/BL/Contracts/IBusinessEntity.cs new file mode 100644 index 0000000..15ba377 --- /dev/null +++ b/TaskyProCloned/Tasky.Core.Cloned/BL/Contracts/IBusinessEntity.cs @@ -0,0 +1,8 @@ +using System; + +namespace Tasky.BL.Contracts { + public interface IBusinessEntity { + int ID { get; set; } + } +} + diff --git a/TaskyProCloned/Tasky.Core.Cloned/BL/Managers/TaskManager.cs b/TaskyProCloned/Tasky.Core.Cloned/BL/Managers/TaskManager.cs new file mode 100644 index 0000000..ed52849 --- /dev/null +++ b/TaskyProCloned/Tasky.Core.Cloned/BL/Managers/TaskManager.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using Tasky.BL; + +namespace Tasky.BL.Managers +{ + public static class TaskManager + { + static TaskManager () + { + } + + public static Task GetTask(int id) + { + return DAL.TaskRepository.GetTask(id); + } + + public static IList GetTasks () + { + return new List(DAL.TaskRepository.GetTasks()); + } + + public static int SaveTask (Task item) + { + return DAL.TaskRepository.SaveTask(item); + } + + public static int DeleteTask(int id) + { + return DAL.TaskRepository.DeleteTask(id); + } + + } +} \ No newline at end of file diff --git a/TaskyProCloned/Tasky.Core.Cloned/BL/Task.cs b/TaskyProCloned/Tasky.Core.Cloned/BL/Task.cs new file mode 100644 index 0000000..0d334d9 --- /dev/null +++ b/TaskyProCloned/Tasky.Core.Cloned/BL/Task.cs @@ -0,0 +1,24 @@ +using System; +using Tasky.BL.Contracts; +using Tasky.DL.SQLite; + +namespace Tasky.BL +{ + /// + /// Represents a Task. + /// + public class Task : IBusinessEntity + { + public Task () + { + } + + [PrimaryKey, AutoIncrement] + public int ID { get; set; } + public string Name { get; set; } + public string Notes { get; set; } + // new property + public bool Done { get; set; } + } +} + diff --git a/TaskyProCloned/Tasky.Core.Cloned/DAL/About.txt b/TaskyProCloned/Tasky.Core.Cloned/DAL/About.txt new file mode 100644 index 0000000..7825750 --- /dev/null +++ b/TaskyProCloned/Tasky.Core.Cloned/DAL/About.txt @@ -0,0 +1,13 @@ +Data Access Layer (DAL) +======================= +The data access layer is responsible for providing an abstraction to the data layer. It makes +sure that no matter what technology(ies) are used for data persistance, a single entry point +that is technology agnostic exists. + +Often, the façade pattern is used, and many times the underlying data persistence technology is +actually more than one. for example, you might have some data store in xml, other data stored in +a SQL database, etc. The DAL makes sure that any complexity associated with accessing and aggregating +these technologies is isolated from the rest of the application. + +The DAL is also considered a sibling to the Service Access Layer (SAL), which is responsible for +access to cloud services, and other network related resources. \ No newline at end of file diff --git a/TaskyProCloned/Tasky.Core.Cloned/DAL/TaskRepository.cs b/TaskyProCloned/Tasky.Core.Cloned/DAL/TaskRepository.cs new file mode 100644 index 0000000..2bae5f9 --- /dev/null +++ b/TaskyProCloned/Tasky.Core.Cloned/DAL/TaskRepository.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Tasky.BL; + +namespace Tasky.DAL { + public class TaskRepository { + DL.TaskDatabase db = null; + protected static string dbLocation; + protected static TaskRepository me; + + static TaskRepository () + { + me = new TaskRepository(); + } + + protected TaskRepository() + { + // set the db location + dbLocation = DatabaseFilePath; + + // instantiate the database + db = new Tasky.DL.TaskDatabase(dbLocation); + } + + public static string DatabaseFilePath { + get { + var sqliteFilename = "TaskDB.db3"; +#if SILVERLIGHT + // Windows Phone expects a local path, not absolute + var path = sqliteFilename; +#else + +#if __ANDROID__ + // Just use whatever directory SpecialFolder.Personal returns + string libraryPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); ; +#else + // we need to put in /Library/ on iOS5.1 to meet Apple's iCloud terms + // (they don't want non-user-generated data in Documents) + string documentsPath = Environment.GetFolderPath (Environment.SpecialFolder.Personal); // Documents folder + string libraryPath = Path.Combine (documentsPath, "../Library/"); // Library folder +#endif + var path = Path.Combine (libraryPath, sqliteFilename); +#endif + return path; + } + } + + public static Task GetTask(int id) + { + return me.db.GetItem(id); + } + + public static IEnumerable GetTasks () + { + return me.db.GetItems(); + } + + public static int SaveTask (Task item) + { + return me.db.SaveItem(item); + } + + public static int DeleteTask(int id) + { + return me.db.DeleteItem(id); + } + } +} + diff --git a/TaskyProCloned/Tasky.Core.Cloned/DL/About.txt b/TaskyProCloned/Tasky.Core.Cloned/DL/About.txt new file mode 100644 index 0000000..ee62d45 --- /dev/null +++ b/TaskyProCloned/Tasky.Core.Cloned/DL/About.txt @@ -0,0 +1,19 @@ +Data Layer: +=========== +The data layer is typically the lowest level layer of an application. Typically it consists +of data persistence/retreival code. In this particular application, it contains two classes: + - SQLite.cs + - TaskDatabase.cs + +SQLite +------ +This is the SQLite.NET library, created by Frank Krueger. It's a lightweight Object Relational +Model (ORM) that allows you to define your database schema via classes and automatically +handles the nitty gritty of data persistance and retrieval into/from SQLite as well as actual +database creation. It also has limited support for LINQ queries. + +TaskDatabase +------------ +TaskDatabase builds on SQLite.Net and represents a specific database, in our case, the Task DB. +It contains methods for retreival and persistance as well as db creation, all based on the +underlying ORM. diff --git a/TaskyProCloned/Tasky.Core.Cloned/DL/SQLite.cs b/TaskyProCloned/Tasky.Core.Cloned/DL/SQLite.cs new file mode 100644 index 0000000..6752f3b --- /dev/null +++ b/TaskyProCloned/Tasky.Core.Cloned/DL/SQLite.cs @@ -0,0 +1,1940 @@ +// +// Copyright (c) 2009-2011 Krueger Systems, Inc. +// +// 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. +// + +#if WINDOWS_PHONE +#define USE_CSHARP_SQLITE +#endif + +using System; +using System.Runtime.InteropServices; +using System.Collections.Generic; +using System.Reflection; +using System.Linq; +using System.Linq.Expressions; + +#if USE_CSHARP_SQLITE +using Community.CsharpSqlite; +using Sqlite3DatabaseHandle = Community.CsharpSqlite.Sqlite3.sqlite3; +using Sqlite3Statement = Community.CsharpSqlite.Sqlite3.Vdbe; +#else +using Sqlite3DatabaseHandle = System.IntPtr; +using Sqlite3Statement = System.IntPtr; +#endif + +namespace Tasky.DL.SQLite { + public class SQLiteException : System.Exception { + public SQLite3.Result Result { get; private set; } + + protected SQLiteException(SQLite3.Result r, string message) + : base(message) + { + Result = r; + } + + public static SQLiteException New(SQLite3.Result r, string message) + { + return new SQLiteException(r, message); + } + } + + /// + /// Represents an open connection to a SQLite database. + /// + public class SQLiteConnection : IDisposable { + private bool _open; + private TimeSpan _busyTimeout; + private Dictionary _mappings = null; + private Dictionary _tables = null; + private System.Diagnostics.Stopwatch _sw; + private long _elapsedMilliseconds = 0; + + public Sqlite3DatabaseHandle Handle { get; private set; } +#if USE_CSHARP_SQLITE + internal static readonly Sqlite3DatabaseHandle NullHandle = null; +#else + internal static readonly Sqlite3DatabaseHandle NullHandle = IntPtr.Zero; +#endif + + public string DatabasePath { get; private set; } + + public bool TimeExecution { get; set; } + + public bool Trace { get; set; } + + /// + /// Constructs a new SQLiteConnection and opens a SQLite database specified by databasePath. + /// + /// + /// Specifies the path to the database file. + /// + public SQLiteConnection(string databasePath) + { + DatabasePath = databasePath; + Sqlite3DatabaseHandle handle; + var r = SQLite3.Open(DatabasePath, out handle); + Handle = handle; + if (r != SQLite3.Result.OK) { + throw SQLiteException.New(r, "Could not open database file: " + DatabasePath); + } + _open = true; + + BusyTimeout = TimeSpan.FromSeconds(0.1); + } + + static SQLiteConnection() + { + if (_preserveDuringLinkMagic) { + var ti = new TableInfo(); + ti.name = "magic"; + } + } + + /// + /// Used to list some code that we want the MonoTouch linker + /// to see, but that we never want to actually execute. + /// + static bool _preserveDuringLinkMagic = false; + + /// + /// Sets a busy handler to sleep the specified amount of time when a table is locked. + /// The handler will sleep multiple times until a total time of has accumulated. + /// + public TimeSpan BusyTimeout + { + get { return _busyTimeout; } + set + { + _busyTimeout = value; + if (Handle != NullHandle) { + SQLite3.BusyTimeout(Handle, (int)_busyTimeout.TotalMilliseconds); + } + } + } + + /// + /// Returns the mappings from types to tables that the connection + /// currently understands. + /// + public IEnumerable TableMappings + { + get + { + if (_tables == null) { + return Enumerable.Empty(); + } else { + return _tables.Values; + } + } + } + + /// + /// Retrieves the mapping that is automatically generated for the given type. + /// + /// + /// The type whose mapping to the database is returned. + /// + /// + /// The mapping represents the schema of the columns of the database and contains + /// methods to set and get properties of objects. + /// + public TableMapping GetMapping(Type type) + { + if (_mappings == null) { + _mappings = new Dictionary(); + } + TableMapping map; + if (!_mappings.TryGetValue(type.FullName, out map)) { + map = new TableMapping(type); + _mappings[type.FullName] = map; + } + return map; + } + + /// + /// Executes a "create table if not exists" on the database. It also + /// creates any specified indexes on the columns of the table. It uses + /// a schema automatically generated from the specified type. You can + /// later access this schema by calling GetMapping. + /// + /// + /// The number of entries added to the database schema. + /// + public int CreateTable() + { + var ty = typeof(T); + + if (_tables == null) { + _tables = new Dictionary(); + } + TableMapping map; + if (!_tables.TryGetValue(ty.FullName, out map)) { + map = GetMapping(ty); + _tables.Add(ty.FullName, map); + } + var query = "create table if not exists \"" + map.TableName + "\"(\n"; + + var decls = map.Columns.Select(p => Orm.SqlDecl(p)); + var decl = string.Join(",\n", decls.ToArray()); + query += decl; + query += ")"; + + var count = Execute(query); + + if (count == 0) { //Possible bug: This always seems to return 0? + // Table already exists, migrate it + MigrateTable(map); + } + + foreach (var p in map.Columns.Where(x => x.IsIndexed)) { + var indexName = map.TableName + "_" + p.Name; + var q = string.Format("create index if not exists \"{0}\" on \"{1}\"(\"{2}\")", indexName, map.TableName, p.Name); + count += Execute(q); + } + + return count; + } + + public class TableInfo { + public int cid { get; set; } + + public string name { get; set; } + + public string type { get; set; } + + public int notnull { get; set; } + + public string dflt_value { get; set; } + + public int pk { get; set; } + } + + void MigrateTable(TableMapping map) + { + var query = "pragma table_info(\"" + map.TableName + "\")"; + + var existingCols = Query(query); + + var toBeAdded = new List(); + + foreach (var p in map.Columns) { + var found = false; + foreach (var c in existingCols) { + found = p.Name == c.name; + if (found) + break; + } + if (!found) { + toBeAdded.Add(p); + } + } + + foreach (var p in toBeAdded) { + var addCol = "alter table \"" + map.TableName + "\" add column " + Orm.SqlDecl(p); + Execute(addCol); + } + } + + /// + /// Creates a new SQLiteCommand given the command text with arguments. Place a '?' + /// in the command text for each of the arguments. + /// + /// + /// The fully escaped SQL. + /// + /// + /// Arguments to substitute for the occurences of '?' in the command text. + /// + /// + /// A + /// + public SQLiteCommand CreateCommand(string cmdText, params object[] ps) + { + if (!_open) { + throw SQLiteException.New(SQLite3.Result.Error, "Cannot create commands from unopened database"); + } else { + var cmd = new SQLiteCommand(this); + cmd.CommandText = cmdText; + foreach (var o in ps) { + cmd.Bind(o); + } + return cmd; + } + } + + /// + /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?' + /// in the command text for each of the arguments and then executes that command. + /// Use this method instead of Query when you don't expect rows back. Such cases include + /// INSERTs, UPDATEs, and DELETEs. + /// You can set the Trace or TimeExecution properties of the connection + /// to profile execution. + /// + /// + /// The fully escaped SQL. + /// + /// + /// Arguments to substitute for the occurences of '?' in the query. + /// + /// + /// The number of rows modified in the database as a result of this execution. + /// + public int Execute(string query, params object[] args) + { + var cmd = CreateCommand(query, args); + + if (TimeExecution) { + if (_sw == null) { + _sw = new System.Diagnostics.Stopwatch(); + } + _sw.Reset(); + _sw.Start(); + } + + int r = cmd.ExecuteNonQuery(); + + if (TimeExecution) { + _sw.Stop(); + _elapsedMilliseconds += _sw.ElapsedMilliseconds; +#if DEBUG + Console.WriteLine("Finished in {0} ms ({1:0.0} s total)", _sw.ElapsedMilliseconds, _elapsedMilliseconds / 1000.0); +#endif + } + + return r; + } + + /// + /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?' + /// in the command text for each of the arguments and then executes that command. + /// It returns each row of the result using the mapping automatically generated for + /// the given type. + /// + /// + /// The fully escaped SQL. + /// + /// + /// Arguments to substitute for the occurences of '?' in the query. + /// + /// + /// An enumerable with one result for each row returned by the query. + /// + public List Query(string query, params object[] args) where T : new() + { + var cmd = CreateCommand(query, args); + return cmd.ExecuteQuery(); + } + + /// + /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?' + /// in the command text for each of the arguments and then executes that command. + /// It returns each row of the result using the mapping automatically generated for + /// the given type. + /// + /// + /// The fully escaped SQL. + /// + /// + /// Arguments to substitute for the occurences of '?' in the query. + /// + /// + /// An enumerable with one result for each row returned by the query. + /// The enumerator will call sqlite3_step on each call to MoveNext, so the database + /// connection must remain open for the lifetime of the enumerator. + /// + public IEnumerable DeferredQuery(string query, params object[] args) where T : new() + { + var cmd = CreateCommand(query, args); + return cmd.ExecuteDeferredQuery(); + } + + /// + /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?' + /// in the command text for each of the arguments and then executes that command. + /// It returns each row of the result using the specified mapping. This function is + /// only used by libraries in order to query the database via introspection. It is + /// normally not used. + /// + /// + /// A to use to convert the resulting rows + /// into objects. + /// + /// + /// The fully escaped SQL. + /// + /// + /// Arguments to substitute for the occurences of '?' in the query. + /// + /// + /// An enumerable with one result for each row returned by the query. + /// + public List Query(TableMapping map, string query, params object[] args) + { + var cmd = CreateCommand(query, args); + return cmd.ExecuteQuery(map); + } + + /// + /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?' + /// in the command text for each of the arguments and then executes that command. + /// It returns each row of the result using the specified mapping. This function is + /// only used by libraries in order to query the database via introspection. It is + /// normally not used. + /// + /// + /// A to use to convert the resulting rows + /// into objects. + /// + /// + /// The fully escaped SQL. + /// + /// + /// Arguments to substitute for the occurences of '?' in the query. + /// + /// + /// An enumerable with one result for each row returned by the query. + /// The enumerator will call sqlite3_step on each call to MoveNext, so the database + /// connection must remain open for the lifetime of the enumerator. + /// + public IEnumerable DeferredQuery(TableMapping map, string query, params object[] args) + { + var cmd = CreateCommand(query, args); + return cmd.ExecuteDeferredQuery(map); + } + + /// + /// Returns a queryable interface to the table represented by the given type. + /// + /// + /// A queryable object that is able to translate Where, OrderBy, and Take + /// queries into native SQL. + /// + public TableQuery Table() where T : new() + { + return new TableQuery(this); + } + + /// + /// Attempts to retrieve an object with the given primary key from the table + /// associated with the specified type. Use of this method requires that + /// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute). + /// + /// + /// The primary key. + /// + /// + /// The object with the given primary key. Throws a not found exception + /// if the object is not found. + /// + public T Get(object pk) where T : new() + { + var map = GetMapping(typeof(T)); + string query = string.Format("select * from \"{0}\" where \"{1}\" = ?", map.TableName, map.PK.Name); + return Query(query, pk).First(); + } + + /// + /// Whether has been called and the database is waiting for a . + /// + public bool IsInTransaction { get; private set; } + + /// + /// Begins a new transaction. Call to end the transaction. + /// + public void BeginTransaction() + { + if (!IsInTransaction) { + Execute("begin transaction"); + IsInTransaction = true; + } + } + + /// + /// Rolls back the transaction that was begun by . + /// + public void Rollback() + { + if (IsInTransaction) { + Execute("rollback"); + IsInTransaction = false; + } + } + + /// + /// Commits the transaction that was begun by . + /// + public void Commit() + { + if (IsInTransaction) { + Execute("commit"); + IsInTransaction = false; + } + } + + /// + /// Executes within a transaction and automatically rollsback the transaction + /// if an exception occurs. The exception is rethrown. + /// + /// + /// The to perform within a transaction. can contain any number + /// of operations on the connection but should never call , + /// , or . + /// + public void RunInTransaction(Action action) + { + if (IsInTransaction) { + throw new InvalidOperationException("The connection must not already be in a transaction when RunInTransaction is called"); + } + try { + BeginTransaction(); + action(); + Commit(); + } catch (Exception) { + Rollback(); + throw; + } + } + + /// + /// Inserts all specified objects. + /// + /// + /// An of the objects to insert. + /// + /// + /// The number of rows added to the table. + /// + public int InsertAll(System.Collections.IEnumerable objects) + { + BeginTransaction(); + var c = 0; + foreach (var r in objects) { + c += Insert(r); + } + Commit(); + return c; + } + + /// + /// Inserts the given object and retrieves its + /// auto incremented primary key if it has one. + /// + /// + /// The object to insert. + /// + /// + /// The number of rows added to the table. + /// + public int Insert(object obj) + { + if (obj == null) { + return 0; + } + return Insert(obj, "", obj.GetType()); + } + + public int Insert(object obj, Type objType) + { + return Insert(obj, "", objType); + } + + public int Insert(object obj, string extra) + { + if (obj == null) { + return 0; + } + return Insert(obj, extra, obj.GetType()); + } + + /// + /// Inserts the given object and retrieves its + /// auto incremented primary key if it has one. + /// + /// + /// The object to insert. + /// + /// + /// Literal SQL code that gets placed into the command. INSERT {extra} INTO ... + /// + /// + /// The number of rows added to the table. + /// + public int Insert(object obj, string extra, Type objType) + { + if (obj == null || objType == null) { + return 0; + } + + var map = GetMapping(objType); + + var cols = map.InsertColumns; + var vals = new object[cols.Length]; + for (var i = 0; i < vals.Length; i++) { + vals[i] = cols[i].GetValue(obj); + } + + var insertCmd = map.GetInsertCommand(this, extra); + var count = insertCmd.ExecuteNonQuery(vals); + + if (map.HasAutoIncPK) { + var id = SQLite3.LastInsertRowid(Handle); + map.SetAutoIncPK(obj, id); + } + + return count; + } + + /// + /// Updates all of the columns of a table using the specified object + /// except for its primary key. + /// The object is required to have a primary key. + /// + /// + /// The object to update. It must have a primary key designated using the PrimaryKeyAttribute. + /// + /// + /// The number of rows updated. + /// + public int Update(object obj) + { + if (obj == null) { + return 0; + } + return Update(obj, obj.GetType()); + } + + public int Update(object obj, Type objType) + { + if (obj == null || objType == null) { + return 0; + } + + var map = GetMapping(objType); + + var pk = map.PK; + + if (pk == null) { + throw new NotSupportedException("Cannot update " + map.TableName + ": it has no PK"); + } + + var cols = from p in map.Columns + where p != pk + select p; + var vals = from c in cols + select c.GetValue(obj); + var ps = new List(vals); + ps.Add(pk.GetValue(obj)); + var q = string.Format("update \"{0}\" set {1} where {2} = ? ", map.TableName, string.Join(",", (from c in cols + select "\"" + c.Name + "\" = ? ").ToArray()), pk.Name); + return Execute(q, ps.ToArray()); + } + + /// + /// Deletes the given object from the database using its primary key. + /// + /// + /// The object to delete. It must have a primary key designated using the PrimaryKeyAttribute. + /// + /// + /// The number of rows deleted. + /// + public int Delete(T obj) + { + var map = GetMapping(obj.GetType()); + var pk = map.PK; + if (pk == null) { + throw new NotSupportedException("Cannot delete " + map.TableName + ": it has no PK"); + } + var q = string.Format("delete from \"{0}\" where \"{1}\" = ?", map.TableName, pk.Name); + return Execute(q, pk.GetValue(obj)); + } + + public void Dispose() + { + Close(); + } + + public void Close() + { + if (_open && Handle != NullHandle) { + try { + foreach (var sqlInsertCommand in _mappings.Values) { + sqlInsertCommand.Dispose(); + } + var r = SQLite3.Close(Handle); + if (r != SQLite3.Result.OK) { + string msg = SQLite3.GetErrmsg(Handle); + throw SQLiteException.New(r, msg); + } + } finally { + Handle = NullHandle; + _open = false; + } + } + } + } + + public class PrimaryKeyAttribute : Attribute { + } + + public class AutoIncrementAttribute : Attribute { + } + + public class IndexedAttribute : Attribute { + } + + public class IgnoreAttribute : Attribute { + } + + public class MaxLengthAttribute : Attribute { + public int Value { get; private set; } + + public MaxLengthAttribute(int length) + { + Value = length; + } + } + + public class CollationAttribute : Attribute { + public string Value { get; private set; } + + public CollationAttribute(string collation) + { + Value = collation; + } + } + + public class TableMapping { + public Type MappedType { get; private set; } + + public string TableName { get; private set; } + + public Column[] Columns { get; private set; } + + public Column PK { get; private set; } + + Column _autoPk = null; + Column[] _insertColumns = null; + + public TableMapping(Type type) + { + MappedType = type; + TableName = MappedType.Name; + var props = MappedType.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty); + var cols = new List(); + foreach (var p in props) { + var ignore = p.GetCustomAttributes(typeof(IgnoreAttribute), true).Length > 0; + if (p.CanWrite && !ignore) { + cols.Add(new PropColumn(p)); + } + } + Columns = cols.ToArray(); + foreach (var c in Columns) { + if (c.IsAutoInc && c.IsPK) { + _autoPk = c; + } + if (c.IsPK) { + PK = c; + } + } + + HasAutoIncPK = _autoPk != null; + } + + public bool HasAutoIncPK { get; private set; } + + public void SetAutoIncPK(object obj, long id) + { + if (_autoPk != null) { + _autoPk.SetValue(obj, Convert.ChangeType(id, _autoPk.ColumnType, null)); + } + } + + public Column[] InsertColumns + { + get + { + if (_insertColumns == null) { + _insertColumns = Columns.Where(c => !c.IsAutoInc).ToArray(); + } + return _insertColumns; + } + } + + public Column FindColumn(string name) + { + var exact = Columns.Where(c => c.Name == name).FirstOrDefault(); + return exact; + } + + + PreparedSqlLiteInsertCommand _insertCommand; + string _insertCommandExtra = null; + + public PreparedSqlLiteInsertCommand GetInsertCommand(SQLiteConnection conn, string extra) + { + + if (_insertCommand == null) { + _insertCommand = CreateInsertCommand(conn, extra); + _insertCommandExtra = extra; + } else if (_insertCommandExtra != extra) { + _insertCommand.Dispose(); + _insertCommand = CreateInsertCommand(conn, extra); + _insertCommandExtra = extra; + } + return _insertCommand; + } + + private PreparedSqlLiteInsertCommand CreateInsertCommand(SQLiteConnection conn, string extra) + { + var cols = InsertColumns; + var insertSql = string.Format("insert {3} into \"{0}\"({1}) values ({2})", TableName, + string.Join(",", (from c in cols + select "\"" + c.Name + "\"").ToArray()), + string.Join(",", (from c in cols + select "?").ToArray()), extra); + + var insertCommand = new PreparedSqlLiteInsertCommand(conn); + insertCommand.CommandText = insertSql; + return insertCommand; + } + + protected internal void Dispose() + { + if (_insertCommand != null) { + _insertCommand.Dispose(); + _insertCommand = null; + } + } + + public abstract class Column { + public string Name { get; protected set; } + + public Type ColumnType { get; protected set; } + + public string Collation { get; protected set; } + + public bool IsAutoInc { get; protected set; } + + public bool IsPK { get; protected set; } + + public bool IsIndexed { get; protected set; } + + public bool IsNullable { get; protected set; } + + public int MaxStringLength { get; protected set; } + + public abstract void SetValue(object obj, object val); + + public abstract object GetValue(object obj); + } + + public class PropColumn : Column { + PropertyInfo _prop; + + public PropColumn(PropertyInfo prop) + { + _prop = prop; + Name = prop.Name; + //If this type is Nullable then Nullable.GetUnderlyingType returns the T, otherwise it returns null, so get the the actual type instead + ColumnType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType; + Collation = Orm.Collation(prop); + IsAutoInc = Orm.IsAutoInc(prop); + IsPK = Orm.IsPK(prop); + IsIndexed = Orm.IsIndexed(prop); + IsNullable = !IsPK; + MaxStringLength = Orm.MaxStringLength(prop); + } + + public override void SetValue(object obj, object val) + { + _prop.SetValue(obj, val, null); + } + + public override object GetValue(object obj) + { + return _prop.GetValue(obj, null); + } + } + } + + public static class Orm { + public const int DefaultMaxStringLength = 140; + + public static string SqlDecl(TableMapping.Column p) + { + string decl = "\"" + p.Name + "\" " + SqlType(p) + " "; + + if (p.IsPK) { + decl += "primary key "; + } + if (p.IsAutoInc) { + decl += "autoincrement "; + } + if (!p.IsNullable) { + decl += "not null "; + } + if (!string.IsNullOrEmpty(p.Collation)) { + decl += "collate " + p.Collation + " "; + } + + return decl; + } + + public static string SqlType(TableMapping.Column p) + { + var clrType = p.ColumnType; + if (clrType == typeof(Boolean) || clrType == typeof(Byte) || clrType == typeof(UInt16) || clrType == typeof(SByte) || clrType == typeof(Int16) || clrType == typeof(Int32)) { + return "integer"; + } else if (clrType == typeof(UInt32) || clrType == typeof(Int64)) { + return "bigint"; + } else if (clrType == typeof(Single) || clrType == typeof(Double) || clrType == typeof(Decimal)) { + return "float"; + } else if (clrType == typeof(String)) { + int len = p.MaxStringLength; + return "varchar(" + len + ")"; + } else if (clrType == typeof(DateTime)) { + return "datetime"; + } else if (clrType.IsEnum) { + return "integer"; + } else if (clrType == typeof(byte[])) { + return "blob"; + } else { + throw new NotSupportedException("Don't know about " + clrType); + } + } + + public static bool IsPK(MemberInfo p) + { + var attrs = p.GetCustomAttributes(typeof(PrimaryKeyAttribute), true); + return attrs.Length > 0; + } + + public static string Collation(MemberInfo p) + { + var attrs = p.GetCustomAttributes(typeof(CollationAttribute), true); + if (attrs.Length > 0) { + return ((CollationAttribute)attrs[0]).Value; + } else { + return string.Empty; + } + } + + public static bool IsAutoInc(MemberInfo p) + { + var attrs = p.GetCustomAttributes(typeof(AutoIncrementAttribute), true); + return attrs.Length > 0; + } + + public static bool IsIndexed(MemberInfo p) + { + var attrs = p.GetCustomAttributes(typeof(IndexedAttribute), true); + return attrs.Length > 0; + } + + public static int MaxStringLength(PropertyInfo p) + { + var attrs = p.GetCustomAttributes(typeof(MaxLengthAttribute), true); + if (attrs.Length > 0) { + return ((MaxLengthAttribute)attrs[0]).Value; + } else { + return DefaultMaxStringLength; + } + } + } + + public class SQLiteCommand { + SQLiteConnection _conn; + private List _bindings; + + public string CommandText { get; set; } + + internal SQLiteCommand(SQLiteConnection conn) + { + _conn = conn; + _bindings = new List(); + CommandText = ""; + } + + public int ExecuteNonQuery() + { + if (_conn.Trace) { +#if DEBUG + Console.WriteLine("Executing: " + this); +#endif + } + + var r = SQLite3.Result.OK; + var stmt = Prepare(); + r = SQLite3.Step(stmt); + Finalize(stmt); + if (r == SQLite3.Result.Done) { + int rowsAffected = SQLite3.Changes(_conn.Handle); + return rowsAffected; + } else if (r == SQLite3.Result.Error) { + string msg = SQLite3.GetErrmsg(_conn.Handle); + throw SQLiteException.New(r, msg); + } else { + throw SQLiteException.New(r, r.ToString()); + } + } + + public IEnumerable ExecuteDeferredQuery() where T : new() + { + return ExecuteDeferredQuery(_conn.GetMapping(typeof(T))); + } + + public List ExecuteQuery() where T : new() + { + return ExecuteDeferredQuery(_conn.GetMapping(typeof(T))).ToList(); + } + + public List ExecuteQuery(TableMapping map) + { + return ExecuteDeferredQuery(map).ToList(); + } + + public IEnumerable ExecuteDeferredQuery(TableMapping map) + { + if (_conn.Trace) { +#if DEBUG + Console.WriteLine("Executing Query: " + this); +#endif + } + + var stmt = Prepare(); + try { + var cols = new TableMapping.Column[SQLite3.ColumnCount(stmt)]; + + for (int i = 0; i < cols.Length; i++) { + var name = SQLite3.ColumnName16(stmt, i); + cols[i] = map.FindColumn(name); + } + + while (SQLite3.Step(stmt) == SQLite3.Result.Row) { + var obj = Activator.CreateInstance(map.MappedType); + for (int i = 0; i < cols.Length; i++) { + if (cols[i] == null) + continue; + var colType = SQLite3.ColumnType(stmt, i); + var val = ReadCol(stmt, i, colType, cols[i].ColumnType); + cols[i].SetValue(obj, val); + } + yield return (T)obj; + } + } finally { + SQLite3.Finalize(stmt); + } + } + + public T ExecuteScalar() + { + if (_conn.Trace) { +#if DEBUG + Console.WriteLine("Executing Query: " + this); +#endif + } + + T val = default(T); + + var stmt = Prepare(); + if (SQLite3.Step(stmt) == SQLite3.Result.Row) { + var colType = SQLite3.ColumnType(stmt, 0); + val = (T)ReadCol(stmt, 0, colType, typeof(T)); + } + Finalize(stmt); + + return val; + } + + public void Bind(string name, object val) + { + _bindings.Add(new Binding { + Name = name, + Value = val + }); + } + + public void Bind(object val) + { + Bind(null, val); + } + + public override string ToString() + { + var parts = new string[1 + _bindings.Count]; + parts[0] = CommandText; + var i = 1; + foreach (var b in _bindings) { + parts[i] = string.Format(" {0}: {1}", i - 1, b.Value); + i++; + } + return string.Join(Environment.NewLine, parts); + } + + Sqlite3Statement Prepare() + { + var stmt = SQLite3.Prepare2(_conn.Handle, CommandText); + BindAll(stmt); + return stmt; + } + + void Finalize(Sqlite3Statement stmt) + { + SQLite3.Finalize(stmt); + } + + void BindAll(Sqlite3Statement stmt) + { + int nextIdx = 1; + foreach (var b in _bindings) { + if (b.Name != null) { + b.Index = SQLite3.BindParameterIndex(stmt, b.Name); + } else { + b.Index = nextIdx++; + } + } + foreach (var b in _bindings) { + BindParameter(stmt, b.Index, b.Value); + } + } + + internal static IntPtr NegativePointer = new IntPtr(-1); + + internal static void BindParameter(Sqlite3Statement stmt, int index, object value) + { + if (value == null) { + SQLite3.BindNull(stmt, index); + } else { + if (value is Int32) { + SQLite3.BindInt(stmt, index, (int)value); + } else if (value is String) { + SQLite3.BindText(stmt, index, (string)value, -1, NegativePointer); + } else if (value is Byte || value is UInt16 || value is SByte || value is Int16) { + SQLite3.BindInt(stmt, index, Convert.ToInt32(value)); + } else if (value is Boolean) { + SQLite3.BindInt(stmt, index, (bool)value ? 1 : 0); + } else if (value is UInt32 || value is Int64) { + SQLite3.BindInt64(stmt, index, Convert.ToInt64(value)); + } else if (value is Single || value is Double || value is Decimal) { + SQLite3.BindDouble(stmt, index, Convert.ToDouble(value)); + } else if (value is DateTime) { + SQLite3.BindText(stmt, index, ((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss"), -1, NegativePointer); + } else if (value.GetType().IsEnum) { + SQLite3.BindInt(stmt, index, Convert.ToInt32(value)); + } else if (value is byte[]) { + SQLite3.BindBlob(stmt, index, (byte[])value, ((byte[])value).Length, NegativePointer); + } else { + throw new NotSupportedException("Cannot store type: " + value.GetType()); + } + } + } + + class Binding { + public string Name { get; set; } + + public object Value { get; set; } + + public int Index { get; set; } + } + + object ReadCol(Sqlite3Statement stmt, int index, SQLite3.ColType type, Type clrType) + { + if (type == SQLite3.ColType.Null) { + return null; + } else { + if (clrType == typeof(String)) { + return SQLite3.ColumnString(stmt, index); + } else if (clrType == typeof(Int32)) { + return (int)SQLite3.ColumnInt(stmt, index); + } else if (clrType == typeof(Boolean)) { + return SQLite3.ColumnInt(stmt, index) == 1; + } else if (clrType == typeof(double)) { + return SQLite3.ColumnDouble(stmt, index); + } else if (clrType == typeof(float)) { + return (float)SQLite3.ColumnDouble(stmt, index); + } else if (clrType == typeof(DateTime)) { + var text = SQLite3.ColumnString(stmt, index); + return DateTime.Parse(text); + } else if (clrType.IsEnum) { + return SQLite3.ColumnInt(stmt, index); + } else if (clrType == typeof(Int64)) { + return SQLite3.ColumnInt64(stmt, index); + } else if (clrType == typeof(UInt32)) { + return (uint)SQLite3.ColumnInt64(stmt, index); + } else if (clrType == typeof(decimal)) { + return (decimal)SQLite3.ColumnDouble(stmt, index); + } else if (clrType == typeof(Byte)) { + return (byte)SQLite3.ColumnInt(stmt, index); + } else if (clrType == typeof(UInt16)) { + return (ushort)SQLite3.ColumnInt(stmt, index); + } else if (clrType == typeof(Int16)) { + return (short)SQLite3.ColumnInt(stmt, index); + } else if (clrType == typeof(sbyte)) { + return (sbyte)SQLite3.ColumnInt(stmt, index); + } else if (clrType == typeof(byte[])) { + return SQLite3.ColumnByteArray(stmt, index); + } else { + throw new NotSupportedException("Don't know how to read " + clrType); + } + } + } + } + + /// + /// Since the insert never changed, we only need to prepare once. + /// + public class PreparedSqlLiteInsertCommand : IDisposable { + public bool Initialized { get; set; } + + protected SQLiteConnection Connection { get; set; } + + public string CommandText { get; set; } + + protected Sqlite3Statement Statement { get; set; } +#if USE_CSHARP_SQLITE + internal static readonly Sqlite3Statement NullStatement = null; +#else + internal static readonly Sqlite3Statement NullStatement = IntPtr.Zero; +#endif + + internal PreparedSqlLiteInsertCommand(SQLiteConnection conn) + { + Connection = conn; + } + + public int ExecuteNonQuery(object[] source) + { + if (Connection.Trace) { +#if DEBUG + Console.WriteLine("Executing: " + CommandText); +#endif + } + + var r = SQLite3.Result.OK; + + if (!Initialized) { + Statement = Prepare(); + Initialized = true; + } + + //bind the values. + if (source != null) { + for (int i = 0; i < source.Length; i++) { + SQLiteCommand.BindParameter(Statement, i + 1, source[i]); + } + } + r = SQLite3.Step(Statement); + + if (r == SQLite3.Result.Done) { + int rowsAffected = SQLite3.Changes(Connection.Handle); + SQLite3.Reset(Statement); + return rowsAffected; + } else if (r == SQLite3.Result.Error) { + string msg = SQLite3.GetErrmsg(Connection.Handle); + SQLite3.Reset(Statement); + throw SQLiteException.New(r, msg); + } else { + SQLite3.Reset(Statement); + throw SQLiteException.New(r, r.ToString()); + } + } + + protected virtual Sqlite3Statement Prepare() + { + var stmt = SQLite3.Prepare2(Connection.Handle, CommandText); + return stmt; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (Statement != NullStatement) { + try { + SQLite3.Finalize(Statement); + } finally { + Statement = NullStatement; + Connection = null; + } + } + } + + ~PreparedSqlLiteInsertCommand() + { + Dispose(false); + } + } + + public class TableQuery : IEnumerable where T : new() { + public SQLiteConnection Connection { get; private set; } + + public TableMapping Table { get; private set; } + + Expression _where; + List _orderBys; + int? _limit; + int? _offset; + + class Ordering { + public string ColumnName { get; set; } + + public bool Ascending { get; set; } + } + + TableQuery(SQLiteConnection conn, TableMapping table) + { + Connection = conn; + Table = table; + } + + public TableQuery(SQLiteConnection conn) + { + Connection = conn; + Table = Connection.GetMapping(typeof(T)); + } + + public TableQuery Clone() + { + var q = new TableQuery(Connection, Table); + q._where = _where; + q._deferred = _deferred; + if (_orderBys != null) { + q._orderBys = new List(_orderBys); + } + q._limit = _limit; + q._offset = _offset; + return q; + } + + public TableQuery Where(Expression> predExpr) + { + if (predExpr.NodeType == ExpressionType.Lambda) { + var lambda = (LambdaExpression)predExpr; + var pred = lambda.Body; + var q = Clone(); + q.AddWhere(pred); + return q; + } else { + throw new NotSupportedException("Must be a predicate"); + } + } + + public TableQuery Take(int n) + { + var q = Clone(); + q._limit = n; + return q; + } + + public TableQuery Skip(int n) + { + var q = Clone(); + q._offset = n; + return q; + } + + bool _deferred = false; + public TableQuery Deferred() + { + var q = Clone(); + q._deferred = true; + return q; + } + + public TableQuery OrderBy(Expression> orderExpr) + { + return AddOrderBy(orderExpr, true); + } + + public TableQuery OrderByDescending(Expression> orderExpr) + { + return AddOrderBy(orderExpr, false); + } + + private TableQuery AddOrderBy(Expression> orderExpr, bool asc) + { + if (orderExpr.NodeType == ExpressionType.Lambda) { + var lambda = (LambdaExpression)orderExpr; + var mem = lambda.Body as MemberExpression; + if (mem != null && (mem.Expression.NodeType == ExpressionType.Parameter)) { + var q = Clone(); + if (q._orderBys == null) { + q._orderBys = new List(); + } + q._orderBys.Add(new Ordering { + ColumnName = mem.Member.Name, + Ascending = asc + }); + return q; + } else { + throw new NotSupportedException("Order By does not support: " + orderExpr); + } + } else { + throw new NotSupportedException("Must be a predicate"); + } + } + + private void AddWhere(Expression pred) + { + if (_where == null) { + _where = pred; + } else { + _where = Expression.AndAlso(_where, pred); + } + } + + private SQLiteCommand GenerateCommand(string selectionList) + { + var cmdText = "select " + selectionList + " from \"" + Table.TableName + "\""; + var args = new List(); + if (_where != null) { + var w = CompileExpr(_where, args); + cmdText += " where " + w.CommandText; + } + if ((_orderBys != null) && (_orderBys.Count > 0)) { + var t = string.Join(", ", _orderBys.Select(o => "\"" + o.ColumnName + "\"" + (o.Ascending ? "" : " desc")).ToArray()); + cmdText += " order by " + t; + } + if (_limit.HasValue) { + cmdText += " limit " + _limit.Value; + } + if (_offset.HasValue) { + if (!_limit.HasValue) { + cmdText += " limit -1 "; + } + cmdText += " offset " + _offset.Value; + } + return Connection.CreateCommand(cmdText, args.ToArray()); + } + + class CompileResult { + public string CommandText { get; set; } + + public object Value { get; set; } + } + + private CompileResult CompileExpr(Expression expr, List queryArgs) + { + if (expr == null) { + throw new NotSupportedException("Expression is NULL"); + } else if (expr is BinaryExpression) { + var bin = (BinaryExpression)expr; + + var leftr = CompileExpr(bin.Left, queryArgs); + var rightr = CompileExpr(bin.Right, queryArgs); + + //If either side is a parameter and is null, then handle the other side specially (for "is null"/"is not null") + string text; + if (leftr.CommandText == "?" && leftr.Value == null) + text = CompileNullBinaryExpression(bin, rightr); + else if (rightr.CommandText == "?" && rightr.Value == null) + text = CompileNullBinaryExpression(bin, leftr); + else + text = "(" + leftr.CommandText + " " + GetSqlName(bin) + " " + rightr.CommandText + ")"; + return new CompileResult { CommandText = text }; + } else if (expr.NodeType == ExpressionType.Call) { + + var call = (MethodCallExpression)expr; + var args = new CompileResult[call.Arguments.Count]; + + for (var i = 0; i < args.Length; i++) { + args[i] = CompileExpr(call.Arguments[i], queryArgs); + } + + var sqlCall = ""; + + if (call.Method.Name == "Like" && args.Length == 2) { + sqlCall = "(" + args[0].CommandText + " like " + args[1].CommandText + ")"; + } else if (call.Method.Name == "Contains" && args.Length == 2) { + sqlCall = "(" + args[1].CommandText + " in " + args[0].CommandText + ")"; + } else { + sqlCall = call.Method.Name.ToLower() + "(" + string.Join(",", args.Select(a => a.CommandText).ToArray()) + ")"; + } + return new CompileResult { CommandText = sqlCall }; + + } else if (expr.NodeType == ExpressionType.Constant) { + var c = (ConstantExpression)expr; + queryArgs.Add(c.Value); + return new CompileResult { + CommandText = "?", + Value = c.Value + }; + } else if (expr.NodeType == ExpressionType.Convert) { + var u = (UnaryExpression)expr; + var ty = u.Type; + var valr = CompileExpr(u.Operand, queryArgs); + return new CompileResult { + CommandText = valr.CommandText, + Value = valr.Value != null ? Convert.ChangeType(valr.Value, ty, null) : null + }; + } else if (expr.NodeType == ExpressionType.MemberAccess) { + var mem = (MemberExpression)expr; + + if (mem.Expression.NodeType == ExpressionType.Parameter) { + // + // This is a column of our table, output just the column name + // + return new CompileResult { CommandText = "\"" + mem.Member.Name + "\"" }; + } else { + object obj = null; + if (mem.Expression != null) { + var r = CompileExpr(mem.Expression, queryArgs); + if (r.Value == null) { + throw new NotSupportedException("Member access failed to compile expression"); + } + if (r.CommandText == "?") { + queryArgs.RemoveAt(queryArgs.Count - 1); + } + obj = r.Value; + } + + // + // Get the member value + // + object val = null; + + if (mem.Member.MemberType == MemberTypes.Property) { + var m = (PropertyInfo)mem.Member; + val = m.GetValue(obj, null); + } else if (mem.Member.MemberType == MemberTypes.Field) { +#if SILVERLIGHT + val = Expression.Lambda(expr).Compile().DynamicInvoke(); +#else + var m = (FieldInfo)mem.Member; + var eee = Expression.Lambda (expr); + val = m.GetValue (obj); +#endif + } else { + throw new NotSupportedException("MemberExpr: " + mem.Member.MemberType.ToString()); + } + + // + // Work special magic for enumerables + // + if (val != null && val is System.Collections.IEnumerable && !(val is string)) { + var sb = new System.Text.StringBuilder(); + sb.Append("("); + var head = ""; + foreach (var a in (System.Collections.IEnumerable)val) { + queryArgs.Add(a); + sb.Append(head); + sb.Append("?"); + head = ","; + } + sb.Append(")"); + return new CompileResult { + CommandText = sb.ToString(), + Value = val + }; + } else { + queryArgs.Add(val); + return new CompileResult { + CommandText = "?", + Value = val + }; + } + } + } + throw new NotSupportedException("Cannot compile: " + expr.NodeType.ToString()); + } + + /// + /// Compiles a BinaryExpression where one of the parameters is null. + /// + /// The non-null parameter + private string CompileNullBinaryExpression(BinaryExpression expression, CompileResult parameter) + { + if (expression.NodeType == ExpressionType.Equal) + return "(" + parameter.CommandText + " is ?)"; + else if (expression.NodeType == ExpressionType.NotEqual) + return "(" + parameter.CommandText + " is not ?)"; + else + throw new NotSupportedException("Cannot compile Null-BinaryExpression with type " + expression.NodeType.ToString()); + } + + string GetSqlName(Expression expr) + { + var n = expr.NodeType; + if (n == ExpressionType.GreaterThan) + return ">"; + else if (n == ExpressionType.GreaterThanOrEqual) { + return ">="; + } else if (n == ExpressionType.LessThan) { + return "<"; + } else if (n == ExpressionType.LessThanOrEqual) { + return "<="; + } else if (n == ExpressionType.And) { + return "and"; + } else if (n == ExpressionType.AndAlso) { + return "and"; + } else if (n == ExpressionType.Or) { + return "or"; + } else if (n == ExpressionType.OrElse) { + return "or"; + } else if (n == ExpressionType.Equal) { + return "="; + } else if (n == ExpressionType.NotEqual) { + return "!="; + } else { + throw new System.NotSupportedException("Cannot get SQL for: " + n.ToString()); + } + } + + public int Count() + { + return GenerateCommand("count(*)").ExecuteScalar(); + } + + public IEnumerator GetEnumerator() + { + if (!_deferred) + return GenerateCommand("*").ExecuteQuery().GetEnumerator(); + + return GenerateCommand("*").ExecuteDeferredQuery().GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + public static class SQLite3 { + public enum Result : int { + OK = 0, + Error = 1, + Internal = 2, + Perm = 3, + Abort = 4, + Busy = 5, + Locked = 6, + NoMem = 7, + ReadOnly = 8, + Interrupt = 9, + IOError = 10, + Corrupt = 11, + NotFound = 12, + TooBig = 18, + Constraint = 19, + Row = 100, + Done = 101 + } + + public enum ConfigOption : int { + SingleThread = 1, + MultiThread = 2, + Serialized = 3 + } + +#if !USE_CSHARP_SQLITE + [DllImport("sqlite3", EntryPoint = "sqlite3_open")] + public static extern Result Open (string filename, out IntPtr db); + + [DllImport("sqlite3", EntryPoint = "sqlite3_close")] + public static extern Result Close (IntPtr db); + + [DllImport("sqlite3", EntryPoint = "sqlite3_config")] + public static extern Result Config (ConfigOption option); + + [DllImport("sqlite3", EntryPoint = "sqlite3_busy_timeout")] + public static extern Result BusyTimeout (IntPtr db, int milliseconds); + + [DllImport("sqlite3", EntryPoint = "sqlite3_changes")] + public static extern int Changes (IntPtr db); + + [DllImport("sqlite3", EntryPoint = "sqlite3_prepare_v2")] + public static extern Result Prepare2 (IntPtr db, string sql, int numBytes, out IntPtr stmt, IntPtr pzTail); + + public static IntPtr Prepare2 (IntPtr db, string query) + { + IntPtr stmt; + var r = Prepare2 (db, query, query.Length, out stmt, IntPtr.Zero); + if (r != Result.OK) { + throw SQLiteException.New (r, GetErrmsg (db)); + } + return stmt; + } + + [DllImport("sqlite3", EntryPoint = "sqlite3_step")] + public static extern Result Step (IntPtr stmt); + + [DllImport("sqlite3", EntryPoint = "sqlite3_reset")] + public static extern Result Reset (IntPtr stmt); + + [DllImport("sqlite3", EntryPoint = "sqlite3_finalize")] + public static extern Result Finalize (IntPtr stmt); + + [DllImport("sqlite3", EntryPoint = "sqlite3_last_insert_rowid")] + public static extern long LastInsertRowid (IntPtr db); + + [DllImport("sqlite3", EntryPoint = "sqlite3_errmsg16")] + public static extern IntPtr Errmsg (IntPtr db); + + public static string GetErrmsg (IntPtr db) + { + return Marshal.PtrToStringUni (Errmsg (db)); + } + + [DllImport("sqlite3", EntryPoint = "sqlite3_bind_parameter_index")] + public static extern int BindParameterIndex (IntPtr stmt, string name); + + [DllImport("sqlite3", EntryPoint = "sqlite3_bind_null")] + public static extern int BindNull (IntPtr stmt, int index); + + [DllImport("sqlite3", EntryPoint = "sqlite3_bind_int")] + public static extern int BindInt (IntPtr stmt, int index, int val); + + [DllImport("sqlite3", EntryPoint = "sqlite3_bind_int64")] + public static extern int BindInt64 (IntPtr stmt, int index, long val); + + [DllImport("sqlite3", EntryPoint = "sqlite3_bind_double")] + public static extern int BindDouble (IntPtr stmt, int index, double val); + + [DllImport("sqlite3", EntryPoint = "sqlite3_bind_text")] + public static extern int BindText (IntPtr stmt, int index, string val, int n, IntPtr free); + + [DllImport("sqlite3", EntryPoint = "sqlite3_bind_blob")] + public static extern int BindBlob (IntPtr stmt, int index, byte[] val, int n, IntPtr free); + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_count")] + public static extern int ColumnCount (IntPtr stmt); + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_name")] + public static extern IntPtr ColumnName (IntPtr stmt, int index); + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_name16")] + private static extern IntPtr ColumnName16Internal (IntPtr stmt, int index); + public static string ColumnName16(IntPtr stmt, int index) + { + return Marshal.PtrToStringUni(ColumnName16Internal(stmt, index)); + } + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_type")] + public static extern ColType ColumnType (IntPtr stmt, int index); + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_int")] + public static extern int ColumnInt (IntPtr stmt, int index); + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_int64")] + public static extern long ColumnInt64 (IntPtr stmt, int index); + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_double")] + public static extern double ColumnDouble (IntPtr stmt, int index); + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_text")] + public static extern IntPtr ColumnText (IntPtr stmt, int index); + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_text16")] + public static extern IntPtr ColumnText16 (IntPtr stmt, int index); + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_blob")] + public static extern IntPtr ColumnBlob (IntPtr stmt, int index); + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_bytes")] + public static extern int ColumnBytes (IntPtr stmt, int index); + + public static string ColumnString (IntPtr stmt, int index) + { + return Marshal.PtrToStringUni (SQLite3.ColumnText16 (stmt, index)); + } + + public static byte[] ColumnByteArray (IntPtr stmt, int index) + { + int length = ColumnBytes (stmt, index); + byte[] result = new byte[length]; + if (length > 0) + Marshal.Copy (ColumnBlob (stmt, index), result, 0, length); + return result; + } +#else + public static Result Open(string filename, out Sqlite3.sqlite3 db) + { + db = new Sqlite3.sqlite3(); + return (Result)Sqlite3.sqlite3_open(filename, ref db); + } + + public static Result Close(Sqlite3.sqlite3 db) + { + return (Result)Sqlite3.sqlite3_close(db); + } + + public static Result BusyTimeout(Sqlite3.sqlite3 db, int milliseconds) + { + return (Result)Sqlite3.sqlite3_busy_timeout(db, milliseconds); + } + + public static int Changes(Sqlite3.sqlite3 db) + { + return Sqlite3.sqlite3_changes(db); + } + + public static Sqlite3.Vdbe Prepare2(Sqlite3.sqlite3 db, string query) + { + Sqlite3.Vdbe stmt = new Sqlite3.Vdbe(); + var r = Sqlite3.sqlite3_prepare_v2(db, query, query.Length, ref stmt, 0); + if (r != 0) { + throw SQLiteException.New((Result)r, GetErrmsg(db)); + } + return stmt; + } + + public static Result Step(Sqlite3.Vdbe stmt) + { + return (Result)Sqlite3.sqlite3_step(stmt); + } + + public static Result Reset(Sqlite3.Vdbe stmt) + { + return (Result)Sqlite3.sqlite3_reset(stmt); + } + + public static Result Finalize(Sqlite3.Vdbe stmt) + { + return (Result)Sqlite3.sqlite3_finalize(ref stmt); + } + + public static long LastInsertRowid(Sqlite3.sqlite3 db) + { + return Sqlite3.sqlite3_last_insert_rowid(db); + } + + public static string GetErrmsg(Sqlite3.sqlite3 db) + { + return Sqlite3.sqlite3_errmsg(db); + } + + public static int BindParameterIndex(Sqlite3.Vdbe stmt, string name) + { + return Sqlite3.sqlite3_bind_parameter_index(stmt, name); + } + + public static int BindNull(Sqlite3.Vdbe stmt, int index) + { + return Sqlite3.sqlite3_bind_null(stmt, index); + } + + public static int BindInt(Sqlite3.Vdbe stmt, int index, int val) + { + return Sqlite3.sqlite3_bind_int(stmt, index, val); + } + + public static int BindInt64(Sqlite3.Vdbe stmt, int index, long val) + { + return Sqlite3.sqlite3_bind_int64(stmt, index, val); + } + + public static int BindDouble(Sqlite3.Vdbe stmt, int index, double val) + { + return Sqlite3.sqlite3_bind_double(stmt, index, val); + } + + public static int BindText(Sqlite3.Vdbe stmt, int index, string val, int n, IntPtr free) + { + return Sqlite3.sqlite3_bind_text(stmt, index, val, n, null); + } + + public static int BindBlob(Sqlite3.Vdbe stmt, int index, byte[] val, int n, IntPtr free) + { + return Sqlite3.sqlite3_bind_blob(stmt, index, val, n, null); + } + + public static int ColumnCount(Sqlite3.Vdbe stmt) + { + return Sqlite3.sqlite3_column_count(stmt); + } + + public static string ColumnName(Sqlite3.Vdbe stmt, int index) + { + return Sqlite3.sqlite3_column_name(stmt, index); + } + + public static string ColumnName16(Sqlite3.Vdbe stmt, int index) + { + return Sqlite3.sqlite3_column_name(stmt, index); + } + + public static ColType ColumnType(Sqlite3.Vdbe stmt, int index) + { + return (ColType)Sqlite3.sqlite3_column_type(stmt, index); + } + + public static int ColumnInt(Sqlite3.Vdbe stmt, int index) + { + return Sqlite3.sqlite3_column_int(stmt, index); + } + + public static long ColumnInt64(Sqlite3.Vdbe stmt, int index) + { + return Sqlite3.sqlite3_column_int64(stmt, index); + } + + public static double ColumnDouble(Sqlite3.Vdbe stmt, int index) + { + return Sqlite3.sqlite3_column_double(stmt, index); + } + + public static string ColumnText(Sqlite3.Vdbe stmt, int index) + { + return Sqlite3.sqlite3_column_text(stmt, index); + } + + public static string ColumnText16(Sqlite3.Vdbe stmt, int index) + { + return Sqlite3.sqlite3_column_text(stmt, index); + } + + public static byte[] ColumnBlob(Sqlite3.Vdbe stmt, int index) + { + return Sqlite3.sqlite3_column_blob(stmt, index); + } + + public static int ColumnBytes(Sqlite3.Vdbe stmt, int index) + { + return Sqlite3.sqlite3_column_bytes(stmt, index); + } + + public static string ColumnString(Sqlite3.Vdbe stmt, int index) + { + return Sqlite3.sqlite3_column_text(stmt, index); + } + + public static byte[] ColumnByteArray(Sqlite3.Vdbe stmt, int index) + { + return ColumnBlob(stmt, index); + } +#endif + + public enum ColType : int { + Integer = 1, + Float = 2, + Text = 3, + Blob = 4, + Null = 5 + } + } + +} + diff --git a/TaskyProCloned/Tasky.Core.Cloned/DL/TaskDatabase.cs b/TaskyProCloned/Tasky.Core.Cloned/DL/TaskDatabase.cs new file mode 100644 index 0000000..4ea921b --- /dev/null +++ b/TaskyProCloned/Tasky.Core.Cloned/DL/TaskDatabase.cs @@ -0,0 +1,68 @@ +using System; +using System.Linq; +using Tasky.BL; +using System.Collections.Generic; +using Tasky.DL.SQLite; + +namespace Tasky.DL +{ + /// + /// TaskDatabase builds on SQLite.Net and represents a specific database, in our case, the Task DB. + /// It contains methods for retrieval and persistance as well as db creation, all based on the + /// underlying ORM. + /// + public class TaskDatabase : SQLiteConnection + { + static object locker = new object (); + + /// + /// Initializes a new instance of the TaskDatabase. + /// if the database doesn't exist, it will create the database and all the tables. + /// + /// + /// Path. + /// + public TaskDatabase (string path) : base (path) + { + // create the tables + CreateTable (); + } + + public IEnumerable GetItems () where T : BL.Contracts.IBusinessEntity, new () + { + lock (locker) { + return (from i in Table () select i).ToList (); + } + } + + public T GetItem (int id) where T : BL.Contracts.IBusinessEntity, new () + { + lock (locker) { + return Table().FirstOrDefault(x => x.ID == id); + // Following throws NotSupportedException - thanks aliegeni + //return (from i in Table () + // where i.ID == id + // select i).FirstOrDefault (); + } + } + + public int SaveItem (T item) where T : BL.Contracts.IBusinessEntity + { + lock (locker) { + if (item.ID != 0) { + Update (item); + return item.ID; + } else { + return Insert (item); + } + } + } + + public int DeleteItem(int id) where T : BL.Contracts.IBusinessEntity, new () + { + lock (locker) { + return Delete (new T () { ID = id }); + } + } + } +} \ No newline at end of file diff --git a/TaskyProCloned/Tasky.Core.Cloned/Tasky.Core.Android.Cloned.csproj b/TaskyProCloned/Tasky.Core.Cloned/Tasky.Core.Android.Cloned.csproj new file mode 100755 index 0000000..473a06b --- /dev/null +++ b/TaskyProCloned/Tasky.Core.Cloned/Tasky.Core.Android.Cloned.csproj @@ -0,0 +1,59 @@ + + + + Debug + AnyCPU + 10.0.0 + 2.0 + {508E0D21-01E5-4A8C-A09F-AC121EDADEB8} + {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Library + Tasky + Assets + Resources + Tasky.Core.Anroid + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + false + None + + + none + false + bin\Release + prompt + 4 + false + false + SdkOnly + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TaskyProCloned/Tasky.Core.Cloned/Tasky.Core.WP7.Cloned.csproj b/TaskyProCloned/Tasky.Core.Cloned/Tasky.Core.WP7.Cloned.csproj new file mode 100755 index 0000000..1beda15 --- /dev/null +++ b/TaskyProCloned/Tasky.Core.Cloned/Tasky.Core.WP7.Cloned.csproj @@ -0,0 +1,82 @@ + + + + Debug + AnyCPU + 10.0.20506 + 2.0 + {5E634ED0-7A59-4A39-8415-175034A02C16} + {C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + Tasky + Tasky.Core.WP7 + v4.0 + $(TargetFrameworkVersion) + WindowsPhone71 + Silverlight + false + true + true + + + true + full + false + Bin\Debug + DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE + true + true + prompt + 4 + + + pdbonly + true + Bin\Release + TRACE;SILVERLIGHT;WINDOWS_PHONE + true + true + prompt + 4 + + + + ..\Dependencies\Community.CsharpSqlite\Community.CsharpSqlite.WP7.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TaskyProCloned/Tasky.Core.Cloned/Tasky.Core.iOS.Cloned.csproj b/TaskyProCloned/Tasky.Core.Cloned/Tasky.Core.iOS.Cloned.csproj new file mode 100755 index 0000000..ad0ddeb --- /dev/null +++ b/TaskyProCloned/Tasky.Core.Cloned/Tasky.Core.iOS.Cloned.csproj @@ -0,0 +1,56 @@ + + + + Debug + AnyCPU + 10.0.0 + 2.0 + {3A95B215-DC1A-4A51-A2FD-AB811103F1CE} + {6BC8ED88-2882-458C-8E55-DFD12B67127B};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Library + Tasky + Tasky.Core + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + false + + + none + false + bin\Release + prompt + 4 + false + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TaskyProCloned/TaskyAndroid/Adapters/TaskListAdapter.cs b/TaskyProCloned/TaskyAndroid/Adapters/TaskListAdapter.cs new file mode 100644 index 0000000..734b1bb --- /dev/null +++ b/TaskyProCloned/TaskyAndroid/Adapters/TaskListAdapter.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using Android.Widget; +using Tasky.BL; +using Android.App; +using Android; + +namespace TaskyAndroid.Adapters { + public class TaskListAdapter : BaseAdapter { + protected Activity context = null; + protected IList tasks = new List(); + + public TaskListAdapter (Activity context, IList tasks) : base () + { + this.context = context; + this.tasks = tasks; + } + + public override Task this[int position] + { + get { return tasks[position]; } + } + + public override long GetItemId (int position) + { + return position; + } + + public override int Count + { + get { return tasks.Count; } + } + + public override Android.Views.View GetView (int position, Android.Views.View convertView, Android.Views.ViewGroup parent) + { + // Get our object for position + var item = tasks[position]; + + //Try to reuse convertView if it's not null, otherwise inflate it from our item layout + // gives us some performance gains by not always inflating a new view + // will sound familiar to MonoTouch developers with UITableViewCell.DequeueReusableCell() + var view = (convertView ?? + context.LayoutInflater.Inflate( + Android.Resource.Layout.SimpleListItemChecked, + parent, + false)) as CheckedTextView; + + view.SetText (item.Name==""?"":item.Name, TextView.BufferType.Normal); + view.Checked = item.Done; + + //Finally return the view + return view; + } + } +} \ No newline at end of file diff --git a/TaskyProCloned/TaskyAndroid/Assets/AboutAssets.txt b/TaskyProCloned/TaskyAndroid/Assets/AboutAssets.txt new file mode 100644 index 0000000..ee39886 --- /dev/null +++ b/TaskyProCloned/TaskyAndroid/Assets/AboutAssets.txt @@ -0,0 +1,19 @@ +Any raw assets you want to be deployed with your application can be placed in +this directory (and child directories) and given a Build Action of "AndroidAsset". + +These files will be deployed with you package and will be accessible using Android's +AssetManager, like this: + +public class ReadAsset : Activity +{ + protected override void OnCreate (Bundle bundle) + { + base.OnCreate (bundle); + + InputStream input = Assets.Open ("my_asset.txt"); + } +} + +Additionally, some Android functions will automatically load asset files: + +Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); \ No newline at end of file diff --git a/TaskyProCloned/TaskyAndroid/Properties/AndroidManifest.xml b/TaskyProCloned/TaskyAndroid/Properties/AndroidManifest.xml new file mode 100644 index 0000000..5caeef7 --- /dev/null +++ b/TaskyProCloned/TaskyAndroid/Properties/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/TaskyProCloned/TaskyAndroid/Properties/AssemblyInfo.cs b/TaskyProCloned/TaskyAndroid/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..920873d --- /dev/null +++ b/TaskyProCloned/TaskyAndroid/Properties/AssemblyInfo.cs @@ -0,0 +1,30 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using Android.App; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("TaskyAndroid")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("xamarin")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + +// This app uses the internet, this can be removed if not needed +[assembly: UsesPermission("android.permission.INTERNET")] diff --git a/TaskyProCloned/TaskyAndroid/Resources/AboutResources.txt b/TaskyProCloned/TaskyAndroid/Resources/AboutResources.txt new file mode 100644 index 0000000..10f52d4 --- /dev/null +++ b/TaskyProCloned/TaskyAndroid/Resources/AboutResources.txt @@ -0,0 +1,44 @@ +Images, layout descriptions, binary blobs and string dictionaries can be included +in your application as resource files. Various Android APIs are designed to +operate on the resource IDs instead of dealing with images, strings or binary blobs +directly. + +For example, a sample Android app that contains a user interface layout (main.axml), +an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) +would keep its resources in the "Resources" directory of the application: + +Resources/ + drawable/ + icon.png + + layout/ + main.axml + + values/ + strings.xml + +In order to get the build system to recognize Android resources, set the build action to +"AndroidResource". The native Android APIs do not operate directly with filenames, but +instead operate on resource IDs. When you compile an Android application that uses resources, +the build system will package the resources for distribution and generate a class called "R" +(this is an Android convention) that contains the tokens for each one of the resources +included. For example, for the above Resources layout, this is what the R class would expose: + +public class R { + public class drawable { + public const int icon = 0x123; + } + + public class layout { + public const int main = 0x456; + } + + public class strings { + public const int first_string = 0xabc; + public const int second_string = 0xbcd; + } +} + +You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main +to reference the layout/main.axml file, or R.strings.first_string to reference the first +string in the dictionary file values/strings.xml. diff --git a/TaskyProCloned/TaskyAndroid/Resources/drawable-hdpi/launcher.png b/TaskyProCloned/TaskyAndroid/Resources/drawable-hdpi/launcher.png new file mode 100755 index 0000000..27e8dc1 Binary files /dev/null and b/TaskyProCloned/TaskyAndroid/Resources/drawable-hdpi/launcher.png differ diff --git a/TaskyProCloned/TaskyAndroid/Resources/drawable-ldpi/launcher.png b/TaskyProCloned/TaskyAndroid/Resources/drawable-ldpi/launcher.png new file mode 100644 index 0000000..08915d9 Binary files /dev/null and b/TaskyProCloned/TaskyAndroid/Resources/drawable-ldpi/launcher.png differ diff --git a/TaskyProCloned/TaskyAndroid/Resources/drawable-mdpi/launcher.png b/TaskyProCloned/TaskyAndroid/Resources/drawable-mdpi/launcher.png new file mode 100644 index 0000000..f784e5c Binary files /dev/null and b/TaskyProCloned/TaskyAndroid/Resources/drawable-mdpi/launcher.png differ diff --git a/TaskyProCloned/TaskyAndroid/Resources/drawable-xhdpi/launcher.png b/TaskyProCloned/TaskyAndroid/Resources/drawable-xhdpi/launcher.png new file mode 100644 index 0000000..32f4a0d Binary files /dev/null and b/TaskyProCloned/TaskyAndroid/Resources/drawable-xhdpi/launcher.png differ diff --git a/TaskyProCloned/TaskyAndroid/Resources/drawable/bluebutton.axml b/TaskyProCloned/TaskyAndroid/Resources/drawable/bluebutton.axml new file mode 100644 index 0000000..d74ebc4 --- /dev/null +++ b/TaskyProCloned/TaskyAndroid/Resources/drawable/bluebutton.axml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TaskyProCloned/TaskyAndroid/Resources/layout/HomeScreen.axml b/TaskyProCloned/TaskyAndroid/Resources/layout/HomeScreen.axml new file mode 100644 index 0000000..9f74e60 --- /dev/null +++ b/TaskyProCloned/TaskyAndroid/Resources/layout/HomeScreen.axml @@ -0,0 +1,17 @@ + + +