2014年11月25日火曜日

Android : Google Drive 上のファイルの作成・読み込み(一般フォルダ編)


このエントリーをはてなブックマークに追加
Google Drive 上のファイルとして見えるファイルを作成、読み込みするアプリを開発する方法のメモです。
共通する部分は多数ありますが、アプリケーション専用フォルダ(APP_FOLDER)ではありません。
また、ファイルの編集(すでにあるファイルを読み込み、その内容を変えて保存、というのもこの記事では扱っていません。


準備1. サービスの作成、API の有効化

Set up the game in the Google Play Developer Console を参考にゲームサービスを作成し、作成したプロジェクトの Google Developers Console で Drive API を有効にします。


準備2. GoogleApiClient の作成

まずは GoogleApiClient を作成し、connect を呼んでログインしておく必要があります。
ここで必要な API は Drive.API, Scope は Drive.SCOPE_FILE です。
(ちなみにアプリケーション専用フォルダへの保存の場合はスコープに SCOPE_APPFOLDER が必要になります)
また、以降サンプル内で定義されていない変数・定数についてはクラスのメンバ変数などとしてどこかで定義されていると思ってください。
mGoogleApiClient = new GoogleApiClient.Builder(this)
        .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
            @Override
            public void onConnected(Bundle bundle) {
                Log.i(LOGTAG, "onConnected");
            }

            @Override
            public void onConnectionSuspended(int i) {
                Log.i(LOGTAG, "onConnectionSuspended:" + i);
            }
        })
        .addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
            @Override
            public void onConnectionFailed(ConnectionResult connectionResult) {
                Log.i(LOGTAG, "onConnectionFailed:" + connectionResult.toString());
                if(connectionResult.hasResolution()) {
                    try {
                        // 初回起動時などで未認証の場合などはこちらにきます
                        connectionResult.startResolutionForResult(MainActivity.this, GOOGLE_API_CLIENT_RESOLUTION);
                    } catch (Exception e) {
                        Log.e(LOGTAG, e.toString());
                    }
                } else{
                    Log.e(LOGTAG, "hasNoResolution()");
                    int errorCode = connectionResult.getErrorCode();
                    Dialog dialog = GooglePlayServicesUtil.getErrorDialog(errorCode, MainActivity.this, GOOGLE_API_CLIENT_ERROR);
                    if( dialog != null){
                        dialog.show();
                    } else {
                        Log.e(LOGTAG, "no dialog");
                    }
                }
            }
        })
        .addApi(Drive.API).addScope(Drive.SCOPE_FILE)
        .build();
mGoogleApiClient.connect();
startResolutionForResult で OK が返ってきた時に再接続を試みるために onActivityResult に以下のようなコードを書き加えておきます。
※ startResolutionForResult は Activity から startActivity される扱いになるので、Activity の onActivityResult で処理必要があります。Fragment 内から呼んで、Fragment の onActivityResult で処理したい場合は、Activity の onActivityResult からその Fragment の onActivityResult を呼ぶよう明示的にコードを書く必要があります(他の Activity 起動系 API も同様です)。
if(requestCode == GOOGLE_API_CLIENT_RESOLUTION){
    switch (resultCode) {
        case Activity.RESULT_OK:
            mGoogleApiClient.connect();
            break;
        default:
            Log.e(LOGTAG, "" + resultCode);
    }
}


ファイルの作成

DriveApi.newDriveContents に設定したコールバック関数内で、DriveApi.newCreateFileAcitivtyBuilder() にメタデータや初期データを設定、アクティビティを呼ぶ、という流れです。

ResultCallback<DriveApi.DriveContentsResult> newDriveContentsCallback = new
    ResultCallback<DriveApi.DriveContentsResult>() {
        @Override
        public void onResult(DriveApi.DriveContentsResult result) {
            // MimeType は適宜変更する。他にも必要な項目があれば set 関数で set する。
            // setTitle で初期タイトルくらいは決めてあげると良さそう
            MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
                    .setMimeType("text/plain").build();

            DriveContents contents = result.getDriveContents();
            // OutputStream を取得して初期データを書き込む。例として、適当な文字列を書き込む。
            try {
                OutputStream os = contents.getOutputStream();
                String hello = "Hello World!";
                os.write(hello.getBytes());
                os.close();
            } catch (Exception e){
                Log.e(LOGTAG, e.toString());
            }

            // デフォルトで用意された UI を表示
            IntentSender intentSender = Drive.DriveApi
                    .newCreateFileActivityBuilder()
                    .setInitialMetadata(metadataChangeSet)
                    .setInitialDriveContents(contents)
                    .build(mGoogleApiClient);
            try {
                startIntentSenderForResult(
                        intentSender, REQUEST_CODE_CREATOR, null, 0, 0, 0);
            } catch (IntentSender.SendIntentException e) {
                Log.w(LOGTAG, "Unable to send intent", e);
            }
        }
    };

Drive.DriveApi.newDriveContents(mGoogleApiClient).setResultCallback(newDriveContentsCallback);

onActivityResult に以下のようなコードを書いておくと作成された Google Drive ファイルの DriveId が取得可能です(作りっぱなしでよいなら取得する必要はありません)。
if(requestCode == REQUEST_CODE_CREATOR){
    Log.i(LOGTAG,"REQUEST_CODE_CREATOR:" + resultCode);
    if(resultCode == RESULT_OK) {
        DriveId driveId = data.getParcelableExtra(CreateFileActivityBuilder.EXTRA_RESPONSE_DRIVE_ID);
        Log.i(LOGTAG, "Drive Id:" + driveId.toString());
    }
}


ファイルの読み込み

DriveApi.newOpenFileActivityBuilder() を使用してアクティビティを呼び出し、onActivityResult で DriveId を取得、DriveId からファイルを開いて内容を読み込むという流れです。
まずはファイル選択ダイアログの呼び出し。
// MimeType でフィルタリングされる
IntentSender intentSender = Drive.DriveApi
        .newOpenFileActivityBuilder()
        .setMimeType(new String[] { "text/plain" })
        .build(mGoogleApiClient);
try {
    startIntentSenderForResult(
            intentSender, REQUEST_CODE_OPENER, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
    Log.w(LOGTAG, "Unable to send intent", e);
}

以下のコードは onActivityResult 内という想定です。DriveId からファイル内容を取得します。
if(requestCode == REQUEST_CODE_OPENER){
    if(resultCode == RESULT_OK){
        DriveId driveId = data.getParcelableExtra(
                OpenFileActivityBuilder.EXTRA_RESPONSE_DRIVE_ID);

        // 取得した driveId からファイルの内容を取得。
        // プログレスを取得して何か行う場合は file.open の第三引数にリスナーを設定する。
        DriveFile file = Drive.DriveApi.getFile(mGoogleApiClient, driveId);
        file.open(mGoogleApiClient, DriveFile.MODE_READ_ONLY, null).setResultCallback(new ResultCallback<DriveApi.DriveContentsResult>() {
            @Override
            public void onResult(DriveApi.DriveContentsResult driveContentsResult) {
                if(driveContentsResult.getStatus().isSuccess()){
                    DriveContents driveContents = driveContentsResult.getDriveContents();
                    // DriveContents の getInputStream で内容を取得する。
                    // 以下はファイルの内容がテキストであることを想定した例。
                    BufferedReader reader = new BufferedReader(
                            new InputStreamReader(driveContents.getInputStream()));
                    StringBuilder builder = new StringBuilder();
                    String contents = null;
                    String line;
                    try {
                        while ((line = reader.readLine()) != null) {
                            builder.append(line);
                        }
                        contents = builder.toString();
                    } catch (IOException e) {
                        Log.e(LOGTAG, "IOException while reading from the stream", e);
                    }
                    // 読み込むだけで内容に変更はなし。discard を呼んで閉じておく(意味は無いかも?)
                    driveContents.discard(mGoogleApiClient);

                    Toast.makeText(MainActivity.this, contents, Toast.LENGTH_LONG).show();
                }
            }
        });
    }
}


ちなみに、認証画面をもう一度出したい場合などで、認証を取り消したいときは このリンク先からアプリを 選択してアクセス権を取り消します。

参考
googledrive/android-demos
com.google.android.gms.drive
Google Drive Android API : Create files
Google Drive Android API : Working with File Contents

2014年11月23日日曜日

『Being Mortal』 感想とレビュー


このエントリーをはてなブックマークに追加



Being Mortal: Medicine and What Matters in the End』を読みました。
"Old age is continuous series of losses."
この一文の引用だけで若干憂鬱になりますが、いつかは死ぬ、immortal(不死)ではない人間として、どうやって最後の時を生きて、どうやって死を迎えようか、というちょっと重い本です。

医療施設の話、筆者が関わった患者の話、筆者自身の父親の話などを通して、自分の親だったら、自分自身だったら、といろいろなことを考えさせられました。

まだまだ考えはまとまりませんが、読んでみて良かったです。
あまり上手くまとめられる気がしないので、内容については特に気に留めている点に触れるだけにとどめておきます。本では、他にも、緩和医療、医療コスト、コミュニティなどいろいろな話題に触れています。

安全と長生きが最優先事項か


病院や施設は、お年寄りの安全(と組織としての効率)を第一に考えた運営がなされているが、それが当人達にとって最善なのか、プライバシーも生きる目的も奪われた状態で管理されて長生きすることが幸せなのか、という問題提起。
"It just isn't home"

At home, you decide how you spend your time, how you share your space, and how you manage your possessions. Away from home, you don't.

治療とリスク、トレードオフ


医療技術は大きく発展したが、それでも治療できない、もしくは治療できる可能性が低い病気はある。また、治療行為にはリスクや副作用も存在する。難しいことではあるが、自分(もしくは家族)が何を大事に思うかと照らしあわせて、どのような可能性のために、どのようなリスクなら受け入れられるか、を考える必要がある、という問いかけ。
What are your fears and what are your hopes? What are the trade-offs you are willing to make and not willing to make?

2014年11月21日金曜日

Android : Saved Games を使う


このエントリーをはてなブックマークに追加
Saved Games in Android 参照。
便利だけど、ログイン時の許可リクエスト量が多いのが難点。
  • Google Play Game Services : Getting Started for Android Game Development の Step 2 に沿って Google Play Services の設定(後で Internal Error が起こらないようにこの辺りに注意)
  • 作成したプロジェクトの Google Developers Console で Drive API、Google Play Game Management、Google Play Game Services をオンにする
  • GoogleApiClient を作るときは addApi(Games.API).addScope(Games.SCOPE_GAMES) と addApi(Drive.API).addScope(Drive.SCOPE_APPFOLDER) が必要?(コード後述)
    • 足りないと実行時に "appropriate api was not requested." というエラーでる
    • Games.API を指定するとログイン時に Google+ のプロフィールアクセスのような、ただデータを保存したい、という用途には必要のない許可まで求めてしまうが嫌だけど、削れそうにないか…。
  • Google play Developer Console のゲームサービス、「ゲームの詳細」で「保存済みゲーム」をオンにする(Saved Games の訳が保存済みゲーム…?)
    • オンにしていないと実行時に "Cannot use snapshots without enabling the 'Saved Game' feature in the Play console" というエラーが出る
  • Snapshot 新規作成時に snapshot.readFully() を呼ぶと長さ 0 の byte 配列が返る(null ではないので注意)
  • SnapshotMetadataChange の setCoverImage はしなくても大丈夫(デフォルトのコントローラの画像が表示される)
  • デバッグのためなどで、一回認証を解除して再度ログイン画面を出すには Google+ のアプリ設定 から接続を解除する
    • 他に方法はある?

GoogleApiClient 作成のコード例

mGoogleApiClient = new GoogleApiClient.Builder(this)
        .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
            @Override
            public void onConnected(Bundle bundle) {
                Log.i(LOGTAG, "onConnected");
            }

            @Override
            public void onConnectionSuspended(int i) {
                Log.w(LOGTAG, "onConnectionSuspended:" + i);
            }
        })
        .addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
            @Override
            public void onConnectionFailed(ConnectionResult connectionResult) {
                Log.e(LOGTAG, "onConnectionFailed:" + connectionResult.toString());
                if(connectionResult.hasResolution()) {
                    try {
                        connectionResult.startResolutionForResult(MainActivity.this, REQUEST_CODE_GOOGLE_API_CLIENT_RESOLUTION);
                    } catch (Exception e) {
                        Log.e(LOGTAG, e.toString());
                    }
                } else{
                    Log.e(LOGTAG, "hasNoResolution()");
                    int errorCode = connectionResult.getErrorCode();
                    Dialog dialog = GooglePlayServicesUtil.getErrorDialog(errorCode, MainActivity.this, REQUEST_CODE_GOOGLE_API_CLIENT_ERROR);
                    if( dialog != null){
                        dialog.show();
                    } else {
                        Log.e(LOGTAG, "no dialog");
                    }
                }
            }
        })
        .addApi(Games.API).addScope(Games.SCOPE_GAMES)
        .addApi(Drive.API).addScope(Drive.SCOPE_APPFOLDER)
        .build();
mGoogleApiClient.connect();

Android : GoogleApiClient (Google Play Services) で発生する Internal Error の原因


このエントリーをはてなブックマークに追加
いろいろ調べた挙句、自分の場合は「Google Play Developer Console で設定したパッケージ名とアプリのパッケージ名が違う」でした。せっかくなので、それも含めてありえる原因をまとめておきます。

  • AndroidManifest.xml に指定した meta-data の com.google.android.gms.games.APP_ID が Google Play Developer Console のリンク済みアプリに記載されたアプリID とは異なる
  • Google Developer Console (API のオンオフを設定したりする方)に入力した SHA1 の値が間違っている
    • ビルド時と異なる keystore ファイルから SHA1 を取得していた、など。
    • デバッグ版の場合は、debug.keystore がある場所(Windows の場合は通常 C:\Users\%ユーザ名%\.android )で以下のコマンド。keytool は java の bin 以下にあるのでパスを通しておく。
      keytool -exportcert -alias androiddebugkey -keystore debug.keystore -list -v
  • Google Developer Console で同意画面を作成していない(「API と認証」セクション)
  • GoogleApiClient の setScope で設定するスコープが足りない
  • Google Play Developer Console で設定したパッケージ名と実際のアプリのパッケージ名が異なる
    • AndroidStudio の場合、build.gradle の defaultConfig の applicationId を変える必要あり(ここを見落としていて、src の方のディレクトリ構造とパッケージ名や AndroidManifest.xml のパッケージ名を変更していた場合も Internal Error でした。)

凡ミスには気をつけたいものです。。。

2014年11月15日土曜日

『ZERO to ONE』 感想とレビュー


このエントリーをはてなブックマークに追加



ゼロ・トゥ・ワン 君はゼロから何を生み出せるか』読了しました。

自分なりに本書の主張を箇条書きでまとめると、以下の項目になります。
  • 「リーン」に代表されるような、ユーザの声を聞いて少しずつ改善していくという手法では真に革新的なことはできない。自分なりのビジョンと計画を持って大きく賭けろ
    • 例えば Apple の製品はジョブズのビジョンの結晶である。
  • 競争下では最終的に全体の収益が消失する。他ができないことを行う、という意味での独占は善である
    • 独占しているかしていないか、独自性はあるか、ということを見極める上で、競争範囲の定義は重要である。範囲を狭く定義して独占を主張しても意味が無い。
  • 販売・売り込みは必要。方法には考えが必要。
    • 「あの会社はそんなことせずに成功したじゃないか」と思うのであれば、優れた売り込みに気づいていない。
  • 良い未来は創れるという希望を持ち、自分の頭で考えよう

この辺りの主張については、本書を読み進める中で、十分な説明やエピソードが紹介されており、なるほどと思え納得はしており、それは興味深く読めたのですが、「さてではどうするか」、というのが難しいですね。結局のところ「新しいことを自分の頭で考えよう」の一言に尽きる気はしています。

一人の会社員、社会人としての行動指針に落としこむと

  • 「こうして欲しい」という要望を待たずに「こうすれば良いのは」と提案できるように考える
  • 既存のプロセスを鵜呑みにせず、本質的な問題をより効率よく解決するために他の方法がないか考える
  • (独占的な)強みとなる技術を身につける

といったところでしょうか(なんだかとてもスケールが小さな話になってしまいましたが…)。

Amazon.com、Amazon.co.jp でもランキング上位にあり、結構なレビューの数が付いていることを見ると、起業家(もしくは起業家を目指している人)以外にも多くの人がこの本を読んだのだと思うのですが、そういう人がどのようにこの本を消化して、どのように行動を変えようと思ったのか、という考えを聞いてみたいものです。

また、この本を読んで、以前読んだ『コピーキャット―模倣者こそがイノベーションを起こす』を再読したくなりました。こちらの本はこちらの本で、納得できる内容であり、とても面白かった覚えがあります。


おまけメモ。Amazon で「序文いらない」というレビュー投稿を見たのですが、それにはちょっと同意しました。冒頭で、あなたの書籍の推薦依頼を受けるかどうかのポリシーを語られてもね、という。それ以外の内容は普通で特に気にならないのですが。関係のないこと(特に見方によっては自慢のように思えること)を書くと余計な印象を持たれてしまうかも、という戒めにメモしておきます。

『幻惑の死と使途』 感想 : 「名前」のための殺人の話


このエントリーをはてなブックマークに追加


幻惑の死と使途』読了。Kindle Voyage を買ったついでに、何か読むものを、と思って買った小説でした。森博嗣作品は『すべてがFになる』と『冷たい密室と博士たち』しか読んでませんが、この3つの中では一番良かったです。手品とか好きですし、トリック・動機も納得のいく方でした。

それから、全体的な「名前」というテーマと、犀川教授の
人はアウトプットするときだけ、個たる「人」であり、それ以外は、「人々」でしかない
という言葉がとても印象的でした。

心に残る一言が一つでもあると、単純な娯楽としての読書以上にその小説は読んでよかった、と思えるので、この本もまた読んでよかったです。

2014年11月14日金曜日

Android : string.xml リソースで太字・斜体などのスタイルを指定する


このエントリーをはてなブックマークに追加
リソースファイル string.xml では太字を html の bold として <b> タグで指定。
html として処理するので、改行はいつもの \n ではなく <br/> で。
%1$d は %d の引数番号指定。(引数一つしか無いのでここでは特に意味は無いですが…)

<string name="best_score"><![CDATA[Score : %1$d<br/><b>Best Score !</b>]]></string>

コード上からは、

mScoreTextView.setText(Html.fromHtml(getString(R.string.best_score, score)));

のように、getString で取得した値を Html.fromHtml でフォーマットすることで TextView にスタイルを反映できる Spanned インターフェースを作る。

参考:Is it possible to have multiple styles inside a TextView?

2014年11月8日土曜日

Android : AlertDialog のタイトル下の線(Divider)の色を変更する


このエントリーをはてなブックマークに追加


id から Divider 用のラインに使われている View を取得して setBackgroundColor で色を設定します。
NumberPicker の時と同じような方法です。)

AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.dialog)
       .setIcon(R.drawable.ic)
       .setMessage(R.string.dialog_msg);
//The tricky part
Dialog d = builder.create();
d.show();
int dividerId = d.getContext().getResources().getIdentifier("android:id/titleDivider", null, null);
View divider = d.findViewById(dividerId);
divider.setBackgroundColor(getResources().getColor(R.color.my_color));


参考 : How can I change the color of AlertDialog title and the color of the line under it

Android Stduio : Generate Signed APK 時の MissingTranslation エラーを回避する


このエントリーをはてなブックマークに追加
values 以下の string リソースを持ったファイル(一般的に strings.xml, arrays.xml)の resources タグに以下のような属性を追加します。

<?xml version="1.0" encoding="utf-8"?>
<resources
    xmlns:tools="http://schemas.android.com/tools"
    tools:ignore="MissingTranslation">

この方法だと、以下のように個別の item に translatable="false" をつけることなく一括で設定できます。

<string name="hello" translatable="false">hello</string>

Settings の Inspections から Incomplete translation の設定を Error から Warning にしても、build.gradle で lintOptions に abortOnError false をつけていても Generate Signed APK で署名付き APK を作るときはどうしてもエラーでビルドが中断してしまっていたので、この方法で解決しました。

参考 : Avoid Android Lint complains about not-translated string

『Race Against The Machine』 感想とレビュー


このエントリーをはてなブックマークに追加


Race Against The Machine: How the Digital Revolution is Accelerating Innovation, Driving Productivity, and Irreversibly Transforming Employment and the Economy(サブタイトル長!)を読みました。いつか買って放置してしまっていたものを Kindle Voyage 購入をきっかけに再発見して読んだのですが、かなり良かったです。

コンピュータ技術の向上がどれだけ生産性に大きなインパクトを与えているか、という説明と、その結果、機械・テクノロジーが人間の労働機会はどんどん奪われている、という現状について書かれています。

コンピュータ技術は、他のあらゆる産業で使用されているため、その発展の影響力は非常に大きい、ということがよく理解できました。

問題として印象的な点は、現在の労働需要がU字型になってきていて、中間レベルの知識階級の仕事がどんどんコンピュータに取って代わられているという点です。U 字型の上の方はコンピュータによる自動化・ルール化がまだできていない職業、下の方は工場の工員など手作業を伴うような職業なわけですが、自分はまさにその中間層辺りにいる気がするので、危機感を覚えざるを得ません。

社会としての対応策としては、テクノロジーを敵として競うのではなく、テクノロジーを上手く用いて生産性を上げられるようにする、つまり compete against machines ではなく compete with machines ということで、起業(労働機会の創出)方法を含めた教育策の改善を挙げられています。

とりあえず、個人としては、コンピュータが取って代わることのできない領域の知識・経験を身につけ、U字型の労働需要の上を目指すという方向になるのではないかと思いますが、なかなか厳しい道ではあります。

ちなみに、生産性が上がることで「そもそも働かなくてもよくなるのでは?」という考えについては、仕事によってもたらされる、社会に貢献しているという実感は重要であり、「働きたくても働けない状況と、働かないという選択ができる状況は異なる」としてフランクリン・ルーズベルトの以下の言葉を引用されています。
(...) Demoralization caused by vast unemployment is our greatest extravagance. Morally, it is the greatest menace to our social order.
 この辺りは、現在における、退職された方の生きがいについての問題が広がる感じですね。


いろいろ考えさせられる良い本でした。

2014年11月7日金曜日

WPF : Datagrid の一番下の行にスクロールする


このエントリーをはてなブックマークに追加
参考先にいくつかあった方法の使い分けについて。
サンプルコードは C# です。

方法1. ScrollIntoView を使う


ScrollIntoView の引数はオブジェクトなので最終行に相当するオブジェクトがわかっていればそれを使います。

myDataGrid.ScrollIntoView(itemInRow);


Items プロパティの Count と GetItem で最終行のオブジェクトを取得することができるので、以下のようなコードにすると常に動くのではないかと思います。

myDataGrid.ScrollIntoView(myDataGrid.Items.GetItemAt(myDataGrid.Items.Count - 1));


また、DataGrid の CanUserAddRows プロパティが true の場合は、DataGrid の最終行に追加用の行が挿入されているため、追加用の行を CollectionView.NewItemPlaceholder で指定することでスクロールできます。

myDataGrid.ScrollIntoView(CollectionView.NewItemPlaceholder);


方法2. Daragrid のスクロールビューを取得して操作する


少々煩雑ですが、常に使えるようです。ScrollIntoView が使えないような状況があれば、こちらの使用を検討します。

if (mainDataGrid.Items.Count > 0)
{
    var border = VisualTreeHelper.GetChild(mainDataGrid, 0) as Decorator;
    if (border != null)
    {
        var scroll = border.Child as ScrollViewer;
        if (scroll != null) scroll.ScrollToEnd();
    }
}

参考 : How to autoscroll on WPF datagrid

2014年11月5日水曜日

Kindle Voyage 購入レビュー : ページめくりボタン!


このエントリーをはてなブックマークに追加
Kindle 3 から始まり新型が出るたびに Kindle を買い続け、Voyage で 5台目になりました(3, PW 2012 Wifi, PW 2012 3G, PW 2013 3G, Voyage)。

スペック的な説明は Engadget : アマゾン Kindle Voyage 正式発表、高解像度で歴代最薄・最軽量の高級モデル や Gizmode : Kindle Voyage、スクリーンの違いはさすがに歴然 のようなガジェット系ニュースサイトを見るのがベストだとして、結局どう感じたの?という個人の感想をつらつら書こうと思います。

ちなみに今回購入したのは Wi-Fi + キャンペーン情報つきモデルです。


Kindle PW (2013)とスペック的な差は感じる? → 意外に軽さはすぐ感じた。ページめくり速度はあまり。


大きさ、重さはカタログスペック的なもので、実感することはないかと思っていましたが、以外にも持った瞬間「お、ちょっと軽い」と思いました。右手で、ディスプレイ側からみて右下1/4で全体を支えるような持ち方をしているので、少し全体がコンパクトになったことも影響してるのかもしれません。これは嬉しい誤算です。

一方、普通の文字ベースの本においてはページめくり速度の違いはあまり感じませんでした。 PW と Voyage を横に並べて試してみても、ほとんど違いはわかりません。ただ、後述するページめくりボタンのお陰で「ページをめくる」という行動全体で見た場合はスムーズです。あと漫画の場合は速度差を感じました。


ページめくりボタンはどう?→ 最高!


ページをめくるたびに指を動かさずに済む、というのはやはりいいです。 3以来。設定で感度を選べて、3のハードウェアボタンよりも軽い力で動かせます。慣れるまで、押したつもりが反応しないということが何度かありましたが、5分くらい弄っているうちに意図通りに動かせるようになりました。

デフォルトで、ページめくりボタン反応時に Kindle が微妙に振動する設定になっているのですがこれはオフに。特にフィードバックがなくても困らないくらいにきちんと動いてくれます。

戻る方のめくりボタンはちょっと上の方にあって自分の手ではすこしずらさないと届かないので、戻るときは画面スワイプの方を使ってます。


自動明るさ調整はどう?→ とりあえず違和感なし。


あまり明るさが変化する環境で読むわけではないのでなんとも言いがたいですが、とりあえず自動設定にしたところ、良い感じに調整されたのでそのまま自動設定を使っています。


漫画は読める? → 読める。解像度の向上とページめくりの速度アップを感じる。


かなり実用的なレベルです。そこそこのスペックのタブレット以上の快適さだと思います。
こうなるとネックになるのは容量(4GB)ですね。多数の漫画コレクションを突っ込むには心もとない容量です。メインの漫画リーダーはタブレットを使いつつ、Kindle には新しい or お気に入りの漫画を数十冊入れておいて不要になったら消す、みたいな使い方をしようと思います。


キャンペーン情報はどう感じる? → 起動中は気にならない。待ち受け時と待ち受け復帰時に少し邪魔。


今回はじめてキャンペーン付きのモデルを買ったのですが、起動中はほとんど邪魔には感じませんでした。リスト表示にしてるんですが下1/10 くらいに広告バーが出ているだけで、しかもコレクションの中に入った時点でその表示も消えると。

ただ、待ち受けがダサくなるのがいただけないですね。さらに待ち受け復帰時に電源ボタンを押した後スワイプ操作が必要になるのもちょっと嫌です(キャンペーンなし版は電源ボタンを押すだけでよい)。Voyage のせっかくのプレミアム感が損なわれますね。差額分のお金を払って消せるなら消したいなー、という感じです。まぁ慣れたら気にならなくなるのかもしれません。実際に害があるというよりは、精神的なものですね。




とりあえず届いてみて数時間触ってみた感触はこんなところ。
「ページめくりボタン」+「小型軽量化」は期待以上で、良い買い物ができました。

よろしければオーナーライブラリー おすすめ本まとめ(洋書含む)+検索方法 と
Kindle 洋書オススメまとめ(とりあえず10冊)もどうぞ。

2014年11月3日月曜日

『アイネクライネナハトムジーク』 感想


このエントリーをはてなブックマークに追加


今日は移動時間があったので アイネクライネナハトムジーク を購入して読みました。

伊坂幸太郎の、この手の時間軸と登場人物が不規則に入り組んで短編が繋がるタイプの小説、たびたび「誰だっけ?」となって読んでて疲れました。Kindle だとパラパラっと前に戻って確認するのも面倒ですし、今回は特に登場人物が多くて。。。

あとがきに
僕の書く本にしては珍しく、泥棒や強盗、殺し屋や超能力、恐ろしい犯人、特徴的な人物や奇妙な設定、そういったものがほとんど出てこない本になりました。ですから、普段の僕の本に抵抗がある人にも楽しんでもらいやすくなったのでは、そうであってほしい、と期待しています。
とあったのですが、僕はどちらかといえばそういう本(『陽気なギャング』とか『マリアビートル』とか)は大好きなので、今回の本はちょっと物足りない感じも。

まぁ「伊坂作品に期待していたものとは少し違った」というだけで、つまらなかった、読むのをやめようかと思った、というわけではないです。良い時間の使い方ができました。移動中は新しい本を読むのが一番ですね。

2014年11月1日土曜日

『LIFE PACKING』感想とレビュー : 何を大切にするかを考えるきっかけに


このエントリーをはてなブックマークに追加


2014年11月の Kindle オーナーライブラリでは LIFE PACKING 未来を生きるためのモノと知恵 を無料で読みました。

写真が主体で文章は少なめ、パラパラめくる感じで軽く読了です。普段はこの手の本を手に取ることはあまりないので良い刺激になりました。

紹介されているもの自体は、著者の職業柄からかガジェット類、高級路線の音楽系の機材、調理器具がそこそこの数あったりと「自分もこの人の真似をして断捨離しよう」という参考にはあまりならないでしょう。というかならなかったです。

でも、そういう個性が出る製品を、ある個人の「必需品」として、想いとともに紹介していることにこの本の面白さがあると思います。

アンケート・多数決で決めたような必需品のリストを紹介されても多分つまらないですよね。

「こんなものあるんだ、これはいいかも」とか「これは自分にはいらないなー」などと思いながら読み終わった後に、さて自分の必需品はなんだろう?と考えを巡らせる気にさせてくれる『個』の出方がいい感じです。

自分も近いうちに必需品リストみたいなものを作りたいですね。きっと、自分の価値観の整理にも役に立つんじゃないかと思います。


その他 Kindle オーナーライブラリおすすめリスト はこちらにまとめていますので、よろしければ参考にどうぞ。