2014年11月17日月曜日

AndroidでBlur effect

要はiOS7以降のBlur effectぽいことをしたい場合。
android-stackblurを使う。
・RenderScriptはradiusがMAX25までしか設定できないぽいので期待しない。
・Eclipse想定


StackBlurライブラリの開発プロジェクトへのインストール

NDK導入
・stackblurをDLして、StackBlurディレクトリをプロジェクトimport。
・プロジェクトのコンテキストメニューから、Build Project
・libsディレクトリ配下にビルド成果物ができるので、まるっと開発プロジェクトのlibs配下にコピー
※stackblurのrenderscript8.jarでの競合が無ければIs Libraryでの追加でも大丈夫かも。


利用例

StackBlurManager _stackBlurManager = new StackBlurManager(orig);
_stackBlurManager.processNatively(radius);
Bitmap blurred = _stackBlurManager.returnBlurredImage();

利用例:Dialogで使う場合

Dialogに背景画像設定する場合は、DialogのWindow層に設定。
>そうするとDialogの位置とサイズが崩れる。styleで色々設定しても駄目。
>なのでAlertDialogは諦めて自前でCustomViewを作る必要がある。
>それも含めたベースのDialogFragment

/hoge/fuga/AbstractDialogFragment.java
package hoge.fuga;

import com.enrique.stackblur.StackBlurManager;

import hoge.fuga.R;

import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnShowListener;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.FrameLayout;

abstract public class AbstractDialogFragment extends DialogFragment implements OnShowListener {

    public static final String TAG = "AbstractDialogFragment";

    protected Dialog mDialog;
    private ViewGroup mContainer;


    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        mDialog = super.onCreateDialog(savedInstanceState);

        // Get status bar height
        Rect rectgle= new Rect();
        getActivity().getWindow().getDecorView().getWindowVisibleDisplayFrame(rectgle);
        int statusBarHeight = rectgle.top;

        // Screen shot http://stackoverflow.com/a/9596132/1121509
        View v = getActivity().getWindow().getDecorView();
        v.setDrawingCacheEnabled(true);
        Bitmap ss = v.getDrawingCache();
        if (ss != null) {
            ss = Bitmap.createBitmap(ss, 0, statusBarHeight, ss.getWidth(), ss.getHeight() - statusBarHeight);
        }
        v.setDrawingCacheEnabled(false);

        // Blur effect
        if (ss != null) {
            int radius = (int) (ss.getHeight() * 0.25);
            radius = radius > 100 ? 100 : radius;
            
            StackBlurManager _stackBlurManager = new StackBlurManager(ss);
            _stackBlurManager.processNatively(radius);
            Bitmap blurred = _stackBlurManager.returnBlurredImage();
            mDialog.getWindow().setBackgroundDrawable(new BitmapDrawable(getResources(), blurred));
        }

        // without title
        mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);

        // set listener
        mDialog.setOnShowListener(this);

        return mDialog;
    }
    
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // コンテナの設定
        mContainer = (ViewGroup) inflater.inflate(R.layout.dialog_base, container, false);
        return mContainer;
    }

    public void addViewToContainer(View contentView) {
        ((ViewGroup) mContainer.findViewById(R.id.dialog_container)).addView(contentView, 0);
    }

    @SuppressLint("NewApi")
    @SuppressWarnings("deprecation")
    @Override
    public void onShow(DialogInterface dialog) {
        // サイズ調整、このタイミングでないとうまく動かない
        Point size = new Point();
        Display display = getActivity().getWindowManager().getDefaultDisplay();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
            display.getSize(size);
        } else {
            size.x = display.getWidth();
            size.y = display.getHeight();
        }

        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams)mContainer.getLayoutParams();
        lp.width = size.x;
        lp.height = size.y;

        ViewGroup dialogContainer = (ViewGroup) mContainer.findViewById(R.id.dialog_container);
        FrameLayout.LayoutParams lp2 = (FrameLayout.LayoutParams) dialogContainer.getLayoutParams();
        lp2.topMargin = (size.y - dialogContainer.getMeasuredHeight()) / 2;
    }
}

/res/layout/dialog_base.xml



    
        
    


・Dialogを使う場合はAbstractDialogFragmentをextend。
・dialogの設定をするときはonCreateDialog()をoverrideしてsuper.onCreateDialog()してから設定。
・ViewはonCreateView()をoverrideして、super.onCreateView()してから子Viewを作り、super.addViewToContainer(childDialogView)する。


GPUImage for androidがblur effect対応してくれたら嬉しいな〜。

ElasticSearchのインストール

検索サーバElasticSearchのインストールと設定。AWS(EC2)での利用。2014.11.15現在。

1. Javaのバージョンを1.7.xにする

もし問題なければ飛ばす。単純アップデートだと古いのものこってて競合するそうなので、古いのはremoveする
http://stackoverflow.com/questions/25518908/issues-after-elasticsearch-1-3-2-upgrade
# java -version

# yum install java-1.7.0-openjdk
# yum remove install openjdk-6-jre
# yum remove java-1.6.0-openjdk


2. ElasticSearch本体及びプラグインのインストール

# rpm -ivh https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.4.0.noarch.rpm

# /usr/share/elasticsearch/bin/plugin --install mobz/elasticsearch-head
# /usr/share/elasticsearch/bin/plugin --install royrusso/elasticsearch-HQ
# /usr/share/elasticsearch/bin/plugin --install elasticsearch/elasticsearch-analysis-kuromoji/2.4.1
# /usr/share/elasticsearch/bin/plugin --install elasticsearch/elasticsearch-cloud-aws/2.4.0
※head, HQはおこのみで。
※kuromoji, cloud-awsはelasticsearchのバージョンに応じて入れるバージョンが変わる。それぞれ公式のsetup情報にバージョン対応表が載ってるので確認。上記は1.4.0の場合。


3. 設定ファイル

1台構成ならcluster.name位設定しとけばほかはいらない。EC2で複数台サーバを連携する場合は自動でネゴシエートするためのbroadcastが使えないので、cloud-awsプラグインを入れて設定を行う。また利用するEC2インスタンスはSecurity Groupで9200-9400のPortを開けておく。
cluster.name: hoge

# multi-server for EC2 settings
discovery.zen.ping.multicast.enabled: false

cloud.aws.access_key: XXXX
cloud.aws.secret_key: XXXX
cloud.aws.region: ap-northeast
discovery.ec2.host_type: private_ip
discovery.ec2.ping_timeout: 5s
discovery.type: ec2
discovery.ec2.groups: IDorNAME
※access_key, secret_keyはAWSのIAM Roleの設定で、EC2のDescribeの権限を付けたものを設定
※host_typeはpublic_ipでも問題ないと思われる(未確認)
※ regionなしだと接続できずv2.4.0


4. 起動

# chkconfig --add elasticsearch on
# /etc/init.d/elasticsearch start



PHPのクライアントをComposerでインストール

$ php ../composer.phar require elasticsearch/elasticsearch:">=1.0"

HQ, headによる状態確認

ttp://ipaddar:9200/_plugin/head/
ttp://ipaddar:9200/_plugin/HQ/
プラグインをインストールできてれば、上記でアクセス可能。



Multi masterは出来ないので複数台構成にしてもmasterサーバはひとつ。可用性を考えるなら、dataを持たないゲートウェイ的なマスタを作り、そこに子をぶら下げる形か。

trittonn, mroonga, Lucene, Solrと比べて検索システムの中ではだいぶ導入が容易な印象。運用考えると日本語対応した今はCloudSearch使ったほうが楽かも。ただElasticSearchからCloudSearchへのスイッチはサービス拡大に応じて行えるレベルかな。

CakePHP3の環境切り分け

CakePHP3でのdevelopment/productionなどの環境ごとの設定をどうするか。
公式ドキュメントを読むと、どうもbootstrapで設定を追加読み込みするよう促しているのでそれに倣う。

以下の設定で、基本は本番環境になり、developmentの環境変数を設定することで、開発環境の設定を上書きする形になる。

config/bootstrap.php

app.phpの読み込みをした後に、追加の設定を環境に応じて読み込む。config/app_development.phpに開発環境で上書きする設定を記述する。
try {
 Configure::config('default', new PhpConfig());
 Configure::load('app.php', 'default', false);

 // Add this 3 lines
 if (env("APPLICATION_ENV") == "dev" || env("APPLICATION_ENV") == "development") {
  Configure::load('app_development.php', 'default', true);
 }
 
} catch (\Exception $e) {
 die('Unable to load config/app.php. Create it by copying config/app.default.php to config/app.php.');
}

Server setting

nginxの場合はvirtualhostなどserverの設定でphpに渡す環境変数を設定する。
server {
    location ~ \.php$ {
        fastcgi_param  APPLICATION_ENV development;
    }
}

Apacheの場合は
SetEnv APPLICATION_ENV development
かな。最近触ってないから忘れたけど。