Friday, June 27, 2014

6/27/14

Whoops, I haven't posted in over a week!  I guess I haven't really felt like spending even more time sitting at my computer.

So since my first app is finished, I've spent most of the past week working on a new project.  It was much more... theoretical than the last one, so I spent the entire week simply trying to create a proof of concept to see if it was actually feasible.  Turns out... it wasn't.  So I spent the entire week working and I have nothing to show for it besides a bunch of code that I will probably never look at again.

I was trying to create an app that sampled noise from the microphone and cancelled it when listening with headphones.  If everything worked out, I was going to try to make a noise-cancelling music player or something similar.  This is another reason I haven't posted in a while; this has never been done before, so I didn't really want to give away anything that I was working on.

Looking back, I suppose that there is a reason it has never been done before.  I guess the way that noise-cancelling headphones work is that there is a direct relationship between the outer microphone and the inner speaker, so that any incoming sound is immediately cancelled.  I knew I didn't have this kind of control in Android; the closest you can get is by using AudioRecord and AudioTrack to work with the individual samples of sound, and those require a buffer.  The buffer has a minimum size (>1), so there's no way you can process the incoming sound before the outside noise has already hit your ear.  So I didn't even try to mess with a direct relationship.  That wasn't my original idea anyway, so I wasn't discouraged.

My plan was to take a fairly small sample and apply the Fast Fourier Transform to get it in the frequency domain.  From there, I was hoping that some frequencies would stand out more than others, and I could use those to create a generic noise wave that could cancel a decent amount of the incoming noise.  However, after transforming the signal, nothing really stood out.  The noise was comprised of frequencies basically spanning the whole spectrum.  I should have just stopped right there, because that should have tipped me off that my next idea would not work either.

Plan B was to use the Levenberg-Marquardt algorithm to try to fit a generic signal directly to the data.  If you aren't familiar with the algorithm, the TL;DR is that it is used to minimize the sum of squares between empirical data and any arbitrary function.  It iteratively adjusts the function's parameters until the function is considered "close enough" or until the parameters stop changing very much.  So I figured, "sound is just a bunch of sinusoidal waves put together, so I can use a linear combination of sinusoids as the input."  The problem here is that noise doesn't really look like "a bunch of sinusoidal waves."  Noise is so random that it would take thousands of different waves, each with a different frequency and/or phase, to even come close to modelling it.  And the Levenberg-Marquardt algorithm has a complexity somewhere in the order of O(N^2) (Disclaimer: that is a complete guess).  In my implementation, trying to fit a function with 7 parameters to a 1-second sample took about 25 seconds.  I was aiming for 10-15, and the resulting function was not even close because of the randomness of the noise.

So that's my past week in a nutshell.  It's a bit disappointing, because it would have been awesome to create something with that level of technical complexity, instead of the rather trivial stuff I have been working on.  Oh well, nothing I can do about it I guess.

Oh yeah, now I'm working on a game.  I'll write more about it soon (assuming I remember to post).

Wednesday, June 18, 2014

Lessons Learned: Part I

I'm going to do one of these posts every time I release an app, where I talk about what I learned throughout the development process.  As with everything on this blog, hopefully someone will find it useful.


1.) Plan ahead

I'm just going to say this in advance: many of these are going to be incredibly obvious.  Including this one.
Before you start, take a few minutes (or hours) to plan your app's layout and figure out what you want in advance.  I did this, and it made the layout process incredibly easy and smooth.  It also helped me to recognize inconsistencies and issues before I even started writing any code.  If you just dive in without thinking about things, there's a good chance at some point you will need to make big changes because you didn't factor in ________.  5 minutes of planning saves 5 hours of headache.


2.) Don't say "I'll reorganize stuff later"

Make your code extensible and reusable from day 1.  I made the mistake of writing messy, copy-pasted code everywhere, and it turned out to be a real pain.  For example, I have some dialogs that I use in multiple places.  When I first started writing the app, I just copied and pasted these where I needed them, because I (for some reason) didn't want to slow down for 5 minutes and think about how I could reuse that code.  Then of course, I needed to make changes to the dialogs, which left me scrambling to remember every place I had copied that code.  When I would miss one, I would waste time trying to figure out why one dialog worked and the other didn't.  At the time, it felt like I was saving myself time and effort, but in retrospect I definitely wasn't.  Even if you don't plan on using something again, it doesn't hurt to take 30 seconds to throw it into a function or its own class.  Which brings me to #3...


3.) Don't be afraid to extend stuff

This one is actually kind of new to me.  Most of my programming experience comes from school assignments, and most in classes don't need to use inheritance at all.  The classes that do need it typically give you a very specific architecture that you need to create, and as such you miss out on the practical side of concepts like this.  I never really realized how easy it is to modify a class's behavior.  For example, bringing up the dialogs again, most of the dialogs that I used had an EditText for entering semester/course/assignment information.  I wanted the soft keyboard to automatically show itself as soon as the dialog appeared.  (I think I mentioned this in another post.)  Anyway, once I found a solution, I ended up copying and pasting it in every single dialog that needed it.  Looking back, I could have simply made my own class extending from AlertDialog that could have handled that automatically.  And there are a lot of other ways this can help; changing the behavior of an ArrayAdapter, or creating a special kind of Button for example.  And if you only need to override one or two functions, you can always use an anonymous class.  (I had no idea that you could do this until a few weeks ago.  Anonymous classes are now by far my favorite Java feature.)



That's all I've got right now.  Basically they all boil down to: don't rush things, and don't avoid a bit of extra work if it will save you trouble later.

Monday, June 16, 2014

GradeBook

Introducing my first app: GradeBook!

GradeBook is an app to help keep track of grades and GPA.  Multiple times throughout my academic career I have needed to sit down, pull out a calculator, and figure out:
  • My cumulative GPA
  • My GPA for a single semester
  • My current grade in a course
  • What grade I need on the final exam to get an A
My school provides a (horribly designed) PeopleSoft-based website to help with the first two, and the third is up to the teacher.  Some teachers use Blackboard, but many teachers do not.  I wanted to design an application that could keep my grades all in one place and help me calculate the things I listed above, while avoiding terrible websites like the ones my school provides.

GradeBook does all of this.  You can use as much detail as you want; you could just fill in your GPA for each semester, or if you feel like it, you can add every single assignment and test you have ever taken.  GradeBook will take all of your assignment grades and automatically calculate course grades, semester GPA, and cumulative GPA.

Semesters that are currently in progress can be "unlocked", so that the grades from that semester will not be counted in the cumulative GPA.  Then once all grades have been finalized, you can lock the semester to see how those grade affected your GPA.

Currently, GradeBook supports courses that are graded either as a sum of points or as a weighted average.  Additionally, the curve for each class can be manually adjusted.  Ideally, this should cover any grading method that the teacher provides, but in case it doesn't, grades for each course can be manually overwritten.

Download it here:

Get it on Google Play 

If you download it, be sure to rate and review!  I plan on reading every single review so I can try to keep everybody satisfied.

Thursday, June 12, 2014

Extending ListPreference to use custom RadioButton drawables

I did some pretty cool stuff today.  Actually, I only did one cool thing, since it took me basically the entire day to get everything working.  I wanted to add color previews to the theme chooser in my settings menu, but I didn't just want to add a colored square next to the name.  I wanted to replace the default RadioButton with a custom icon that would represent the color of the theme.  So I had to learn all about custom Drawables... although in retrospect it probably would have been easier to just make an image.  Oh well, I guess I still learned a lot.

So this was pretty much the first thing that I had trouble researching online.  Turns out, not many people want to do something like this.  I will post the code and explain the problems I ran into, and hopefully they may end up helping someone.  The end result can be seen right over there...  -->

Let me start off with the drawable.  It uses two layer-lists combined in a selector, so that the icon can have "checked" and "unchecked" states.

theme_radio_checked.xml
 @drawable/check is an image containing the gray check mark.  The file for the unchecked drawable is similar, but uses a blank image of the same size.  I went with a white background so that later when I use a color filter to change the icon color, it produces the exact color I want, so I don't have do mess around with all the filter types.  These two drawables are then combined with a selector:

theme_radio_selector.xml
theme_list_row.xml
 Most of that is pretty simple, but I'd like to point out the last 4 attributes of the RadioButton:

android:focusable="false"
This is needed because if you have something in the row that can take focus, you will not be able to select the row.  This makes sense for things like buttons, but I want them to be able to click anywhere on the row to select it.
 
android:button="@null"
android:background="@null"
android:drawableLeft="@drawable/theme_radio_selector"
At first glance, this probably seems like a silly way to add the drawable to the button.  I ended up doing it this way to address two problems that I ran into:

1.) If I assign the drawable to the button, there is no way (that I could find) to access the drawable in the code.  I needed to access it to dynamically change the color, so that wouldn't work.
2.) If I assign the drawable as the background, it sometimes becomes distorted to fill its spot in the layout.  I couldn't figure out any way to force a square layout aside from creating a custom View, which I didn't want to do.

So by turning the button into a compound drawable, I am able to both keep it square and dynamically change the color.  Awesome.  Finally, I created a custom Preference extending from ListPreference to handle the color changing.  The only thing I had to override was the onPrepareDialogBuilder function:

ThemeListPreference.java
(Sorry about the formatting, one of these days I'll get around to editing the CSS of this blog. EDIT: Told you!)

 The array adapter handles mapping the entry array to the ListView, but I also had to overload its getView function to change the view as I needed.  I discovered that I had to manually check the selected radio button.  I then got the drawable with getCompoundDrawables()[0], and gave it a color filter based on which element it was.

Before I implemented the selector, I was able to use ((LayerDrawable) button.getBackground()).getDrawable(0) to change the color of only the background layer, (leaving the check mark pure gray instead of tinting it along with the background,) but I couldn't find a way to get the drawable for a single state of the StateListDrawable.  Oh well, I actually like it better this way.

So that's really all there is to it.  Seems a lot simpler now that I have it all written out, haha.  Hopefully someone finds that helpful. 

EDIT: I discovered a bug.  With the above code, if you click on the drawable itself, it will appear to switch to the "checked" state, but it won't actually do anything.  This is easily fixed by adding the android:clickable="false" attribute to the RadioButton.

Wednesday, June 11, 2014

6/11/14

Getting close now.  Monday I added styles and themes to my whole app.  I simultaneously love and hate designing things myself...  I have the technical skill, but I'm just not that creative.  Whenever I design something like this, it never quite looks how I want.  But at the same time, I love just messing around with things and seeing how they change.  If I had someone else to design everything for me, would that take all of the fun out of it?  Hmm...

I can already tell how much better I have gotten at this.  Yesterday and today I've just been adding features, but now it only takes me a few hours instead of the entire day.  And that is including the time it takes to deal with my incredibly messy code.

Speaking of messy code, I need to clean things up before I release, but it's tough to find the motivation.  Things are currently kind of slow and inefficient, but I hate spending so much time working on something that a.) won't really be noticeable to users and b.) will likely introduce some new bugs.  I mean, it's better to fix it before things get too complicated, but this app probably won't become that complicated anyway.  And I'm so close to finishing that it's hard to work on something that seems so unimportant.  But my "good programmer" senses are tingling...

Friday, June 6, 2014

6/6/14

My sloppy code design is really starting to catch up with me.  I'm wasting way too much time copy/pasting code in different places, or fixing bugs because I changed a line somewhere and forgot to change similar blocks of code.  I'm probably going to have to spend a day or two just cleaning up everything.  Hopefully I will plan things a bit better in my next app, because honestly this first one is embarrassingly bad.  Good thing I'm not planning on releasing the source code.  :p

In other news, I finished the last of my app's core functionality today.  I still have a few extra things that I may decide to add, and I know there are a couple of potential bugs still hanging around, but it now officially does what I originally planned for it to do.  So that's pretty cool.  On Monday I will start looking into themes and whatnot to (hopefully) make things look nice.

Thursday, June 5, 2014

6/5/14

Haven't had much to write about the past couple of days.  Things seem to be going really smoothly; longest I've gotten stuck was for an hour or two, fixing a bug that showed up when I modified my database to cascade on delete.  Turned out the SQLiteDatabase.replace function does strange things when the database is set up to cascade, so I ended up re-writing a few functions to avoid using that.  Other than that little hiccup, I've been making a lot of progress.  In fact, this app should be ready to release by the end of next week at the latest.

I did run into an interesting issue today though.  I wanted to make the soft keyboard pop up whenever I show a dialog, so users don't have to tap the text field an extra time. I had to try a couple different things to get it to work correctly. 

The first method that appeared to work was to use:

((InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE))

        .toggleSoftInput(InputMethodManager.SHOW_FORCED,0);

And a similar method to hide the keyboard.  But I had some issues depending on where I put the function.  Sometimes, it wouldn't show itself, other times it wouldn't hide, even if you quit the app.

I ended up using something like this:
But this had an interesting problem of its own.  It worked flawlessly for one dialog (it even knew when to close itself, unlike the other method where you had to force it to close), but when I put the exact same code in another dialog, it didn't work.  Both of them started with the correct editText in focus, but the problem was caused by when the field was put into focus.  The second dialog used a custom layout, and in the XML I had added the <requestFocus /> tag, because I obviously wanted to start with that field focused.  (Turns out I didn't need to, because I guess the first editText is automatically focused, but that's besides the point.)  The problem was, because I asked for focus in the XML, the field started with focus before I established the listener.  Therefore, there was no change of focus to trigger the listener.  All I had to do was remove that tag, and it worked flawlessly.  (Ignoring the fact that it will show the soft keyboard even when a physical keyboard is present.)

So yeah, that was kind of interesting.  (Finally I can share something helpful that I learned!)

Monday, June 2, 2014

6/2/14

Back from vacation!  I feel like I didn't get a whole lot done today, but that's probably just me trying to get back in the swing of things after spending a week in Florida.

I've only been working on this app for about a week and it already feels like my code is becoming... unwieldy.  I normally pride myself on my clean and organized code.  In fact, I probably spend the same amount of time adding comments and making things look nice as I do writing the actual code.  But this time, my code just feels like a mess.  I'm not sure if it's because I keep distracting myself by thinking of new bits of code to write, or if I feel pressured to spend less time on comments and formatting so I can release my app sooner.  It doesn't help not having an hourly wage; although I'm doing this more for the experience than the money, a little bit of money would still be nice.  So functional code seems to be taking priority over beautiful code.  Maybe I'll spend a couple hours tomorrow cleaning everything up.

I'm still struggling a bit with handling the activity hierarchy.  Namely, if I pass information to Activity A through an Intent, then it can read the Extras from that Intent and everything works fine.  But, if I go up from Activity B back to Activity A, then it doesn't use an Intent (or at least one that I wrote), so Activity A crashes when it tries to use information from the Intent that I never added.  I imagine that there is a way to handle this... actually, I know you can use startActivityForResult, but I'm not really looking for a result, I'm just trying to display the information that was there before I started Activity B.  I've kind of worked around it by calling onBackPressed every time someone clicks up, but I feel like that might not always work depending on what I decide to add.

Finally, Game of Thrones season 4 episode 8...  HOLY CRAP