2014/12/30

[Android]テキストをクリップボードにコピーする

wada811/Android-Material-Design-Colorsサンプルアプリを作ったら
カラーコードをコピーしたいという要望があったので実装しました。

Honeycomb 未満と以上で方法が変わっていました。
クラス名は同じでパッケージが異なるだけだったので長ったらしくてちょっとアレです。

@SuppressWarnings("deprecation")
public void copyText(Context context, String text){
    if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB){
        android.text.ClipboardManager clipboard = (android.text.ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE);
        clipboard.setText(text);
    }else{
        android.content.ClipboardManager clipboard = (android.content.ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE);
        ClipData clip = ClipData.newPlainText("Copied Text", text);
        clipboard.setPrimaryClip(clip);
    }
}
Android: Copy to clipboard selected text from a TextView - Stack Overflow

[Android]タブレット対応 〜 Master / Detail パターンを実装する

みなさん、タブレット対応してますか?
僕はまだあんまりなので
とりあえず一番有名な Master / Detail パターンを実装してみることにしました。
Android Studio の Wizard に Master Detail パターンがあるので
コイツを見ておきたいと思います。

ということで Android Studio の Wizard で Master Detail パターンを生成してみると
結構わかりにくいことをしていて、
values-sw600dp/refs.xml で @layout/activity_item_list を参照すると
タブレットでは @layout/activity_item_twopane を参照されるように設定しています。
<item type="layout" name="activity_item_list">@layout/activity_item_twopane</item>
しかも、スマートフォンとタブレットの判別の仕方が微妙で
ItemListActivity.java で setContentView(R.layout.activity_item_list) すると
タブレットでは R.layout.activity_item_twopane が読み込まれて
そのレイアウトの中に R.id.item_detail_container な View があるかで
スマートフォンとタブレットのレイアウトを振り分けています。
setContentView(R.layout.activity_item_list);

if(findViewById(R.id.item_detail_container) != null){
    mTwoPane = true;
}

タブレットかどうかの判定と View の実装は分離しておきたいので
タブレットかどうかのフラグをリソースに定義することにします。
Yukiの枝折: Android:タブレットorスマートフォンのフラグをリソースに定義する
<resources>
    <bool name="isTablet">false</bool>
</resources>
<resources>
    <bool name="isTablet">true</bool>
</resources>

ItemListActivity.java ではこのフラグを参照して処理をわけます。
レイアウトもこのフラグで振り分けます。

public class ItemListActivity extends ActionBarActivity implements ItemListFragment.Callbacks{

    private boolean isTablet;

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

        isTablet = getResources().getBoolean(R.bool.isTablet);
        setContentView(isTablet ? R.layout.activity_item_twopane : R.layout.activity_item_list);

        if(isTablet){
            getSupportActionBar().setDisplayShowHomeEnabled(false);
            getSupportActionBar().setDisplayUseLogoEnabled(false);

            ItemListFragment itemListFragment = (ItemListFragment)getSupportFragmentManager().findFragmentById(R.id.item_list);
            itemListFragment.setActivateOnItemClick(true);
            onItemSelected(0);
        }
    }

    @Override
    public void onItemSelected(int position){
        if(isTablet){
            Bundle arguments = new Bundle();
            arguments.putInt(ItemDetailFragment.ARG_ITEM_ID, position);
            ItemDetailFragment fragment = new ItemDetailFragment();
            fragment.setArguments(arguments);
            getSupportFragmentManager().beginTransaction()
                .replace(R.id.item_detail_container, fragment)
                .commit();
        }else{
            Intent detailIntent = new Intent(this, ItemDetailActivity.class);
            detailIntent.putExtra(ItemDetailFragment.ARG_ITEM_ID, position);
            startActivity(detailIntent);
        }
    }
}
wada811/Android-Material-Design-Colorsサンプルアプリ
Android Studio の Wizard の Master Detail パターンを元に
上記の変更を加えたアプリになっているので参考になるかもしれません。
ソースコードはこちら↓
Android-Material-Design-Colors/sample at master · wada811/Android-Material-Design-Colors

2014年に @wada811 が読んだ技術書まとめ

2014年も終わりに近づいているので@wada811が読んだ技術書を振り返ってみようかと思います。
2013年はこちら→2013年に @wada811 が読んだ技術書まとめ | DevAchieve(11冊)
2012年はこちら→2012年に @wada811 が読んだ技術書まとめ | DevAchieve(約40冊)

2014/05/07 [書評]ノンデザイナーズ・デザインブック | DevAchieve

色々な所で読んでおくべきデザイン本として紹介されている本です。
4つの基本原則も良いけど、個人的にはジョシュア・ツリーの悟りの話が好きです。
一度名前を呼ぶことができれば、
あなたはそれを意識し、それを支配し、それを所有し、
それをコントロールできるようになる。
名前大事ですね。

1年で書籍1冊の書評を書いた!

全然読んでないけど大丈夫か、という感じだ…。
Linuxエンジニア養成読本は職場にあったので読んだけど
【改訂新版】Linuxエンジニア養成読本が出ていた。
他の事に時間使っていて本読んでない…。

2014年に @wada811 が積んだ技術書まとめ

増補改訂版Java言語で学ぶデザインパターン入門
増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編
JUnit実践入門 体系的に学ぶユニットテストの技法
レガシーコード改善ガイド
Gradle徹底入門 次世代ビルドツールによる自動化基盤の構築

重い本ばっかり積んでる…。
手を動かしつつ、ちゃんと本も読んでいきたい。
2014/12/09

Scala + sbt + Sublime Text で 始める Play2

インストール

Homebrew で scala, sbt, play をインストール

$ brew install scala
$ brew install sbt
$ brew install play

$ scala -version
Scala code runner version 2.11.1 -- Copyright 2002-2013, LAMP/EPFL
$ sbt --version
sbt launcher version 0.13.5
$ play
[info] Loading global plugins from /Users/ssl001/.sbt/0.13/plugins
[info] Loading project definition from /Users/ssl001/Documents/workspace_play/hello-play/project
[info] Set current project to hello-play (in build file:/Users/ssl001/Documents/workspace_play/hello-play/)
           _
     _ __ | | __ _ _  _
    | '_ \| |/ _' | || |
    |  __/|_|\____|\__ /
    |_|            |__/

play 2.2.3 built with Scala 2.10.3 (running Java 1.6.0_65), http://www.playframework.com

Sublime Text 3 で sublimescala/sublime-ensime をインストール

Cmd+Shift+P -> Package Control: Install Package

Ensime

ENSIME Server (ensime/ensime-src)をインストール

Download releases

$ cd ~/Library/Application Support/Sublime Text 3/Packages/Ensime
$ mkdir Server
$ mv ~/Downloads/ensime_2.10.0-0.9.8.9/* Server

sbt に Ensime プラグイン(ensime/ensime-sbt-cmd)を追加

~/.sbt/0.13/plugins/plugin.sbt に以下を追加(ファイルがなければ作成)

addSbtPlugin("org.ensime" % "ensime-sbt-cmd" % "0.1.2")

sbtコマンドによる.ensimeファイルの作成

$ play new hello-play
$ cd hello-play
$ sbt
> ensime generate

以降、Sublime Text で Cmd+Shift+P -> Ensime: startup で補完が効くようになる


Sublime Text 3 で guillaumebort/play2-sublimetext2 をインストール

Cmd+Shift+P -> Package Control: Install Package

Play 2.0


Sublime Text 3 で jarhart/SublimeSBT をインストール

Cmd+Shift+P -> Package Control: Install Package

SublimeSBT


実行

Cmd+Shift+P -> SBT: Run


参考

最強!?Ensimeを利用したSublime Text3のScala開発環境(playでもliftでも) - Qiita
PANAXIOM — Sublime Text 2 with SBT and Play 2


感想

sublime-ensime と SublimeSBT の開発が止まっていてな…
快適とは言い難いので他の IDE を使った方が良いと思うんだ。

Ruby on Rails 開発環境構築 in Mac OS X Mavericks

久しぶりに Ruby on Rails で開発することになり、開発環境を再構築しました。
関連: Mac OSX10.8でrbenvを使ってRuby1.9とRails4.0をインストールする

HomeBrew を使用するので MacPorts の人は読み替えてください。
また、僕は zsh を使っているので bash の人も読み替えてください。
HomeBrew や zsh をインストールしようという人は以下の事が参考になるかと思います。
Homebrew + Homebrew-caskで Mac の開発環境を構築する | DevAchieve
MacにHomeBrewとzshを導入してみた | DevAchieve

rbenv と ruby-build のインストール

brew install rbenv
brew install ruby-build
# rbenv の初期化
eval "$(rbenv init -)"
source .zshrc
eval "$(rbenv init -)" で rbenv へのパスを通すのとコマンドの補完や引数の処理、rehash をしています。
echo "$(rbenv init -)" で処理内容を確認できます。

rehashとは、
rbenv -h で確認すると以下のように書かれています。
rehash: Rehash rbenv shims (run this after installing binaries)
gem とか入れたらやれってことでいいのだと思います。
gem を入れまくると重くなる?のか rehash が重くなることがあるらしいです。
猫好きモバイルアプリケーション開発者記録 rbenv init が重い問題に対する対処法
gem を(アン)インストールしたら自動で rehash してくれる gem があるのでこれを入れておくと良いかも。
ryansouza/rbenv-rehash - github

依存パッケージのインストール

brew install readline
brew link readline
brew install openssl
brew link openssl

Ruby をインストールする

CONFIGURE_OPTS="--with-readline-dir=/usr/local --with-openssl-dir=/usr/local" rbenv install 2.1.5
CONFIGURE_OPTS="--with-readline-dir=/usr/local --with-openssl-dir=/usr/local" rbenv install まで
入力して Tab で補完すればバージョンを指定できます。

rehash して使用するバージョンを指定する

rbenv rehash
rbenv global 2.1.5

バージョンを確認する

rbenv version
2.1.5 (set by /Users/wada/.rbenv/version)
ruby -v
ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-darwin13.0]

gem 管理システム bundler のインストール

Ruby のライブラリである gem をパッケージ管理できる Bundler をインストールします。
gem install bundler
基本的にシステムにgemをインストールせず、
bundler でプロジェクトごとに gem をインストールするのが環境をクリーンに保てて良いとされています。
Rails も bundler でインストールしたいと思います。

Rails をインストールする

% mkdir project
% cd project
% bundle init
Writing new Gemfile to /path/to/project/Gemfile
Rails プロジェクト作成する際にbundle initして Gemfile を作成します。
# A sample Gemfile
source "https://rubygems.org"

# gem "rails"
4行目のコメントアウトを外して以下のコマンドを実行すれば rails をインストールできます。
bundle install --path vendor/bundle
他の gem を追加するときも上のコマンドでプロジェクト内にインストールします。

bundle exec rails new .
bundler でインストールした gem は
プロジェクトのディレクトリで bundle exec を付けてコマンドを実行する必要があります。

参考
Ruby - システムのgemにrailsをインストールせずrails newする - Qiita

libiconv is missing. で gem install nokogiri が失敗する場合

Homebrew で libiconv とその依存パッケージをインストールします。
### For nokogiri
brew tap homebrew/dupes
brew install libiconv
brew install libxml2
brew install libxslt
brew link --force libxml2
brew link --force libxslt

nokogiri をインストールします。
gem install nokogiri -- --use-system-libraries
Bundler を使用している場合は以下のコマンドを実行します。
bundle config build.nokogiri --use-system-libraries
bundle install

参考
Ruby - bundle install でnokogiriをインストールしろと言われる - Qiita
OSX - Mac OS X Mavericksで”gem i nokogiri”失敗の解決方法 - Qiita
2014/12/05

Homebrew + Homebrew-caskで Mac の開発環境を構築する

Brewfile + Homebrew + Homebrew-caskで Mac の環境構築をするの更新版です。

パッケージ管理システム「Homebrew」とその拡張版「Homebrew Cask」を使って
Mac の開発環境を構築します。
Brewfile と brew bundle コマンドはサポートされなくなったので
brew-update コマンドを作成して、brew-update でインストール/アップデートを行うようにします。

Homebrew を導入する

Command Line Tools for Xcode をインストールする

Xcode をインストールする
まずは AppStore から Xcode をインストールして下さい。
Command Line Tools for Xcode をインストールする
[ Xcode > Open Developer Tool > More Developer Tools... ] をクリックすると
Downloads for Apple Developer ページが開くので
Command Line Tools for Xcode をダウンロードしてインストールします。
参考: MavericksでCommand Line Tools for Xcodeをインストールする - Qiita

Homebrew をインストールする

Terminal で以下のコマンドを実行します。
ruby -e "$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)"
参考: Homebrew — The missing package manager for OS X
Homebrew が正常にインストールできたか確認する
以下のコマンドを実行してエラーがあれば対処、再度実行します。
brew doctor

Homebrew-cask を導入する

Homebrew-cask をフォーミュラに追加する

Formula(フォーミュラ)は Homebrew で管理されているパッケージのことです。
Homebrew-cask は Homebrew では管理されていないので自分で追加する必要があります。
以下のコマンドで Homebrew-cask を Formula に追加することができます。
brew tap caskroom/cask
参考: これは便利!Homebrewに追加されたtapコマンドはリポジトリを追加して簡単にフォーミュラを増やせる | Macとかの雑記帳

Homebrew-cask をインストールする

Homebrew-cask を追加したら Homebrew の以下のコマンドでインストールします。
brew install brew-cask

おまけ: Homebrew-cask の使い方

アプリケーションを検索する
brew cask search chrome
google-chrome
アプリケーションをインストールする
brew cask install google-chrome
アプリケーションの実体は /opt/homebrew-cask/Caskroom に入り、
~/Applications にシンボリックリンクが作成されます。
それぞれのパスは以下のコマンドオプションで変更することができます。
--caskroom=/my/path, --appdir=/my/path
参考: homebrew-caskが良さそうなので導入してみた - About Digital
アプリケーションをアンインストールする
brew cask uninstall google-chrome
アプリケーションを更新する
brew cask update
古いアプリケーションを削除する
brew cask cleanup
その他の使い方
homebrew-cask/USAGE.md at master · caskroom/homebrew-cask

brew-update コマンドを作成して Homebrew パッケージを管理する

Brewfile を作成する

以下のようなファイルを作成します。
Install Packages と Install Applications の欄は自由にインストールしたいものを記述して下さい。
#!

# Update Homebrew
brew update

# Upgrade Formulas
brew upgrade

# Install HomebrewCask
brew tap caskroom/cask
brew install brew-cask

####################
# Install Packages #
####################

brew install zsh

########################
# Install Applications #
########################

brew cask install google-chrome

# Remove outdated versions
brew cleanup
brew cask cleanup
参考までに僕の brew-update にリンクをはっておきます。
dotfiles/brew-update at master · wada811/dotfiles

brew-update でインストール/アップデートする

パスの通った場所に brew-update を置いて、実行します。
brew bundle

さいごに

これで brew-update コマンドで Homebrew パッケージをインストール/アップデートできるようになりました。
2014/11/28

exif-js と ios-imagefile-megapixel でスマホからの画像アップロードのプレビュー表示に対応する

画像アップロードするときにプレビュー欲しいよね〜
プレビューするとたまに画像が回転されてるよね〜
スマホだと画像選択するアプリで EXIF 情報を見て回転させて表示しているから
プレビューが横向きとかになってておかしいと思われがちだよね〜

そんな問題を解決するのが exif-js です。

exif-js

jseidelin/exif-js

使い方は https://github.com/jseidelin/exif-js/blob/master/example/index.html を見てください。
例えば、アップロードされたファイルに対して Orientation を取得する場合は以下のように記述します。
$('#jsFileInput').on("change",function(){
    var file = $(this).prop("files")[0];
    EXIF.getData(file, function(){
        var orientation = file.exifdata.Orientation;
    });
});
EXIF 情報を取得するメソッドが提供されていて、
EXIF.getTag(file, "Orientation") で Orientation を取得できるようだけど
取得できないこともあるみたい?で file.exifdata.Orientation で直接取得してしまっています。
よくわからないので動作確認してもらえればと思います。

さて Orientation は取れましたが、どうやって画像を回転しましょう。
そんな時は megapix-image.js が便利でした。

megapix-image.js

stomita/ios-imagefile-megapixel

iOS とか入っていますが気にしなくても大丈夫です。
megapix-image.js は大きな画像を表示する際に便利なライブラリですが、
回転もやってくれるので表示は全部任せてしまいましょう。
スマホからだと数 MB 〜 十数 MB の画像が平気でアップロードされるので
普通にプレビューさせようとするとブラウザごとメモリ不足で落ちることがあるのでが
megapix-image.js を使えば、そういったことが少なくなります。
(※直接は見たことないが、画面が真っ暗になるなどの報告を受けたことがあるので銀の弾丸ではない?)

さきほどの exif-js と組み合わせて使うには以下のようにします。
$('#jsFileInput').on("change",function(){
    var file = $(this).prop("files")[0];
    EXIF.getData(file, function(){
        var orientation = file.exifdata.Orientation;
        var mpImg = new MegaPixImage(file);
        mpImg.render($("#jsPreviewImage")[0], { orientation: orientation });
    });
});

まとめ

これで回転している大きな画像をプレビュー表示することができるようになったと思います。
まれに表示できないこともあるらしいので、もっと良い方法があれば教えて下さい。
2014/11/27

jquery.transform.js で CSS3 の transform をアニメーションさせる

要素を CSS3 の transform で移動・回転・拡大縮小などさせつつ、
アニメーションもさせたい場合に便利な jQuery プラグインを紹介します。

jquery.transform.js

eenox/jQuery-3D-transform

このプラグインは指定の面倒な transform 属性を簡単に指定できるようにします。
transform 属性については transform-CSS3リファレンス を参照して下さい。
transform 属性は各種変換関数をスペース区切りで指定しなくてはならないので面倒でしたが、
普通の CSS の属性を扱うように transform 属性の関数を扱うことができます。
$(".transform").css({
    "top": top1,
    "left": left1,
    "opacity": "0",
    "rotate": rotate1 + "deg",
    "scale": "2, 2"
  }).animate({
    "top": top2,
    "left": left2,
    "opacity": "1",
    "rotate": rotate2 + "deg",
    "scale": "1, 1"
  }, 2000, 'swing');

transform 属性を上記のように簡単に扱えるというだけで
アニメーションさせなくても使いたい jQuery プラグインです。

参考

jQueryアニメーションでCSS3のTransformを操作して拡大縮小/回転などを実装させるいろいろ | BlackFlag
2014/11/19

さくらのVPSにスタードメインで取得した独自ドメインを設定してみた

さくらののVPSにスタードメインで取得した独自ドメインの設定してみました。
個人でVPSを借りて遊ぶのには hosts ファイル書き換えで十分でしたが、
サイトを他の人にも見せることになったので
IP でアクセスしてもらうのもカッコがつかないなぁ、ということでドメインを取得しました。
安くて Twitter などでも評判が良く、Whois情報公開代行も無料で行ってくれるスタードメインにしました。

さくらのVPS

コントロールパネルにログイン

さくらのVPSコントロールパネルにログインし、左メニューにある「ネームサーバ登録」をクリックします。

「さくらインターネットの会員メニュー」ログインページに移動します。

「会員ID」「パスワード」を入力し、「ログイン(認証)」をクリックします。

新しいネームサーバの登録

ページの下の方に「新しいネームサーバの登録」があります。

設定する「ドメイン名」を入力し、「送信する」をクリックします。

ゾーンの追加が実施されるので、ゾーンの初期設定をするために「ドメインメニューへ」をクリックします。

ドメイン名の一覧が表示されるので、設定するドメインの「ゾーン編集」をクリックします。

簡単設定の項目の「サーバのIPアドレス」にVPSのIPアドレスを入力し、「送信する」をクリックします。

さくらインターネットのネームサーバ

設定したドメインの情報が表示されます。

先ほど入力したIPアドレスは、「Aレコード」に追加されています。
ネームサーバーの情報(タイプが NS のもの)は、
スタードメイン側に登録するのでページは閉じないでおきましょう。

スタードメイン

「さくらのネームサーバ」を利用するように設定したので、
スタードメイン側から「さくらのネームサーバ」を参照するように設定します。

スタードメインを運営している NetOwl の管理ページから
「スタードメイン管理」の「管理ドメイン一覧」を開いて「ドメイン管理ツール」をクリックします。

「ネームサーバの確認・変更」をクリックします。

ネームサーバにさくらのVPSのネームサーバーの情報を入力して保存します。

ドメイン設定完了

数時間くらいすれば名前解決されてドメインでアクセスすれば
さくらのVPS側にアクセスできるようになります。

DNS逆引きレコード変更

DNS逆引きは IP アドレスからドメイン名を取得することで、
DNS逆引きレコードが設定してあれば取得できる。
DNS逆引きレコードが設定してあると
どこの馬の骨かもわからないサーバではない(※自称)と宣言できるので
設定しておくといいかもね(※ただし必須ではない)というくらいのノリっぽいです。

以下を参考に設定しました。
DNS逆引きレコードを変更する|さくらのVPS|さくらインターネット公式サポートサイト

設定すると nslookup IP.ADD.RE.SS で設定したドメイン名が取得できます。

参考

さくらのVPSに独自ドメインを設定してみた - VPS比較 - Webkaru
2014/10/29

[SublimeText] Sublime Text 3 の検索で日本語をエンターすると文字が消えるのを防ぐ

Sublime Text で地味に困るのが検索窓に日本語を入力した時。
エンターを押した瞬間、入力した文字が消えるので全然検索ができない。
ずっと困ったまま放置していたんだけど
たまたま Twitter で解決方法が流れてきたのでメモ。

参考

Sublime Textの検索窓に日本語を入力する方法
Sublime Text 3 で日本語を検索したとき文字が消える不具合を直す - MEMOGRAPHIX

キーマップを変更してエンターで動作しないようにする

参考元ではコメントアウトされていましたが使えないのは面倒なので
ctrl + enter に全て置き換えました。
// Find panel key bindings
{ "keys": ["ctrl+enter"], "command": "find_next", "context":
    [{"key": "panel", "operand": "find"}, {"key": "panel_has_focus"}]
},
{ "keys": ["shift+enter"], "command": "find_prev", "context":
    [{"key": "panel", "operand": "find"}, {"key": "panel_has_focus"}]
},
{ "keys": ["alt+enter"], "command": "find_all", "args": {"close_panel": true},
     "context": [{"key": "panel", "operand": "find"}, {"key": "panel_has_focus"}]
},

// Replace panel key bindings
{ "keys": ["ctrl+enter"], "command": "find_next", "context":
    [{"key": "panel", "operand": "replace"}, {"key": "panel_has_focus"}]
},
{ "keys": ["shift+enter"], "command": "find_prev", "context":
    [{"key": "panel", "operand": "replace"}, {"key": "panel_has_focus"}]
},
{ "keys": ["alt+enter"], "command": "find_all", "args": {"close_panel": true},
    "context": [{"key": "panel", "operand": "replace"}, {"key": "panel_has_focus"}]
},
{ "keys": ["ctrl+alt+enter"], "command": "replace_all", "args": {"close_panel": true},
     "context": [{"key": "panel", "operand": "replace"}, {"key": "panel_has_focus"}]
},

// Incremental find panel key bindings
{ "keys": ["ctrl+enter"], "command": "hide_panel", "context":
    [{"key": "panel", "operand": "incremental_find"}, {"key": "panel_has_focus"}]
},
{ "keys": ["shift+enter"], "command": "find_prev", "context":
    [{"key": "panel", "operand": "incremental_find"}, {"key": "panel_has_focus"}]
},
{ "keys": ["alt+enter"], "command": "find_all", "args": {"close_panel": true},
    "context": [{"key": "panel", "operand": "incremental_find"}, {"key": "panel_has_focus"}]
},
2014/10/24

AndroidStudio で APK を特定のフォルダにコピーする Gradle の設定

AndroidStudio で APK のファイル名を変更する Gradle の設定 | DevAchieve
APK をリネームしましたが、
出力されるフォルダが build/outputs/apk/ なので
変更したいことがあるかと思います。
Gradle の Task を定義してあげれば
任意のフォルダにコピーする処理を実行することができます。

Gradle で Signed APK とProGuard 関連ファイルをコピーするタスクを設定する


Add move apk task and move proguard task · c7cdd90 · wada811/Android-Material-Design-Colors

applicationVariants.all { variant ->
    if (variant.buildType.name.equals("release")) {
        variant.outputs.each { output ->
            if (output.outputFile != null && output.outputFile.name.endsWith('.apk')) {
                // Rename APK
                def applicationId = defaultConfig.applicationId
                def versionCode = defaultConfig.versionCode
                def versionName = defaultConfig.versionName
                def date = new java.text.SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date())
                def newName = "${applicationId}_r${versionCode}_v${versionName}_${date}.apk"

                def publish = project.tasks.create("publishAll")

                // Move and Rename APK
                def task = project.tasks.create("publish${variant.name.capitalize()}Apk", Copy)
                task.from(output.outputFile)
                task.rename(output.outputFile.name, newName)
                task.into(file("${variant.name}/apk").getAbsolutePath())

                task.dependsOn variant.assemble
                publish.dependsOn task

                // Move ProGuard
                if (variant.buildType.runProguard) {
                    def copyTask = project.tasks.create("copy${variant.name.capitalize()}MappingText", Copy)
                    def buildTypeName = variant.buildType.name
                    copyTask.from(file("build/outputs/proguard/${buildTypeName}").path)
                    copyTask.into(file("${variant.name}/proguard").getAbsolutePath())

                    copyTask.dependsOn variant.assemble
                    task.dependsOn copyTask
                }
            }
        }
    }
}

実行

./gradlew publishAll
app/release/apk/ に Signed APK が、
app/release/proguard/ に ProGuard 関連ファイルがコピーされます。

参考

AndroidStudioでAPKを作ったあとに特定のディレクトリにAPKをコピーする - Qiita
2014/10/23

AndroidStudio で APK のファイル名を変更する Gradle の設定

Android Studio で Sigined APK を生成するには
ツールバーの [ Build > Generate Signed APK... ] から
ガイダンスに従えば生成できます。
しかし、GUI からではファイル名は app-release.apk などになるので
変更したい場合などは Gradle の設定が必要になります。

Gradle で Signed APK のファイル名を設定する

Generate signed APK has been named by program · bc41551 · wada811/Android-Material-Design-Colors

apply plugin: 'com.android.application'

android {
    compileSdkVersion 21
    buildToolsVersion "21.0.1"

    defaultConfig {
        applicationId "at.wada811.android.material.design.colors.sample"
        minSdkVersion 8
        targetSdkVersion 21
        versionCode 2
        versionName "1.1.0"
    }
    signingConfigs {
        release
    }
    buildTypes {
        release {
            runProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }

    applicationVariants.all { variant ->
        if (variant.buildType.name.equals("release")) {
            def file = variant.outputFile
            def applicationId = defaultConfig.applicationId
            def versionCode = defaultConfig.versionCode
            def versionName = defaultConfig.versionName
            def date = new java.text.SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date())
            def newName = "${applicationId}_r${versionCode}_v${versionName}_${date}.apk"
            variant.outputFile = new File(file.parent, newName)
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:21+'
    compile project(':library')
}

./gradlew assembleReleaseすると
at.wada811.android.material.design.colors.sample_r2_v1.1.0_20141022_173737.apk
のようなファイル名で app/build/outputs に生成されます。

参考

gradle - AndroidStudioでAPKのファイル名にバージョン番号などを入れる設定 - Qiita

追記: Android Gradle Plugin 0.13, Gradle 2.1 で outputFile が deprecated になっている

WARNING [Project: :sample] variant.getOutputFile() is deprecated. Call it on one of variant.getOutputs() instead. が表示されるので書き直しました。

applicationVariants.all { variant ->
    if (variant.buildType.name.equals("release")) {
        variant.outputs.each { output ->
            System.println("* output.outputFile.name : ${output.outputFile.name}")
            if (output.outputFile != null && output.outputFile.name.endsWith('.apk')) {
                // Rename APK
                def applicationId = defaultConfig.applicationId
                def versionCode = defaultConfig.versionCode
                def versionName = defaultConfig.versionName
                def date = new java.text.SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date())
                def newName = "${applicationId}_r${versionCode}_v${versionName}_${date}.apk"
                output.outputFile = new File(output.outputFile.parent, newName)
            }
        }
    }
}

outputs というから複数あるのかとおもいきや apk しかありませんでした。
無駄にネストが深くなる…。

参考

android - Gradle warning: variant.getOutputFile() and variant.setOutputFile() are deprecated - Stack Overflow
2014/10/22

AndroidStudio で APK の署名の設定を gradle.properties に記述する

Android Studio で Sigined APK を生成するには
ツールバーの [ Build > Generate Signed APK... ] から
ウィザードに従えば生成できます。
しかし、CUI から Signed APK を生成したいことがあるかと思います。
また、そのプロジェクトを public リポジトリで管理している場合に
署名に関する設定を公開しないようにするにはどうすればいいのかという問題もあります。

以下に示す方法では、証明に関する設定を gradle.properties に記述し、
.gitignore で gradle.properties を公開しないようにすることで
署名に関する情報を秘密にしておくおことが可能です。

Generate signed APK has been named by program · bc41551 · wada811/Android-Material-Design-Colors

apply plugin: 'com.android.application'

android {
    compileSdkVersion 21
    buildToolsVersion "21.0.1"

    defaultConfig {
        applicationId "at.wada811.android.material.design.colors.sample"
        minSdkVersion 8
        targetSdkVersion 21
        versionCode 2
        versionName "1.1.0"
    }
    signingConfigs {
        release
    }
    buildTypes {
        release {
            runProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }
}

...

if (project.hasProperty('storeFile')) {
    android.signingConfigs.release.storeFile = new File(System.getenv('HOME'), storeFile)
}
if (project.hasProperty('storePassword')) {
    android.signingConfigs.release.storePassword = storePassword
}
if (project.hasProperty('keyAlias')) {
    android.signingConfigs.release.keyAlias = keyAlias
}
if (project.hasProperty('keyPassword')) {
    android.signingConfigs.release.keyPassword = keyPassword
}

storeFile=/path/to/your.keystore
storePassword=keystorePass
keyAlias=appAlias
keyPassword=appPass

gradle.properties

new File(System.getenv('HOME'), storeFile) がキモで、
file(storeFile) にするとプロジェクトの相対パスで認識されるから
プロジェクトに keystore を含めるか、無理やり相対パスからたどるかになるんだけど
前者はプロジェクトごとに keystore を入れなければならなくて一元管理できないし、
後者は環境によってパスが変わりうるから微妙になります。
その点、new File(System.getenv('HOME'), storeFile) は $HOME からの絶対パスになるので統一しやすいです。

参考

AndroidStudio - Android Studio(Gradle)でapkファイルを作成する時にstorePassword/keyAlias/keyPasswordの指定方法をいくつか検証してみた。 - Qiita
2014/10/15

[Android]ActionBar のタイトルの文字色を変更する

ActionBar のタイトルの文字色を変えるメソッドはないので
あまり変えてやろうと思うこともないかと思いますが
気まぐれに文字色を変えたくなった時のためにメモしておきます。

Theme でActionBar のタイトルの文字色を変更する

普通の方法ですが、動的に変更することができません。
<resources>

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="actionBarStyle">@style/AppTheme.ActionBarStyle</item>
    </style>

    <style name="AppTheme.ActionBarStyle" parent="Widget.AppCompat.Light.ActionBar">
        <item name="titleTextStyle">@style/AppTheme.ActionBar.TitleTextStyle</item>
    </style>

    <style name="AppTheme.ActionBar.TitleTextStyle" parent="TextAppearance.AppCompat.Widget.ActionBar.Title">
        <item name="android:textColor">@color/red</item>
    </style>

</resources>
<resources>

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="android:actionBarStyle">@style/AppTheme.ActionBarStyle</item>
    </style>

    <style name="AppTheme.ActionBarStyle" parent="Widget.AppCompat.ActionBar">
        <item name="android:titleTextStyle">@style/AppTheme.ActionBar.TitleTextStyle</item>
    </style>

</resources>

Programmatically

String title = getResources().getString(R.string.title);
int titleColor = getResources().getColor(R.color.titleColor);
String htmlColor = String.format(Locale.US, "#%06X", (0xFFFFFF & Color.argb(0, Color.red(titleColor), Color.green(titleColor), Color.blue(titleColor))));
String titleHtml = "<font color=\"" + htmlColor +  "\">" + title + "</font>";
getSupportActionBar().setTitle(Html.fromHtml(titleHtml));

Html とかウケる。こんな方法があったとは。

参考
android - ActionBar text color - Stack Overflow
2014/10/05

git diff で JSON を整形して比較する

Git でJSON を整形して比較できるらしいのでメモ。
JSON だけでなく Word や ODT、 Jpeg や PNG なども
実ファイルを比較時に変換させることができるみたいです。

参考
Git - Git Attributes
Git - gitattributes Documentation

設定

*.json diff=json
[diff "json"]
 textconv = "jq ."
2014/09/22

[zsh]peco で git をもっと便利にする

peco を導入しました。すごく便利です。
導入についてはコチラ↓
peco を導入した | DevAchieve

peco で色々フィルタリングして選択できるのは
ファイル、ハッシュ、ブランチを選択する事が多い Git と相性が良さそうです。

peco script を色々作るとエイリアスとか大変っぽいので
zsh のグローバルエイリアスを使ってみました。

編集されたファイルをフィルタリングする

function peco-git-changed-files(){
    git status --short | peco | awk '{print $2}'
}
alias -g F='$(peco-git-changed-files)'

使い方

git add F
Ctrl + Space で複数ファイル選択して add も可能です。

コミットのハッシュをフィルタリングする

function peco-git-hash(){
    # git log --oneline --branches | peco | awk '{print $1}'
    git lg | peco | sed -e "s/^[\*\|][ |\\\/\*]*//g" | awk '{ print $1 }'
}
alias -g F='$(peco-git-hash)'
[alias]
 lg = log --all --graph --decorate --oneline
git lg (log graph) は僕の git log を置き換えるコマンドです。
以下のようにコミットが確認できるので履歴確認、ブランチ確認、マージする際に便利なコマンドです。

peco ではこの色付けがなくなってしまうので残念です。頑張って保持して欲しいところ。

使い方

git diff F とか git cherry-pick F とか。
おおよそコミットのハッシュを指定するところ全てで使えるんじゃないですかね?
git diff で複数選択とか便利です。

ブランチをフィルタリングする

function peco-git-branch(){
    git branch | peco | sed -e "s/^\*[ ]*//g"
}
alias -g B='$(peco-git-branch)'

使い方

git checkout B とか
git merge B とか
git diff B など。

参考

pecoでgit向けの汎用的なフィルターを2つ作成してみた - Qiita
私のpeco活用事例 - k0kubun's blog

[zsh]cdr: 開いたディレクトリの履歴からディレクトリを開く

peco を導入した | DevAchieve で実は cdr を導入していました。
zsh に組み込まれているコマンドに cdr という
開いたディレクトリの履歴からディレクトリを開くコマンドがあります。
~/.zshrc で cdr を有効にしてあげれば使えるようになります。

$HOME/.cache/shell/ は用意しないといけないので
mkdir -p $HOME/.cache/shell/ しておいて下さい。
# cdr, add-zsh-hook を有効にする
autoload -Uz chpwd_recent_dirs cdr add-zsh-hook
add-zsh-hook chpwd chpwd_recent_dirs

# cdr の設定
zstyle ':completion:*' recent-dirs-insert both
zstyle ':chpwd:*' recent-dirs-max 500
zstyle ':chpwd:*' recent-dirs-default true
zstyle ':chpwd:*' recent-dirs-file "$HOME/.cache/shell/chpwd-recent-dirs"
zstyle ':chpwd:*' recent-dirs-pushd true
設定については以下を参考にして下さい。
zsh: 26. User Contributions
zshでcdの履歴管理に標準添付のcdrを使う - @znz blog

使い方

確認
cdr -l
1 ~/.zsh
2 ~/.zsh/peco-scripts
3 ~/Documents/workspace
4 ~/dotfiles

開く
cdr 3

こんな感じですね。
これだけじゃイチイチ開く前に確認しないといけないので peco で選択したいですね。

function peco-cdr() {
    local selected_dir=$(cdr -l | awk '{ print $2 }' | peco)
    if [ -n "$selected_dir" ]; then
        BUFFER="cd ${selected_dir}"
        zle accept-line
    fi
    zle clear-screen
}
zle -N peco-cdr
2行目で何をしているか。
cdr -l
1 ~/.zsh
2 ~/.zsh/peco-scripts
3 ~/Documents/workspace
4 ~/dotfiles
↑を awk でディレクトリ名だけ抜き出して
cdr -l | awk '{ print $2 }'
~/.zsh
~/.zsh/peco-scripts
~/Documents/workspace
~/dotfiles
peco で選択して cd する。
シンプルなコマンドの組み合わせで凄く便利に!
これなら自分でも何か作れそうでワクワクします。

peco を導入した

コマンドラインの行選択ツール peco を導入しました。
peco - Simplistic Interactive Filtering Tool
どんなものかわからないと思うので上のリンクから Demo の gif 画像を見て下さい。
なんとなく便利そうな雰囲気がしますよね?

インストール

それではインストールしてみましょう。Mac では Homebrew で以下の通り。
brew tap peco/peco
brew install peco
それ以外の場合は上のリンクからインストール方法を確認して下さい。
Go で作られているのでインストールが簡単らしいです。

peco のカスタマイズ

peco はパイプでコマンドの出力を行選択して、パイプで次のコマンドに渡すコマンドラインツールなわけだけど
ある程度頻繁に使いそうなものについてはスクリプトファイルを書いておいたほうが便利ですね。
こういうものはどんどん拡張したくなるのでスクリプトファイルの保存場所を決めておきます。
~/.zshrc
~/.zsh/peco-scripts/ の読み込みやキーバインドの設定
~/.zsh/peco-scripts/
peco のスクリプトファイルの保存場所

# load peco scripts (require cdr)
for f (~/.zsh/peco-scripts/*) source "${f}"
% tree peco-scripts
peco-scripts
├── peco-cdr.zsh
└── peco-select-history.zsh

peco-cdr.zsh: 開いたディレクトリの履歴からディレクトリを選択する

[zsh]cdr: 開いたディレクトリの履歴からディレクトリを開く | DevAchieve
function peco-cdr() {
    local selected_dir=$(cdr -l | awk '{ print $2 }' | peco)
    if [ -n "$selected_dir" ]; then
        BUFFER="cd ${selected_dir}"
        zle accept-line
    fi
    zle clear-screen
}
zle -N peco-cdr
# 履歴から cd
bindkey '^@' peco-cdr

peco-select-history.zsh: コマンドの履歴からコマンドを選択する

function peco-select-history() {
    local tac
    if which tac > /dev/null; then
        tac="tac"
    else
        tac="tail -r"
    fi
    BUFFER=$(history -n 1 | eval $tac | peco --query "$LBUFFER")
    CURSOR=$#BUFFER
    # zle clear-screen
}
zle -N peco-select-history
# 履歴からコマンド選択
bindkey '^r' peco-select-history

追記: 2014/12/10
コマンド履歴の重複を削除して peco で選択できるようにしました。
[peco]peco-select-history.zsh で表示されるコマンド履歴の重複を削除する - Qiita

まとめ

使ってみるとわかりますが、すごく便利です!

参考: pecoを使い始めた - $shibayu36->blog;
2014/09/21

iTerm2 を導入した

Mac をようやく Marvericks にアップデートできたので
ついでに便利そうなツール類を入れまくる活動の第一弾です。
Mac の Terminal より便利で色々設定できる iTerm2 というものを入れてみました。

iTerm2 って何よ?どう便利なんよ?

って言われると僕も困ってしまう。↓この辺を読むと便利そうって思うかもしれない。
iTerm2の使い方まとめてみた - uittie::blog
事故らないために普段守っているターミナルの運用ポリシ(Mac + iTerm2) | TechRacho

僕は色々設定できてカラースキーマも設定できればそれで良いかもしれない。

カラースキーマ

iTerm 2 で使えるカラースキーム、Japanesque を作った - Sexual Knowing
最強カラースキームと言えば Solarized だよね! | blog.remora.cx
このあたりが人気っぽい。

Iterm Themes - Color Schemes and Themes for Iterm2
あとはコレとか。
僕は Monokai が好きなのでコレで設定した。

設定を GitHub の dotfiles リポジトリで複数端末一括管理したい

ハイライト部分がキモ。初回実行時はインストールされてないからエラーでそう。その時考える。
#! /bin/bash
ln -s ~/dotfiles/Brewfile ~/Brewfile
ln -s ~/dotfiles/Settings.terminal ~/Settings.terminal
ln -s ~/dotfiles/.gitconfig ~/.gitconfig
ln -s ~/dotfiles/.gitignore ~/.gitignore
ln -s ~/dotfiles/.sqliterc ~/.sqliterc
ln -s ~/dotfiles/.vim/ ~/.vim
ln -s ~/dotfiles/.vimrc ~/.vimrc
ln -s ~/dotfiles/.zsh/ ~/.zsh
ln -s ~/dotfiles/.zshrc ~/.zshrc
rm ~/Library/Preferences/com.googlecode.iterm2.plist
ln -s ~/dotfiles/.iterm2/com.googlecode.iterm2.plist ~/Library/Preferences/com.googlecode.iterm2.plist
ln -s ~/dotfiles/.iterm2/ ~/.iterm2
Mac のソフトウェアの設定はキャッシュされているので設定を変えたら iTerm2 を再起動しないと設定ファイルには反映されないので注意。

おまけ

vim,iTerm2で★とか■とか※とかがずれるのでなんとかした - Qiita
"tmux-powerlineのステータスバーがおかしくなる"とかなんとか。
tmux も便利そうなので入れたいが、 iTerm2 に統合されているという話もあり良くわからない。
その件についてはまた今度。
2014/09/20

Mac を Marvericks にアップデートした時にやったこと

App Store から Marvericks にアップデート


一時停止になったままアップデートされない。
LaunchPad から一時停止しているやつを削除する。
バージョンアップできた。

Marvericks で問題が起きてないかチェック

Terminal を開いたらすぐに閉じてしまうので設定をいじったら以下の問題が…。
Homebrew-Cask がもしかしたら状態が良くなさそうだけど確認できないので一旦放置。

おわり

Nexus7(2013) を KitKat にバージョンアップするための長い道のり

Android 4.4 KitKat が2013年の10月くらいに発表されてな…
Android 4.4 | Platforms | Android Developers

11月の初めに @tao_gaku さんが外部記憶領域とパーミッションまわりの変更について記事を書いてくれてな…


Android 4.3 と Android 4.4 の挙動の変化を見るための
サンプルプロジェクトを作って記事を書こうと決意してな…

手持ちの Nexus7(2013) の一台しかないから Android 4.3 の挙動を確認したら
Android 4.4 にバージョンアップして挙動を確認して比較する(ただしダウングレードはできない)という
めんどくさそうな感じで放置してな…(今は焼き方を覚えたのでできる)

そんな感じで放置していたら会社で Nexus7(2013) を手に入れられたので
手持ちの Nexus7(2013) を Android 4.3 で維持しておいて
会社の Nexus7(2013) を Android 4.4 にすることができてな…

そんなこんなしてたら Android Studio がリリースされてな…
Android Studio で Gradle に productFlavor という便利なものがあってな…
プロジェクト再開して wada811/Android-StorageReadWriteChecker ができてな…

↓この記事を先日やっと書いてな…
[Android]KitKatと外部記憶領域とREAD_EXTERNAL_STORAGEとWRITE_EXTERNAL_STRAGEとmaxSdkVersion | DevAchieve

ようやく Nexus7(2013) をアップデートじゃぁああああああああああ!!!!!!!!!!!

[Android]Google Play アカウント所有者を変更する

Google Play アカウント所有者が個人Gmailアカウントに紐付いていて
ユーザーに開発用Gmailアカウントを追加していたのですが、
Google Play アカウント所有者じゃないとできないことがあったので
Google Play アカウント所有者を開発用Gmailアカウントに切り替える手続きをしました。

移行できれば普段使っている開発用Gmailアカウントで
Google Play Developer Publishing API などを使えるようになるので嬉しいです。


アカウント所有者 と ユーザー の違い

アカウント ユーザーの追加と権限の管理 - Android デベロッパー ヘルプ

タイプアクセス
アカウント所有者
デベロッパー コンソールにフルアクセスできる
新しいユーザーの招待、ユーザーのデベロッパー コンソールへのアクセスの取り消し、各ユーザーの権限の設定を行うことができる
有料アプリを販売するため、関連付けられた Google ウォレット Merchant アカウントを所有できる(アカウント所有者のみ)
ユーザー
デベロッパー コンソールにさまざまなレベルでアクセスできる
すべてまたは指定のアプリにアクセスできる
新しいユーザーの招待やユーザーの権限の編集はできない

Google Play アカウント所有者を変更する

手順は以下のとおり。

  • Google サポートチームに連絡し、アプリを別のアカウントに移行します。
  • アプリの移行の確認に返信し、既存のアカウントを閉鎖します。

アカウント情報の変更または更新 - Android デベロッパー ヘルプ


Google Play アカウントを移行する

普通だったら以下の対応だけでOK!

  • 新しい Google Play アカウントを登録する
  • 新しい Google Wallet アカウントを登録して紐付ける
  • アプリ移行リクエストを送る

アプリを移行する - Android デベロッパー ヘルプ
アプリ移行リクエスト - Android デベロッパー ヘルプ

アプリの収益を受け取るための Google Wallet のお支払い設定を忘れないようにしましょう!


デベロッパー アカウントの解約

2,3営業日待てば移行もされるし、
Google Wallet のデポジットも振り込まれてお支払い設定完了できるだろうから
すべてが終わったら移行元の Google Play アカウントを解約しましょう!

登録料の25ドルが返ってくるらしいです。
アカウント情報の変更または更新 - Android デベロッパー ヘルプ

2014/09/19

[Android]KitKatと外部記憶領域とREAD_EXTERNAL_STORAGEとWRITE_EXTERNAL_STRAGEとmaxSdkVersion

KitKat で外部記憶領域の読み書きに関するパーミッションに変更がありました。
また、READ_EXTERNAL_STORAGE パーミッションも正式対応となりました。

この変更についてまとめたいと思います。
まずは言葉の説明から。

外部記憶領域(primary external storage)

以前は外部記憶領域といえば SD カード(外部ストレージ、外部SDなどと呼ばれる)のことを指しましたが、
最近はストレージ内部の仮想的な記憶領域(内部ストレージ、内部SDなどと呼ばれる)のことを主に指します。
そのため、内部ストレージと呼ばれるのに外部記憶領域であり、
READ_EXTERNAL_STORAGE や WRITE_EXTERNAL_STRAGE が必要となるという少々混乱する状況でした。
(Environment#getExternalStorageDirectory()が内部SDを返すようになったり)
primary external storage などについては以下のサイトを読んでください。
External Storage Technical Information | Android Developers

KitKat での変更

KitKat からは EXTERNAL_STORAGE パーミッションの対象ディレクトリが変更となり、
外部記憶領域上のアプリケーションデータ領域はパーミッションが不要となりました。
アプリケーションデータ領域の外部については EXTERNAL_STORAGE パーミッションが必要となっています。

アプリケーションデータ領域

本体メモリのアプリケーションデータ領域(アプリ内領域などと呼ばれる)
/data/data/<パッケージ名>/
外部記憶領域上のアプリケーションデータ領域
/<外部記憶領域>/Android/data/<パッケージ名>

本題

READ_EXTERNAL_STORAGE と WRITE_EXTERNAL_STRAGE が必要なのは
どのディレクトリなのか、どのメソッドなのか調べました。

wada811/Android-StorageReadWriteChecker
BuildVariants と パーミッションREAD_EXTERNAL_STORAGEWRITE_EXTERNAL_STRAGE
PreKitKat
PreKitKatRead
PreKitKatWrite
PreKitKatReadWrite
KitKat
KitKatRead
KitKatWrite
KitKatReadWrite
KitKatWriteCompat○(maxSdkVersion="18")
KitKatReadWriteCompat○(maxSdkVersion="18")
データ:Android - KitKatと外部記憶領域とREAD_EXTERNAL_STORAGEとWRITE_EXTERNAL_STRAGEとmaxSdkVersion - Qiita

結果

PreKitKatKitKat の結果から
getExternalCacheDir と getExternalFilesDir で取得できる外部記憶領域上のアプリケーションデータ領域は
KitKat からパーミッションなしでも読み書き可能であることが確認できます。

READ_EXTERNAL_STORAGE が正式対応となったため
KitKat 以降で READ_EXTERNAL_STORAGE なしでは
Environment.getExternalStorageDirectory で取得できる外部記憶領域は
読み込みできないことが確認できます。(PreKitKatKitKat)
KitKatRead から READ_EXTERNAL_STORAGE があれば
外部記憶領域の読み込みができることが確認できますね。

本題の本題

さて、getExternal~Dir で取得できる外部記憶領域上のアプリケーションデータ領域は
KitKat からパーミッションなしでも読み書き可能ということが確認できましたが、
PreKitKat ではパーミッションが必要です。
KitKat では必要でないパーミッションを PreKitKat のために宣言すると
KitKat ユーザーに対して不要なパーミッションを取得することになります。
結局パーミッションは減らせないのかとなりそうですが、
KitKat で追加された Uses-Permission の maxSdkVersion 属性によって対応が可能です。

以下のように記述することで PreKitKat では WRITE_EXTERNAL_STORAGE を宣言し、KitKat ではパーミッションを宣言しないことができます。
<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="18" />
アプリ情報画面のパーミッションの欄を見ると PreKitKat では WRITE_EXTERNAL_STORAGE があり、
KitKat では WRITE_EXTERNAL_STORAGE はないことがわかります。
KitKatWriteCompat KitKatReadWriteCompat
PreKitKat
KitKat

結論

  • Environment への書き込みがあるなら WRITE_EXTERNAL_STORAGE が必要
  • getExternal~Dir への書き込みだけなら WRITE_EXTERNAL_STORAGE に maxSdkVersion="18" を付けておく
  • 外部記憶領域への読み込みがあるなら READ_EXTERNAL_STORAGE が必要

参考

External storage access | Android 4.4 APIs | Android Developers
Android:KitkatのREAD_EXTERNAL_STORAGEと外部記憶領域 | Taosoftware
WRITE_EXTERNAL_STRAGEと下位互換のためのmaxSdkVersion | Taosoftware
2014/09/12

[Android]ライブラリプロジェクト(aar)をMavenリポジトリとしてGitHubで配布する

Androider の皆さんこんにちは。
もう Eclipse から Android Studio への移行は済みましたか?
僕はまだ途中です。
俺々ライブラリが移行できないと Android Studio に移行できないので
今回はそのための手順をご紹介したいと思います。

環境

OS
Mac OS X 10.8.5
Android Studio
0.8.9

ライブラリプロジェクト(aar)とは

Android Studio 、というより Android Studio で導入された Gradle というビルドシステムでの
ライブラリプロジェクトの配布用バイナリ形式を aar (Android archive) と呼ぶらしいです。
.aar の拡張子を持つ ZIP ファイルで、以下が入っています。
  • /AndroidManifest.xml (必須)
  • /classes.jar (必須)
  • /res/ (必須)
  • /R.txt (必須)
  • /assets/ (オプション)
  • /libs/*.jar (オプション)
  • /jni//*.so (オプション)
  • /proguard.txt (オプション)
  • /lint.jar (オプション)
参考: AAR Format - Android Tools Project Site

配布編

まずは Android Studio でライブラリプロジェクトを作成します。詳しい方法については割愛。

試しに aar を作成するためリリースビルドするため、
上の画像の Terminal で ./gradlew assembleRelease を実行します。
以下のコマンドで aar が作成されているか確認します。
find . -name '*.aar'
./library/build/outputs/aar/library.aar

問題ないので build.gradle に Maven プラグインを使用して aar やら何やらを生成するタスクを追記します。
以下のハイライト部分です。
(余談だけど build.gradle って説明された時に初めは root の build.gradle なのか
Modules の build.gradle なのかわからなくて戸惑いました。だいたい Modules の方らしい。)
apply plugin: 'com.android.library'

android {
    compileSdkVersion 19
    buildToolsVersion "20.0.0"

    defaultConfig {
        minSdkVersion 10
        targetSdkVersion 19
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:20.0.0'
}

def repo = new File(rootDir, "repository")

apply plugin: 'maven'

uploadArchives {
    repositories {
        mavenDeployer {
            repository url: "file://${repo.absolutePath}"
            pom.version = '1.0.0'
            pom.groupId = 'at.wada811'
            pom.artifactId = 'android-dialog-fragments'
        }
    }
}
pom.version がライブラリのバージョン、
pom.groupIdが自分用の識別子(Java の package 名の最初の部分で良さそう)、
pom.artifactIdがライブラリの識別子、みたいです。
詳しくは 第52章 Mavenプラグイン を読んで下さい。

追記したら Android Studio の Terminal で ./gradlew uploadArchivesすると
repository ディレクトリに aar が生成されます。
tree repository
repository
└── at
    └── wada811
        └── android-dialog-fragments
            ├── 1.0.0
            │   ├── android-dialog-fragments-1.0.0.aar
            │   ├── android-dialog-fragments-1.0.0.aar.md5
            │   ├── android-dialog-fragments-1.0.0.aar.sha1
            │   ├── android-dialog-fragments-1.0.0.pom
            │   ├── android-dialog-fragments-1.0.0.pom.md5
            │   └── android-dialog-fragments-1.0.0.pom.sha1
            ├── maven-metadata.xml
            ├── maven-metadata.xml.md5
            └── maven-metadata.xml.sha1
この repository を GitHub に Push すれば配布側の手順としては完了です。

利用編

GitHub で配布されているライブラリプロジェクト(aar) を参照することで利用することができます。
プロジェクトは準備出来ているとして、app などの build.gradle に以下を記述します。
repositories {
    maven { url 'http://raw.github.com/wada811/Android-DialogFragments/master/repository/' }
}

dependencies {
    compile 'at.wada811:android-dialog-fragments:1.0.0'
}
全体は HelloGradle/build.gradle at master · wada811/HelloGradle を見て下さい。
読み込めると以下のように app/build/intermidiates/exported-aar/ 以下に追加されます。


これで Public な俺々ライブラリが手軽に配布、参照できて便利ですね!

参考

u1aryzの備忘録とか: githubをMavenリポジトリとしてAndroidライブラリプロジェクト(aar)をデプロイして使用する
2014/09/10

Android Studio で support library のソースコードをアタッチする

Android Studio でサポートライブラリのソースコードが読めなくて困ったので調べると
サポートライブラリのソースコードを別途ダウンロードして設定が必要でした。

参考
Android Studio - Attach Source - web系な備忘録

cd $ANDROID_SDK_HOME/extras/android
git clone https://android.googlesource.com/platform/frameworks/support/ support-src

FragmentActivity などを command + B で開いて
右上の Attach Source... をクリックして
$ANDROID_SDK_HOME/extras/android/support-src を指定してあげれば
サポートライブラリのソースコードを読めるようになります。
2014/09/05

Mac で Command + M のウィンドウ最小化を無効化する

基本的にウィンドウの最小化って使わないです。
なのにCommand + mという
Command + nCommand + , のミスを
待ち構える配置になっているので微妙にストレスがたまります。
トグルだったらまだ良かったのですが最小化したっきり戻ってこないので
メニューバーのウィンドウから前面に表示させるしかありません。
そこが一番のストレスポイントですね。

ということでCommand + mを無効化したいと思います。

システム環境設定 > キーボード > キーボードショートカット

アプリケーションの「+」ボタンを押すと以下のような表示が出ます。
ここで全アプリケーションに対して
メニュータイトル「しまう」、「最小化」、「Minimize」のキーボードショートカットを設定しましょう。

とりあえずCommand + Ctrl + mに変更しました。


アプリケーションを開き直すと適用されるとか、開き直さなくても適用されるとか。
これで操作ミスでウィンドウが最小化されることもなくなるかと思います。

参考
How to Create a Keyboard Shortcut for Menu Items Everywhere - Tuts+ Computer Skills Tutorial
2014/09/04

Mac でウィンドウを切り替えるキーボードショートカット

Mac で作業しているとアプリケーションもそのウィンドウもどんどん開いてしまいます。
アプリケーションなら Command + Tab で切り替えできるので良いのですが
ウィンドウの切り替えに困っていました。
これが長らくの課題でしたが、参考サイトのおかげで
キーボードショートカットの「次のウィンドウを操作対象にする」を設定すると
ウィンドウの切り替えできました。

システム環境設定 > キーボード > キーボードショートカット

キーボードと文字入力の「次のウィンドウを操作対象にする」の
キーボードショートカットを好きなものに変えましょう。
僕は Command + Option + Tab に変えました。


FInder とかついつい開きすぎて切り替えが面倒だったのでコレで便利になります!やったぁ!

参考サイト

橋本商会 » Macの「次のウィンドウを操作対象にする」をなぜ設定しないのか
2014/08/16

[ShellScript]エラーが起きたり、未定義の変数が使用されたら停止する

adb-screencap で Android 端末の画面をキャプチャしてPCに持ってくるコマンドで作った adb-screencap コマンドが
複数端末接続されていて -s で端末指定されていない場合に
error: more than one device and emulator エラーが連発して
残念な感じだったので修正しました。
1行目の#!/bin/sh#!/bin/sh -euに変更することで
エラーが起きたり、未定義の変数が使用されたら処理を停止するようになります。
参考: Bash - シェルスクリプトを書くときはset -euしておく - Qiita

修正したものは wada811/ADB-Tools に置きました。
README は わかりやすいREADME.mdを書く | SOTA を参考に充実させてみました。
2014/08/10

SublimeText3で行頭・行末に移動するキーボードショートカット

Sublime Text 3 に移行したのですが、Sublime Text 2 でできていた
Command + で行頭に移動、
Command + で行末に移動ができなくなっていました。
調べてみると Sublime Text 2 でも以下のページのように設定が必要だったみたいです。
Bruno "Clef" : Sublime Text 2 - End and Begin of Line in Mac OS
設定して記事を書いていなかったから設定したことすら忘れていました。
ということで覚え書きです。

Sublime Text 3 では上記のページの記述とは少し異なっていて以下のように記述します。
[
    { "keys": ["super+right"], "command": "move_to", "args": { "to": "hardeol" } },
    { "keys": ["super+left"], "command": "move_to", "args": { "to": "hardbol" } }
]
moveto が move_to になったみたいです。
以上です。
2014/07/17

Redmine の通知メールを Gmail で送信する設定

さくらのVPSのお試し期間が終わったので
さくらのVPSのCentOS6でRedmineを動かす | DevAchieve
動かした Redmine の通知メールを設定しました。

環境

OS
CentOS6.5
Redmine
2.5.1

設定

production:
  email_delivery:
    delivery_method: :smtp
    smtp_settings:
      enable_starttls_auto: true
      address: "smtp.gmail.com"
      port: 587
      domain: "smtp.gmail.com"
      authentication: :plain
      user_name: "mail_for_redmine@gmail.com"
      password: "gmail_password"

  rmagick_font_path: /usr/share/fonts/ipa-pgothic/ipagp.ttf
参考
メールの設定 — Redmine Guide 日本語訳
参考サイトでは設定にtls: trueが記述されていたけど記述するとメールが送信されなくなりました。

動作は管理者アカウントで[管理 > 設定 > 通知メール]の右下の「テストメールを送信」で確認できます。


注意点

通知メールアドレスに普段使っているアカウントを使用すると
変なところからアクセスしようとしたという判定でメールが送信できませんでした。

Redmine の通知専用の Gmail アカウントを作成するのが良いと思います。

タグ(RSS)

ブログ アーカイブ