Material Designの目指すところとAndroid側の変更点

モバイルファースト室の @rejasupotaro です。

今年のGoogle I/OではAndroid One、Android L、Material Design、Android Ware、Android Auto、Android TV、Google Cloud Platform、Google Fitなどの発表がありました。弊社からは私と @sys1yagi@__gfx__ が参加したので、何回かに分けてGoogle I/Oの発表の内容を紹介します。

今回のテーマはMaterial Designです。内容は公式ドキュメントやセッション動画の要約や参考リンクからの引用を含みますが、筆者の考察や主観も含まれます。

Material Designとは

Material Designとは、Googleが発表したビジュアル、モーション、インタラクションのプラットフォームやデバイス間の包括的なガイドです。Material Designは、現実世界の素材をメタファーとすることでユーザにとって分かりやすく表現するための視覚言語です。

Gmailアプリの比較したのが下の図です。左が現在のGmailアプリで、右が新しいGmailアプリです。

f:id:rejasupotaro:20140708102937p:plain

タイポグラフィは見やすく、グリッドを調整して、サーフェスを一新されました。

フラットデザインの主眼は、視覚的な混乱を取り除くことにありました。でもそのおかげで、例えば影のような、スクリーンを見やすく、操作しやすくするためのディテールも取り去られてしまいました。Material Designでは、ピクセルは色と深さを持っています。リアルな光によって繋ぎ目を表現し、空間を分割し、動くパーツとして表現して、それ以外の無駄な装飾を省いています。

f:id:rejasupotaro:20140708102936p:plain

Material Designの目指すところ

今年のGoogle I/OでAndroid Ware、Android Auto、Android TVなどのプラットフォームの発表がありました。KeynoteでPichai氏はInternet of Things(モノのインターネット:家電や自動車など多種多様な「モノ」がインターネットにつながり、お互いに情報をやり取りすることで新しい価値を生み出すという概念)と掛けて、Android of Thingsと言っており、身の回りのあらゆるものにGoogleのプラットフォームを浸透させようとしています。そうなると、ユーザとのインタフェースになるデザインは今後さらに重要になってくると考えられます。

小さな画面で膨大な情報を扱うAndroid Wearにも、車の運転に注意力の大半を持っていかれるAndroid Autoにも、OSでありブラウザでもあるChromeでも、ユーザが迷わないためにプラットフォームやデバイスを超えてデザインをする必要があります。各プラットフォームがバラバラにデザインされるとユーザが混乱してしまうので、Material Designという統一的なデザインガイドラインを定めて、ユーザは一つのコンテキストを知っているだけで操作できるようにしようとしているのだと思います。ガイドラインを読むとMaterial Designは、インタフェースはシンプルに、タイポグラフィはどのデバイスでも見やすく、クロスプラットフォーム・マルチデバイスを意識して設計されているということが分かります。

クロスプラットフォームとMaterial Design

それでは、ウェブもiOSもAndroidも、同じデザインで作らなければならないのでしょうか。その疑問について Going responsive with the Google Play Apps Suite というセッションで、クロスプラットフォームでデザインをどうスケールさせるかという話が興味深かったです。

下の図は2012年のPlayアプリです。

f:id:rejasupotaro:20140708102944p:plain

アプリのデザインが他のものと統一されていないと感じるでしょう。プラットフォームごとにメンタルモデル、ルックアンドフィール、インタラクションに対して異なるアプローチをしていました。そのため、同じ機能のために何度もデザインをしていて、デザインをスケールさせることができませんでした。そこで、Playアプリ間でデザインを統一しようとしました。

統一するといっても単純に同じものにするということではありません。ウェブとアプリでは異なるインタラクションを持っています。なぜならウェブではユーザとシステムのインタフェースにマウスを使っています。インタラクションは異なっていますが、パターンは一貫している必要があります。

Playではサイドナビゲーションを使っています。下の図の左がウェブで右がアプリです。ウェブとアプリは同じタイプの情報を提供していますが、ウェブの方がスペースが広かったりするので、表示の仕方や中身を変えています。

f:id:rejasupotaro:20140708102940p:plain

タブは、ウェブから見た場合マウスではスワイプが困難なので、クリックで展開されるようにしています。

f:id:rejasupotaro:20140708102941p:plain

メニューボタンは、ウェブではロールオーバー時にメニュー表示するようになっています。しかし、デバイスにはロールオーバーはありません。iOSではメニューボタンを押したときにパネルが下からスライドしてくるようになっています。なぜならそれがiOSのパターンだからです。Androidではポップアップするようにしています。なぜならそれがAndroidのパターンだからです。

このように、インタラクションの表現はプラットフォームによって少しづつ変える必要があるのです。

f:id:rejasupotaro:20140708102942p:plain

f:id:rejasupotaro:20140708102943p:plain

私はMaterial Designが発表されたからといって、すべてのプラットフォームやデバイスでまったく同じ見た目やインタラクションにする必要はないと考えています。Material Designの良いところは積極的に取り入れつつ、自社サービスを使っているユーザが一番使いやすいデザインを模索して、実装していきたいと思います。

Android側の変更点

さて、そんなMaterial Designですが、Android L Developer Previewではマテリアルデザインのアプリを作るための機能追加やライブラリが提供されたので、それらの一部を紹介します。実際に実装する際には、Material Designの公式のガイドライン(Material Design - Google design guidelines)を一読することをオススメします。また、現在はPreviewなので今後APIや挙動は変わる可能性があるのでご注意ください。

Material Theme

Material Themeはウィジェットにカラーパレットをセットしたり、タッチフィードバックやアクティビティのトランジションなど、アプリに新しいスタイルを提供します。styleでMaterial Themeを継承することで使うことができます。

<resources>
    <style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar">
        <item name="android:colorPrimary">@color/primary</item>
        <item name="android:colorPrimaryDark">@color/primary_dark</item>
        <item name="android:colorAccent">@color/accent</item>
    </style>
</resources>

colorPrimaryやcolorPrimaryDarkなどを上書きすることでActionBarやStatusBarの色を変更することができます。

New Widgets

ListViewの新しいバージョンであるRecyclerViewと、情報の一片をカードとして表示するCardViewの二つのウィジェットが提供されています

RecyclerView

dependenciesに compile "com.android.support:recyclerview-v7:+" を追加してください。レイアウトマネージャを使って柔軟にアイテムのポジションを変えたり、デフォルトでビューの追加と削除のアニメーションが追加されています。RecyclerViewにはaddやremoveなどの操作にデフォルトでアニメーションが付きます。

<android.support.v7.widget.RecyclerView
    android:id="@+id/list"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

データとのアダプターにRecyclerView.Adapterを使うようになりました。従来のgetViewを継承するスタイルからCreateしてBindするスタイルに変わっています。setOnItemClickListenerが実装されていなかったり、今までと違ってLayoutManagerを渡さないとクラッシュしたりと、ちょっと癖がありました。しかし、LayoutManagerを使うことで柔軟なレイアウトが組めそうです。

CardView

dependenciesに compile "com.android.support:cardview-v7:+" を追加してください。CardViewはFrameLayoutの拡張で、どのアプリでも統一した見た目で情報をカードとして表示することができます。CardViewに影を付けたり、角を丸くしたりすることができます。

<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:elevation="@dimen/spacing_small"
    card_view:cardCornerRadius="@dimen/spacing_small">

    <ImageView
        android:id="@+id/list_item_photo"
        android:layout_width="match_parent"
        android:layout_height="180dp"
        android:scaleType="centerCrop" />

</android.support.v7.widget.CardView>

View Shadow

ビューに高さを表現するZプロパティが追加されました。これは影の大きさと並びに影響を与えます。ZプロパティはelevationとtranslationZから成り立っています。

Animations

UI操作のタッチフィードバック、ビューの状態の変更、アクティビティのトランジションのためのアニメーションのAPIが提供されました。

新しいアニメーションのAPIで以下のようなことができます。

  • タッチイベントに反応するタッチフィードバックアニメーション
  • ビューの表示・非表示を分かりやすくするアニメーション
  • アクティビティを遷移するときのアニメーション
  • より自然な動きを表現するカーブドモーション
  • ビューの状態の変化のアニメーション

SceneTransitionAnimation

Activity間でビューをSharedElementとして渡すことができるようになりました。ビューのidを指定したtransitionを定義して、styleでtransitionを上書きします。

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- moveImage is used for the ImageViews which are shared -->
    <moveImage>
        <targets>
            <target android:targetId="@id/detail_photo" />
            <target android:targetId="@id/list_item_photo" />
        </targets>
    </moveImage>

</transitionSet>
<resources>
    <style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar">
        ...

        <item name="android:windowContentTransitions">true</item>
        <item name="android:windowSharedElementEnterTransition">
            @transition/grid_detail_transition
        </item>
        <item name="android:windowSharedElementExitTransition">
            @transition/grid_detail_transition
        </item>
    </style>
</resources>

定義したらstartActivityの引数にActivityOptionsをBundleとして渡します。

// ActivityOptionsに共有するビューのidを渡す
ActivityOptions activityOptions = ActivityOptions.makeSceneTransitionAnimation(
        activity,
        new Pair<View, String>(view.findViewById(R.id.list_item_photo),
                ArticleDetailActivity.VIEW_NAME_PHOTO),
        new Pair<View, String>(view.findViewById(R.id.list_item_title),
                ArticleDetailActivity.VIEW_NAME_TITLE));

// 呼び出すときに組み立てたActivityOptionsを渡す
private void openDetailActivity(Article article, View view) {
    startActivity(ArticleDetailActivity.createIntent(this, article),
            ArticleDetailActivity.createOption(this, view));
}

サンプルアプリケーション

新しいAPIを使ってアプリを書いてみました。懸念していたActionBarはほぼそのままで動いて良かったです。まだ至らぬ点があるのでアップデートしていこうと思っています。リポジトリはこちらです: rejasupotaro/MaterialDesignSample

f:id:rejasupotaro:20140708102947p:plain

f:id:rejasupotaro:20140708102946g:plain

全体のまとめ

  • Material Designは静止画で見るとフラットデザインっぽいですが、思想や使い心地は異なったものです
  • ウェブやiOSにもサービスを提供している場合は、どのようにMaterial Designを取り入れるのがユーザにとって一番良いのか考える必要があります
  • Android L Developer Previewは、まだプレビュー感が強いのでただちに移行するということはないが、ウォッチはしていた方が良いです

参考リンク

料理動画を支える技術

インフラストラクチャー部 星野(@con_mame)です。

少し前から、一部レシピページに料理動画を掲載していました。当初はYoutubeを使用していましたが、本日から自社配信に切り替わりました。現在はまだ掲載数は少ないですが、今後掲載数を増やしていきたいと考えております。

そこで、今回は、動画配信プラットフォームの裏側がどうなっているかという点を簡単にですがご紹介したいと思います。

構成図

構成図を見ていただくのが一番わかり易いと思うので、最初に掲載します。

structure

見て分かる通り、今回は全てAWSのサービスを使用して構築しています。

今回使用したサービスは

  • エンコード: Elastic Transcoder
  • データストア: DynamoDB + DynamicDynamoDB
  • ストレージ・配信: S3 + CloudFront
  • エンコード通知など: SNS

今回、構築までの期間とエンコードや動画というデータサイズの大きなコンテンツ配信を安定して提供するためにこのような構成を取りました。動画形式は、PCやスマートフォンで使用するネットワークに合わせて、Adaptive BitrateのHLSで配信を行っています。

動画のエンコードに関しては、Elastic Transcoderを用いて、1jobにて複数BitrateのHLSとサムネイルを同時に生成しています。Elastic Transcoderを使用したことによって、動画変換サーバの構築・運用やJOBキューイング、負荷状況に応じたサーバスペックの調整が不要になりリリースまで、そしてその後のサービスの安定化・開発効率を維持できるようにしています。

また、クライアントからのUploadはS3に直接行い、STSを使って一時的な認証情報を発行するToken vending machine (TVM)方式を取ってます。この方法にした理由は、動画投稿はアップロードするコンテンツサイズが大きくなってしまうため、長期間S3とProxyするサーバのセッションを確保されてしまうのを防ぐためと、安定性のためです。

簡単な動作

  1. 動画をアップロードする端末がTVMからSTS経由でS3の特定のBucketにPUTする権限のついた認証情報を取得
  2. その認証情報を用いて動画ファイルをPUTし、APIにむけて通知を送る
  3. APIはDynamoDBに情報を書き込み、Elastic Transcoderにjobを投げる
  4. エンコード状態の変化・完了通知はSNSを経由してAPIサーバに通知され、適時DynamoDBの中身を書き換える
  5. エンコード完了したファイルはS3に書きだされ、CloudFront経由で配信
  6. 動画表示時は、アプリケーションサーバがAPIサーバ経由で情報を取得し、DynamoDBから情報の読み書きを行なう

といったシンプルなものになっています。 アプリケーションサーバや管理用のサーバにはInternal ELB経由で接続し、DynamoDBはDynamic DynamoDBを用いることによってリクエスト数の増減に対応しています。

また、Dynamic DynamoDB自体は、AutoScalingを用いてSelf Healを行っております。

まとめと罠

今回、ほぼAWSのサービスで動画配信プラットフォームを構築することで、リリースまでの時間短縮や安定した基盤を作ることが出来ました。

今回の構成では、基本的にはAPIサーバだけを管理すればいいので、今後の用途の増加にも対応しやすい構成になったと思っています。

注意点としては、ブラウザからS3への直接UPの際はBucketのCORS設定もお忘れなく。

今後は、DynamoDBとRedshift連携したデータ解析やKinesisを使った何かなども組み込んでいきたいところです。

最後に、Elastic Transcoderはあまり利用事例が無いようで、エンコード完了通知に動画の再生時間作成されたサムネイルの枚数が通知されないなど細かいところで改善して欲しいところはありますが、コスト・安定性的にいいソリューションの1つではないかと思います。

COOKPAD料理動画はこちら!!

OS X キーチェーンから環境変数をセットするツールを作りました

こんにちは、技術部の福森 (@sora_h) です。

最近は環境変数に API トークンや credential といった認証情報を入れる事が増えてきています。 たとえば、AWS を利用するツールでは AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY といった環境変数にだいたいの場合で対応しています。

そのため、~/.bashrc~/.zshrc などシェルの設定に export を書いておき常に使える状態にしている方も多いと思いますが、 それって実は危険ではないでしょうか?

例えば、下記のようなリスクが考えられます:

  • 意図せず情報が利用されて意図しない副作用が発生してしまう危険性
    • 本番に変更を与えるつもりはなかったけれど事故を起こしてしまう等
  • 悪意のあるスクリプトを実行した際に環境変数を送信などされてしまう危険性

事故や漏洩を防ぐためにも、筆者はかならずセットする必要はあまりないと考えています。 前から個人的に aws-exec() { env AWS_ACCESS_KEY_ID=... $* } のようなシェル関数を利用していたのですが、 この方法をもっと広めたほうが良いのでは?、と社内で意見を貰ったため汎用的なツールにしてみました。

大きな特長は、平文ではなく OS X のキーチェーンに値を保存するようにした点です。 これにより plain text で残らない上、パスフレーズの確認といったアクセスコントロールを調整できるようになりました。 (設定次第ですが) 読み出しが発生するタイミングでプロンプトさせる事ができ、意図しない読み出しを防ぐことができます。

また、このツールを利用して気軽にいくつかの環境変数の組合せ (アカウントや設定類) を簡単に切り替えながら開発する事ができるようになりました。 同じサービスのトークンを複数切り替える、といった際にも便利だと思います。

注意

  • OS X の keychain を利用するため、OS X のみのサポートです

インストール

homebrew

$ brew install https://raw.githubusercontent.com/sorah/envchain/master/brew/envchain.rb

make

$ git clone https://github.com/sorah/envchain.git
$ cd envchain
$ make

$ make install
(あるいは手動で)
$ cp ./envchain ~/bin/

使い方

環境変数をセットする

envchain --set <namespace> <variable name> <variable name>... のように呼び出す事で環境変数を keychain に登録できます。 envchain では複数の環境変数をセットとして登録して切り替えて利用する事ができます。

$ envchain --set foo SECRET_TOKEN USERNAME
foo.SECRET_TOKEN: foobar
foo.USERNAME: alice
$ envchain --set bar SECRET_TOKEN
bar.SECRET_TOKEN: hogehoge

セットした環境変数を利用する

envchain <namespace> <cmd> <arg>... のように呼び出す事で namespace から登録してある環境変数を読み込んでコマンドを実行する事ができます。

$ printenv SECRET_TOKEN || echo 'not found'
not found

$ envchain foo printenv SECRET_TOKEN
foobar
$ envchain foo printenv USERNAME
alice

$ envchain bar printenv SECRET_TOKEN
foobar

その他

envchain --set --noechoenvchain --set --require-passphrase といったオプションも存在します。詳細は envchain コマンド (引数無し) のヘルプを参照してください。

セキュリティについて

環境変数に機密情報を入れる上でのセキュリティに関して、筆者の見解を、個人のブログに書いたのでこちらを参照ください: http://diary.sorah.jp/2014/06/05/securing-environment-variables

仕組み

OS X の Security framework に含まれる Keychain Service を利用しています。 (リファレンス)
namespace の前に "envchain-" をつけたもの (例: envchain-foo, envchain-bar) を KeychainItem のサービス名、環境変数名をアカウント名としてデフォルトの keychain に登録するようにしています。

OS X のキーチェーンアクセス (/Applications/Utilities/Keychain Access.app) からアイテムを探す事で登録されている様子を見る事ができます。

Keychain Access

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/