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)内でパスを記述できない。