【DIY】700円でつくる Magic Keyboard/Trackpad2 向け Mac Book Pro 13インチ型縦配置の枠

横に並べて置くことを想定して設計されていますよね、Magic Keyboard/Trackpad2。

Magic Keyboard - 英語(US) - Apple(日本)

MacのためのMagic Trackpad 2(シルバー)を購入 - Apple(日本)

Keyboard の厚さが 4.1-10.9mm、
Trackpad2 の厚さが 4.9-10.9mm。

元々 Mac Book Pro 13インチ から移行した人にとっては、手の横移動が億劫に感じます。

かといって、縦に並べておくと誤操作連発でイライラすぎ。

この厚みの差が問題なんですよね。

探してみると、こんなのありますが。

BulletTrain eXpress Keyboard Platform | Indiegogo

商品詳細 Crispy Backboard 2

なんだかな...。

ホームセンターの木材加工サービスを利用して作ることにします。

材料

Mac Book Pro 13インチの寸法を雑に測っておきます。

これに近い雰囲気で縦に並べて置くことにして考えます。

縦に並べて置くには、厚さが、6.8-10.9mm の板が良さそう。

ホームセンターに行ってみると、300x300x7mm の木板が、328円であったのでこれにしました。

プラスチック板もあったのですが、今回は木製にします。

設計

ホームセンター内の加工サービス受付で、簡単な図面を書きます。

「直線で切り落としのみ」ということで、途中でカット作業は止めることができないそうなので、切り落とした部材をパズルのように並べることにします。

数分でカットされてきます。

1カット80円ということで、320円でした。

組み立て

とはいっても並べるだけです。

7mmなので木工用ボンドで接合しても強度が弱いので、その場合は薄い何かの上に並べて貼り付ければいいでしょう。

できあがり

手前の板は不要に思っていたのですが、パームレストのようにあると腕が疲れません。

とりあえず、まあいいか。

使いながら改良していこうと思います。

→ A Mac Book Pro style Wooden Dock for Magic Keyboard and Trackpad 2

めんどくさいでの買うか、電動ノコ。

木端、気になるので丸めたいんだよな、グラインダーか。


【Android】Kotlin でモダンな concurrency その1

現在の Java/Android の concurrency フレームワークはコールバック地獄の原因となります。

それは、スレッドセーフを保証するシンプルな方法がないからです。

kotlin coroutine は、concurrency を管理するための効果的でシンプルなフレームワークです。

Suspending と Blocking

coroutine は、スレッドを置き換えるものではなく、それを管理するフレームワークのようなものです。

元のスレッドをブロックすることなく、バックグラウンド処理完了に対しての wait を可能にする実行コンテキストを定義しています。

コールバックを避けて、簡単に concurrency を行ってみましょう。

基本

最初は非常にシンプルな例です。UIコンテキストで coroutine を起動し、その中でIOコンテキストでイメージを取得します。

その後、UIコンテキストに戻ります。


launch(UI) {
    val image = withContext(IO) { getImage() } 
    imageView.setImageBitmap(image) 
}

シングルスレッドのように直感的なコードです。

getImage() がIOスレッドで実行されている間、メインスレッドは、自由に他の処理を行うことができます。

withContext は、その coroutine を getImage() が実行されている間は中断します。

getImage() のあとすぐに、メインlooperが利用可能になり、coroutine はメインスレッドを再開します。

そして imageView.setImageBitmap(image) が呼ばれます。

 

次の例は、2つのバックグラウンド処理を完了させ、その結果を利用する、というものです。

async/await を使ってパラレルに実行し、それら両方の結果取得のあとメインスレッドでそれを利用した処理を行います。


val job = launch(UI) {
    val deferred1 = async { getFirstValue() }
    val deferred2 = async(IO) { getSecondValue() }
    useValues(deferred1.await(), deferred2.await())
}

job.join() 

async は launch に似ていますが、deferred (Kotlin版 Future) を返します。

そして、await() で結果を取得します。パラメータなしで実行した場合は、CommonPool コンテキストで実行されます。

前の例と同様に、2つの結果を待っている間は、メインスレッドは自由に処理を行うことができます。

例にあるように、launch は処理が完了するのを待つのに利用できる Job を返します。

「スレッドをブロックせず coroutine をブロックする」ということを除けば、他の言語と同様です。

(つづく)


Android Architecture Components: Room の Migration で IllegalStateException

データを移行せずに捨ててしまうのなら、以下でいいのですが。


Room.databaseBuilder(context, RepoDatabase.class, DB_NAME)
    .fallbackToDestructiveMigration()
    .build();

きっと捨てることができませんよね。

テーブル定義を変更しながら、データを移行しますよね。


Room.databaseBuilder(context, RepoDatabase.class, DB_NAME)
    .addMigrations(FROM_1_TO_2)
    .build();

static final Migration FROM_1_TO_2 = new Migration(1, 2) {
    @Override
    public void migrate(final SupportSQLiteDatabase database) {
        database.execSQL("ALTER TABLE Repo
                         ADD COLUMN createdAt TEXT");
        }
    };

database.execSQL()でSQLをベタに実行しながらデータを別テーブルにRENAME後、CREATE→INSERT→DROP というかんじでスキーマを変更していますが。

すると、こんなのに遭遇します。

java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you’ve changed schema but forgot to update the version number. You can simply fix this by increasing the version number.

データベースのバージョンナンバーは上げることは、まあ上げるとして、それでもインデクスなどうまく意図通りに移行できてない場合があります。

データモデルにアノテーションで記述した「Room が利用しようとしているスキーマ」と、Migration部分にベタ記述した「SQLiteのスキーマ」が合致しないといけません。

また、最近のAndroidでは、.dbファイルが、OS上で取り回しづらく、実態を把握しづらかったりします。

Roomが認識しようとしているテーブルスキーマは以下で書き出すことができます。


android {
    javaCompileOptions {
        annotationProcessorOptions {
            arguments = ["room.schemaLocation":
                         "$projectDir/schemas".toString()]
            }
        }
    }
}


"tableName": "Repo",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, `url` TEXT, PRIMARY KEY(`id`))"

モデルクラスに記述したアノテーションのRoomが認識している状態(expected)をSQLで書き出してくれます。

これと、MIGRATION部分のベタSQL(found)を比較すると、意味が分かってきます。

複数フィールドに対してのUNIQUE なインデクスなど、公式ドキュメントとは違う内部的絶賛更新中な処理な部分など、書き出してみると先に進むことができます。