2019年11月2日土曜日

iPhoneからandroidへのファイル転送

2019 Nov. 02.
android端末でブラウザを起動する。
https://www.icloud.com/ にアクセスしiPhoneのApple IDでログインする。
目的のファイルをダウンロードする。ダウンロード時に指定するアプリはブラウザにする。
android端末内部共有ストレージ/Download ディレクトリ内にファイルが保管される。

2019年10月26日土曜日

RUBY 実行ファイル、ライブラリの配置

2019 Oct. 26.

とりあえず動いている。

${HOME}/ruby/VERSION/PROJECT/ 自作プログラムを配置。各プロジェクト内でbundlerを設定。
${HOME}/ruby/VERSION/lib/ 上記自作プログラムの内、ライブラリとなる実行ファイルのリンクを配置
 ${}HOME}/bin/ 上記自作プログラムの実行ファイルのリンクを配置

 export RUBYLIB=${HOME}/ruby/VERSION/lib:${HOME}

2019年10月21日月曜日

ポート利用中のプロセスを調べる lsof

2019 Oct. 21.


4567番ポートを使っているプロセスを調べる

$ lsof -i :4567

COMMAND PID USER    FD  TYPE  DEVICE SIZE/OFF NODE NAME
ruby    818 ryuichi 5u  IPv4  21246  0t0      TCP *:4567 (LISTEN)


4567番ポートを使っているプロセス(818)を終了させる


$ kill -9 818

hosts.allow hosts.denyの設定はファイル保存した時点から反映される

2019 Oct. 21.

hosts.allow hosts.denyの設定はファイル保存した時点から反映される

netplanでの固定IPアドレス設定

2019 Oct. 21.

設定ファイル

/etc/netplan/HOGE.yaml

xubuntu18.04インストール時の設定

/etc/netplan/01-network-manager-all.yaml
# Let NetworkManager manage all devices on this system
network:
  version: 2
  renderer: NetworkManager

固定IPアドレス設定 (192.168.1.10)

 /etc/netplan/01-network-manager-all.yaml を編集

#########################
network:
  version: 2
  renderer: networkd

  ethernets:
    enp1s0:
        dhcp4: no
        dhcp6: no
        addresses: [192.168.1.10/24]
        gateway4: 192.168.1.1
        nameservers:
#            addresses: [192.168.1.1]  これでは機能しなかった
            addresses: [8.8.8.8]
#########################

設定を適用

# netplan --debug generate
# netplan apply

2019年10月14日月曜日

コマンドラインからの印刷

2019 Oct. 14.

インストールされたプリンタ名を検索

/etc/printcap の冒頭にプリンタ名が書かれている。

(例)
$ cat /etc/printcap
# This file was automatically generated by cupsd(8) from the
# /etc/cups/printers.conf file.  All changes to this file
# will be lost.
Brother_DCP_J562N|Brother DCP-J562N:rm=pc17:rp=Brother_DCP_J562N:
DCPJ562N|DCPJ562N:rm=pc17:rp=DCPJ562N:

印刷

$ lpr -P Brother_DCP_J562N FILE

2019年10月12日土曜日

"sudo make install"を考慮してumask 022

2019 Oct. 28.
2019 Oct. 12.

umaskを027にしていたが、
$ sudo make install
でシステムのファイルのパーミッションが750になるのはまずいので
022に変更した。

システムワイドなPATH設定

2019 Oct. 12.

ubuntu18.04にて

/etc/profile あるいは /etc/profile.d/FILE に export文を記述する。
  export PATH=$PATH:/SYSTEM/WIDE/PATH/DIR

2019年9月16日月曜日

動画ファイル連結

2019 Sep. 16.

参照元 https://nyanshiba.hatenablog.com/entry/2018/02/03/071256#%E8%A4%87%E6%95%B0%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%921%E3%81%A4%E3%81%AB%E7%B9%8B%E3%81%92%E3%82%8B

再エンコード無しで動画連結

(1)連結するファイルを1行毎に記載したテキストファイルを作成する
   各行の先頭はfileで始まり、スペースで区切って2列めにファイル名を
   フルパス(カレントディレクトリならばファイル名のみで可)を記載する。

    (input.txt)
    file FILE1.mp4
    file FILE2.mp4

(2)連結コマンド
         $ ffmpeg -fflags +discardcorrupt -f concat -safe 0 -i input.txt -c copy output.mp4

2019年8月30日金曜日

atコマンド

2019 Aug. 30.

時刻指定


$ at 時刻
      2019年1月2日12時13分
      $ at 12:13 01022019
  • 時間:分
  • 時間:分 日.月.年
  • 時間:分 月日年
  • 時間:分 月/日/年
  • 時間:分 年-月-日

コマンド入力終了

Ctrl+D

2019年8月18日日曜日

Android AsyncTaskの処理完了時に実行する処理のプログラム

2019 Aug. 18.
2019 Aug. 17.

(参考サイト)
https://teratail.com/questions/195681
https://akira-watson.com/android/asynctask.html


(MyActivity.java)


public class MyActivity extends AppCompatActivity {

    Button buttonOK;

    /*
     * MyAsyncTask内のListenerインターフェースを呼び出す
     */
    private MyAsyncTask.Listener createListener() {
        return new MyAsyncTask.Listener() {

            /*
             * Listenerインターフェースの抽象メソッドonSuccessにコードを実装
             */
            @Override
            public void onSuccess(String result) {
                CODES_TO_RUN_AFTER_MyAsyncTask;
            }
        };
    }

    /*
     * buttonOKがタップされたらMyAsyncTaskが呼び出される
     */
    buttonOK.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            myAsyncTask = new MyAsyncTask();

            /*
             * MyAsyncTask内のListenerインターフェース型変数listenerに
             * createListener()をセットする
             *     createListener()ではListenerインターフェースの
             *     抽象メソッドonSuccessの実装コードが記述されている。
             *     MyAsyncTask内で"listener.onSuccess()"によって実装コードを実行できる。
             */
            myAsyncTask.setListener(createListener());
            myAsyncTask.execute();
        }
    });

}
(MyAsyncTask.java)
public class MyAsyncTask extends AsyncTask {
    private Listener listener;

    /*
     * 抽象メソッドonSuccess()は、MyAsyncTask内で実行させたい処理。
     * 抽象メソッドonSuccess()は、MyAsyncTaskを呼び出すMyActivity内の
     * createListener()内で実装されている。
     */
    interface Listener {
        void onSuccess(String str);
    }
   
    void setListener(Listener listener) {
        this.listener = listener;
    }

    @Override
    protected String doInBackground(Void... params) {
    }

    @Override
    protected void onPostExecute(String result) {
        super.onPostExecute(result);
        CODES_TO_EXECUTE_onPostExecute;

        /*
         * onSuccess()メソッド実行
         */
        listener.onSuccess(result);
        return;
    }
}

2019年7月23日火曜日

HTML imgタグに指定したスクリプトへの値の受け渡し

2019 Jul. 23.

HTMLのimgタグに指定したスクリプトへの値の受け渡しはGETリクエスト。
書式: <img src='SCRIPT.php?var1=VALUE1&var2=VALUE2'>
 値は文字列でも引用符で囲まない。
PHPでの受け取りは $_GET['var1'] に VALUE1 が入る。

[呼び出し元]
<?php
$phpFilePath = "/myProject/my_php/display_jpg.php?imgFile=myImage.jpg";
echo "<img src='{$phpFilePath}'>";
?>

[呼ばれる側](/myProject/my_php/display_jpg.php)
<?php
$jpgpath = "/virtual/rokkonet/public_html/www.rokkonet.ga/dispose_list/data/{$_GET['imgFile']}";
?>

XREA WordPress imgタグからphpスクリプトを読み込んで画像を表示する img src="SCRIPT.php"

2019 Jul. 23.

テキストとして読み込まれたHTMLファイルの途中で header("Content-Type: image/jpeg") を発行しても有効にならない。
"Content-Type: image/jpeg" を指定してimgタグに直接画像を指定しても、画像はテキスト扱いされ文字化け表示される。
 「fopen(), fread(), fclose()」「file_get_contents()」「readfile()」「imagejpeg()」はいずれも画像表示に失敗する。
imgタグに画像を表示するphpスクリプトを指定する。

[画像を表示するページとなるfoo.phpの保存場所]

/public_html/www.MyXrea.Domain/myProject/my_php/foo.php



[foo.php内の画像表示部]

// imgタグに指定するphpファイルのパス指定は /myProject から始まる
$phpFilePath = "/myProject/my_php/display_jpg.php";
echo "<img src='{$phpFilePath}'>";


[foo.phpから呼び出されるdisplay_jpg.phpの保存場所] 

/public_html/www.MyXrea.Domain/myProject/my_php/display_jpg.php

[画像ファイルの保存場所]

 /virtual/XREA-NAME/public_html/www.MyXrea.Domain/myProject/data/myImage.jpg

[display_jpg.phpの画像表示部]

<?php
header("Content-Type: image/jpeg");
$jpgpath = "/virtual/XREA-NAME/public_html/www.MyXrea.Domain/myProject/data/myImage.jpg";
  // 画像ファイルパスは /virtual/XREA-NAME/public_html/www.MyXrea.Domain から始める
$img = imagecreatefromjpeg( $jpgpath );
imagejpeg( $img );
imagedestroy( $img );
?>

2019年7月15日月曜日

android AsyncTaskによるJSchを用いたSSH通信とAsyncTask内イベントの呼び出し元クラスでのキャッチ

2019 Jul. 15.

MyJsch.java
package your.package;

import android.content.Context;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
import com.jcraft.jsch.UserInfo;

import java.io.File;
import java.io.InputStream;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;

class MyJsch {
    private final String ServerIp;
    private final int Port;
    private final String User;
    private final String PassPhraseWord;
    private final String IdentityKeyPath;
    private final Context AppContext;

    /** Channel接続タイプ */
    private static final String CHANNEL_TYPE = "sftp";

    /*
     * コンストラクタ
     */
    MyJsch( final String ServerIp, final int Port, final String User, final String PassPhraseWord,
            final String IdentityKeyPath, final Context AppContext) {
        this.ServerIp = ServerIp;
        this.Port = Port;
        this.User = User;
        this.PassPhraseWord = PassPhraseWord;
        this.IdentityKeyPath = IdentityKeyPath;
        this.AppContext = AppContext;
    }


    /*
     * Sessionを開始
     */
    protected Session connectSession() throws JSchException {
        Session session = null;

        // android端末内の外部ストーリッジ確認
        List sdCardFilesDirPaths =
                SdCardDirPaths.getSdCardFilesDirPathListForLollipop( AppContext );
        Collections.sort(sdCardFilesDirPaths, new CompStringLength());
        String externalPath = sdCardFilesDirPaths.get(0);
        externalPath = externalPath.replaceAll("/Android.*$", "");

        final JSch jsch = new JSch();

        // クライアント側のknownHostsチェックを行わない
        Hashtable config = new Hashtable();
        config.put("StrictHostKeyChecking", "no");
        jsch.setConfig(config);

        // パスフレーズ・秘密鍵方式
        String privKeyFilePath = externalPath + "/" + IdentityKeyPath;
        File privKeyFile = new File(privKeyFilePath);
        if (privKeyFile.exists() ) {
            jsch.addIdentity(privKeyFilePath, PassPhraseWord);
        }

        // Session取得
        session = jsch.getSession(User, ServerIp, Port);

        // パスワード方式でも可とする
        session.setPassword(PassPhraseWord);

        final UserInfo userInfo = new SftpUserInfo();
        session.setUserInfo(userInfo);
        session.connect();
        return session;
    }


    /**
     * SFTPのChannelを開始
     *
     * @param session
     *            開始されたSession情報
     */
    protected ChannelSftp connectChannelSftp(final Session session)
            throws JSchException {
        final ChannelSftp channel = (ChannelSftp) session.openChannel(CHANNEL_TYPE);
        try {
            channel.connect();
        } catch (JSchException e) {
            return null;
        }
        return channel;
    }


    /**
     * Session・Channelの終了
     *
     * @param session
     *            開始されたSession情報
     * @param channels
     *            開始されたChannel情報.複数指定可能
     */
    protected void disconnect(final Session session, final Channel... channels) {
        if (channels != null) {
            for (Channel c: channels ) {
                if (c != null) {
                    c.disconnect();
                }
            }
        }
        if (session != null) {
            session.disconnect();
        }
    }


    /**
     * SFTPに接続するユーザ情報を保持するクラス
     */
    private static class SftpUserInfo implements UserInfo {
        @Override
        public String getPassword() {
            return null;
        }
        @Override
        public boolean promptPassword(String arg0) {
            return true;
        }
        @Override
        public boolean promptPassphrase(String arg0) {
            return true;
        }
        @Override
        public boolean promptYesNo(String arg0) {
            return true;
        }
        @Override
        public void showMessage(String arg0) {
        }
        @Override
        public String getPassphrase() {
            return null;
        }
    }


    /**
     * ファイルアップロード
     *
     * @throws JSchException
     *             Session・Channelの設定/接続エラー時に発生
     */
    public void putFile(ChannelSftp channel, InputStream inStream, String destPath)
            throws SftpException {

        /*
         * sftp通信を実行
         */
        String absoluteDestPath = channel.getHome() + "/" + destPath;
        String destFile = new File(absoluteDestPath).getName();
        int numDestPath = absoluteDestPath.length();
        int numDestFile = destFile.length();
        String destParentPath = absoluteDestPath.substring( 0, numDestPath - numDestFile );
        channel.cd(destParentPath);
        channel.put( inStream, destFile);

        // confirm existance of destFile
        channel.lstat(destFile);
    }

    /*
     * サーバー上のファイルの削除
     *   destFileNameFront文字列から始まるファイルを全て削除する
    */
    public void deleteFiles(ChannelSftp channel, String destFileNameFront) throws SftpException {
        channel.rm(destFileNameFront + "*");
    }
}

SdCardDirPaths.java
package your.package;

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.os.Environment;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class SdCardDirPaths {

    /**
     * SDカードのfilesディレクトリパスのリストを取得する。
     * Android5.0以上対応。
     *
     * @param context
     * @return SDカードのfilesディレクトリパスのリスト
     */
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public static List getSdCardFilesDirPathListForLollipop(Context context) {
        List sdCardFilesDirPathList = new ArrayList<>();

        // getExternalFilesDirsはAndroid4.4から利用できるAPI。
        // filesディレクトリのリストを取得できる。
        File[] dirArr = context.getExternalFilesDirs(null);


        for (File dir : dirArr) {
            if (dir != null) {
                String path = dir.getAbsolutePath();

                // isExternalStorageRemovableはAndroid5.0から利用できるAPI。
                // 取り外し可能かどうか(SDカードかどうか)を判定している。
                if (Environment.isExternalStorageRemovable(dir)) {

                    // 取り外し可能であればSDカード。
                    // このパスをパスリストに加える
                    if (!sdCardFilesDirPathList.contains(path)) {
                        sdCardFilesDirPathList.add(path);
                    }
                }
            }
        }
        return sdCardFilesDirPathList;
    }
}

(利用例)
Send2ServerTask.java
package your.package;

import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.widget.Toast;

import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;

import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;

// 複数のファイルをSSH送信する
public class Send2ServerTask extends AsyncTask {
    final String ServerIp = "SSH.SERVER.IP.ADDRESS";
    final int Port = 22;
    final String User = "SshLogInUser";
    final String IdentityKeyPath = "PRIVATE/KEY/FILE/PATH/IN/CLIENT/DEVICE";
    //     if IdentityKeyPath is illegal, password authentication is used.
    final String DestParentPath = "SERVER/DIR/TO/BE/STORED";

    private WeakReference weakRef;
    private HashMap inStreamMap;
    final private String passPhrase;
    final private String fileNameFront;
      // SSH失敗時のファイル削除のためにファイル名共通文字列を保持
      // 複数の送信ファイルはいずれもfileNameFront文字列で始まるファイル名とすること

    private Listener listener;

    /*
     * constructor
     *   inStreamMap 送信する複数ファイルのファイル名とインプットストリームをHashMapで渡す
     */
    Send2ServerTask(Activity parentActivity, HashMap inStreamMap,
                    String passPhrase, String fileNameFront) {
        weakRef = new WeakReference<>(parentActivity);
        this.inStreamMap = new HashMap<>(inStreamMap);
        this.passPhrase = passPhrase;
        this.fileNameFront = fileNameFront;
    }


    @Override
    protected String doInBackground(Void... params) {
        MyJsch mJsch;
        Session sftpSession = null;
        ChannelSftp sftpChannel = null;

        Activity refActivity = weakRef.get();
        if (refActivity == null || refActivity.isFinishing()) {
            return null;
        }

        Context appContext = weakRef.get().getApplicationContext();

        // initialise Jsch
        mJsch = new MyJsch(ServerIp, Port, User, passPhrase, IdentityKeyPath, appContext);

        // connect session, channel
        try {
            sftpSession = mJsch.connectSession();
            sftpChannel = mJsch.connectChannelSftp( sftpSession );

            // upload
            int count = 0;
            for ( Map.Entry entry : inStreamMap.entrySet()) {
                // entry.getKey()    entry.getValue()
                String destPath = DestParentPath + "/" + entry.getKey();
                try {
                    mJsch.putFile(sftpChannel, entry.getValue(), destPath);
                } catch (SftpException e) {
                    try {
                        // このセッションでアップロードされたファイルをすべて削除する
                        mJsch.deleteFiles(sftpChannel, fileNameFront);
                        mJsch.disconnect( sftpSession, sftpChannel);
                        return "Failed to upload files. count: " + count + ". " + destPath + " "  + e.toString();
                    } catch (SftpException e1) {
                        mJsch.disconnect( sftpSession, sftpChannel);
                        return "Failed to delete remote files. " + e1.toString();
                    }
                }

                try {
                    // take sleep between SFTP#puts
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    // このセッションでアップロードされたファイルをすべて削除する
                    mJsch.deleteFiles(sftpChannel, fileNameFront);
                    mJsch.disconnect( sftpSession, sftpChannel);
                    return "Failed! sleep" + e.toString();
                }
                count++;
            }
            if ( count != inStreamMap.size()) {
                // このセッションでアップロードされたファイルをすべて削除する
                mJsch.deleteFiles(sftpChannel, fileNameFront);
                mJsch.disconnect( sftpSession, sftpChannel);
                return "Could not upload " + inStreamMap.size() + " files";
            }
        } catch (JSchException | SftpException e) {
            mJsch.disconnect( sftpSession, sftpChannel);
            return "connection failed. " + e.toString();

        } finally {
            // disconnect channel, session
            mJsch.disconnect( sftpSession, sftpChannel);
        }
        return "Succeeded. " + inStreamMap.size() + " files upload";
    }


    @Override
    protected void onPostExecute(String result) {
        super.onPostExecute(result);

        Activity refActivity = weakRef.get();
        if (refActivity == null || listener == null)  {
            Toast.makeText(refActivity, "failed sending to server.", Toast.LENGTH_LONG).show();
        } else {
            if ( result.contains("Succeeded") ) {
                Toast.makeText(refActivity, result, Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(refActivity, result, Toast.LENGTH_LONG).show();
            }
            listener.onSuccess(result);
        }
        return;
    }

    void setListener(Listener listener) {
        this.listener = listener;
    }

    interface Listener {
        void onSuccess(String str);
    }
}

MyActivity.java
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_confirm_before_send);
        buttonOK = findViewById(R.id.buttonOK);
        setListeners();
    }

    /*
     * Send2ServerTaskへのリスナ
     *   サーバーへのアップロード終了後に行う処理を記述
     */
    private Send2ServerTask.Listener createListener() {
        return new Send2ServerTask.Listener() {
            @Override
            public void onSuccess(String result) {
                deleteFiles();
            }
        };
    }

    protected void setListeners(){
        buttonOK.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                /*
                 * HashMap SSHサーバーに送るデータ
                 *   String 送信先ファイルパス
                 *   InputStream 送信ファイルのインプットストリーム
                 */
                HashMap dataMap = new HashMap<>();

                setData2dataMap(); // dataMapにSSH通信データをセットする
                setPassPhraseWord();
                setFileNameFront();

                /*
                 * dataMapを保管サーバーに送る
                 */
                send2serverTask = new Send2ServerTask(thisActivity, dataMap, passPhraseWord, fileNameFront);

                // Listenerを設定し、send2serverTaskを実行してdataMapを保管サーバーに送る
                send2serverTask.setListener(createListener());
                send2serverTask.execute();
            }
        });
    }

httpサーバー リダイレクト設定

2019 Jul. 15.

https://www.jaskun.com/server/xrea-server/xrea301/ より

aaa.com を www.aaa.com にリダイレクトする例


.htaccess に次の3行を記述する
  RewriteEngine on
  RewriteCond %{HTTP_HOST} ^aaa\.com
  RewriteRule ^(.*)$ http://www.aaa.com/$1 [R=301,L]

xrea SSH通信許可設定

2019 Jul. 15.

  1. xreaにログイン
  2. 旧コントロールパネルを表示
  3. 管理メニュー -> ホスト情報登録
  4. 「SSH登録」をクリック

2019年7月13日土曜日

xreaサイトのSSL化

2019 Jul. 13.

参考元

https://damema.net/article/17/
https://www.hobofoto.net/xrea-ssl/

  1. xreaのサイト設定変更で「無料SSL」設定する
  2. xrea画面右上のプルダウンメニューから「旧コンパネ切替」する
  3. マイドメイン利用->ドメインウェブ->SSL設定 で「SSLを有効にするドメイン名」にSSL設定するドメインを選択し、「証明書設定」をクリックする
  4. 4つの大きな窓が内、下部の3つが埋まるまで、キャンセルと前記「証明書設定」クリックを繰り返す。あるいは、参考元サイトに書かれているように、コピー&ペーストする。
    3つの窓が埋まると、下部の「インストール」をクリックする。
    (必要3データ )
     プライベートキー[パスフレーズなし]SSLCertificateKeyFile
     発行された証明書(必須) SSLCertificateFile
     発行された中間証明書(必須) SSLCACertificateFile
  5.  マイドメイン利用->ドメインウェブ画面の下部の「ドメイン設定」をクリックする

xreaに独自ドメイン設定

2019 Jul. 13.


参考元 https://www.homepage-tukurikata.com/domain/coreserver-dns.html
  1. freenomで独自ドメイン取得
  2. freenomで"Manage Domain" ->  "Manage Freenom DNS" -> "Add Records"に進みNameにホスト名(wwwにした)、TTLに3600、TargetにxreaのサーバーIPアドレスを指定
  3. バリュードメインサイトの「無料ネームサーバー」->「他社登録しているドメインを登録」でfreenomで取得したドメインを登録
  4. バリュードメインの「無料ネームサーバー」->「ドメインのDNS設定」で「当サービス内サーバーの自動DNS設定」からxreaで取得したサーバーのドメインを選択
  5. xreaの「ドメイン設定」->「ドメイン設定の新規作成」で "ホスト名.freecomで取得のドメイン" を設定


2019年7月8日月曜日

NHKらじるらじるの録音

2019 Jul. 08.

らじるらじるの配信形式


  • HLS(HTTP Live Streaming) HE-AAC形式
  • M3Uプレイリストフォーマット
  • 各チャンネルのURLはconfig_web.xmlファイルに記載




 録画コマンド

$ ffmpeg -i ChannelAspxUrl -t SECONDS -codec copy OUTFILE.m4a

2019年7月6日土曜日

radikoの音声コーデックは48kbps HE-AAC v2 ステレオ

2019 Jul. 06.

radikoの音声コーデックは「48kbps HE-AAC v2 ステレオ」。
「MPEG-4 AAC Plus SBR」「aacPlus v2」「eAAC+」とも言われる。

2019年6月22日土曜日

JavaMail メモリ上のBitmapオブジェクトのメール添付

2019 Jun. 22.



// BitmapクラスbitmapInstanceをバイト配列byteArrayImageに変換する
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
bitmapInstance.compress(Bitmap.CompressFormat.JPEG, 80, outStream);
byte[] byteArrayImage = outStream.toByteArray();

// byteArrayImageをメールに添付にする
final MimeBodyPart filePart = new MimeBodyPart();
filePart.setDataHandler(new DataHandler( new ByteArrayDataSource(byteArrayImage, "image/jpeg")));
String fileName = "FILE_NAME";
try {
    filePart.setFileName(MimeUtility.encodeWord(fileName));
} catch (UnsupportedEncodingException e) {
    e.printStackTrace();
}

2019年6月8日土曜日

2019年5月30日木曜日

android開発 シンプルなダイアログ表示

2019 Jun. 30.
2019 Jun. 29.
2019 Jun. 22.
2019 May 31.
2019 May 30.

参照元
https://developer.android.com/guide/topics/ui/dialogs
https://akira-watson.com/android/alertdialog.html
http://furudate.hatenablog.com/entry/2014/01/09/162421

[MainActivity]
package YOUR.PACKAGE.alertdialogsample;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;


public class MainActivity extends AppCompatActivity {

    Button button_dialog1, button_dialog2, button_dialog3;
    private TextView text_view;
    private FragmentManager flagmentManager;

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

        text_view = findViewById(R.id.text_view);
        button_dialog1 = findViewById(R.id.button_dialog1);
        button_dialog2 = findViewById(R.id.button_dialog2);
        button_dialog3 = findViewById(R.id.button_dialog3);


        // button_dialog1ボタンタップでAlertを表示させる
        button_dialog1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                flagmentManager = getSupportFragmentManager();

                // DialogFragment を継承したAlertDialogFragment1のインスタンス
                AlertDialogFragment1 dialogFragment = AlertDialogFragment1.newInstance(100);
                // DialogFragmentの表示
                dialogFragment.show(flagmentManager, "test alert dialog1");
            }
        });


        // button_dialog2ボタンタップでダイアログのメッセージを変えたAlertを表示させる
        //   newInstance()への引数を変更してメッセージを変える
        button_dialog2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                flagmentManager = getSupportFragmentManager();

                // DialogFragmentを継承したAlertDialogFragment2のインスタンス
                AlertDialogFragment1 dialogFragment = AlertDialogFragment1.newInstance(200);
                // DialogFragmentの表示
                dialogFragment.show(flagmentManager, "test alert dialog2");
            }
        });


        // button_dialog3ボタンタップでItemを選択するAlertを表示させる
        button_dialog3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                flagmentManager = getSupportFragmentManager();

                // DialogFragment を継承したAlertDialogFragmentのインスタンス
                AlertDialogFragment2 dialogFragment = new AlertDialogFragment2();
                // DialogFragmentの表示
                dialogFragment.show(flagmentManager, "test alert dialog");
            }
        });
    }


    public void setTextView(String message){
        text_view.setText(message);
    }


    /*
     * DialogFragmentを継承したクラスAlertDialogFragment1
     */
    public static class AlertDialogFragment1 extends DialogFragment {

        /*
         * コンストラクタを記述してはならない。
         * newInstance()でのsetArguments、onCreateDialog()でのgetArgumentsを利用する。
         */
        public static AlertDialogFragment1 newInstance( int requestCode) {
            AlertDialogFragment1 fragment = new AlertDialogFragment1();

            Bundle arguments = new Bundle();
            arguments.putInt("reqCode", requestCode);
            fragment.setArguments(arguments);

            return fragment;
        }

        @Override
        @NonNull
        public Dialog onCreateDialog(Bundle savedInstanceState) {

            int requestCode = getArguments().getInt("reqCode");

            AlertDialog.Builder alert = new AlertDialog.Builder(getActivity());

            switch (requestCode) {
                case 100:
                    alert.setTitle("Test AlertDialog1");
                    alert.setMessage("Message is 100");
                    break;

                case 200:
                    alert.setTitle("Test AlertDialog2");
                    alert.setMessage("Message is 200");
                    break;

            }
            alert.setPositiveButton(R.string.dialog_ok, null);

            return alert.create();
        }
    }


    /*
     * DialogFragmentを継承したクラスAlertDialogFragment2
     */
    public static class AlertDialogFragment2 extends DialogFragment {
        // 選択肢のリスト
        private String[] menulist = {"選択(1)","選択(2)","選択(3)"};

        @Override
        @NonNull
        public Dialog onCreateDialog(Bundle savedInstanceState) {

            AlertDialog.Builder alert = new AlertDialog.Builder(getActivity());

            alert.setTitle("Test AlertDialog3");
            alert.setItems(menulist, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int idx) {
                    // 選択1
                    if (idx == 0) {
                        setMassage(menulist[0]);
                    }
                    // 選択2
                    else if (idx == 1) {
                        setMassage(menulist[1]);
                    }
                    // 選択3, idx == 2
                    else{
                        setMassage(menulist[2]);
                    }
                }
            });
            return alert.create();
        }


        private void setMassage(String message) {
            MainActivity mainActivity = (MainActivity) getActivity();
            if (mainActivity != null) {
                mainActivity.setTextView(message);
            }
        }
    }
}

2019年5月26日日曜日

android開発  端末の外部ストレージ内のファイルに対しファイル名部分一致検索

2019 May 26.




package YOUR.PACKAGE.APPLICATION;

/*
 * Re-use of https://akira-watson.com/android/file_search.html
 */

import android.os.Environment;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class SearchExternalStorageFile {

    // 外部ストレージ内のfileStrにマッチするファイルを検索
    public String[] searchFiles( String fileStr ){

        // マッチしたファイルの絶対パスを格納する
        List listMatchedFiles = new ArrayList<>();

        // String 型の ArrayList を生成
        List listDirectory = new ArrayList<>();

        // 外部ストレージのパスを取得する、パスは機種によって異なる
        File file = Environment.getExternalStorageDirectory();
        String storagePath = file.getPath();

        listDirectory.add(storagePath);

        int m = 0;
        int n = 0;
        String[] fileNames;
        String imgPath = null, fName;

        while(listDirectory.size() > m){

            File directory = new File(listDirectory.get(m));
            fileNames = directory.list();

            n = 0;
            while(fileNames.length > n){

                File subFile;
                subFile = new File(directory.getPath() + "/" + fileNames[n]);

                if (subFile.isDirectory()) {
                    listDirectory.add(directory.getPath() + "/" + fileNames[n]);

                } else {
                    if (fileNames[n].contains(fileStr)) {
                        listMatchedFiles.add(directory.getPath() + "/" + fileNames[n]);
                    }
                }
                n++;
            }
            m++;
        }
        int listSize = listMatchedFiles.size();
        return listMatchedFiles.toArray(new String[listSize]);
    }
}

java 時間フォーマット

2019 May 26.

Java で 24時間表記の場合の時間は kk とする。
 例 DateFormat.format("yyyy-MM-dd_kk:mm:ss", dateTaken).toString()

2019年5月25日土曜日

android開発 カメラ起動・写真撮影

2019 May 25.

"AndroidManifest.xml"
 <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="YOUR.PACKAGE.sampletakephotogetimagefile">
    <uses-feature android:name="android.hardware.camera" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.hardware.camera.autofocus" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>




package YOUR.PACKAGE.sampletakephotogetimagefile;
import android.Manifest;
import android.annotation.TargetApi;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.format.DateFormat;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    Button button_take_photo;
    ImageView image_view;
    TextView text_view_uri_path, text_view_file_path, text_view_image_title;
    static final int REQUEST_CAPTURE_IMAGE = 100;
    private final int REQUEST_PERMISSION__EX_STORAGE = 1000;
    private final int REQUEST_PERMISSION_CAMERA = 1010;

    Uri pictureUri;

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

        findViews();

        // check & get permissions
        if(Build.VERSION.SDK_INT >= 23){
            checkPermission();
        }
        if (checkSelfPermission(Manifest.permission.CAMERA) !=
                PackageManager.PERMISSION_GRANTED){
            button_take_photo.setText(R.string.button_unable_phote );
            button_take_photo.setEnabled(false);
        }

        setListeners();
    }


    protected void findViews(){
        button_take_photo = findViewById(R.id.button_take_photo);
        image_view = findViewById(R.id.imageView);
        text_view_uri_path = findViewById(R.id.textViewUriPath);
        text_view_file_path = findViewById(R.id.textViewFilePath);
        text_view_image_title = findViewById(R.id.textViewImageTitle);
    }


    // Permissionの確認
    //   from https://akira-watson.com/android/mediastore.html
    @TargetApi(Build.VERSION_CODES.M)
    public void checkPermission() {
        if (checkSelfPermission(Manifest.permission.CAMERA) !=
                PackageManager.PERMISSION_GRANTED){
            requestCameraPermission();
        }
        if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
                PackageManager.PERMISSION_GRANTED){
            requestExStoragePermission();
        }
    }


    // CameraPermission許可を求める
    @TargetApi(Build.VERSION_CODES.M)
    private void requestCameraPermission() {
        if (shouldShowRequestPermissionRationale( Manifest.permission.CAMERA)) {
            requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_PERMISSION_CAMERA);

        } else {
            Toast toast = Toast.makeText(this, "許可されないとアプリが実行できません",
                    Toast.LENGTH_SHORT);
            toast.show();
            requestPermissions(new String[]{Manifest.permission.CAMERA }, REQUEST_PERMISSION_CAMERA);
        }
    }


    // ExternalStoragePermission許可を求める
    @TargetApi(Build.VERSION_CODES.M)
    private void requestExStoragePermission() {
        if (shouldShowRequestPermissionRationale( Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
            requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    REQUEST_PERMISSION__EX_STORAGE);

        } else {
            Toast toast = Toast.makeText(this, "許可されないとアプリが実行できません",
                    Toast.LENGTH_SHORT);
            toast.show();
            requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE },
                    REQUEST_PERMISSION__EX_STORAGE);
        }
    }


    // requestPermission結果に対する処理
    @Override
    public void onRequestPermissionsResult( int requestCode, @NonNull String[] permissions,
                                            @NonNull int[] grantResults) {
        if (requestCode == REQUEST_PERMISSION_CAMERA) {
            if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                // 拒否された時の対応
                Toast toast = Toast.makeText(this, "これ以上なにもできません",
                        Toast.LENGTH_SHORT);
                toast.show();
            }
        } else if (requestCode == REQUEST_PERMISSION__EX_STORAGE) {
            if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                // 拒否された時の対応
                Toast toast = Toast.makeText(this, "これ以上なにもできません",
                        Toast.LENGTH_SHORT);
                toast.show();
            }
        } else {
            Toast toast = Toast.makeText(this,
                    "Bad requestCode", Toast.LENGTH_SHORT);
            toast.show();
        }
    }


    protected void setListeners(){
        // カメラ起動ボタンが押された時
         button_take_photo.setOnClickListener(new View.OnClickListener() {

             @Override
             public void onClick(View v) {
                /*
                写真用情報を収集し、ContentResolverを使ってandroidに備わっている
                MediaStore.Images.Mediaデータベースに写真用情報を追加し、そのURI
                を取得する
                */
                long dateTaken = System.currentTimeMillis();
                String filename = DateFormat.format("yyyy-MM-dd_kk:mm:ss", dateTaken).toString() + ".jpg";
                ContentResolver contentResolver = getContentResolver();
                ContentValues values = new ContentValues(5);
                values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
                values.put(MediaStore.Images.Media.DATE_MODIFIED, System.currentTimeMillis()/1000);
                values.put(MediaStore.Images.Media.TITLE, filename);
                values.put(MediaStore.Images.Media.DISPLAY_NAME, filename);
                values.put(MediaStore.Images.Media.DATE_TAKEN,System.currentTimeMillis());
                pictureUri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

                // カメラを起動する
                Intent intent = new Intent();
                intent.setAction("android.media.action.IMAGE_CAPTURE");
                intent.putExtra(MediaStore.EXTRA_OUTPUT, pictureUri);
                  // MediaStore.EXTRA_OUTPUTで指定したpictureUriに、撮影後に画像データが書き込まれる
                startActivityForResult(intent, REQUEST_CAPTURE_IMAGE);
             }
         });
    }


    // このアクティビティから起動された他の機能から戻ってきた時の処理
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data ) {

        /*
          正しい結果が得られなかった場合の処理
          撮影キャンセルなどするとこっちに来る
        */
        if (resultCode != RESULT_OK) {

            // カメラ撮影からの復帰の時
            if (requestCode == REQUEST_CAPTURE_IMAGE) {
                Toast toast = Toast.makeText(this,"resultCode != RESULT_OK",
                        Toast.LENGTH_LONG);
                toast.show();
                return;
            }
        }

        /*
         * 写真撮影できた場合
         */
        if (requestCode == REQUEST_CAPTURE_IMAGE) {
            if (data == null ) {
                Toast.makeText(this, "data == null ",
                        Toast.LENGTH_SHORT).show();

            } else if ( data.getData() == null) {
                Toast.makeText(this, "data.getData() == null",Toast.LENGTH_SHORT).show();
            }

            // 撮影した写真を取り出す
            image_view.setImageURI(pictureUri);
            text_view_uri_path.setText("Uri path: " + pictureUri.getPath());

            // get filepath & title from uri
            ContentResolver contentResolver = this.getContentResolver();
            String[] columns =
                {
                    MediaStore.Images.Media.DATA,
                    MediaStore.Images.Media.TITLE
                };
            Cursor cursor = contentResolver.query(pictureUri, columns, null,
                    null, null);
            if(cursor == null) return;
            int pathIndex = cursor.getColumnIndex( MediaStore.Images.Media.DATA);
            int titleIndex = cursor.getColumnIndex( MediaStore.Images.Media.TITLE);
            cursor.moveToFirst();
            String filePath  = cursor.getString(pathIndex);
            String imageTitle = cursor.getString(titleIndex);

            text_view_file_path.setText(filePath);
            text_view_image_title.setText(imageTitle);

            return;
        }
    }
}

android開発 permission取得

2019 May 25.

"AndroidManifest.xml"
 <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="xxx.xxx.sampletakephotogetimagefile">
    <uses-feature android:name="android.hardware.camera" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.hardware.camera.autofocus" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>



if(Build.VERSION.SDK_INT >= 23){
            checkPermission();
}
PROCESS_AFTER_GETTING_PERMISSIONS


// Permissionの確認
//   reuse of https://akira-watson.com/android/mediastore.html
@TargetApi(Build.VERSION_CODES.M)
public void checkPermission() {
    if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED){
        requestCameraPermission();
    }
    if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
        requestExStoragePermission();
    }
}

// CameraPermission許可を求める
@TargetApi(Build.VERSION_CODES.M)
private void requestCameraPermission() {
    if (shouldShowRequestPermissionRationale( Manifest.permission.CAMERA)) {
        requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_PERMISSION_CAMERA);

    } else {
        Toast toast = Toast.makeText(this, "許可されないとアプリが実行できません", Toast.LENGTH_SHORT);
        toast.show();
        requestPermissions(new String[]{Manifest.permission.CAMERA }, REQUEST_PERMISSION_CAMERA);
    }
}

// ExternalStoragePermission許可を求める
@TargetApi(Build.VERSION_CODES.M)
private void requestExStoragePermission() {
    if (shouldShowRequestPermissionRationale( Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
        requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_PERMISSION__EX_STORAGE);

    } else {
        Toast toast = Toast.makeText(this, "許可されないとアプリが実行できません", Toast.LENGTH_SHORT);
        toast.show();
        requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE }, REQUEST_PERMISSION__EX_STORAGE);
    }
}


// requestPermission結果に対する処理
@Override
public void onRequestPermissionsResult( int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (requestCode == REQUEST_PERMISSION_CAMERA) {
        if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
            // 拒否された時の対応
            Toast toast = Toast.makeText(this, "これ以上なにもできません", Toast.LENGTH_SHORT);
            toast.show();
        }
    } else if (requestCode == REQUEST_PERMISSION__EX_STORAGE) {
        if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
            // 拒否された時の対応
            Toast toast = Toast.makeText(this, "これ以上なにもできません", Toast.LENGTH_SHORT);
            toast.show();
        }
    } else {
        Toast toast = Toast.makeText(this, "Bad requestCode", Toast.LENGTH_SHORT);
        toast.show();
    }
}

ファイル修正時刻が現在時刻からn分前以内のファイルを検索

2019 May 25.

$ find DIR/ -mmin -N
    N: さかのぼる時間(単位:分)

2019年5月19日日曜日

ユーザーからの文字列入力を受け取るダイアログ android開発

2019 May 19.

https://www.ipentec.com/document/android-custom-dialog-using-dialogfragment-return-value より

[activity_main.xml]

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textViewPhrase"
        android:layout_width="0dp"
        android:layout_height="66dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.118" />

</android.support.constraint.ConstraintLayout>



[input_text_dialog.xml]

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textViewPhraseLabel"
        android:layout_width="132dp"
        android:layout_height="27dp"
        android:text="Input text"
        app:layout_constraintBottom_toTopOf="@+id/editTextPhrase"
        tools:layout_editor_absoluteX="8dp" />

    <EditText
        android:id="@+id/editTextPhrase"
        android:layout_width="381dp"
        android:layout_height="52dp"
        android:layout_marginTop="268dp"
        android:ems="10"
        android:inputType="text"
        app:layout_constraintTop_toTopOf="parent"
        tools:layout_editor_absoluteX="8dp" />

    <Button
        android:id="@+id/buttonOK"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="52dp"
        android:layout_marginEnd="8dp"
        android:text="OK"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/editTextPhrase" />
</android.support.constraint.ConstraintLayout>



MainActivity.java
package YOUR.PACKAGE;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    boolean is1stRun = true;

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

        if ( is1stRun ) {
            // get text by dialog
            InputTextDialogFragment cdf = new InputTextDialogFragment();
            cdf.show(getSupportFragmentManager(),"dialog");
            is1stRun = false;
        }
    }


    public void onReturnValue(String value) {
        TextView textViewPhrase = (TextView)findViewById(R.id.textViewPhrase);
        textViewPhrase.setText(value);
    }

}

InputTextDialogFragment.java
package YOUR.PACKAGE;

import android.app.Dialog;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.EditText;

public class InputTextDialogFragment extends DialogFragment {
    EditText editTextPhrase;

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Dialog dialog = new Dialog(getActivity());
        dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
        dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
        dialog.setContentView(R.layout.input_text_dialog);

        editTextPhrase = (dialog.findViewById(R.id.editTextPhrase));

        dialog.findViewById(R.id.buttonOK).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                String textPhrase = editTextPhrase.getText().toString();
                MainActivity callingActivity = (MainActivity) getActivity();
                callingActivity.onReturnValue(textPhrase);

                dismiss();
            }
        });

        return dialog;
    }
}

2019年5月6日月曜日

Android Studioのエラー対処

2019 May 06.

対処法
Settings -> Instant Run -> Set "Instant Run" false

エラー表示内容
execution failed for task
execute transform
java.lang.RuntimeException: java.lang.RuntimeException: java.io.FileNotFoundException: /home/xxx/AndroidStudioProjects/MyApp/app/build/intermediates/instant_run_split_apk_resources/debug/instantRunSplitApkResourcesDebug/out/slice_9/resources_ap

android開発 外部ストーリッジ/SDカードのパスの取得

2019 May 06.

https://qiita.com/h_yama37/items/11b8658b2de9625200aa より


private final Context AppContext;
private final String file;

// android端末内の外部ストーリッジ確認
List sdCardFilesDirPaths = SdCardDirPaths.getSdCardFilesDirPathListForLollipop( AppContext );
Collections.sort(sdCardFilesDirPaths, new CompStringLength());
for (String p : sdCardFilesDirPaths) {
    Log.d("My", "SDパスの1つ: " + p);
}
externalPath = sdCardFilesDirPaths.get(0);
externalPath = externalPath.replaceAll("/Android.*$", "");
Log.d("My", "SDパス: " + externalPath);

// get filePath
final String sourcePath = externalPath + "/" + file;
Log.d("My", "sourcePath " + sourcePath );
if ( new File(sourcePath).exists()) {
    Log.d("My", sourcePath + " exists.");
} else {
    Log.d("My", sourcePath + " not exist.");
}

SdCardDirPaths.java
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.os.Environment;
import android.util.Log;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class SdCardDirPaths {
    /**
     * SDカードのfilesディレクトリパスのリストを取得する。
     * Android5.0以上対応。
     *
     * @param context
     * @return SDカードのfilesディレクトリパスのリスト
     */
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public static List getSdCardFilesDirPathListForLollipop(Context context) {
        List sdCardFilesDirPathList = new ArrayList<>();

        // getExternalFilesDirsはAndroid4.4から利用できるAPI。
        // filesディレクトリのリストを取得できる。
        File[] dirArr = context.getExternalFilesDirs(null);


        for (File dir : dirArr) {
            if (dir != null) {
                String path = dir.getAbsolutePath();

                Log.d("My", "Externalパスの1つ: " + path);

                // isExternalStorageRemovableはAndroid5.0から利用できるAPI。
                // 取り外し可能かどうか(SDカードかどうか)を判定している。
                if (Environment.isExternalStorageRemovable(dir)) {

                    // 取り外し可能であればSDカード。
                    if (!sdCardFilesDirPathList.contains(path)) {
                        sdCardFilesDirPathList.add(path);
                    }

                } else {
                    // 取り外し不可能であれば内部ストレージ。
                }
            }
        }
        return sdCardFilesDirPathList;
    }
}

CompStringLength.java
import java.util.Comparator;

public class CompStringLength implements Comparator {
    @Override
    public int compare(String first, String second){
        //null評価
        //両方nullなら等価とする
        if(first == null && second == null){
            return 0;
        }
        //片方nullなら、nullを小さいとする。
        if(first == null){
            return -1;
        }else if(second == null){
            return 1;
        }

        //idの文字列長でソート。文字列数がが小さい順に並べる。
        if(first.length() > second.length()){
            return 1;
        }else if(first.length() < second.length()){
            return -1;
        }else if(first.length() == second.length()){
            return 0;
        }
        return 0;
    }
}

2019年5月3日金曜日

android開発 メモリリークを防ぐWeakReference

2019 May 03.


FirstActivity.java
(OnClickListener等は省略)


public class FirstActivity extends AppCompatActivity {
  Button button1 = (Button) findViewById(R.id.button1);
  button1.setEnabled(true);
  TextView textView1 = (TextView) findViewById(R.id.textView1);
  textView1.setText("start")

  new SecondTask(this).execute();
}

SecondTask.java
public class SecondTask extends AsyncTask {
  private WeakReference weakReference;

  /**
   * make WeakReference in constructor
   *   参照しているActivityを引数にしてWeakReferenceのインスタンスを生成する
  **/
  SecondTask(Context referenceContext) {
    private WeakReference weakReference;
    weakReference = new WeakReference<>(referenceContext);
  }

  /**
   * use of WeakReference in onPostExecute
   *   weakReference.get()で参照しているActivityのContextを取得し、それを通じてView を設定したりする
  **/
  @Override
  protected void onPostExecute(String result) {
    Activity activity = (Activity) weakReference.get();

    if (activity == null || activity.isFinishing()) return;

    activity.findViewById(R.id.button1).setEnabled(false);
    TextView tv = activity.findViewById(R.id.textView1);
    tv.setText("end");
  }
}

2019年5月2日木曜日

JAVA JSch SFTP AndroidクライアントからSSHサーバーへのファイル転送

2019 May. 03.
2019 May. 02.

https://qiita.com/nenokido2000/items/a00348c9f6a0f942773b より




MainActivity.java

package YOUR.PACKAGE.NAME;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {


    /** Channel接続タイプ */
    private static final String CHANNEL_TYPE = "sftp";

    private MyTask task;

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

        setContentView(R.layout.activity_main);

        /*
        final String ServerIP = "ServerIP";
        final int Port = PortNum;
        final String UserId = "USER";
        final String PassPhrase = "YOURPASSPHRASE";
        final String IdentityKeyPath = "YOUR/SECRET/FILE/PATH"; // Do not attach '/' at head of path
        final String SourcePath = "PATH/TO/SOURCE/FILE"; // Do not attach '/' at head of path
        final String DestPath = "PATH/TO/DEST/FILE";
        */

        final String ServerIP="";
        final int Port = ;
        final String UserId = "";
        final String PassPhrase = "";
        final String IdentityKeyPath = "";
        final String SourcePath = "";
        final String DestPath = "";

        Button button1= (Button) findViewById(R.id.button1);
        TextView textView1 = (TextView) findViewById(R.id.textView1);

        // タスクの生成
        task = new MyTask(ServerIP, Port, UserId, PassPhrase, IdentityKeyPath, SourcePath, DestPath,this);

        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                v.setEnabled(false);
                // 非同期処理を開始する
                task.execute();
            }
        });
    }
}


MyTask.java
package YOUR.PACKAGE.NAME;
import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.widget.TextView;

import com.jcraft.jsch.JSchException;

import java.lang.ref.WeakReference;

public class MyTask extends AsyncTask {
    private final String ServerIp;
    private final int Port;
    private final String User;
    private final String IdentityKeyPath;
    private final String PassPhrase;
    private final String SrcPath;
    private final String DestPath;

    private WeakReference weakReference;


    // constructor
    MyTask(final String ServerIp, final int Port, final String User,
                  final String PassPhrase, final String IdentityKeyPath, final String SrcPath,
                  final String DestPath, Context referenceContext) {
        super();

        // 呼び出し元へのweakReference
        weakReference = new WeakReference<>(referenceContext);

        this.ServerIp = ServerIp;
        this.Port = Port;
        this.User = User;
        this.PassPhrase = PassPhrase;
        this.IdentityKeyPath = IdentityKeyPath;
        this.SrcPath = SrcPath;
        this.DestPath = DestPath;
    }



    /**
     * バックグランドで行う処理
     */
    @Override
    protected String doInBackground(Void... value) {
        // startJsch
        MyJsch mJsch = new MyJsch( ServerIp, Port, User, PassPhrase, IdentityKeyPath, SrcPath,
                DestPath, weakReference.get());
        try {
            mJsch.putFile();
        } catch (JSchException e) {
            e.printStackTrace();
            return e.toString();
        }
        return "Success";
    }


    /**
     * バックグランド処理が完了し、UIスレッドに反映する
     */
    @Override
    protected void onPostExecute(String result) {

        // get a reference to the activity if it is still there
        Activity activity = (Activity) weakReference.get();
        if (activity == null || activity.isFinishing()) return;

        activity.findViewById(R.id.button1).setEnabled(true);
        TextView tv = activity.findViewById(R.id.textView1);
        tv.setText(result);
    }

}


MyJsch.java
package YOUR.PACKAGE.NAME;
import android.content.Context;
import android.util.Log;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
import com.jcraft.jsch.UserInfo;

import java.io.File;
import java.util.Collections;
import java.util.List;

public class MyJsch {
    private final String ServerIp;
    private final int Port;
    private final String User;
    private final String PassPhrase;
    private final String IdentityKeyPath;
    private final String SrcPath;
    private final String DestPath;
    private Context activityContext;

    private String externalPath;

    /** Channel接続タイプ */
    private static final String CHANNEL_TYPE = "sftp";

    /**
     * コンストラクタ
     */
    MyJsch(final String ServerIp, final int Port, final String User, final String PassPhrase,
           final String IdentityKeyPath, final String SrcPath, final String DestPath,
           Context activityContext) {
        this.ServerIp = ServerIp;
        this.Port = Port;
        this.User = User;
        this.IdentityKeyPath = IdentityKeyPath;
        this.PassPhrase = PassPhrase;
        this.SrcPath = SrcPath;
        this.DestPath = DestPath;
        this.activityContext = activityContext;
    }


    /**
     * ファイルアップロード
     *
     * @throws JSchException
     *             Session・Channelの設定/接続エラー時に発生
     */
    public void putFile()
            throws JSchException {

        Session session = null;
        ChannelSftp channel = null;


        // 外部ストーリッジ確認
        List sdCardFilesDirPaths =
                SdCardDirPaths.getSdCardFilesDirPathListForLollipop( activityContext );
        Collections.sort(sdCardFilesDirPaths, new CompStringLength());
        for (String p : sdCardFilesDirPaths) {
            Log.d("My", "SDパスの1つ: " + p);
        }
        externalPath = sdCardFilesDirPaths.get(0);
        externalPath = externalPath.replaceAll("/Android.*$", "");
        Log.d("My", "SDパス: " + externalPath);

        // get sourcePath
        final String sourcePath = externalPath + "/" + SrcPath;
        Log.d("My", "sourcePath " + sourcePath );
        if ( new File(sourcePath).exists()) {
            Log.d("My", sourcePath + " exists.");
        } else {
            Log.d("My", sourcePath + " not exist.");
        }

        /*
         * sftp通信を実行
        */
        try {
            session = connectSession();
            channel = connectChannelSftp(session);

            String absoluteDestPath = channel.getHome() + "/" + DestPath;
            String destFile = new File(absoluteDestPath).getName();
            int numDestPath = absoluteDestPath.length();
            int numDestFile = destFile.length();
            String destParentPath = absoluteDestPath.substring( 0, numDestPath - numDestFile );

            Log.d("My", "absoluteDestPath is " + absoluteDestPath);
            Log.d("My", "destFile is " + destFile);
            Log.d("My", "destParentPath is " + destParentPath);
            Log.d("My", "current local directory is " + channel.lpwd());
            Log.d("My", "remote home directory is " + channel.getHome());

            channel.cd(destParentPath);
            channel.put(sourcePath, destFile);


            try {
                channel.lstat(destFile);
            } catch (SftpException e) {
                Log.d("My", DestPath + " does not exist.");
                Log.d("My", e.toString());
            }

        } catch (SftpException e) {
            Log.d("My",e.toString());
        } finally {
            disconnect(session, channel);
        }
    }

    /**
     * Sessionを開始
     */
    private Session connectSession()
            throws JSchException {

        final JSch jsch = new JSch();

        // 鍵追加
        String keyFilePath = externalPath + "/" + IdentityKeyPath;
        if ( new File(keyFilePath).exists()) {
            Log.d("My",  keyFilePath + " exists.");

        } else {
            Log.d("My",  keyFilePath + " not exist.");
        }

        jsch.addIdentity(keyFilePath, PassPhrase);

        // Session設定
        final Session session = jsch.getSession(User, ServerIp, Port);
        final UserInfo userInfo = new SftpUserInfo();

        // TODO 今回は使用しないがパスフレーズ等が必要な場合はUserInfoインスタンス経由で設定する

        session.setUserInfo(userInfo);

        session.connect();

        return session;
    }

    /**
     * SFTPのChannelを開始
     *
     * @param session
     *            開始されたSession情報
     */
    private ChannelSftp connectChannelSftp(final Session session)
            throws JSchException {
        final ChannelSftp channel = (ChannelSftp) session.openChannel(CHANNEL_TYPE);
        try {
            channel.connect();

        } catch (JSchException e) {
            Log.d("My",   e.toString());
        }
        return channel;
    }


    /**
     * Session・Channelの終了
     *
     * @param session
     *            開始されたSession情報
     * @param channels
     *            開始されたChannel情報.複数指定可能
     */
    private void disconnect(final Session session, final Channel... channels) {
        if (channels != null) {
            for (Channel c: channels ) {
                if (c != null) {
                    c.disconnect();
                }
            }
        }
        if (session != null) {
            session.disconnect();
        }
    }

    /**
     * SFTPに接続するユーザ情報を保持するクラス
     */
    private static class SftpUserInfo implements UserInfo {

        @Override
        public String getPassword() {
            return null;
        }

        @Override
        public boolean promptPassword(String arg0) {
            return true;
        }

        @Override
        public boolean promptPassphrase(String arg0) {
            return true;
        }

        @Override
        public boolean promptYesNo(String arg0) {
            return true;
        }

        @Override
        public void showMessage(String arg0) {
        }

        @Override
        public String getPassphrase() {
            return null;
        }
    }
}

JAVA JSch sftp sshサーバーへのファイル転送時のサーバー側ディレクトリ・ファイルの指定要領

2019 May 02.

sftp接続後、サーバーのカレントディレクトリを転送先ディレクトリに変更(ChannelSftp#cd)した上で、ファイル転送する。
ファイル転送コマンド(ChannelSftp#put)内でパスを記述できない。

2019年4月14日日曜日

cron 第何曜日に実行

2019 Apr. 14.

参照元 https://orebibou.com/2015/11/cron%e3%81%a7%e7%ac%ac%e4%bd%95%e4%bd%95%e6%9b%9c%e6%97%a5%e3%81%ab%e3%82%b9%e3%82%af%e3%83%aa%e3%83%97%e3%83%88%e3%82%92%e5%ae%9f%e8%a1%8c%e3%81%99%e3%82%8b%e3%81%8b%e6%8c%87%e5%ae%9a%e3%81%99/

# 第1月曜の深夜3時にコマンド実行
0 3 1-7 * * root [ $(date "+\%w") -eq 1 ] && コマンド
 
# 第2水曜の深夜3時に以下略
0 3 8-14 * * root [ $(date "+\%w") -eq 3 ] && コマンド
 
# 第3木曜の以下略
0 3 15-21 * * root [ $(date "+\%w") -eq 4 ] && コマンド
 
# 第4日曜の以下略
0 3 22-28 * * root [ $(date "+\%w") -eq 0 ] && コマンド

RADIO TxT FM dramacityを録音する

2019 May 16.
2019 Apr. 14.

RADIO TxTを1時間2分3秒間録音するコマンド例

時:分:秒 方式
$ ffmpeg -i http://mtist.as.smartstream.ne.jp/30044/livestream/playlist.m3u8 -t 01:02:03  -movflags faststart -vn -acodec copy OUTFILE.m4a

秒 方式
$ ffmpeg -i http://mtist.as.smartstream.ne.jp/30044/livestream/playlist.m3u8 -t  3723 -movflags faststart -vn -acodec copy OUTFILE.m4a


 -tオプションは-iオプションよりも後ろに記述する

RADIO TxT FM dramacityをvlcで聞く

2019 Apr. 14.


放送されているリスラジ( http://listenradio.jp/ )の放送局情報を入手する。

リスラジ放送局情報
 http://listenradio.jp/service/channel.aspx

RADIO TxT FM dramacity の放送局情報を入手する。


vlcの「ネットワークストリームを開く」メニューで http://mtist.as.smartstream.ne.jp/30044/livestream/playlist.m3u8 を開く。

 

リスラジの各放送局のストリームURL

http://fsyublog.blog13.fc2.com/blog-entry-1214.html より )
http://mtist.as.smartstream.ne.jp/CH-ID/livestream/playlist.m3u8
 CH-IDの部分に放送局のChannelIdを指定する。


Radiko 放送局ID 一覧

2019 Apr. 14.

https://www.norikistudio.com/station-id-list
http://www.arugoworks.net/korec/stationid

動画から音声を無劣化抽出

2019 Apr. 14.

音声フォーマットを確認する

$ ffmpeg -i INFILE 2>&1 | grep Audio

出力ファイルの拡張子に音声フォーマットに応じたものを指定して、音声を抽出する

$ ffmpeg -i INFILE -vn -acodec copy OUTFILE.extension

2019年4月13日土曜日

ruby 正規表現 マッチした行数を取得

2019 Apr. 13.

# テキストFILE内で hoge を含む行数を取得する
WORD = "hoge"
all_text = File.read(FILE)
count_match=all_text.scan(/^.*#{WORD}/).size

2019年4月6日土曜日

OpenVPN接続失敗 crl.pem期限切れ VERIFY ERROR CRL has expired

2019 Apr. 06.

OpenVPN接続失敗ログ

TLS: Initial packet from [AF_INET]xxx.xxx.xxx.xxx.:pppp, sid=xxxx
VERIFY ERROR: depth=0, error=CRL has expired: CN=xxxx
 OpenSSL: error:1417C086:SSL routines:tls_process_client_certificate:certificate verify failed
TLS_ERROR: BIO read tls_read_plaintext error
TLS Error: TLS object -> incoming plaintext read error
TLS Error: TLS handshake failed

対策

 参考サイト( https://blog.cosnomi.com/archives/1031 )

手順

OpenVPNをインストールした時のCA証明書(認証局証明書)生成時のパスフレーズを用意する。

  次の要領でbuild-caコマンドを実行した時に入力したパスフレーズ
    $ cd YOUR/WORK/DIR/easy-rsa/easyrsa3
    $ ./easyrsa build-ca

crl.pem(証明書失効リスト)の再生成

$ cd YOUR/WORK/DIR/easy-rsa/easyrsa3
$ ./easyrsa gen-crl
  パスフレーズを尋ねられるので、CA証明書のパスフレーズを入力する。
$ sudo cp ./pki/crl.pem /etc/openvpn/
$ sudo chmod o+r /etc/openvpn/crl.pem

OpenVPNの再起動

$ sudo systemctl restart openvpn.service
$ sudo systemctl restart openvpn@server.service

2019年3月30日土曜日

android開発 assets内のファイルを外部ストレージに書き出す

2019 Mar. 30.

サンプル

// assets内のファイル名
private String fileName = "sample-image.jpg";

// 外部ストレージのパス
private String extFilePath;

extFilePath = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/" + fileName;


InputStream inputStream = getResources().getAssets().open(fileName);
// 外部ストレージのパスにファイルを保存
FileOutputStream output =new FileOutputStream(extFilePath)) {
    // バッファーを使って画像を書き出す
    int DEFAULT_BUFFER_SIZE = 10240 * 4;
    byte buf[]=new byte[DEFAULT_BUFFER_SIZE];
    int len;
    while((len=inputStream.read(buf)) != -1){
        output.write(buf,0,len);
    }
    output.flush();

2019年3月25日月曜日

ubuntu18.04でのネームサーバーアドレス設定

2019 Mar. 25.

 

NetworkManagerを設定する


NetworkManagerを使わない場合は、/etc/netplan/以下の


01-network-manager-all.yaml あたりを編集する

 /etc/netplan/01-network-manager-all.yaml

network:
  version: 2
  renderer: networkd
  ethernets:
    enp0s7:
      dhcp4: no
      dhcp6: no
      addresses: [192.168.1.10/24]
      gateway4: 192.168.1.1
      nameservers:
        addresses: [192.168.1.1, 8.8.8.8, 8.8.4.4]

2019年3月24日日曜日

aacオーディオのファイルのファイルフォーマットはadts

2019 Mar. 24.

aacオーディオのファイルのファイルフォーマットはadts

$ ffmpeg -ss 5 -i INFILE.m4a -t 10 -f adts -acodec aac -absf aac_adtstoasc -strict -2 -threads 2 OUTFILE..m4a

2019年3月10日日曜日

2019年2月8日金曜日

Google code-prettifyによるBloggerでのコード埋め込み

2019 Jun. 30.
2019 May 02.
2019 Feb 08.

参照元
https://torina.top/detail/460/
http://labs.kamimoto.biz/javascript/google-code-prettify-linenums.html

・HTML編集モードで編集する。

・記述するタグ

  • <script></script> ページ先頭に記述
  • <style></style>  行番号・スクロールバー設定
  • <pre class="prettyprint linenums"></pre> 本文記述 


・HTML構成

<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script><style>
.prettyprint ol.linenums > li {
list-style-type: decimal;
}
pre.prettyprint {
white-space: pre;
overflow: auto;
}
</style>

<pre class="prettyprint linenums">
コンテンツ1
</pre>

<pre class="prettyprint linenums">
コンテンツ2
</pre>

・行番号表示

<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>

 <style>
.prettyprint ol.linenums > li {
  list-style-type: decimal;
}
</style>
<pre class="prettyprint linenums">
 YOUR CODE HERE
</pre>

・横スクロールバー

<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>

<style>
pre.prettyprint {
  white-space: pre;
  overflow: auto;
}
</style>
<pre class="prettyprint linenums">
 YOUR CODE HERE
</pre>

2019年2月2日土曜日

JAVA SSHサーバーとの間で公開鍵認証でファイルをやり取りする

2019 Feb. 02.

https://qiita.com/nenokido2000/items/a00348c9f6a0f942773b より


import com.jcraft.jsch.*;
import java.util.Arrays;
import java.util.Hashtable;
public class Main {

    /** Channel接続タイプ */
    private static final String CHANNEL_TYPE = "sftp";

    /**
     * ファイルアップロード
     *
     * @param hostname
     *            接続先ホスト
     * @param port
     *            接続先ポート
     * @param userId
     *            接続するユーザ
     * @param identityKeyFileName
     *            鍵ファイル名
     * @param sourcePath
     *            アップロード対象ファイルのパス<br>
     *            アプリ実行環境上の絶対パスを指定
     * @param destPath
     *            アップ先のパス
     * @throws JSchException
     *             Session・Channelの設定/接続エラー時に発生
     * @throws SftpException
     *             sftp操作失敗時に発生
     */
    public void putFile(final String hostname, final int port,
                        final String userId, final String identityKeyFileName,
                        final String passPhrase,
                        final String sourcePath, final String destPath,
                        final String remotePath, final String localPath)
            throws JSchException, SftpException {

        Session session = null;
        ChannelSftp channel = null;

        try {
            session = connectSession(hostname, port, userId, identityKeyFileName, passPhrase);
            channel = connectChannelSftp(session);
            channel.put(sourcePath, destPath);
            channel.get(remotePath, localPath);
        } finally {
            disconnect(session, channel);
        }
    }

    /**
     * Sessionを開始
     *
     * @param hostname
     *            接続先ホスト
     * @param port
     *            接続先ポート
     * @param userId
     *            接続するユーザ
     * @param identityKeyFileName
     *            鍵ファイル名
     */
    private Session connectSession(final String hostname, final int port,
                                   final String userId, final String identityKeyFileName,
                                   final String passPhrase)
            throws JSchException {

        final JSch jsch = new JSch();

        // HostKeyチェックを行わない
        Hashtable config = new Hashtable();
        config.put("StrictHostKeyChecking", "no");
        jsch.setConfig(config);

        // 鍵追加
        jsch.addIdentity(identityKeyFileName, passPhrase );

        // Session設定
        final Session session = jsch.getSession(userId, hostname, port);
        final UserInfo userInfo = new SftpUserInfo();

        // TODO 今回は使用しないがパスフレーズ等が必要な場合はUserInfoインスタンス経由で設定する

        session.setUserInfo(userInfo);

        session.connect();

        return session;
    }

    /**
     * SFTPのChannelを開始
     *
     * @param session
     *            開始されたSession情報
     */
    private ChannelSftp connectChannelSftp(final Session session)
            throws JSchException {
        final ChannelSftp channel = (ChannelSftp) session
                .openChannel(CHANNEL_TYPE);
        channel.connect();

        return channel;
    }

    /**
     * Session・Channelの終了
     *
     * @param session
     *            開始されたSession情報
     * @param channels
     *            開始されたChannel情報.複数指定可能
     */
    private void disconnect(final Session session, final Channel... channels) {
        if (channels != null) {
            Arrays.stream(channels).forEach(c -> {
                if (c != null) {
                    c.disconnect();
                }
            });
        }
        if (session != null) {
            session.disconnect();
        }
    }

    /**
     * SFTPに接続するユーザ情報を保持するクラス
    */
    private static class SftpUserInfo implements UserInfo {

        @Override
        public String getPassword() {
            return null;
        }

        @Override
        public boolean promptPassword(String arg0) {
            return true;
        }

        @Override
        public boolean promptPassphrase(String arg0) {
            return true;
        }

        @Override
        public boolean promptYesNo(String arg0) {
            return true;
        }

        @Override
        public void showMessage(String arg0) {
        }

        @Override
        public String getPassphrase() {
            return null;
        }
    }


    public static void main(String[] args) throws JSchException, SftpException {

        String Hostname="SSH-SERVER-IP-ADDRESS";
        int Port=22;
        String UserId="USER";
        String PassPhrase = "YOUR-PASSPHRASE";
        String IdentityKeyFileName=System.getProperty("user.home")+"/PRIVATEKEY";
        String SourcePath=System.getProperty("user.home")+"/PATH/TO/FILE";
        String DestPath="PATH/TO/DESTFILE";
        String RemotePath="PATH/TO/REMOTEFILE";
        String LocalPath=System.getProperty("user.home")+"/ANOTHER/PATH/TO/FILE";

        final Main sftp = new Main();
        sftp.putFile(Hostname, Port, UserId, IdentityKeyFileName, PassPhrase,
                SourcePath, DestPath, RemotePath, LocalPath);
    }
}