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.

AI code at Google Play

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.

The first release of Stock Room app is at Google Play


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));
    }

Replace substring in substrings with Regular expressions

21 April, 2019


A regular expression defines a search pattern for strings. Replace is possible for text matches, but repeated substring replace in substrings is difficult. Especially if the text you want to replace is also outside of the initial substrings. Using cascaded match evaluators, this replacements gets very easy. For example to replace quoted text withing square brackets.
Please note the middle text "text" in this example which must not be replaced.

["text1"] "text" ["text2"]
Begin[Pre"1txet"Post]End "text" Begin[Pre"2txet"Post]End

First the text is splitted by the primary substrings defined by square brackets.
Then these substrings are recomposed according to the business logic. In this example the string gets enclosed in a "Pre" / "Post" and then reverted. The primary substrings gets enclosed in "Begin" / "End".

string inputText = "[\"text1\"] "text" [\"text2\"]";

string replacedText = Regex.Replace(
    inputText,
    $"(\\[)(.*?)(\\])",
    arrayMatch =>
    {
        return 
        $"Begin" +
        arrayMatch.Groups[1].Value +
        Regex.Replace(
            arrayMatch.Groups[2].Value,
            $"(\".*?\")+",
            contentMatch =>
            {
                return $"Pre" + new string(contentMatch.Groups[1].Value.Reverse().ToArray()) + "Post";
            }) +

        arrayMatch.Groups[3].Value +
        $"End";
    });

use TLS1.2

24 February, 2018


If you found that your code to download from a website started failing with an error like:

The request was aborted: Could not create SSL/TLS secure channel.

or

Ausnahme beim Aufrufen von "DownloadFile" mit 2 Argument(en):  "Die Anfrage wurde abgebrochen: Es konnte kein 
geschützter SSL/TLS-Kanal erstellt werden."
+ $webclient.DownloadFile($file, $toZipFile)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : WebException

then your webserver security is updated and now requires TLS1.2.
Tls is a security protocol to transfer data over the Internet. To solve this issue, simply set the security protocol to Tls1.2.

For example in PowerShell:

$webclient = New-Object System.Net.WebClient
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$webclient.DownloadFile($file, $toZipFile)

Neuere Beiträge →← Vorherige Beiträge