CAT | Programming
Since my Eclipse installation blew up I’ve been using the IntelliJ IDEA Community Edition for Android programming. Like many others I prefer working with a dark editor background instead of a blinding bright white background but I found there are very few places you can find pre-made IntelliJ color schemes and there’s no place like http://studiostyl.es for IntelliJ. So I translated a couple of my preferred Visual Studio color schemes (Son of Obsidian and Ragnarok Blue) to IntelliJ.
You can download either or both color schemes from my Dropbox. Both SonOfObsidian.zip and RagnarokBlue.zip contain an XML file of the same name; put the XML file in your home\.IntelliJIdea10\config\colors directory (you will need to restart IntelliJ before it will pick up the files.) After you restart IntelliJ, go to File->Settings->Editor->Color & Fonts and select the scheme you want to use from the Scheme name dropdown. Both schemes use the Consolas font.
Translating the schemes was pretty straightforward with only a couple of gotchas. First is you have many more customization options in IntelliJ than you have in Visual Studio. In IntelliJ you can have different colors for instance fields vs. static fields vs. parameters, line comments vs. block comments (plus others). The second gotcha is that the settings for Red and Blue seem to be reversed. For example, in the Ragnarok Blue scheme the plain text background setting is 0x00261A10. I used a web site to quickly translate the hex code to it’s RGB equivalence; there are many sites that can handle the conversion. Drop ‘0x’ and the first ‘00’ and plug in ‘261A10’; the code comes back as Red:38, Green:26, Blue:16. But if you plug that into IntelliJ’s RGB color tab, it won’t be right. You have to enter it backwards as Red:16, Green:26, Blue:38. I tried a couple of different translation sites with the same results so the problem is either in Visual Studio or IntelliJ.
Mappings from the .vssettings file to IntelliJ are pretty simple (anyplace in .vssettings where the color is 0×02000000 it means the color was left at the default:)
- Comment: Java->Line comment & Java->Block comment
- Plain Text: General->Default text
- Selected Text: split between General->Selection background and General-Selection foreground
- Brace Matching (Rectangle): General->Matched brace (background)
- Identifier: Used this in a few places. Java->Instance field, Java->Local variable,Java->Method call, Java->Static field
- Number: Java->Number
- Operator: Java->Operator sign
- String: Java->String
- User types: Java->Class
- User Types(Interfaces): Java->Interface
- Line Numbers: General->Line number
- Keyword: Java->Keyword
- XML Attribute: XML->Attribute name
- XML Attribute Value: XML->Attribute value
- XML Comment: XML->Comment
- XML Name: XML->Tag name
28
Preview 1 of the 2011 CodeStock App
No comments · Posted by Roger in Android, Conferences, Programming
This afternoon I posted the first preview of the 2011 CodeStock Android app on the project page at http://code.google.com/p/codestock2010 (in hindsight I probably shouldn’t have included “2010” in the project name.) So far this year’s edition has a few features:
The home page displays a special background with a countdown timer until the opening of the conference.
It can also display a running stream of postings from Twitter than either mention CodeStock or originate from the official @CodeStock Twitter account. From Menu->Options, select Enable Twitter. You’ll be asked to authenticate to Twitter and grant Aremaitch Dev Test permission to access your Twitter account (this name will change when the app is finally released to the Market.) Once this is done you’ll be able to set how often your device will download tweets, how long each tweet is displayed, and how many days of tweets to keep.
There have been many internal code changes to help minimize memory and cpu usage; feel free to take a look. And, of course, if you see something that could have been done better, don’t hesitate to let me know.
I’m planning to deepen the Twitter integration by letting you send tweets tagged with #CodeStock. I’m also planning to add session notifications and integration with the device calendar. Let me know if you have any ideas for additional things to add.
No tags
I’ve been exploring the Windows Phone 7 SDK in preparation for a launch event in New York next week by porting the CodeStock Android application to WP7 and I’ve run into a bit of a headscratcher.
The Android app uses a context menu option to permit the user to refresh the data from the website “on-demand” so I created an ApplicationBarIconButton (named “downloadScheduleButton” surprisingly enough) for the same functionality. On first test the app crashed with a null object reference on the line in the main page’s constructor added to wire-up the click handler. The line of code looks like this:
this.downloadScheduleButton.Click += new EventHandler(downloadScheduleButton_Click);
The null reference was “downloadScheduleButton” and yes, I did put the line after the call to InitializeComponent(). The generated SilverLight code for the page included this line in InitializeComponent:
this.downloadScheduleButton = ((Microsoft.Phone.Shell.ApplicationBarIconButton)(this
.FindName("downloadScheduleButton")));
but FindName was returning null. The docs for FindName included this note:
"A run-time API such as FindName is working against an object tree. These objects are loaded into the content area and the CLR runtime engine of the overall Silverlight plug-in. When part of the object tree is created from templates or run-time loaded XAML, a XAML namescope is typically not contiguous with the entirety of that object tree. The result is that there might be a named object in the object tree that a given FindName call cannot find."
The docs continue by discussing using GetTemplateChild, VisualTreeHelper, or XamlReader.Load but none of the cases seem to apply here. Further, the ApplicationBarIconButton class doesn’t expose a Name property so I can’t write my own method to loop through buttons and matching by name.
I got around it by using
((ApplicationBarIconButton)this.ApplicationBar.Buttons[0]).Click += new
EventHandler(downloadScheduleButton_Click);
but that’s awfully fragile and will break if the order of buttons is rearranged. What am I missing here?
No tags
At the end of 2009 I found myself with a little too much free time and started looking for something new to experiment with. I’ve always been interested in mobile development but I don’t own a Mac or an iPhone nor do I have any interest in purchasing one. I had read an article about Android and was starting to think about a new phone so I started looking at the Android SDK. There’s no fee to download the SDK or any of the tools.
Developing for Android requires writing code in Java and designing screens (called Layouts) using XML (although layout elements can be created all in code if you wish.) I’ve never programmed in Java before but Java and C# are so similar in syntax and there is such a wealth of information on the Internet about Java that I found it very easy to learn the basics. Also, the concept of designing layouts using XML is very similar to designing WPF and Silverlight UI’s in XAML.
To start Android developing you need a few things (all the details are at http://developer.android.com/intl/fr/sdk/requirements.html and http://developer.android.com/intl/fr/sdk/installing.html.)
- A PC running Windows XP, Vista, or Windows 7 (32bit or 64bit); Mac OS X 10.5.8 or better; or Linux. I’ve been using 64bit Vista and 64bit Windows 7.
- The Eclipse IDE version 3.4 or 3.5
- The Eclipse JDT plug-in.
- The Java JDK 5 or JDK 6 (the JRE isn’t enough, you need the full dev kit.)
- The Android Development Tools (ADT) plug-in. This isn’t strictly required but it does make life easier.
- Apache Ant (http://ant.apache.org) if you want to be able to build from the command line or if you want to use a continuous integration server. Yes, Ant is where the .NET tool Nant came from.
- The Android SDK itself.
- SDK Add-ons (mainly the Google Maps API) and the USB driver for Windows. The driver allows you to run and debug an application on an actual Android device but isn’t required if you don’t have a device or are using an OS other than Windows.)
You need 500MB of additional disk space for the SDK tools, platforms, samples, and offline documentation plus additional space for each virtual device you create. Each platform targets a different version of the Android OS and you must install at least one platform. If you don’t have an Android device you’ll need to create at least one Android Virtual Device (AVD). When you create an AVD you’ll specify which version of the Android OS it will run and how large the SD card will be. If you create an AVD with an 8GB SD card the emulator will create an empty 8GB image file that represents the SD card so plan disk space accordingly.
I won’t go into the details of installing all the pieces because the web site has excellent step-by-step directions.
So what have I been doing with it? Well, I started with the tutorials of course. The ubiquitous “Hello, World" tutorial does a good job of walking you through all the steps needed to build an app. The “Hello, Views” tutorial has several examples that highlight the different base layouts and views. “Hello, Localization” talks about using resources to localize an application and “Notepad Tutorial” goes step-by-step into writing a notepad application.
In March 2010 at the Microsoft MIX conference one of the announcements was about the Open Data Protocol and one of the partners was Restlet and their Java extension for OData. Restlet has a version of the library for Android. One of the things it will do is query the metadata from a .NET WCF service and generate all the service refs and POCO, excuse me, POJO objects similar to using “Add service reference” in Visual Studio. I started using Restlet to query the Netflix OData feed that was demonstrated at MIX but discovered some problems with the Restlet library that made working with the Netflix feed difficult (Restlet is in the process of fixing the problems.) Then I saw a tweet from Chris Woodruff (Woody from Deep Fried Bytes) that he had made a database of baseball statistics available via an OData feed. For the last couple of weeks I’ve been working on a demo Android app that can search this database by player last name and display player details, batting, fielding, and pitching stats. In the next post I’ll start showing the application and talk about what mistakes I made and what I learned while building it.
No tags
I just upgraded my source control server. I’ve been using SourceGear’s Vault for several years and while I don’t keep up with every version I don’t like to let too much time go by between upgrades. Their current version is 4.1.4 and was released in November 2008. I was still running 4.0.6 and since I’ve started using CruiseControl.NET I wanted to use SourceGear’s own Vault plugin.
I always hold my breath a little bit when upgrading Vault not because I’ve had any problems (I haven’t) but just because, hey, it’s my source code repository! It’s backed up regularly but if I ever had a problem it would be a major hassle to straighten it out. Luckily everything worked this time as well.
This will also be my first time using the enhanced client that uses the VSIP api instead of the MSSCCI api.
No tags
While setting up a CruiseControl.NET build server I added a project that uses ClickOnce deployment. I didn’t want to install Visual Studio on the build server and I don’t want to install the test certificate into the ccservice account so I wrote a plugin for CC.NET that takes the files from the project’s working folder, copies them to a deployment folder, creates the manifests, then signs them (I’ll blog about the plugin later.) But I ran into some weirdness while testing the deployment that I haven’t been able to resolve.
It works fine on XP Pro and IE 7. It works fine on XP Pro and IE 8. It works find on Vista 32 bit and IE 7. It doesn’t work so fine on Vista 64 bit and IE 8. IE 8 displays a dialog prompting me to download and install the .NET framework on a computer that already has the full version of Visual Studio2008 and the framework installed. If I cancel out of the dialog the whole process just stops. If I let the install continue, the framework installer comes up in maintenance mode (so it’s detecting that the framework is already installed.) If I log into the Vista 64 computer with a user that never logged in before then it works. So something in the main profile is corrupt. I can’t find it and Google is no help (too many entries about how to install IE 7 on a hacked Windows.)
No tags
Since the dashboard project reached a new milestone last month I’ve had time to start planning the next two projects. The first is a private Internet site for customers to place orders, check on order status, check on payment status, etc. The second, and much larger, is a total rewrite of the order entry, shipping, inventory, receivables, payables, and general ledger system from the mish-mash of legacy languages and data storage to a .NET / SQL Server based system.
The dashboard uses a combination of the patterns & practices Enterprise Library and naked ADO.NET. The client is deployed via ClickOnce and talks to the server via ASPX Web Services. It’s built using the 2.0 framework and that was about it for data access options (I’m ignoring things like NHibernate, Subsonic and LLBLGen for the moment.) Now with the new projects I’m reviewing the current crop of options, starting with Entity Framework (which RTM’d when SP1 for VS2008 was released) and LINQ to Entities. I built the database and then created the entity model:
I’m starting with some simple CRUD operations for the salesman, route, chain, and customer tables:
For the moment the data model closely matches the legacy data model; that’s why the orders table references the customer table and the chain table while the customer table also references the chain table. This will most likely change going forward as I refine the model.
Then I created an ugly-as-sin, break-every-rule form for the chain table:
Here’s the FormLoad:
1: void HandleFormLoad(object sender, EventArgs e)
2: {
3: InitializeForm();
4: LoadStatesDropdown();
5: TransitionToMode(FormMode.KeyMode);
6: txtChainCode.Focus();
7: }
InitializeForm() simply sets the MaxLength of each TextBox; it’s hard-coded now but I’d like it to come from the model’s meta-data.
LoadStatesDropdown() does exactly what it says; it uses a small piece of LINQ to populate an Infragistics combobox with the states:
1: private void LoadStatesDropdown()
2: {
3: var q = from s in ctx.states orderby s.abbrev select s;
4: cboChainState.DataSource = q;
5: cboChainState.ValueMember = "abbrev";
6: cboChainState.DisplayMember = "abbrev";
7: }
TransitionToMode() enables or disables controls and sets up or clears data binding depending on the form’s mode (KeyMode, EditMode, or AddMode.)
Here’s the data binding code:
1: private void SetDataBinding(bool setIt)
2: {
3: if (setIt)
4: {
5: txtChainCode.DataBindings.Add(new Binding("Text", currentChn, "chaincode", false, DataSourceUpdateMode.OnPropertyChanged));
6: txtChainName1.DataBindings.Add(new Binding("Text", currentChn, "chainname1", false, DataSourceUpdateMode.OnPropertyChanged));
7: txtChainName2.DataBindings.Add(new Binding("Text", currentChn, "chainname2", false, DataSourceUpdateMode.OnPropertyChanged));
8: txtChainAddr.DataBindings.Add(new Binding("Text", currentChn, "chainaddr", false, DataSourceUpdateMode.OnPropertyChanged));
9: txtChainCity.DataBindings.Add(new Binding("Text", currentChn, "chaincity", false, DataSourceUpdateMode.OnPropertyChanged));
10: cboChainState.DataBindings.Add(new Binding("Value", currentChn, "chainstate", false, DataSourceUpdateMode.OnPropertyChanged));
11: txtChainZip.DataBindings.Add(new Binding("Text", currentChn, "chainzip", false, DataSourceUpdateMode.OnPropertyChanged));
12: txtChainPhone.DataBindings.Add(new Binding("Text", currentChn, "chainphone", false, DataSourceUpdateMode.OnPropertyChanged));
13: }
14: else
15: {
16: txtChainCode.DataBindings.Clear();
17: txtChainName1.DataBindings.Clear();
18: txtChainName2.DataBindings.Clear();
19: txtChainAddr.DataBindings.Clear();
20: txtChainCity.DataBindings.Clear();
21: cboChainState.DataBindings.Clear();
22: txtChainZip.DataBindings.Clear();
23: txtChainPhone.DataBindings.Clear();
24: }
25: }
I spent a lot of time searching online for databinding examples. All the ones I found were in the context of binding a grid or a list. None of them illustrated binding other controls to entities. After I figured it out it was pretty simple (note that the chain table contains no foreign keys; more on that later.)
Here’s the code behind the Submit button:
1: void HandleSubmitClick(object sender, EventArgs e)
2: {
3: if (!String.IsNullOrEmpty(txtChainCode.Text))
4: {
5: string findChain = txtChainCode.Text.Trim().PadLeft(3, '0');
6:
7: currentChn = ctx.chains.Where(c => c.chaincode.Equals(findChain, StringComparison.OrdinalIgnoreCase)).FirstOrDefault<chain>();
8:
9: if (currentChn == null)
10: {
11: if (MessageBox.Show(this, String.Format("Chain '{0}' was not found; would you like to add it?", findChain), "Chains",
12: MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
13: {
14: currentChn = new chain();
15: currentChn.chaincode = findChain;
16: TransitionToMode(FormMode.AddMode);
17: txtChainName1.Focus();
18: }
19: else
20: {
21: txtChainCode.Text = String.Empty;
22: txtChainCode.Focus();
23: }
24:
25: }
26: else
27: {
28: TransitionToMode(FormMode.EditMode);
29: }
30: }
31: }
In the legacy system, things like a chain code are numeric only, right-justified, zero-filled. In the original system they were the primary key; I plan on changing that to use a Guid as the primary key and use the human-readable code as a surrogate key.
It took my a little while to figure out how to handle foreign key constraint violations and I’m still not sure it’s the right way to do it:
1: void HandleDeleteClick(object sender, EventArgs e)
2: {
3: // only edit mode
4: if (DialogResult.Yes == MessageBox.Show(this, "Are you sure you want to delete this chain?", "Delete Chain",
5: MessageBoxButtons.YesNo, MessageBoxIcon.Question))
6: {
7: try
8: {
9: ctx.DeleteObject(currentChn);
10: ctx.SaveChanges();
11: currentChn = null;
12: TransitionToMode(FormMode.KeyMode);
13: txtChainCode.Focus();
14: }
15: catch (UpdateException ex)
16: {
17: // referential integrity exception
18: ctx.Refresh(RefreshMode.StoreWins, currentChn);
19: MessageBox.Show(this, "Unable to delete chain; the chain is currently referenced by another part of the system.",
20: "Delete Chain", MessageBoxButtons.OK, MessageBoxIcon.Information);
21: }
22: }
23: }
Again, I couldn’t find any mention of how to handle foreign key constraint violations. There are plenty of example on how to handle optimistic concurrency collisions; that’s where RefreshMode.StoreWins comes in and it seems to work correctly here as well. Without the ctx.Refresh() line, the failed DeleteObject() stays in the object context and the framework will try to execute it each time you call SaveChanges().
One thing I’m still trying to figure out is how to databind related objects. In the diagram above there are foreign keys from the customer table to the route table, salesman table, and chain table. In other words, a customer is in a route, has a salesman, and is in a chain. Here’s a portion of the code in the customer form’s SetDataBinding() method:
1: Binding b;
2: txtAccount.DataBindings.Add(new Binding("Text", currentCust, "account", false, DataSourceUpdateMode.OnPropertyChanged));
3: umeChain.DataBindings.Add(new Binding("Value", currentCust.chain, "chaincode", false, DataSourceUpdateMode.OnValidation));
4:
5: b = new Binding("Value", currentCust, "wkscredit", true, DataSourceUpdateMode.OnPropertyChanged, 0);
6: b.Format += new ConvertEventHandler(HandleFormatWeeksCredit);
7: b.Parse += new ConvertEventHandler(HandleParseWeeksCredit);
8: spnWeeksCredit.DataBindings.Add(b);
9: umeSalesman.DataBindings.Add(new Binding("Value", currentCust.salesmen, "smancode", false, DataSourceUpdateMode.OnPropertyChanged));
umeChain is a masked edit control that displays the customer’s chain code.
If the user changes the value for the chain control I want to retrieve the matching chain entity, attach it to the customer entity, and update the display. If the user entered an invalid chain, I want to whine at them and change it back to what it was. Pretty basic stuff that’s been done since the year one.
Here’s the code attached to the chain control’s Validating event:
1: void umeChain_Validating(object sender, CancelEventArgs e)
2: {
3: // Did it change?
4:
5: if (!String.IsNullOrEmpty(umeChain.Text))
6: {
7: string tempChn = umeChain.Text.Trim().PadLeft(3,'0');
8: if (tempChn.Equals(currentCust.chain.chaincode)) // if the chain didn't change, don't do the query.
9: return;
10:
11: // Have to return a chains entity here so we can attach it to the customer.
12: chain chn = ctx.chains.Where(c => c.chaincode.Equals(tempChn)).FirstOrDefault<chain>();
13: if (chn == null)
14: {
15: MessageBox.Show(this, "Invalid chain", "Customers", MessageBoxButtons.OK, MessageBoxIcon.Warning);
16: e.Cancel = true;
17: umeChain.Value = currentCust.chain.chaincode;
18: }
19: else
20: {
21: if (!currentCust.chain.Equals(chn))
22: {
23: currentCust.chain = chn;
24: umeChain.Value = currentCust.chain.chaincode;
25: }
26: }
27: }
28: }
This is where I’m currently having trouble. If I change the chain to one that actually exists (for example, 079 instead of 078) it throws an exception on SaveChanges():
A duplicate key error. Opening a quick watch on the context object shows a ModifiedEntityCount of 1 and an AddedEntityCount of 1. Nothing should have been added; this example is editing an existing entity. Drilling down into the ObjectStateManager shows one item in the _addedRelationshipStore (the EntityKey for the chain we changed to,) one item in the _deletedRelationshipStore (the EntityKey for the chain we changed from) and one item in the _modifiedEntityStore (the EntityKey for the chain we changed from not the EntityKey for the customer we changed.) If this was a bug I can’t believe it would have gone undiscovered so obviously I’m doing something wrong.
More to come…
No tags