Celeste Engineer

Androidとか自転車とか

甘い口溶けのスタウトビールを飲みました

この記事はBeer Advent Calendar 2017の9日目の記事です。

adventar.org

きっかけ

f:id:KeithYokoma:20171208225419j:plain

なにがきっかけでチョコレートビールの存在を知ったのかは忘れてしまいましたが、その後たまたま見かけたアニメでも取り上げられていて、なるほどこれは良さそうと思いアドベントカレンダーの記事にしようと購入しました。 なかなか置いているお店もないのでアマゾンでポチッとお取り寄せ。注文を受けてから瓶詰めしてくれるそうで相当気合が入っています。今回は4本セットの商品を買いました。この記事にはそのうち、Sweet Vanilla StoutとBrown Porterについて書きます。

Sweet Vanilla Stout

f:id:KeithYokoma:20171208230323j:plain

名前の通り、ほんのりバニラの香りがする甘い口当たりのスタウトビールです。もともとスタウトビールが好きで、特に柔らかい感じがある濃い目のスタウトが好みです。 そしてこのSweet Vanilla Stoutは香りもいい😋。最高です。

Brown Porter

写真撮り忘れましたorz

こちらはSweet Vanilla Stoutにくらべるとすこし苦味が強いビールです。コーヒーのような風味が苦味を出していますが、こちらも柔らかい口当たりで飲みやすいです。 コーヒー風味はあくまで風味として楽しめる感じなので、コーヒーが苦手なひとでも飲みやすい気がします。最高です。

おわりに

本当はインペリアルチョコレートスタウト買うぞって思ってたのに、気がついたら同じ醸造所の違うビールを頼んでいました。 酔った勢いでポチッとしてはいけませんね。でも美味しかったので結果よしです。

potatotips #45 で発表してきた

potatotips #45 にて WebView😇😇😇 と題して発表してきました。

speakerdeck.com

概要

ChromeCustomTabs の登場で使い所が限られてきている WebView ですが、法的情報(プライバシーポリシーや規約など)の簡素な静的 HTML を表示するために手軽に使えるため、意外に使う機会があります。ただし、WebView で表示する HTML 内に file:///android_asset/ の中にある別の HTML へのリンクが有ると、Nougat からのセキュリティ仕様によって FileUriExposureException でクラッシュする場合があります。

なぜクラッシュするのか

WebView には WebViewClient というクラスがあり、このクラスの shouldOverrideUrlLoading メソッドをオーバライドすることで、ページ内のリンクを踏んだときにアプリがどのような挙動をするかを決められます。ただしこの WebViewClient の設定は任意のため、WebView に何も設定しないことも可能です。この WebViewClient の有無がみそで、WebViewClient を WebView に設定した場合は shouldOverrideUrlLoading の返り値をもとに挙動が決められますが、WebViewClient が無い場合はリンクを踏んだときに、そのリンクを Uri として適切にハンドリングできるアプリを探しに行きます。ここで Intent に file スキームのリンクを踏んでしまうと、そのリンクが Uri になり Intent に詰め込まれてしまうため、FileUriExposureException が発生するというわけです。

ドキュメントへの記載はあるものの WebViewClient の shouldOverrideUrlLoading メソッドにしか書いていないこと、StrictMode でも検出できるものではありますが、Debug 用途にカスタマイズしている場合設定漏れで検出されなくなることから、地味に気づきにくい問題です。

FileUriExposureException と言うと、アプリが生成したファイルを直接他のアプリに共有しようとして起こるものというイメージがありますが、WebView でも起こりうるということを気に留めておきましょう。

RTL かどうかを判定する

RTL(Right To Left; アラビア語などの右から左へテキスト等をレイアウトする必要のある環境)対応にはいくつかのステップがあります。

単にレイアウトの属性を RTL に対応したもの(gravity や margin、padding や RelativeLayout の各種属性を Left/Right ではなく Start/End)にするだけでも多くの範囲をカバーすることができます。 Drawable resource については Kitkat から autoMirrored という属性が増えました。これを使うことで RTL 向けの画像を作らずとも自動で反転処理をしてくれるようになります。

ここまででおおよそ RTL 対応ができていそうな気がしますが、もう一つ大事な要素があります。それは自分で View をカスタマイズしている場合に必要な対応です。 レイアウト処理を自分で書いたり、Canvas による描画を自分で書いたりなど、様々なカスタマイズの方法があるとおもいますが、ここで LTR(Left To Right; 左から右へテキスト等をレイアウトする環境) を前提にしたコードを書いていると、RTL な環境でうまく動かなくなります。

RTL 環境での処理を LTR と分ける必要がある場合には、Configuration#getLayoutDirectionを使います。ただしこのメソッドは Jelly Bean MR1 からしか使えないため、それ以前のバージョンをサポートするにあたってはサポートライブラリのViewCompat#getLayoutDirectionを使います。帰ってきた値をViewCompat.LAYOUT_DIRECTION_RTLと比較することで、RTL かどうかを判定します。

StateListDrawable の子にあたる Drawable の背景色を Animator で変えてみる

概要

Android Framework に数ある Drawable のなかでも、View の状態に応じて使う Drawable を切り替えることのできる StateListDrawable は多くの人が使っていると思います。ListView のアイテムの背景、ボタンの背景などインタラクションをする View に対して使うことが多いでしょうか。

ところでAndroid の場合、ユーザの指による操作以外にもマウスやキーボードなどの外部入力装置による操作もインタラクションの手段として使えます。外部入力装置を使う場合、いまどこを操作しようとしているかを見せるために、StateListDrawable の state_focused を使って Drawable を切り替えます。

StateListDrawable の各状態で使う Drawable は ShapeDrawable や BitmapDrawable など他の Drawable です。今回は、StateListDrawable の各状態に ShapeDrawable を設定し、state_focused などを考慮しつつ Java や Kotlin のコードから ShapeDrawable の背景を Animator で変えてみようと思います。

<selector>

次のような StateListDrawable を xml で書きます。クリック時には背景を濃いグレーに、フォーカスが当たった時は枠線を描きます。クリック時でかつフォーカスが当たったときは濃いグレーの背景に枠線を描きます。

<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:state_focused="true" android:state_pressed="true">
    <shape android:shape="rectangle">
      <solid android:color="#333333"/>
      <stroke android:width="3dp" android:color="00AAFF"/>
    </shape>
  </item>
  <item android:state_pressed="true">
    <shape android:shape="rectangle">
      <solid android:color="#333333"/>
    </shape>
  </item>
  <item android:state_focused="true">
    <shape android:shape="rectangle">
      <solid android:color="#999999"/>
      <stroke android:width="3dp" android:color="00AAFF"/>
    </shape>
  </item>
  <item>
    <shape android:shape="rectangle">
      <solid android:color="#999999"/>
    </shape>
  </item>
</selector>

Drawable の背景色を変更する

StateListDrawableDrawableContainerの子クラスで、DrawableContainerにはgetCurrentというメソッドがあります。StateListDrawableのような状態を持つものの場合は、現在表示しているものを返してくれるのでこれを使います。

さきほど xml で作った StateListDrawable の子はどれも<shape>ですので、getCurrentメソッドで得られるのもその時の状態で指定している<shape>です。<shape>タグを読み込むと、どの形(四角形、円など)かという ShapeDrawable とどのような背景色や枠線を描画するかという GradientDrawable のふたつが作られます。StateListDrawable#getCurrent()で直接得られる Drawable はGradientDrawableであることに注意しましょう。 そして取り出したGradientDrawablesetColor(int)メソッドで背景色を変えることができます。 あとは、Animator を使えば背景色をアニメーションできます。

fun animateBackgroundColor(view: View, color1: Int, color2: Int, duration: Long) {
  val background = view.background as StateListDrawable
  val animator = ValueAnimator.ofObject(ArgbEvaluator(),
      ContextCompat.getColor(view.context, color1),
      ContextCompat.getColor(view.context, color2))
  animator.duration = duration
  animator.addUpdateListener {
    (background.current as GradientDrawable).setColor(it.animatedValue as Int)
  }
  animator.start()
}

懐かしいゲームがリメイクされていたのでテンションあがって遊んでみた

懐かしいゲームというのはこれのことです。

store.steampowered.com

内容は、60年代の米国とソ連が宇宙からきた未知の資源を巡って太陽系の惑星やら衛星でドンパチするという完全に硬派でシブいSFゲームですが、発売当時(98年)のPCでもモリモリ動く3Dのファーストパーソン・シューティングゲームで、かつリアルタイムストラテジーゲームでもあるという今でもなかなか無さそうなゲームです。シングルプレイモードとネットワーク対戦モードがあって、シングルプレイは米軍とソ連軍それぞれにシナリオがあり、プレイヤーは様々な司令をこなしていくゲーム、ネットワーク対戦モードはLANやインターネットで繋いでそれぞれ好きにたたかうゲームになります。どちらのモードにしても、プレイヤーは司令官として資源を集めて基地を作り、自軍を強化・防衛して敵を倒すのが大まかな流れになりますが、自分自身が先陣を切って敵陣に突撃してもよいし、ひたすら遠くから戦いを眺めるでもよいという感じで遊びの幅がある、本当に硬派でシブいSFゲームです。

それが20年ちかく経ってSteamでリメイクを果たしMacにまで配信されるようになると言うのは、本当に長きにわたってコアなファンがついている硬派でシブいゲームだということだと思います。いやほんと、プレイしてみると分かりますがとにかく硬派でシブいゲームです。やることは非常にたくさんあって同時にあれこれこなさないと敵にやられてしまうし、ミッションに失敗するとすごい勢いで上官(General なので一番えらい人)が怒ります。かと言って操作自体は単純なので遊びやすくはあります。

これを遊んでいた当時はまだ小学生でしたが、父親がどこからか原版(日本語翻訳版も売られていたそう)を手に入れていて、気がついたら一緒に遊んでいました。 小学生なので当然英語も分からないし、しかも軍隊なのでさらに英語が難しい。ゲームの解説もとくにないしインターネットも無かったので、自分にとってはとにかくトライ・アンド・エラーを繰り返してどうにかミッションをクリアする方法を見つけ出すゲームでした。今ならネットで調べて勘所を抑えれば失敗も少なくクリアできるところでしょうが、そういうわけにもいかなかったし、ゲームを買ってきた父親自身も英語が分かるわけではないので(!)聞くに聞けず、とにかくクリアしたければ自分で何とかするしかない感じがまた面白かったのです。

多分これを遊ばなかったら英語を頑張って勉強する気にもならなかったし、PCそのものもそんなに興味を持たなかったような気もするので、そういう意味でも懐かしいゲームなので、ついテンションが上ってしまいました。

RecyclerView の item に focus が当たったことを検知する

やりたいこと

旧来の AdapterView (ListView や GridView など)でいうところの OnItemSelectedListener と同じように、RecyclerView がもつアイテムにフォーカスが当たったときにその情報をコールバックする方法です。

方法

RecyclerView には OnChildAttachStateChangeListener というインタフェースが定義されています。このインタフェースのメソッドで、Adapter の生成する View が RecyclerView に attach されたタイミングが分かるので、そこで attach された View に OnFocusChangeListener をセットしてフォーカスが当たったことを検知します。

class FocusListeningRecyclerView(context: Context?, attrs: AttributeSet?, defStyle: Int)
     : RecyclerView(context, attrs, defStyle), RecyclerView.OnChildAttachStateChangeListener {
  var focusListener: OnItemFocusedListener? = null

  constructor(context: Context?): this(context, null)
  constructor(context: Context?, attrs: AttributeSet?): this(context, attrs, 0)

  override fun onAttachedToWindow() {
    super.onAttachedToWindow()
    addOnChildAttachStateChangeListener(this)
  }

  override fun onDetachedFromWindow() {
    removeOnChildAttachStateChangeListener(this)
    super.onDetachedFromWindow()
  }

  override fun onChildViewAttachedToWindow(view: View?) {
    view?.setOnFocusChangeListener { child, hasFocus ->
      if (!hasFocus)
        return@setOnFocusChangeListener
      focusListener?.onItemFocused(getChildAdapterPosition(child))
    }
  }

  override fun onChildViewDetachedFromWindow(view: View?) {}

  interface OnItemFocusedListener {
    fun onItemFocused(position: Int)
  }
}

ツールド東北

ツールド東北に同僚と参加してきました。

出場するのは日曜日の南三陸コース。前日に民泊のお家に到着して、そこから受付をしに自転車を組み立てました。
f:id:KeithYokoma:20170920100235j:image

石巻の会場にはSubaru XV が!上には Bianchi の自転車が乗っかっていて、よさそう(小並感)と思いました。普段はFitやSwift、Demioくらいのコンパクトカーで十分だと思っていましたが、コンパクトSUVも使い勝手が良さそう。コンパクトカーだと荷室の広さではFitの圧勝なのでな…
f:id:KeithYokoma:20170920100257j:image
同僚は去年も参加していて、今年も多分ピカチュウがいるとおもう、ということで探したらありました。
f:id:KeithYokoma:20170920100320j:image

受付をサッと済ませて民泊のお家に帰りました。オーナーのおばちゃんが料理大好きで、晩御飯が超豪華!
f:id:KeithYokoma:20170920100336j:image

滅多なことでは食べないカニ、最近値上がり気味のサンマ、そして初めて食べたホヤ。お野菜や漬物などとにかくおかずがたくさん出てきて、ご飯なしでも満腹になりました😊
f:id:KeithYokoma:20170920100414j:image

 翌朝は晴れていて、台風の接近を感じさせるものの自転車には乗れる天気!
f:id:KeithYokoma:20170920100433j:image

すこし風が強い場所がありましたが、女川エイドステーションに到着。すごい人!
f:id:KeithYokoma:20170920100559j:image

女川汁いただきました。
f:id:KeithYokoma:20170920100615j:image

途中人だかりができていた、眺めの良い場所でパシャリ📷
f:id:KeithYokoma:20170920100729j:image

そんなこんなで雄勝エイドステーション。ホタテ!
f:id:KeithYokoma:20170920100808j:image

太鼓の演奏も見てゴールへ向かいました。
f:id:KeithYokoma:20170920100837j:image

無事ゴール。
f:id:KeithYokoma:20170920100908j:image

 帰ってお昼を食べてからは、市内を車で回ることに。

f:id:KeithYokoma:20170920101114j:image

日和山のうえから、津波の押し寄せてきた海を見ました(写真天地がひっくり返ってる…)。車の中でも、いろいろと震災当時のことを教えてもらいました。
f:id:KeithYokoma:20170920101138j:image

仮面ライダーとツーショット。
f:id:KeithYokoma:20170920101156j:image

原画展もやっていました。
f:id:KeithYokoma:20170920101217j:image

翌日のお昼前に東京へ向けて出発。途中柿の種の車が走っていました。

f:id:KeithYokoma:20170920101259j:image

那須高原SAで。
f:id:KeithYokoma:20170920101322j:image

牛肉〜。
f:id:KeithYokoma:20170920101336j:image

二泊三日のツールド東北でしたが、民泊でのご飯がずっと美味しくて、オーナーのおばちゃんとの会話は絶え間なく続いて実家に帰ってきたような安心感がありました。