みんからきりまで

きりみんです。

様々なTODOアプリやタスク管理方法を試行した結果最終的にプレーンテキストに行き着いた話

TODOアプリという永遠のテーマ

このブログでは過去に何度かタスク管理についてエントリを書いてきました。

kirimin.hatenablog.com

kirimin.hatenablog.com

kirimin.hatenablog.com

タスク管理のためのいわゆるTODOアプリについては色々なものを試してきて、KanbanFlow→Habitica→GitHub→Todoistと移り変わっていった。

でもやっぱりメモ帳を使ってしまう問題

このように最高のタスク管理をしようとポモドーロ機能やリマインド機能、優先度設定やタグ付け、定期タスク登録など様々な機能を持ったタスク管理アプリを使ってきたが、いつも気がつくとWindows標準のメモ帳かVScodeでプレーンテキストに書きなぐってしまう。
しかもそれが一番しっくりくるのだ。

多分理由はいろいろあって、たとえば

  • 一瞬で開ける
  • いらなくなったら閉じれば削除される
  • 終わったタスクが勝手に消えたり移動したりしない
  • プレーンテキストなので自由に書き殴れる
  • 保存しようと思ったら適当にデスクトップなどにファイルを保存すればいい

などだ。

やっぱりプレーンテキストが最高なんだよな

アプリエンジニアが言うのもなんだけど、リッチなUIが容易されているアプリやリッチテキストよりも、結局プレーンテキストが一番速くて柔軟性があるということに気づいてしまったのだ。
markdawnというフォーマットが支持されているのもそういう理由なんだと思う。

プレーンテキスト(markdown)でタスクを全て管理する

というわけで色々考えた結果、最終的にプレーンテキストで全てのタスクを管理するという方法に落ち着いた。

具体的にはInkdropというmarkdownメモ帳アプリを使っている。
このアプリはクラウド同期に対応していて、かんたんに言えば軽量版Evernoteという感じだ。

今はこのアプリを全ての端末(PC,スマホ)で同期してタスク管理も含めた統合メモ帳環境として使っている。

文章で説明するよりも画像を見せた方が早いと思うのでスクショを貼る。
基本的には次の画像のようにマークダウンフォーマットでタスクを単純に箇条書きしているだけだ。

gyazo.com

しかしこれはかなり便利で、当たり前だけどプレーンテキストなので並べ替えもまとめてコピーや貼り付けも自由自在だ。
月ごとにページを分けていて、終わらなかったタスクは新しい月のページにコピペして移動している。
また、プレーンテキストのよいところとして、終わったタスクを移動させずにチェックをつけていくことで、過去にどんなタスクを消化したかが視認できる。マークダウンなら取り消し線を使えばより分かりやすい。
これは達成感というモチベにもかなり影響していて、多くのTODOアプリが終了したタスクを自動で削除したり終了済みの別ページに移動させたりしてしまうが、それが不満だった。

なお、画像内のタスクの右側に書かれているアルファベットはタスクの「優先度」と「重さ」を表していて、これもテキストなので一瞬で書けるが、タスクを整理するのに非常に便利だ。

最近は普通のTODOに加え、積本の管理も同じフォーマットでやり始めた。

https://i.gyazo.com/fa857efa4e3e32d0bcff0e5df6cf86d5.png

積み本も多くなってくると優先度などが管理出来なくなってくるので、このようにまとめると整理しやすい。
タスク管理と同じく優先度と重さを付与しているが、更に最後に"K" "P"とつけてKindleか物理本かを区別してる。

その他のInkdropの活用

タスク管理以外のメモも最近はすべてInkdropに移行している。
ぼくはEvernoteも有料会員で使っているんだけど、Evernoteはリッチテキストなのでイライラすることが多かったり、重くて使い勝手が悪かったりするのであまり積極活用できていなかった。
ただし、旅行のメモなどの場合はメールやWebサイトの内容をそのままコピーしてメモしたいことが多いため、リッチテキストのEvernoteの方が便利な時もある。

とにかくプレーンテキストで箇条書きするというのは頭を整理するのにとてもよい方法だと感じている。
そのツールとして軽量かつMarkdownが補完、ハイライトされるInkdropはとても便利だ。

ただし例外として定期タスク(習慣化したいもの)だけはテキストベースだと面倒なので、日課の管理だけはTodoistを使っている。

夏コミ(C96)4日目にサークル参加して漫画や技術書典の既刊などを配布します

8/12(月)に開催される夏コミ4日目、スペース南キ05-aにてサークル「きりみんちゃんねる」としてVTuberきりみんちゃんの創作漫画や技術書典の既刊などを配布します。

詳細はこちらのページをご覧ください。

www.pixiv.net

夏コミでも技術書典と同様にpixiv PAYによる前払い取り置きを行います。
また、BOOTHにて電子版を販売中で累計1000部以上売れている「フリーランスを完全に理解出来る本」の紙版をイベント特別価格(という名の在庫整理)で半額の500円にて配布します。
コミケ4日目はTechBoosterなど技術本系のジャンルも配置されてる日なので、遊びにきてくれるとうれしいです。

https://fanbox.pixiv.net/images/post/485544/w/1200/2Y46ndKbeEXbITz9FMFNHT2K.jpeg

こんな漫画を(きりみんちゃんと一緒に)描きました。
ほのぼの日常姉妹百合エンジニアネタです。

https://fanbox.pixiv.net/images/post/485544/w/1200/tb1Ltltguyw5lua5M3FgkLlM.jpeg

https://fanbox.pixiv.net/images/post/485544/w/1200/fx6Qfg3jgoz4EzTiGsc7DqHb.jpeg

https://fanbox.pixiv.net/images/post/485544/w/1200/cve60uKp1o2qnTNX1VG0GgNp.jpeg

「エンジニアのためのマネジメントキャリアパス」は情熱プログラマーの次くらいに全エンジニアにオススメしたい仕事に関する本だった

少し前に話題になっていた「エンジニアのためのマネジメントキャリアパス」を読みました。

エンジニアのためのマネジメントキャリアパス ―テックリードからCTOまでマネジメントスキル向上ガイド

エンジニアのためのマネジメントキャリアパス ―テックリードからCTOまでマネジメントスキル向上ガイド

タイトルからは完全にEMやCTOなどを視野に入れ始めた中堅のつよつよエンジニア向けの本という印象を受けますが、実際には特にマネージャーになる予定がない若手エンジニアや学生、IT業界で働くエンジニア以外の職種の人にもオススメしたい汎用的な組織や仕事に関する知見が書かれた本でした。

たしかにこの本はエンジニア組織のマネージャー職について書かれた本であり、一番の想定読者はマネージャーになった(なる予定がある)エンジニアだと思うのですが、マネージャーや管理職という立場はどんな組織にも必ず存在し、かつ直接的にそういう肩書をもっていなくても誰もが時には役割を担う可能性のある業務なので、結果的にこの本を読むことで組織というものやそこで働く様々な立場の人達の考え方や事情が理解出来るようになっている。

また、この本では章ごとに段々と大きなスコープの話になっており、最初は「マネージメントを受ける側の心構え」といった内容があり、次に「メンターとして後輩と接する」といった、いわゆる管理職以外でのソフトスキルについても詳しく書かれている。

更に面白いと思ったのは、マネージャーオブマネージャーやVPO、CTOなど平のエンジニアにはあまり想像しにくい立場の人達がどんな課題や苦労に直面し、どういう行動を取るのが望ましいかということが書かれており、「よい上司とはどんな人物か」「組織の偉い人達はどんな事情を抱えているのか」といったことが想像できるようになる。

そういう意味で、むしろ学生や新卒の人こそこの本を読むことで会社組織というものがどういうものなのかという事が理解できるようになり、組織を意識した働き方や職場の良し悪しの判断がしやすくなるのではと感じた。

とにかくとてもオススメの本だとおもいました。
自分も定期的に再読したい。

pixiv App Nightで「アプリエンジニアでも神絵師になりたい!」というLTをしてきました

pixiv App Nightというイベントでお絵かきエンジニア枠という募集があって面白そうだったので、久しぶりにLTをしてきました。

pixiv.connpass.com

発表資料は以下になります。

speakerdeck.com

内容は以前このブログに描いたイラストの話と被ってる部分もありますが、2019年versionとして改めてという感じです。
あんまりアプリ開発と関係ない話になってしまったので大丈夫かな?と不安だったのですが、会場ではとても好評だったようで安心しました。

他の方の発表も単純なアプリの実装に留まらない内容で刺激をうけました。
特に趣味でTensorFlowを使ってイラストの画像識別をやっているという話がとても興味深くて、自分も機械学習に手を出したいなぁと思いました。

ピクシブ社のオフィスもおしゃれで普段インターネットで見る方々と交流できたりしてとてもたのしかったです。

こっそり連れて行ったきりみんちゃんもたのしそうでした。

Gboardに「ステッカー」という機能があることにようやく気がついたのでTwitterとかでLINEスタンプみたいなことが出来るアプリを試作してみた

こんにちは、VTuberきりみんちゃんのマネージャーをしているきりみんという者です。

LINEスタンプ、いいですよね。
好きなキャラクターのLINEスタンプが発売されるとつい無条件で買ってしまいますよね。
ちなみに使う機会はほぼありません。

LINEスタンプみたいな機能がTwitterにもあったら、ユーザーも合法的にキャラクターの画像を貼れるしTwitterも版権元も収益化ができてみんな幸せになりそうなのになぁ、と思っていたら、Gboardにステッカーという機能が存在することに気が付きました。

みなさんはGboardにこんなgifアニメステッカーを簡単に貼れる機能がついているのを知っていましたか?
ぼくは全く知りませんでした。

気になったので調べてみると、どうやら自分でもGboard用のステッカーアプリを作ることが出来るようです。
それなのにやはりあまり存在に気付いている人がいないのか、ストアで調べてもGboardステッカーアプリは数えるほどしかないっぽく、特に日本人向けのステッカーはほぼ存在していなさそうな感じでした。

もしかしてこれは一発当てるチャンスなのでは?と思い、実装方法を調べて実際にアプリをリリースしてみました。

実装

とりあえず公式の紹介記事をみる。が、情報が少なくてよくわからない。

android-developers.googleblog.com

Common builders for Indexable objects  |  Firebase

ググるといくつか解説しているエンジニアブログの記事があったのでそちらを参考にさせてもらいました。

spin.atomicobject.com

proandroiddev.com

基本的には上のエントリに実装方法が丁寧に書かれているので、そちらを読んでくださいという感じです。

ざっくりと実装コードを紹介します。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="kirimin.me.emojires">

    <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_title"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="mystickers"/>
                <data android:host="sticker"/>
            </intent-filter>
        </activity>
        <service android:name=".StickerIndexingService"
                 android:permission="android.permission.BIND_JOB_SERVICE"/>
    </application>

</manifest>
class StickerIndexingService : JobIntentService() {

    companion object {
        private const val UNIQUE_JOB_ID = 42
        private const val URI = "mystickers://sticker/"
        
        private const val IS_PART_OF = "isPartOf"
        private const val HAS_STICKER = "hasSticker"
        private const val STICKER = "Sticker"
        private const val STICKER_PACK = "StickerPack"
        private const val STICKER_PACK_NAME = "emojirespack"

        fun enqueueWork(context: Context) {
            enqueueWork(context, StickerIndexingService::class.java, UNIQUE_JOB_ID, Intent())
        }
    }

    override fun onHandleWork(intent: Intent) {
        val update = FirebaseAppIndex.getInstance().update(
            Indexable.Builder(STICKER_PACK)
                .setName(STICKER_PACK_NAME)
                .setImage(convertUrlFromDrawableResId(applicationContext, R.drawable.teetee))
                .setUrl(URI + "pack/0")
                .setDescription("A sticker pack of Nihongo")
                .put(
                    HAS_STICKER,
                    Indexable.Builder(STICKER)
                        .setName("Etsu")
                        .setImage(convertUrlFromDrawableResId(applicationContext, R.drawable.etsu))
                        .setUrl(URI + "etsu")
                        .setDescription("Etsu")
                        .put(IS_PART_OF, STICKER_PACK_NAME)
                        .build(),
                    Indexable.Builder(STICKER)
                        .setName("Gomenne")
                        .setImage(convertUrlFromDrawableResId(applicationContext, R.drawable.gomenne))
                        .setUrl(URI + "gomenne")
                        .setDescription("Gomenne")
                        .put(IS_PART_OF, STICKER_PACK_NAME)
                        .build(),
                   // 以下略
                )
                .build()
        )
    }

    private fun convertUrlFromDrawableResId(context: Context, drawableResId: Int): String {
        val sb = StringBuilder()
        sb.append(ContentResolver.SCHEME_ANDROID_RESOURCE)
        sb.append("://")
        sb.append(context.resources.getResourcePackageName(drawableResId))
        sb.append("/")
        sb.append(context.resources.getResourceTypeName(drawableResId))
        sb.append("/")
        sb.append(context.resources.getResourceEntryName(drawableResId))
        return Uri.parse(sb.toString()).toString()
    }
}

要約すると、FirebaseのAppIndexingの機能を使ってGboardに自作アプリのステッカーを紐付けるだけです。 そのためにJobIntentServiceを継承したServiceクラスを作り、FirebaseAppIndexにStickerとStickerPackを登録します。
実装中、ImageとUrlに何を入れればいいのかよく分からなくて迷ったのですが、Imageにはステッカー画像のURL(ローカルデータならResourceのURI)、UrlにはURL Scheme(AndroidManifestに設定したものと一致していれば何でもいい)を入れれば動きました。

とりあえずこれだけでこんな感じに動くアプリを作ることができました。

ストアにリリースしてます。 play.google.com

GitHubでもコードを公開しています。 github.com

今回はとりあえず試作なのでSlackみたいな文字スタンプを使いましたが、gifアニメにも対応しているのでLINEスタンプみたいな感じでいろいろなキャラクターのスタンプアプリを作れれば面白いんじゃないかなーと思いました。

というわけで(実装公開しちゃったけど)Gboardステッカーアプリ作りたいという案件お待ちしております。