どくぴーの備忘録

真面目なことを書こうとするクソメガネのブログ。いつ投げ捨てられるのかは不明

第50回 情報科学若手の会に参加してきた #wakate2017

通算3回目の参加だそうです。社会人になってからは初めての参加っぽいです。

というわけで、今年も伊東市は山喜旅館にて開催された情報科学若手の会に参加してきました。なんと50回目。アニバーサリーって感じです。おまけに伊東市はその日は秋祭りで市政70周年だったそうで、こちらもアニバーサリーって感じでした。

前回の参加ブログはこちらです。

e10dokup.hateblo.jp

雑なまとめ

  • 相も変わらず話題が豊富
  • 50周年記念なので記念セッションがあった
    • 国立国会図書館に眠る記録が掘り出されていた
    • 案外昔想像されていたことが今実現されてたりして心温まる感じ
    • 手書きの報告書の書き手が村井純先生だったり
  • 交流イベント
    • 今年は何をやらされるかと思ったらQRコード陣取りゲーム
      • QRコードが読めなくなるまでお互いに付箋を貼って読めなくなった時点で一番多い枚数を貼った人が勝ちとか
    • 第1問の回答が各位絞り出したエモさで強かった
      • 「相互投票」になると思っていなかったので幹事の方のサンプルと100%一致する答えをしてしまって選外になりました :bow:
    • 強いQRコードリーダーは強い
      • スマホのリーダーは全滅してもなお読めたりする
  • ナイトセッション
    • 相変わらずエモい話で盛り上がる
    • ピングー
    • 今年のLT用プロジェクタの配置が神がかっていて一杯の人が聴ける感じでとても良かった
    • 結局毎日3-4時就寝
    • 突発的スプラトゥーンバイト若手の会
      • オフラインよりオンラインプラベのほうが落ちないっぽい
      • 評価が155%まで上がりました :bow:
  • お祭り
    • 二日目にお祭りがあったのでみんな外に出てみた
    • ついに旅館から出ることの許されなかった若手の会の参加者が外へと足を踏み出す奇跡の瞬間
      • こう書くとシャバの空気を吸うみたいな話になる
  • ネットワーク
    • 今年は明らかにオーバーキルだった
    • 幹事/野生の幹事の方々ありがとうございます
    • 快適にも程がある

ところで来年は…

幹事を勤めさせていただくことになりました

噂によると「初参加の人が突然肩を叩かれて気付いたら幹事になっている」とのことでしたが3年目にして突然肩を叩かれました。 今年幹事を引退される方々に負けないように幹事業をやっていきたいと思います。とにかくやっていくぞという気持ちです。

ちなみに幹事の杯として50年物のワインをみんなで頂きました。ワイン苦手なのですがなんかドチャクソ美味しくてすごかったです。

来年の情報科学若手の会もよろしくお願いいたします :bow:

ところで何かあなた発表したの?

ちゃんとした学術・技術的な発表はしませんでした。ただ幹事の方からご希望を頂いたので新宿御苑をひたすら勧めるLTをしていました。新宿御苑はリビングであると同時にコワーキングスペースなのでみんな行きましょう。寝ましょう。コーディングもしましょう。

ちなみに新宿御苑というと映画「言の葉の庭」で雪野先生が東屋で金麦片手にチョコレートを食べているのが印象にある方も多いと思いますが新宿御苑は禁酒ですのでお気をつけください。一体何を気をつけるのか…

potatotips #41 に参加してきました

potatotips.connpass.com

Yahoo! JapanさんのLODGEで開催されたpotatotips #41にAndroid ブログまとめ枠として参加してきました。 (2日ほど遅れてしまっていますが…)

当日の様子はToggeterからどうぞ

togetter.com

Android Tips まとめ

Physics-based Animations (rkowase さん)

qiita.com

Google I/O 2017中のAndroid Animations Spring to Life で発表された Physics-based Animationsに関するLTでした。

  • 物理法則や関数に従ったAnimationが簡単に実現できるようになった
    • 自然な見た目になり、視覚的違和感が軽減できる
    • Interpolator等を使っても関数のチューニングが必要だったりしたのでありがたい…
  • 途中で移動先の座標が変わっても自然に軌跡を修正してくれる
    • ちゃんと連続関数的になるように座標移動を修正している感じがすごい
  • 係数調整も自由度が高くて良さそう
    • SplingAnimationならバネ係数や剛性、FingAnimationなら摩擦係数など、ちゃんと係数レベルから調整できるのは細かい人には嬉しそう

Icon Fonts in XML (anikaido さん)

SupportLibrary 26で追加されたFonts in XMLに関するLTでした。

  • Fonts in XMLでレイアウトXMLでFont指定できるようになった
      • 従来ならCalligraphyでやっていたことがSupportLibraryでできるようになった、という感じ
    • これを利用してMaterial Icon Fontを導入したい
  • res/font/material_icon.ttfに配置して
  • android:fontFamilyで指定するとandroid:textで対応するASCIIコードを指定することでアイコンフォントが表示されるように
    • ASCIIコードを直打ちするの大変そう…、strings.xmlとかで対応付けしたい…

AsyncLayoutInflater vs Litho (KeithYokoma さん)

speakerdeck.com

非同期でLayoutのInflateを行うAsyncLayoutInflatorと宣言型のUIフレームワークである facebook/Litho の非同期UI読み込みを比較したLTでした

  • まだLithoはベータだけど非同期UI読み込みではAsyncLayoutInflatorのほうが速い
    • 相対的な大きさのせいか、AsyncLayoutInflatorの時間のブレが大きく感じる…
  • 1万のTextViewが並んだLayout XMLを見た後にLithoのforループで宣言できるのを見ると場合によってはとても良さそう

Android Font Updates (uecchi さん)

speakerdeck.com

本日二件目のフォントに関するLT。内容がかぶっていないDownloadable FontsとEmojiCompatに関するお話がメインでした

  • Downloadble Fonts
    • Google Fontsであれば端末内に入れることなく使えるようになった
      • FontとかにこだわってもAPKサイズを下げることができるので良さそう
      • CJK対応はまだなので日本で使われるようになるのは…
      • M以下で使おうとするとNPE… (´・ω・`)
    • 同じフォントはアプリ間で共有してリソース削減を狙う仕様は細かい配慮が効いていていいなぁと思った
  • EmojiCompat
    • Downloadable Fonts/APK Bundleで最新Emojiを古い端末にも適用させる機能
      • 豆腐削減の1手
    • Emoji/EmojiAppCompatHogeViewがある
      • TextView/EditText/Buttonがあるみたい
  • Android OからEmojiが丸くなるらしいです

swagger-codegenpojo生成 (kgmyshin さん)

swagger.jsonからPOJOを生成するswagger-codegenに関するLTでした。

  • リクエストやレスポンスがネストが深いときや複雑な構造になっているときにミスの可能性を減らせそう
  • 新規や後発で開発が始まったときにこういうことができると幸せが強い
    • swagger.jsonを使ってきっちりサーバサイドの仕様を決めている前提はあるが… ()

      Instant Apps (bina1204 さん)

www.slideshare.net

正式に使えるようになったInstant Appsの紹介LTでした。

  • Instant Apps 正式リリース :tada:
    • インストールいらずになる
    • どこからでもアクセスできて
    • 5.0移行でも動くようになるはずの
    • 既存のアプリにちょちょいと手を加えて動く様になる機能
  • Module細分化してAndroidManifestにエントリーポイントを貼るとok
  • デバッグ仕様だとUpdate Error -27なのでInstant Apps版のアプリをアンインストールしてから入れよう
  • 4MB未満が推奨されてるけど10MBでも動くよ!今はとりあえず!
  • Nearbyと組み合わせてイベントとかでNearby範囲内に入ったときにイベント専用アプリをインストールさせる、とか楽しそう

自動テストが無ければDeviceFarmを使おう (としさん)

openSTF/AWS Device Farm/Firebase Test Lab といったAndroidクラウドテスト環境に関するLTでした。

  • いろんな端末で自動テストが走る
  • 最近は自動テストがなくても走る
    • OpenSTF/AWS Device FarmはMonkeyTest
    • Firebase Test LabはRoboTest
    • Firebaseのほうがある程度こちらで制御できる部分があったりして賢くできそう
  • AWS DeviceFarm/Firebase Test Lab
    • AWS、Firebaseも無料枠が出来た
  • まだ発展途上だけど、やれることレベル的にFirebase Test Labが一番強そう

感想

前回(#21だったはず)に出たときもブログまとめ枠でした。今回も濃い内容で充実した時間でした。毎回こういう勉強会に出ると「次こそは発表したい…!」ってなるのでモチベーションを維持して行きたいです。

会場・お寿司・お酒を提供してくださったヤフーさん、どうもありがとうございました!

次回参加するときがあればよろしくお願いいたします!

DataBinding + RxJavaでMVVMパターンな設計を考える

今更感がすごいが、DataBindingを使うことによってAndroidアプリケーションの実装でMVVMパターンな設計を考えやすくなったし、DroidKaigi 2017のアプリがMVVMで実装されていたりするので、自分なりに設計をまとめてみる。

全体図

他で実装されている記事を見るとDDDなりと混ぜ合わせた感じの設計がちらほら見えて、一番シンプル(かつ集合知的な知見が溜まっている)と感じたDroidKaigi/conference-app-2017のアーキテクチャを丸パクリする形になった。

f:id:e10dokup:20170507160545g:plain

github.com

何をしているかざっと書くと

  • View
    • Activity/Fragment/Adapter ItemといったViewは1対1で対応するViewModelを持つ
    • 各Layout XMLには対応するViewModelをDataBindingでbindする
  • ViewModel
    • Viewの要素をクリックしたときの処理の定義やAPI/DBとのModelのやり取り、Viewへの反映を行う
    • ViewModelが取り扱うRepositoryはDagger2のDIを用いてインジェクトして利用する
  • Repository
    • ModelのCRUD操作を提供する
    • 各Modelのクラスに対応するRepositoryが存在する
    • RepositoryはDataSourceを持ち、DataSourceを利用してDBやAPIからデータを取得するが、ViewModel以下には隠蔽する。
  • Local/RemoteDataSource
    • 実際にModelのCRUD操作を行う
  • DataSourceからViewModelまでの処理はRxJavaでストリーミングに扱う

という感じ。

View

Viewは対応するViewModelをInjectして、Layout XMLにbindする。

public class MainActivity extends BaseActivity {

    @Inject
    MainActivityViewModel viewModel;

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        getComponent().inject(this);
        bindViewModel(viewModel);

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.setViewModel(viewModel);
    }

    // 以下省略
}

ちなみにBaseActivityの中は次のような感じ。ViewModelにもライフサイクル系メソッドを用意して、ActivityやFragmentのライフサイクルと同期して呼び出すようにしている。

public abstract class BaseActivity extends AppCompatActivity {

    private ActivityComponent component;
    private ActivityViewModel viewModel;

    @NonNull
    public ActivityComponent getComponent() {
        if (component == null) {
            MyApplication application = (MyApplication) getApplication();
            component = application.getComponent().plus(new ActivityModule(this));
        }
        return component;
    }

    protected void bindViewModel(ActivityViewModel viewModel) {
        this.viewModel = viewModel;
    }

    @Override
    protected void onStart() {
        super.onStart();
        checkViewModel();
        viewModel.onStart(this);
    }
    
    // 以下省略
}

ViewModel

ViewModelの実装は次のようにする。画面遷移は、Navigatorという画面遷移を取り扱うクラスを用意して、それをViewModelにinjectして行うようにしてみた。今回は直面していないがContextが必要な処理があるときはEventBusを使ってActivityにイベントとして流したほうがいい気がする…。

public class MainActivityViewModel extends ActivityViewModel {

    private final Navigator navigator;
    private final TaskRepository taskRepository;

    private ObservableList<TaskViewModel> taskViewModels;


    @Inject
    public MainActivityViewModel(Navigator navigator, TaskRepository taskRepository) {
        this.navigator = navigator;
        this.taskRepository = taskRepository;

        this.taskViewModels = new ObservableArrayList<>();
    }

    @Override
    public void onStart() {

    }

    @Override
    public void onResume() {
        taskRepository.findAll()
                .map(tasks -> Stream.of(tasks)
                        .sorted((o1, o2) -> (int)(o1.deadlineEpoch - o2.deadlineEpoch))
                        .toList())
                .map(tasks -> convertToViewModel(tasks))
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(taskViewModels1 -> {
                    this.taskViewModels.clear();
                    this.taskViewModels.addAll(taskViewModels1);
                });
    }

    // 以下省略
}

Navigatorの実装はこんな感じ。

@ActivityScope
public class Navigator {

    private final Activity activity;

    @Inject
    public Navigator(AppCompatActivity activity) {
        this.activity = activity;
    }

    public void navigateToCreateTask() {
        activity.startActivity(CreateTaskActivity.createIntent(activity));
    }

    public void navigateToTaskDetail(int taskId) {
        activity.startActivity(TaskDetailActivity.createIntent(activity, taskId));
    }

    public void closeActivity() {
        activity.finish();
    }
}

Repository

RepositoryにはLocal/RemoteDataSourceを持たせて、それらDataSourceにてCRUD操作を行う。例はローカルDBしかおいてないのでほぼ効果はないが、「ローカルDBにデータが有るときはローカルDBから、そうでないときはAPIから」という風に処理を分ける際にはここで分岐させる。

@Singleton
public class TaskRepository {

    private final TaskLocalDataSource taskLocalDataSource;

    @Inject
    public TaskRepository(TaskLocalDataSource taskLocalDataSource) {
        this.taskLocalDataSource = taskLocalDataSource;
    }

    public Single<List<Task>> findAll() {
        return taskLocalDataSource.findAll();
    }

    // 以下、省略
}

DataSource

DataSourceからの返り値はRxJavaのSingleで包んでストリーミングに流すようにする。例はLocalDataSourceだけだが、Retrofit等を用いてRemoteDataSourceを作るときはRetrofitのClientをinjectしてClientのアクセス結果をSingleで包む形になるはす。

public class TaskLocalDataSource {

    private final OrmaDatabase ormaDatabase;

    @Inject
    public TaskLocalDataSource(OrmaDatabase ormaDatabase) {
        this.ormaDatabase = ormaDatabase;
    }

    public Single<List<Task>> findAll() {
        return ormaDatabase.relationOfTask()
                .selector()
                .executeAsObservable()
                .toList()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }
   
   // 以下省略
}

終わりに

とりあえずこれを踏襲してサンプルでToDoアプリを作ってみた。

github.com

先人の知識に頼りっぱなしで実装したが、いざ組んで見てわかること、記事にして思い違いだったと気づくことがたくさんあるのでとても重要だと思った(感想)

ObservableList#OnListChangedCallbackでListViewやRecyclerViewのAdapterを更新する

TL;DR

  • ObservableList#OnListChangedCallbackでObservableListに格納されているデータの変更を通知できる
  • 変更を通知できるので「通知のあったitemのみViewを更新する」と言った処理も可能
    • RecyclerViewなら各コールバックメソッドに対応した変更通知が使える
    • ListViewには変更通知として notifyDataSetChanged() しかないので同様のことをしたい場合、getViewを独自に叩くことになるのでActivity/FragmentにObservableList#OnListChangedCallbackを配置するのが早そう
    • チャットUI等の実装にはListViewではなくてRecyclerViewを使ったほうがViewの更新回数が減りそう

ObservableList

DataBindingで実装されているObservableなコレクションの一種で、イメージ的には「データ変更を通知する機能を備えたList」。DataBindingにおける各データの通知方法(BaseObservable、ObservableField等)はこの辺を参照

developer.android.com

ObservableListは、コールバックにOnListChangedCallbackを持っており、(RecyclerViewについてきた)SortedListよろしくリスト内の要素の変更を通知することができる。というわけで、ListViewやRecyclerViewのAdapterに持たせるListにコールバックを実装したObservableListを与えることで、リストの要素が変更されたときや追加されたときなどにその変更をUIに反映させることができる。

Listの変更をListViewやRecyclerViewに通知したい

ListViewの場合

色々見ているとAdapterのコンストラクタ内でObservableList#OnListChangedCallbackをセットしていることが多そう。Adapterの変更通知は notifyDataSetChanged() しかないので、基本的にOnListChangedCallbackのどのコールバックメソッドが走っても(表示されている)全部の要素に対して getView() が実行され、描画されることになる。

public class ContentsAdapter extends ArrayAdapter<Content> {

    public ContentsAdapter(Context context, ObservableArrayList<Content> objects) {
        super(context, 0, objects);

        objects.addOnListChangedCallback(new ObservableList.OnListChangedCallback<ObservableList<Content>>() {
            @Override
            public void onChanged(ObservableList<Content> contents) {
                // リストそのものが変更されたとき
                notifyDataSetChanged();
            }

            @Override
            public void onItemRangeChanged(ObservableList<Content> contents, int i, int i1) {
                // iを始点としてi1までの範囲の要素が変更されたとき
                notifyDataSetChanged();
            }

            @Override
            public void onItemRangeInserted(ObservableList<Content> contents, int i, int i1) {
                // iを始点としてi1までの範囲に要素が挿入されたとき
                notifyDataSetChanged();
            }

            @Override
            public void onItemRangeMoved(ObservableList<Content> contents, int i, int i1, int i2) {
                // iからi1へi2の数だけの要素が移動したとき
                notifyDataSetChanged();
            }

            @Override
            public void onItemRangeRemoved(ObservableList<Content> contents, int i, int i1) {
                // iを始点としてi1までの範囲の要素が削除されたとき
                notifyDataSetChanged();
            }
        });
    }

    // 以下省略...
}

変更が通知された要素のみViewを再描画したい場合、 notifyDataSetChanged() せずに、直接AdapterのgetViewを叩くことで指定のViewのみを更新できる。しかしCallbackをAdapterのコンストラクタ内で定義するとうまくいかないのでListViewが表示されるActivity/FragmentでCallbackを定義してあげる必要があるし、実行するにしても getFirstVisiblePosition()getLastVisiblePosition() で表示されている範囲内の要素か判定する必要がありそう。 「ListViewの中に更にListViewやRecyclerViewがあって、再描画するとListView/RecyclerViewの表示が崩れたり一瞬消えたりする」場合に使うと言ったケースはあるかもしれない。

stackoverflow.com

RecyclerViewの場合

ObservableList#OnListChangedCallbackのセットはListViewのAdapterと同様、Adapterのコンストラクタ内でやっているのが多い感じ。ただ、RecyclerViewのAdapterはListViewのAdapterとは違って、notify系メソッドが用途別に揃っているっぽい。

  • notifyDataSetChanged()
    • データセットが変更されたことを登録されているすべてのObserverに通知する。表示されているItem全体を更新する
    • この場合のデータセットの変更は「既存の要素が全て有効ではなくなる」ことを想定させるもの。なのでこれを実行するとLayoutManagerは表示される範囲のViewの再描画を行う。
  • notifyItemChanged(int position)
    • 指定したpositionのitemが変更されたことを登録されているすべてのobserverに通知する。そのpositionのItemのみを更新する
  • notifyItemInserted(int position)
    • 指定したpositionのitemが挿入されたことを登録されているすべてのobserverに通知する。そのpositionのItemのみを更新する
  • notifyItemRemoved(int position)
    • 指定したpositionのitemが削除されたことを登録されているすべてのobserverに通知する。そのpositionのItemのみを削除する
  • notifyItemMoved(int fromPosition, int toPosition)
    • fromPositionのitemがtoPositionに移動したことを登録されているすべてのobserverに通知する。移動に関係のあるItemのみを更新する
  • notifyItemRangeChanged(int positionStart, int itemCount)
    • positionStartからitemCountの範囲だけのitemが変更されたことを登録されているすべてのobserverに通知する。該当範囲のItemのみを更新する
  • notifyItemRangeInserted(int positionStart, int itemCount)
    • positionStartからitemCountの範囲だけのitemが挿入されたことを登録されているすべてのobserverに通知する。該当範囲のItemのみを更新する
  • notifyItemRangeRemoved(int positionStart, int itemCount)
    • positionStartからitemCountの範囲だけのitemが削除されたことを登録されているすべてのobserverに通知する。該当範囲のItemのみを削除する

(引数にObject payloadを与えるものもあるが今回は特に使う機会がないので見なかったことにする)

RecyclerView.Adapter | Android Developers

qiita.com

この中からObservableList#OnListChangedCallbackのそれぞれのコールバックメソッドに相当するものを選ぶと、

  • onChanged - notifyDataSetChanged
  • onItemRangeChanged - notifyItemRangeChanged
  • onItemRangeInserted - notifyItemRangeInserted
  • onItemRangeMoved - notifyItemRangeRemoved

になる。onItemRangeRemoved(T sender, int positionStart, int itemCount)だけは対応するnotify系メソッドがないというか、notifyItemRangeMovedが存在しないので困ったところだが、

for (int i = 0; i < itemCount; i++) { 
        notifyItemMoved(fromPosition + i, toPosition + i); 
} 

でforでちまちま回すか notifyItemRangeChanged(fromPosition, toPosition + itemCount) で範囲変更として扱うかで対応できそう。(ここに関しては試していないので自信がない)

public class ContentsRecyclerAdapter extends RecyclerView.Adapter<ContentsRecyclerAdapter.BindingViewHolder> {

    private ObservableArrayList<Content> objects;

    public ContentsRecyclerAdapter(ObservableArrayList<Content> objects) {
        this.objects = objects;
        this.objects.addOnListChangedCallback(new ObservableList.OnListChangedCallback<ObservableList<Content>>() {
            @Override
            public void onChanged(ObservableList<Content> contents) {
                notifyDataSetChanged();
            }

            @Override
            public void onItemRangeChanged(ObservableList<Content> contents, int i, int i1) {
                notifyItemRangeChanged(i, i1);
            }

            @Override
            public void onItemRangeInserted(ObservableList<Content> contents, int i, int i1) {
                notifyItemRangeInserted(i, i1);
            }

            @Override
            public void onItemRangeMoved(ObservableList<Content> contents, int i, int i1, int i2) {
                for (int j = 0; j < i2; i++) {
                    notifyItemMoved(i + j, i1 + j);
                }

            }

            @Override
            public void onItemRangeRemoved(ObservableList<Content> contents, int i, int i1) {
                notifyItemRangeRemoved(i, i1);
            }
        });
    }

    // 以下省略...
}

このようにしてあげることでListViewのAdapterのnotifyDataSetChanged()ではできなかった「変更があった要素のみViewを更新する」ことが可能になる。ついでに何かアニメーションもしてくれる。このようにすると、単純にListViewに比べてもViewの描画回数が減るので、ポーリングでリストの内容を更新したり、チャットUIの実装を行うときにDataBindingを扱う場合、RecyclerViewを使ったほうが良さそう。

東京に来ました(就職)

やべぇブログ書いてねぇ

広告に目をやられました。ブログに広告が出っぱなしなのはつらいのでとにかく書いていこうかなと思います。

そういえば大学を卒業していました

いわゆる「17新卒社会人」になりました。明石高専から編入した神戸大学を学部で卒業し、就職して東京に住むことになりました。今は渋谷駅へのアクセスがし易いところ(曖昧)に住んでいます。今後もずっとプログラムを書いていく事になりそうなので当面の目標は 渋谷のイケイケパリピプログラマ です。嘘です。ちゃんと真面目にプログラミングしたいです。

高専から大学に編入した人だと、だいたい大学院への進学が多いような印象があるのですが、僕は色々考えた上で院への進学を諦めました。学力面とかではない…はずです。究極的に言うと

「研究室のM1の先輩方が大学院の授業関連でめちゃくちゃ忙しそうにされていた」

のが決め手でした。大学院に進んだ編入生の同期もなんだかんだ忙しそう。

東京来て変わったこと

  • イベントとかに参加するのに全く苦労しなくなった
    • 地方でイベント、あまりないしやるにしても大変というのはある
  • 人がいっぱいいる
  • 自己管理が大変
    • 体調崩しても頼れる人がいない…
    • 自堕落になると戻れない
  • 食費がやばい
    • ランチで1000円とか飛んで行くのでぐるぐる目になってきた
    • 自炊しないと財布も胃も持たない
  • 休日が家事で溶ける
    • 今日も買い出しと掃除と洗濯と料理してたら一日溶けた
    • ありきたりだけど親のありがたみがすごい

ネタがなくなった

技術的な話をしようと思ったけどここ一ヶ月とても忙しかったせいかまとめられるようなスタックがないのでGW中に何か技術的な記事書きます。

  1. なんでこんな時期にこんなエントリを書いたんだ
  2. 一ヶ月経って落ち着いたのもあるけど一番の理由は 「4月直後だとみんな書いてるし便乗感が出るから」

おまけ

ひとりぐらしはたいへんなので助けてください

http://amzn.asia/7chMyfR

寿司を回そう

この記事は クソアプリ advent calendar 21日目の記事です.遅刻.

qiita.com

クソを作ろう

去年 @amparsand_xyz さんとクソアプリでadvent calendarしたいぞ!ってやってたら現実になってしまったのがことの始まりだった気がします.今年もなんか存在してるので積極的にクソを投下した買ったのですが研究等々で時間が滅亡したのでMAで作った作品の供養をしようと思います.

できたもの

f:id:e10dokup:20161121013108j:plain

hacklog.jp

今年はついに目に見える物に手を出して寿司を回しました.大体のインスピレーションはこちらの記事から得ています

yumulog.hatenablog.com

動いてるシーン

youtu.be

ちゃんと寿司を乗せて回したりもしています

何が起きているのか

「年収が800万円を超えると寿司が止まって見える」「ポリエチレンテレフタレートで覆うことによって『イオン化現象』によって安価に寿司を止める」「寿司と等速で回転することで寿司を止める」「ゾートロープの原理で寿司の可視・不可視を切り替え止まったように見せる」など「動いている寿司を止める」ことを目的とした取り組み(?)はかなり行われていたのですが,「寿司が生み出す不平等」(??)は全く問題視されていないので問題視した結果

「年収っぽいものを診断して高ければ高いほど高速で寿司を回せばいいんじゃね?」

となった結果がこちらです.Androidアプリ上で質問に答えると寿司が回るようになりました.これ以上はありません.ちなみに実装は8時間です.ひどい.

何をやっているのか

f:id:e10dokup:20161221232940p:plain

Raspberry Pi上にsinatraアプリを立てて,そこにAndroidアプリでなんかこう判定した結果をそれっぽく処理して,モータードライバ搭載のArduino互換機「Studuino」をGPIOから信号を送ることで速度を切替えながら回してます.ちなみにGPIOの本数が生える限り回転速度を制御ことが出来ます.

あとMAなのにAPIを一個も使わないのは寂しかったので,株式会社エーアイさんのAITalk Web APIを使って判定結果に応じて琴葉茜ちゃんが関西弁で煽ってくれるようにしました.琴葉姉妹の音源が使えるのはMA限定だったそうです.

おわりに

もっと真面目に時間かけて作ればよかったとおもいました.

来年のMAもガンバルゾー

ニューでなハウス

この記事は denari01 Advent Calendar 2016 - Adventar 16日目の記事です

前日の記事は @sonet_sou くんでした.まだ記事が存在しないので楽しみに待ってます

前置き

当記事はでなりの家に泊まりに行った際の記憶とそこから学ぶ一人暮らしに関する知見を並べたいと思います.ちなみにkosen10sにおけるでなハウスの位置づけは以下のツイートに集約されます.

最初に言っておきたいこと

これからでなりの家に関して色々書きますが,とにかく東京で宿を探すのが大変なので泊めていただけることには大変感謝しております.私も来年は多分上京するので誰かを泊めれるような家の環境を保とうと思います.

ニューでなハウスに泊まりに行かせてもらうことになった

実はでなハウスには2回泊まりに行ったことがある.でなりが福井県にいた頃のオールドでなハウス(以下:オールド)に1回.千葉県に行った後のニューでなハウス(以下:ニュー)に1回だ.ちなみにどちらも「カレーメシを食す会」絡みなので同時に @c_bata_ も泊まったことがあることになる.どうでもいいけど「ニューでなハウス」という文字列からは場末のホテルっぽい響きが感じ取れるので個人的には大好きだ.

オールドにはこれ以降触れないのでこの際説明しておくと,2Kでプロジェクタをおける程度の広さで堂々とカレーメシの調理をすることが可能だった記憶がある.地味に家具もいろいろ揃えていたのでなんだかんだ住みやすそうにはしていたと思う.

なんやかんやあってニューでなハウスに泊めていただくことになった時,でなりは「いやぁうち引っ越したばかりだからやばいよ,なんかにおいするし」とか言っていた記憶があるが当時の僕は「いやまさかそんな限界生活みたいなことしてるはずは無いやろwwwww」とか楽観視していた.

Problem 1 : 布団を買う

当時はまだ立川シネマシティでガルパンの上映が続いていたので,僕はガルパンをみて「いいぞ」ってなりながらニューでなハウスに向かった.最寄り駅につくとでなりが待ってくれていたので

「いやぁ引越し代下げるためにほとんど家具処分したんよ」
「まじ?」

とか話しながら駅からちょっと歩いたところにあるニューでなハウスに到着する.お邪魔してみるとなるほど何もない.どれくらい何もないかというと布団すら無いので一体こいつどこで寝ているんだとかそんな疑問がふつふつと浮かんでいると

「これからクソメガネとカレーメシ先輩(@c_bata_)が寝るための布団買いに行く」

と言い出すので最寄りのニトリに行くことになった.最寄りと言ってもGoogle Maps調べで約2kmの距離があり,それを往復でトボトボ歩いて片道30分らしい.「いや車使えよ」となるのだが免許を持っているのが僕(しかも初心者マーク)だけなので当然のように断念する.行きだけは「はえー,いろいろ店あるねぇ」とか言いながら歩いていたが,ニトリでセミダブルサイズの布団を2つ買って帰ろうとした時に僕たちは気づいてしまった.

「えっ,つらくね…?」

いくら軽めの布団を選んだとは言え,2kmをセミダブルの布団を抱えてトボトボ歩くのは流石に厳しい,しかも梅雨の入りくらいでちょっと蒸し暑くなってきたくらいなので,余計に厳しい.そんなこんなでひぃひぃ言いながら帰りは45分位かけてニューでなハウスにたどり着くことができた.

Problem 2 : 風呂に入る

寝る前にシャワーを浴びようとすると,でなりからファブリーズを渡される.「とりあえずこれで服の臭いを抑えとけ」という意味なのかなと思うと,全然違うって顔をするのでとりあえずはいってみると,これは確かにやばいにおいがする.具体的な言及は避けるが,脱衣所によくある洗濯機置場の排水口からどうやら発生しているみたいなのでそこにファブリーズを散布しろということらしいので言われたとおりにする.なんで洗濯機買ってないんだと思ったが,家の目の前にコインランドリーがあるのでそっちを使うと考えるとまぁ大丈夫だろうという感じがする.40回も使えば減価償却できるんじゃね?という気もしなくもないが,多分突っ込んではいけないのだろう.

シャワーを浴びて出てくると,もう一度臭いが立ち込めているので「うへぇ」という顔をしながらもう一度ファブリーズを散布して急いで服を着て脱出する.他の二人がシャワーを浴びている間に「そういえば排水管って2回曲がっててそこに水が溜まって臭いを防ぐんだったよな」ということに気付いたので,二人に「排水口に水流し込んだら臭い止まるんじゃね?」って言って実際に流し込んでみると無事に臭いは止まった.どうやら溜まってた水がなくなったので臭いがダイレクトに届いていたらしい.あとで聞いてみると「排水管から臭いがするのはずっと人が済んでいない証拠って言われまくった」って言われたので色々と察してしまった.最近もう一度臭いがしたらしく,もう一度水を流し込んだらしい.いやいい加減洗濯機買えよ.

Problem 3 : 単純に住みにくい

梅雨の入りとはいえ,別に空調いれないとやっていけないほど暑いと言うわけでもないはずだったのだが,ニューでなハウスはなぜかとにかく暑かった.空調を仕方ないのでいれさせてもらうと,これがどっこい異様に効かない.後々のでなり曰く「夏は死ぬほど暑くて冬は死ぬほど寒い」ということらしいので単純に断熱的な何かが異様に足りてないのではないかという結論になった.

そしてニューでなハウスでまったりしてると突然「ガタガタガタ…」と揺れたりする.@c_bata_ と二人で「え?地震ですか?」みたいな顔をしているとでなりは「あっ,トラックが通っていますね」みたいなことを言う.家が幹線道路に面しており.トラックとかがバンバン通るため,その影響で家がガタガタと揺れるようだ.本当に地震が来たらあっさり倒壊しそうで冗談抜きで心配している.ちなみにニューでなハウスには謎の隆起があったり,すでに床が斜めになっていたりしているので地震が来なくても倒壊しそうとかは口が裂けても言ってはいけない.

最後に,でなりの住んでいる階にはどうやらネットが配線的に届かないらしく,インターネット環境はなんとWiMAXルータでしのいでいた.しかも別に電波がビンビンに入るわけでもない感じだったのでエンジニアとしては本当に厳しい環境だなと一人で考えていた.

以上より得られた知見

どうやらでなりは家賃に目がくらんでニューでなハウスを選定したようだが,「給料の三分の一を家賃に貢がないと人間的な生活は出来ない」と誰かが言うように本当にケチると限界生活が始まるんだなという感想が得られた.内見していればこんなことにはならないのではないかという気はするが,1-3月期の東京での家探しではそうも言ってられない感じなので不安が止まらない.そして,いくらもったいないとは言え家具は揃えたほうがいい.絶対にQoLに響くので最低限の冷蔵庫,洗濯機,電子レンジくらいは揃えようという気持ちになった.いくら「いやぁ家は寝に帰るだけだし〜」と言っても,そもそもその家が寝るだけで苦しい環境とかだと本当に心が折れかねないので,家探しはまじで大切なんだなとでなりの尊い犠牲からえられたので,これから家を探すにあたって,住みよい家に出会えるように努力をしていきたい.

最後に

ニューでなハウスのことを散々言いましたが,本当に泊めていただいたことには感謝をしております.Thanks a lot for denari's kindness.

叶うならば,でなりがもっといい家に住めますように…