Borbin the 🐱

Project AI code

27 March, 2021


AI code is a programmable calculator for Android.
It is also available within the StockRoom App to support financial calculations.

AI code supports Forth-style definitions, variables and lamda expressions. As an engineer, I have been using a HP calculator since University and this is how I imagine a modern programmable calculator. The app comes preloaded with lots of example code to try out.
Use :clearcode to remove all examples, and :defaultcode to restore all examples.

See also Project AI code update and AI code calculus examples

AI code @github

Observe the secret message to find out what AI stands for and enjoy the psychedelic dots while staring at the keys.


Project - StockRoom

22 August, 2020


StockRoom is a financial stock tracking app for Android which I have developed with some talented young engineers.

It was a great learning experience using the most recent Android APIs and the Kotlin language to create a native app. The code structure is clean and simple and is using LiveData technology to present the stock data in all the activity screens.

See StockRoom @github


Android Room database migration and testing in Kotlin

08 June, 2020


The Android Room database is a popular choice for mobile apps. For the planned improvement for the StockTicker app, the database needs to be upgraded from version 1 to 2. This requires a migration path with a test case for the upgrade from version 1 to version 2.

To get started, the migration test is using the Android migration test helper. It is best to start with the test cases first to ensure a proper data base migration.

@RunWith(RobolectricTestRunner::class)
class MigrationTest {
  private val TEST_DB = "quotes-db"

  val helper: MigrationTestHelper = MigrationTestHelper(
      InstrumentationRegistry.getInstrumentation(),
      QuotesDB::class.java.canonicalName,
      FrameworkSQLiteOpenHelperFactory()
  )

The test case first creates a database with version 1 and adds sample values, either using the ContentValues, or by using the SQL INSERT statement.

  @Test
  @Throws(IOException::class)
  fun migrate1To2() {
    helper.createDatabase(TEST_DB, 1)
        .apply {
          val values = ContentValues()
          values.put("symbol", "ktln")
          values.put("name", "kotlin inc")
          values.put("last_trade_price", 42)
          values.put("change_percent", 4.2)
          values.put("change", 1.764)
          values.put("exchange", "NYSE")
          values.put("currency", "EURO")
          values.put("description", "desc")

          this.insert("QuoteRow", SQLiteDatabase.CONFLICT_REPLACE, values)
          this.execSQL(
              "INSERT INTO QuoteRow (symbol, name, last_trade_price, change_percent, change, exchange, currency, description) VALUES ('a1', '123', 1.2, 1.1, 1.0, 'e1', 'Euro', 'desc1');"
          )
          this.execSQL(
              "INSERT INTO QuoteRow (symbol, name, last_trade_price, change_percent, change, exchange, currency, description) VALUES ('a2', '456', 2.2, 2.1, 2.0, 'e2', 'Euro', 'desc2');"
          )

          this.execSQL("CREATE TABLE IF NOT EXISTS `PropertiesRow` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `quote_symbol` TEXT NOT NULL, `notes` TEXT NOT NULL, `alert_above` REAL NOT NULL, `alert_below` REAL NOT NULL)")

          // Prepare for the next version.
          this.close()
        }

Once the database is created, the migration test runs the migration path to version 2.

    // Re-open the database with version 2 and provide
    // MIGRATION_1_2 as the migration process.
    val db = helper.runMigrationsAndValidate(TEST_DB, 2, true, MIGRATION_1_2)

The next step is to test the content of the converted database.

    // MigrationTestHelper automatically verifies the schema changes,
    // but you need to validate that the data was migrated properly.
    val cursor = db.query("SELECT * FROM QuoteRow")
    cursor.moveToFirst()

    // Expect 10 columns
    assertThat(cursor.columnCount, Matchers.equalTo(10))

    // Expect 3 entries
    assertThat(cursor.count, Matchers.equalTo(3))

    var stringEntry: String = ""
    var floatEntry: Float = 0.0f

    stringEntry = cursor.getString(cursor.getColumnIndex("symbol"))
    assertThat(stringEntry, Matchers.equalTo("ktln"))

    stringEntry = cursor.getString(cursor.getColumnIndex("name"))
    assertThat(stringEntry, Matchers.equalTo("kotlin inc"))

    floatEntry = cursor.getFloat(cursor.getColumnIndex("last_trade_price"))
    assertThat(floatEntry, Matchers.equalTo(42f))

    floatEntry = cursor.getFloat(cursor.getColumnIndex("change_percent"))
    assertThat(floatEntry, Matchers.equalTo(4.2f))

    floatEntry = cursor.getFloat(cursor.getColumnIndex("change"))
    assertThat(floatEntry, Matchers.equalTo(1.764f))

    stringEntry = cursor.getString(cursor.getColumnIndex("exchange"))
    assertThat(stringEntry, Matchers.equalTo("NYSE"))

    stringEntry = cursor.getString(cursor.getColumnIndex("currency"))
    assertThat(stringEntry, Matchers.equalTo("EURO"))

    // description column is removed
    val index = cursor.getColumnIndex("description")
    assertThat(index, Matchers.equalTo(-1))
  }

The actual migration is done by the creating a table of the new version, copying over the content from the table of the old version.

  val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(database: SupportSQLiteDatabase) {
      val TABLE_NAME = "QuoteRow"
      val TABLE_NAME_TEMP = "new_QuoteRow"

      database.execSQL(
          """
          CREATE TABLE `${TABLE_NAME_TEMP}` (
            symbol TEXT NOT NULL, 
            name TEXT NOT NULL, 
            last_trade_price REAL NOT NULL, 
            change_percent REAL NOT NULL, 
            change REAL NOT NULL, 
            exchange TEXT NOT NULL, 
            currency TEXT NOT NULL, 
            is_post_market INTEGER NOT NULL, 
            annual_dividend_rate REAL NOT NULL, 
            annual_dividend_yield REAL NOT NULL, 
            PRIMARY KEY(symbol)
          )
          """.trimIndent()
      )
      database.execSQL(
          """
          INSERT INTO `${TABLE_NAME_TEMP}` (symbol, name, last_trade_price, change_percent, change, exchange, currency, is_post_market, annual_dividend_rate, annual_dividend_yield)
          SELECT symbol, name, last_trade_price, change_percent, change, exchange, currency, 0, 0, 0 FROM `${TABLE_NAME}`  
          """.trimIndent()
      )
      database.execSQL("DROP TABLE `${TABLE_NAME}`")
      database.execSQL("ALTER TABLE `${TABLE_NAME_TEMP}` RENAME TO `${TABLE_NAME}`")

      database.execSQL("CREATE TABLE IF NOT EXISTS `PropertiesRow` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `quote_symbol` TEXT NOT NULL, `notes` TEXT NOT NULL, `alert_above` REAL NOT NULL, `alert_below` REAL NOT NULL)")
    }
  }
}

The migration function is then used by the database builder to perform the upgrade.

return Room.databaseBuilder(
    context.applicationContext,
    QuotesDB::class.java, "quotes-db")
    .addMigrations(MIGRATION_1_2)
    .build()

To ensure you have the latest schema available, add exportSchema = true to the Database definition.

@Database(
    entities = [QuoteRow::class, HoldingRow::class, PropertiesRow::class], version = 2,
    exportSchema = true
)

If you run into the problem that the automatic generated schema is not found while the test is run, add this to the app/build.gradle.

  sourceSets {
     debug.assets.srcDirs += files("$projectDir/schemas".toString())
  }

Xamarin - change image for ImageButton

21 April, 2020


For my project I'm working on I needed a button with an image that needs to be changed according to a state variable using XAML and MVVM. In this case, the changing image for a start / stop button:

Add the ImageButton to the MainPage.xaml:

<ContentPage.BindingContext>
    <local:MainPageViewModel />
</ContentPage.BindingContext>

<StackLayout>
    <ImageButton Source="{Binding StartStopImage}"
                 Command="{Binding StartStopCommand}"
                 WidthRequest="50"
                 HeightRequest="50"
                 HorizontalOptions="Center"
                 VerticalOptions="Center" >
    </ImageButton>

Add the implementation for MVVM:

    public MainPageViewModel()
    {
        StartStopCommand = new Command(async () => await StartStop());
    }


    public async Task StartStop()
    {
        recordEnabled = !recordEnabled;

        StartStopImage = recordEnabled ? "stop.png" : "start.png";
    }

    public Command StartStopCommand { get; }

    private string startStopImage = "stop.png";
    public string StartStopImage
    {
        get => startStopImage;
        set
        {
            startStopImage = value;
            OnPropertyChanged(nameof(StartStopImage));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

Neuere Beiträge →← Vorherige Beiträge