TextViewのリンクに確認ダイアログをつけてみる。

今回はTextView!
半日悩んで解決したのでメモです。

条件

  1. TextViewに複数のURLが含まれる可能性がある。
  2. そのテキストは自分以外のだれかが入力したもので、HTMLフォーマット化されていない。
  3. そのURLに関して正しいかどうかを判断するロジックはGoodleさんに任せる。
  4. それぞれのURLごとに正しいURLに飛ぶこと。
  5. それぞれのURLをタップしたときに確認ダイアログをつける。

とりあえず簡単なやつから。
まずGoogleさんに判断させる部分だけど、

textView.setAutoLinkMask(Linkify.WEB_URLS);
この一行でOK。あとはGoogleさんがよきに計らってくれる。
複数URLがあっても問題なし。HTMLフォーマットも必要なし。
(ただし、複数あるが、連続してしまうとさすがに判別不能。まぁそこは無視。スプリットでもなんでもすればいい。)

大半の条件はこれで満たせるのだけど、これだけでは確認ダイアログが付けられない。
タップするとアプリから勝手にブラウザに飛んで行ってしまう。

それでは条件満たせないので、それぞれのリンクに確認ダイアログをつけることにする。
そしてここから泥沼へ。

それぞれのリンクはURLSpanというオブジェクトとして保管されている。(それすら今回初めて知りました)
このURLSpanのonClickメソッドを継承し、onClickメソッド内でダイアログ表示→OKならブラウザへで完成するはず。

しかし、残念ながら、TextViewのsetTextメソッドはfinalメソッドのため、継承できず、autoLinkの動きをかえることはできません!何か方法があれば教えてください。

ということで苦肉の策。。。

CharSequence text2 = textView.getText();
if( text2 instanceof Spannable ) {
Spannable spannable = (Spannable) text2;
URLSpan[] spans = spannable.getSpans(0, text2.length(), URLSpan.class);
for (URLSpan urlSpan : spans) {
int start = spannable.getSpanStart(urlSpan);
int end = spannable.getSpanEnd(urlSpan);
spannable.removeSpan(urlSpan);
spannable.setSpan(new MyURLSpan(urlSpan.getURL()), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}

やってること

  1. textView.getText()の中身がSpannableであったら、URLSpanを取得する。
  2. それぞれのURLSpanのスタートとエンドのポジションを取得する。
  3. そのURLSpanを削除する。
  4. 同じ場所にMyURLSpanをぶち込む!

全体
MainActivity.java

package com.example.textviewsample;

import android.app.Activity;
import android.os.Bundle;
import android.text.Spannable;
import android.text.Spanned;
import android.text.style.URLSpan;
import android.text.util.Linkify;
import android.view.Menu;
import android.widget.TextView;

public class MainActivity extends Activity {

private static final String text = "やふる:http://yahoo.co.jp\nぐぐる:http://www.google.co.jp\nびんぐる:http://www.bing.com/";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

TextView textView = (TextView)this.findViewById(R.id.textview);
textView.setAutoLinkMask(Linkify.WEB_URLS);
textView.setText(text);

CharSequence text2 = textView.getText();
if( text2 instanceof Spannable ) {
Spannable spannable = (Spannable) text2;
URLSpan[] spans = spannable.getSpans(0, text2.length(), URLSpan.class);
for (URLSpan urlSpan : spans) {
int start = spannable.getSpanStart(urlSpan);
int end = spannable.getSpanEnd(urlSpan);
spannable.removeSpan(urlSpan);
spannable.setSpan(new MyURLSpan(urlSpan.getURL()), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

}

MyURLSpan.java

package com.example.textviewsample;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Parcel;
import android.text.style.URLSpan;
import android.view.View;

public class MyURLSpan extends URLSpan {

public MyURLSpan(Parcel src) {
super(src);
}

public MyURLSpan(String url) {
super(url);
}

@Override
public void onClick(final View widget) {
AlertDialog.Builder builder = new AlertDialog.Builder(widget.getContext());
builder.setTitle("確認");
builder.setMessage(getURL()+"へ飛びますか?");
builder.setPositiveButton("おっけー", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
onClickSuper(widget);
}
});
builder.setNegativeButton("やだ", null);
builder.show();
}

private void onClickSuper(View widget) {
super.onClick(widget);
}
}

activity_main.xml


実行結果

確認ダイアログ

何が驚いたって、textView.getText()がString以外のもの返すこと。
CharSequenceのことを全く気にせずStringしか使ってこなかった自分が恥ずかしい。

MyURLSpan込みのTextViewを作成してみたい!がんばる。
今日はここまで。

chef-soloでさくらVPSにJenkinsを立ててみる。(詳細編)

途中gdgdになってしまった、chef-soloでさくらVPSにJenkinsを立ててみる。ですが、たぶん今回が最終回。

プライベートに時間がさけるようになったので、chef-solo関連を整理してみた。
ただ、やっぱり独学なので、本当にこれでいいのかわかりません。本を買おうか悩み中です。

ではまず、自分的に綺麗にしたレポジトリは以下
https://github.com/shiraji/chef-repo

よくよく考えてみたら、chefだけで一つレポジトリ持っておけば、Jenkins専用とかにしなくてもいいじゃんと思ったので、chef-repoに改名しました。
これ書き終わったら、jenkins_server_settingsとか消す予定。

変更があったのはinstall_cookbook.shができたこと。これをキックするとBerkshelfを実行する。
Berkshelfは非常に便利で、gemでインストール後、必要なcookbookをOPSCODEのライブラリーから持ってこれる。
基本OPSCODEのものでいいと思うけど、違うものを指定することもできる。
今回必要なものはJavaとJenkins。設定ファイルはBerksfile
https://github.com/shiraji/chef-repo/blob/master/Berksfile

site :opscode
cookbook 'java'
cookbook 'jenkins'

たった三行・・・。理解すれば本当に簡単です。
最初に持ってくるサイトを指定し、必要なcookbookを指定する。
cookbookを引っ張ってくるとBerksfile.lockというファイルができる。これはGit管理する必要がないので、.gitignoreに入れておく。
あと、基本cookbookの中は他のところから引っ張ってきたcookbook、
site-cookbooksには自分が作成したcookbookをというのが流儀っぽいので、cookbookも.gitignoreに入れておく。

次にcookbooks関連。
これは以前説明した通り、サーバの基本的な設定とユーザ作成。

nodes
nodeはインストールさせる各サーバごとに持たせるもの。
今回はjenkinsをインストールするサーバ用なので、jenkins.jsonを作成。
中身はユーザ作って、サーバの設定をして、jenkinsをインストールする。

roles
2つ作成。
1つ目
https://github.com/shiraji/chef-repo/blob/master/roles/adduser_shiraji.json

これは以前作成したaddusersというレシピのデフォルトの値が外出しされているものです。
なぜこのようなことをしたかというと、次にshiraji2というユーザを作成したい場合、roleに同じようなadduser_shiraji2.jsonを作成して、それをnodeで呼べばいいため。
基本、デフォルトの値を使わず、roleで上書きするようにすると毎回変更しなくてもすむ。(デフォルトの値は残しておいても問題ないのだけど。)

2つ目
https://github.com/shiraji/chef-repo/blob/master/roles/jenkins.json

これは以前にもあったものと同じ。Javaのoverride_attributesとJenkinsインストールするという設定が記載されている。

こんな感じでroleとnodeをうまく使って、拡張性を持たせたほうがいいですよという話。(ただ、それが正しいかどうかは知りませんw)

一番難しかったのはroleとnodeの関係。この2つを理解すれば、本当に簡単にセットアップできました。

chef-soloでさくらVPSにJenkinsを立ててみる。(Jenkinsさんが立った編)

この記事は古いです。
jenkins_server_settingsのレポジトリは削除しました。

こちらのURLを確認して下さい。
http://d.hatena.ne.jp/shiraji/20130713/1373679753

===

Jenkinsサーバが立った!のでまとめてみます。
はっきりいって、まだ汚いので、後で自分なりに綺麗にしてみるつもりです。
まずは一つのレポジトリにまとめてみました。

https://github.com/shiraji/jenkins_server_settings

とりあえず、めんどくさいので、chef-soloが動かせるようにprepare_chef_solo_no_yum.shなるものを作成しました。
これは以前公開したものにberkshelfなどを追加したものです。
さくらVPSでOS初期化後すぐに利用するという想定なので、まぁ問題ないはず。

次にキックするのがprepare_jenkins.shです。
この中身が

Berkshelfをキックしてから、chef-soloを実行しています。

nodes/chef.jsonの中身が↓

roleにある、jenkinsという名前がついたroleを利用する。

role/jenkins.jsonの中身が↓

やっていることとして、orcleのjavaをインストールし、shirajiというユーザを作成し、サーバの設定をして、jenkinsをインストールするといったもの。
詳細はhttp://d.hatena.ne.jp/shiraji/20130222/1361547796

Berkshelfがほんとに便利。おかげで、javaとJenkinsのインストールほぼ自分は何もせずに完了した。
細かい設定は後々するとして、とりあえず、Jenkinsおじさんの顔が見れたので満足しました。

各ファイルの説明は後々します。できた!という報告でした。

Jenkinsで変更点をメールする方法

以前書いた、Jenkinsでログの一部分からメールを作成する方法ではログの一部分を送る方法を記載した。
メールで変更点もわかるようなのだが、公式のドキュメントに一切説明がない。

トークンリファレンスにさらっと書かれているが非常にわかりづらいため、メモとして残す。
(このエントリー書いたのに消えてた・・・。)

変更点を送るには3つ方法がある。
${CHANGES, showPaths, showDependencies, format, pathFormat}
直近のビルドからの変更点
${CHANGES_SINCE_LAST_SUCCESS, reverse, format, showPaths, changesFormat, pathFormat}
直近の成功ビルドからの変更点
${CHANGES_SINCE_LAST_UNSTABLE, reverse, format, showPaths, changesFormat, pathFormat}
直近のUNSTABLEか成功ビルドからの変更点



showDependencies - 使ったことないからわからない
showPaths - ファイルパスを表示するかどうか
format - 表示フォーマット
pathFormat - ファイルパスの表示方法のフォーマット

formatとpathFormatで使える変数
%a コミットした人
%d コミットした日付
%m コミットメッセージ
%p ファイルパス
%r リビジョン

ただし、%dと%rは全てのバージョン管理システムでサポートされているか不明。

CHANGES_SINCE_LAST_SUCCESSとCHANGES_SINCE_LAST_UNSTABLEの設定
reverse - true: 最新の変更が上にくる。デフォルトがfalse
format - それぞれのビルドでの表示方法。
%c 変更点
%n ビルド番号

showPaths, changesFormat, pathFormatはCHANGESのshowPaths, format, pathFormatと同じ


それで今回こんな感じで作ってみた。

${CHANGES, format="コミッター: %a\nリビジョン: %r\n変更ファイル: %p\nコメント: %m", pathFormat="\n\t-\ %p"}
結果:
コミッター: shiraji
リビジョン: SHA1のリビジョン
変更ファイル:
- folder/filename
- folder/filename2
コメント: ext-mail test

なぜか、showPaths=trueを設定すると動かない(v2.25)。まぁ問題ないから別にいいやということで放置。

Thorを使ってみた(Rubyバッチ編)

ちょっとJenkinsのことは置いておいて、rubyでバッチを書くことになった。

悩んで、いろいろ試してみた。
まずは、Thor。
Railsにデフォルトで入っているのだけど、Thorだけで利用してみる。

実行結果
ヘルプ1
# ruby thor_test.rb help
Commands:
thor_test.rb execute [OPTION] # Test execute
thor_test.rb help [COMMAND] # Describe available commands or one specific command

Options:
-h, [--help] # Thor test

ヘルプ2
# ruby thor_test.rb help execute
Usage:
thor_test.rb execute [OPTION]

Options:
-p, [--production] # Run production DB server
-n, [--number=N] # test number
# Default: 3
-h, [--help] # Thor test

Test execute

デフォルト
# ruby thor_test.rb
DEVELOPMENT
3

パラメータ設定
# ruby thor_test.rb -p -n=100
PRODUCTION
100

こんな感じで、ソース少ないのに、複雑なことができちゃう。

===
共通部分を抽出して、バッチ作成を簡単にできるようにしてみました。
Thorを使いやすくしてみた(Ruby・Railsバッチ編)

pleiadesの英語化

Eclipsepleiadesはall-in-oneで色々便利。でもEclipse自体は英語のが便利。
だから、pleiadesの英語化を行う。


eclipse.iniのコメントアウト
#-javaagent:dropins/MergeDoc/eclipse/plugins/jp.sourceforge.mergedoc.pleiades/pleiades.jar

以下削除する。
plugins/jp.sourceforge.mergedoc.jstyle_4.2.2.1
configuration/jp.sourceforge.mergedoc

Eclipse再起動

chef-soloでさくらVPSにJenkinsを立ててみる。(GitHub設定編)

chef-soloの勉強をしつつ、色々触っている。
いくつかCOOKBOOKを作成したのですが、新しい環境で使って、その場で手を入れようとすると、毎回環境ごとに公開鍵を設定する必要がある。
そこで、最初に考えたのが、秘密鍵の共有。→だめだろと即却下。



色々調べて、GitHubAPIを使うことにした。
GitHub APIの日本語のドキュメントって少ない。最初どうやればいいのかわからなくてハマりました。

以下が今回利用したUser Public Keys APIの詳細
http://developer.github.com/v3/users/keys/

他のGitHub API
http://developer.github.com/

できたてホヤホヤな環境で使うため、shellで作ってみた。

やったことはcurlGitHub APIを叩く!だけ。
access_tokenの取得

curl -u "username:password" -d "{"scopes":["user"]}" https://api.github.com/authorizations
SSHの設定をする
curl -H "Authorization: token 上記で得たトークン" https://api.github.com/user/keys -d "{ "title":"SSH鍵の名称", "key":"公開鍵" }"

JSONでやり取りしてるのだからperlでやるべきだった。
他にもGitHub APIで便利なのがあったらperlで書き直す!(今のところ、ない・・・。)