Introduction to DbTradeAlert

Update 2018-04-12: Had to pull the app from the Play Store because the data provider isn’t anymore.

Update 2017-08-24: By now Android has got better tooling like ConstraintLayout, data binding and a persistence layer. Both avoid a lot of manual work that’s described in the posts. Be shure to check out the new tooling when you start a data heavy app.

Trade alert with custom action for reminders

DbTradeAlert in action

This is the first in a series of posts showing how to create a simple app on various platforms.

DbTradeAlert alerts you to securities reaching a specified price or at a specified date. It has a simple data model offering mostly CRUD operations like the ubiquituous to-do apps demonstrating the usage of current web frameworks. In addition to that it gets data from the internet at specific intervals and notfies you when your conditions are met. And there is a tiny bit of UI drawing in code involved.

The code is available at GitHub and the app is available in the Play Store.

In the picture DbTradeAlert shows 2 trade alerts – a reminder and a signal. In the background you see the app’s main screen with the stock’s data.

Posts in this series

DbTradeAlert for end users explains how to use the app.

Creating DbTradeAlert for Android – with points of interest listed for each post:

  • Setup part 1: Install Android Studio
    • Prerequisites
    • Java 64 bit vs. 32 bit
    • How to get around the “No JVM installation found. Please install a 64-bit JDK.” error message
  • Setup part 2: Connect a device
    • How to developer unlock a phone
    • How to enable USB debugging
    • What to do if Android Studio doesn’t list your phone as a deployment target
    • How to check if your computer supports VT-X
    • How / whether to install Intel HAXM
    • How to create a virtual device
  • Setup part 3: Install Git
    • Why Git?
    • How to install TortoiseGit
  • Setup part 4: Set up a Public Repository
    • Why and where to set up a public repository
    • HTTPS versus SSH to access GitHub
    • Create SSH keys and connect them to GitHub and TortoiseGit
    • Create a GitHub repository and configure TortoiseGit for it
    • Why and how to create a file
    • Pushing to and pulling from GitHub
    • Add a screenshot to your project’s GitHub page
  • Create the Application
    • Chose the right Wizard steps
    • Dissecting the app
  • Create the Basic Watchlist UI
    • Extend the UI infrastructure
    • How to fix errors in the generated RecyclerViewAdapter
    • Create sample data
    • Dissecting the app again
  • Add a Database
    • Why use a database for Android apps?
    • Patterns for creating and accessing a SQLite database
    • How to remove the file header comments from Android Studio’s templates
    • SQLite’s type system – or lack thereof
    • How to force SQLiteOpenHelper.onCreate() is called
    • How to get copy of the database from a device – or not
    • Create tables and sample data in SQLite
    • Flowing the data through PagerAdapter, Fragment, RecyclerViewAdapter, ViewHolder
  • Finish the Watchlist UI
    • How to use a RelativeLayout including right-to-left layouts
    • How to use hardcoded sample data in a layout without tripping Lint warnings
    • Quick introduction to themes and styles
    • How to deal with null values for variables of type float
    • How to underline or hide text
    • How to add and use color resources
    • Dealing with SimpleDateFormat and Date types
    • How to custom draw in a view
    • Performance and memory considerations when drawing
    • Measuring text width and height
    • How to return multiple results from a method
  • Update Quotes
    • Adding menu items
    • Normal versus dangerous permissions
    • Requesting permissions and Marshmallow’s changes to that
    • Downloading data in the background with AsyncTask
    • Handling possible download errors
    • Parsing csv files
    • Converting strings to dates, timestamps, floats
    • Logging exceptions
    • INSERT / UPDATE SQLite records
    • Using LocalBroadcastManager to communicate from an AsyncTask to an Activity
    • Imlicit vs. explicit Intents
    • Using Android Studio’s logcat window – and its 2 Clear buttons
    • Reporting to the user with Toasts
    • Triggering a screen update with Adapter.notifyDataSetChanged()
    • Getting an app’s title from its resources
    • Managing application state during life-cycle events caused for example by device rotation or a switched off screen
    • CursorAdapter.changeCursor() vs. CursorAdapter.swapCursor()
    • Using tags with RecyclerViews
  • Schedule Quote Updates – Part 1
    • Discussion of classes that can run tasks periodically
    • Create an AlarmManager schedule and recreate it after device booted
    • Using a WakefulBroadcastReceiver and an IntentService to do work in a background thread and stop the device from falling asleep
    • Receive broadcasts from AlarmManager and start a service to do the work without UI
    • Use “adb shell dumpsys alarm” to check alarms
    • Send the BOOT_COMPLETED broadcast to an app
  • Schedule Quote Updates – Part 2
    • Handle Marshmallow’s Doze and App Standby modes
    • Force a device into Doze mode
    • Check if an app is exempt from battery optimization
    • Ask the user to exempt an app from battery optimization
  • Schedule Quote Updates – Part 3
    • Create non-trivial SQLite queries
    • Build and send notifications
    • Check the user’s settings for an app’s notifications
  • Add Security and Watchlist Management – Part 1
    • Using Android translations for general strings like “OK”, “Cancel” – or not
    • When to set a screen’s title with setTitle()  instead of “android:label”
    • Avoiding “IllegalStateException: The application’s PagerAdapter changed the adapter’s contents without calling PagerAdapter#notifyDataSetChanged!”
    • Passing values to screens with Intent’s Extras
    • Again non-trivial SQLite queries
  • Add Security and Watchlist Management – Part 2
    • Adding buttons and a confirmation dialog to a ListView’s item layouts
    • Comunication between an app’s parts with local broadcasts
  • Add Security and Watchlist Management – Part 3
    • Validating user input and reporting errors both from screen and database
  • Add Security and Watchlist Management – Part 4
    • Nothing special
  • Add Reminders
    • Integrating a new feature into an app
    • The importance of DateFormat.setLenient(false)
    • Validating user input again
    • Using a different strategy to decide whether to UPDATE or to INSERT
    • Using custom actions in notifications
  • Add Settings and Finishing Touches
    • Create a settings screen
    • Act on settings changes
    • Integrate network usage related settings into Android
    • Use sets for settings values
    • Get a copy of the preferences.xml file
    • Initialize settings with default values
    • Problems with 12h and 24h setting in Android
    • Show user friendly summaries for settings
    • Add an icon
    • Reacting to taps on RecyclerView items
    • Adding a help page to the app’s assets and showing it in a WebView control
    • Features that didn’t make it
  • Add Backup
    • Backup vs. import / export
    • Using Android’s built-in backup in Marshmallow and before it
    • How to find an app’s database file
    • How to find external storage directories
    • How to make a new file in external storage visible to a connected computer
    • When you need a permission to write to external storage
    • How to corrupt an SQLite database file
  • Enter the Play Store
    • How to create a Google account
    • How to register for a Publisher account
    • How define flavors in build.gradle
    • How to create flavor specific classes and resources
    • How to upload an APK and provide the store listing
    • What data does the Play Store provide for a published app?
  • Integrate Google Play Services
    • Firebase Analytics vs. Google Analytics
    • App flavors, build types and build variants
    • How classes and resources get selected for a variant
    • How to get google-services.json and where to place it in your project
    • How to perform flavor specific compilation and avoid errors like “File google-services.json is missing.” and “No matching client found for package name …”
    • How to upload data to Firebase Analytics – which you cannot download
    • How to enable local Firebase logging and what its output looks like
    • What data shows up where in Firebase Analytics
    • How to add a Firebase user property and some info about its limits
    • How to remove settings at runtime and use the generated BuildConfig class to determine the app’s flavor
    • How to access Firebase user properties of various types in code
    • How Firebase deals with intermittent connections
  • Add Google AdMob
    • Google AdMob, AdSense, and AdWords
    • Create an AdMob Account
    • Register an App with AdMob, get an ad unit and optionally connect to Firebase Analytics
    • Thoughts and tricks about how to organize three flavors
    • Defining sourceSets in Gradle
    • Adding another app to google-services.json
    • Connnect an AdView Control to AdMob
    • How to use Gradle to switch between test and actual adverts
    • How to keep your ad unit Id out of VCS by using a buildConfigField and how to avoid the “integer number too large” error when doing it
    • Inject an AdView Control into a screen at runtime to contain it in a flavor
    • Examining AdMob / AdSense behaviour in logcat
    • Interface and abstract base classes don’t support static methods
  • More finishing touches
    • Add Firebase Crash Reporting
      • What Firebase Crash Reporting does for you
      • Logging errors / exceptions and debug messages
      • Using the Firebase console to check for errors
      • Uploading a mapping file to get readable stacktraces when using ProGuard
      • Notes on Firebase Crash Reporting’s shortcomings
    • Add Firebase Remote Config
      • Creating Firebase Remote Config parameters in Firebase console
      • Creating local counterparts of those parameters for caching
      • Initializing parameter values
      • Fetching parameter values from Firebase Remote Config
      • Reading a parameter’s current value
      • Peeking into the current state of a parameter and of Firebase Remote Config in general
      • Changing Firebase Remote Config parameters
    • Add an About box
      • Making text in a TextView readonly but selectable
      • Getting version information from the BuildConfig class and an app’s title
    • Perform Play Store optimization
      • What is Play Store optimization?
      • Optimizing keywords with Google’s Keyword Planner tool
  • Automated UI Tests – Part 1: Thoughts about Testing
    • Why test at all?
    • Unit tests vs. integration tests vs. end-to-end tests – oh and automated tests
    • Why you want professional testers
    • Benefits and constraints of using Espresso
    • Creating a test plan for DbTradeAlert
  • Automated UI Tests – Part 2: Testing with Espresso
    • Fixing Espresso tests:
      • Adding swipes
      • Fix how the R class is accessed
      • Fix error “PerformException: Error performing ‘fast swipe’ on view. Animations or transitions are enabled on the target device”
      • Fix error “Action will not be performed because the target view does not match one or more of the following constraints: at least 90 percent of the view’s area is displayed to the user.”
      • Fix error “java.lang.SecurityException: Injecting to another application requires INJECT_EVENTS permission”
    • How to add a file asset to instrumented tests
    • SQLite tips:
      • Improve bulk insert speed with transactions
      • Use compiled statements to improve speed
      • Additional bulk insert statements supported by SQLite
      • Indexes in SQLite asre 1-based
      • Nested transactions and savepoint / release
    • Read a file asset from an instrumented test’s APK
    • Using InstrumentationRegistry to get the context of both the test and the tester app
    • How to clean up data inserted for a test
  • Automated UI Tests – Part 3: Testing with WireMock and Firebase Test Lab for Android
    • Adding a file asset to an app’s test package
    • Multidexing in an app’s test package
    • How to find the culprit for a “ZipException: duplicate entry” error by listing dependencies with gradlew
    • How to resolve a DuplicateFileException with packagingOptions in build.gradle
    • How to define a build type and use it as the default build type for tests
    • How to eliminate unneeded build variants with a variantFilter
    • Redirecting URLs to a mock with build.gradle and
    • Some tips about the execution order of rules and test methods in instrumented tests
    • Reading a file asset from an app’s test package
    • WireMock
      • Using it to provide canned responses
      • How to log served events, unmatched requests and near misses
    • Firebase Test Lab for Android:
      • Upgrading to the Blaze plan – if necessary
      • Specifying the devices, API levels, orientations, and locales on which to run a test
      • Dealing with “java.lang.NoClassDefFoundError:” on pre-Lollipop devices
      • Calculating the cost for testing on physical devices
      • Why Firebase Test Lab for Android isn’t suited for testing app animation smoothness
      • Dealing with “NoActivityResumedException: No activities in stage RESUMED” errors
      • How to run a Firebase Test Lab for Android Robo Test – and whether it’s useful
    • WireMock, 2nd scene
      • How to simulate network faults with it
      • Using its stateful behavior with scenarios
      • WireMock bugs
    • Using the Android SDK’s test monkey and a honorable mention of the monkeyrunner tool

Some history

The main reason for creating the apps is interest in learning how to program for those platforms.

I actually wrote the app for Android 4 years ago and use it since. Then some Android update broke Eclipse, its integration with ADT, with the SDK or any combination of them and the general consensus was that you needed to reinstall everything.

In the mean time Android Studio had gotten to a usable state and I decided to give it a try which meant not being able to use the existing project. Of course I had no idea anymore what goes where in the app because I didn’t document anything as the information was readily available. So I was forced decided to rewrite it from scratch, document it properly, and skip all the cool features that I never used. This also created the opportunity to use fragments which I skipped in the first run.

This entry was posted in Uncategorized and tagged . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s