Java活用生活

ログイン

オンライン状況

オンラインユーザー3人
ログインユーザー0人
登録ユーザー2人

カウンタ

COUNTER355634

Androidスニペット

Androidスニペット
123456
2024/03/06

非推奨のstartActivityForResultの呼び出し元判別

固定リンク | by:aramaki
startActivityForResultの呼び出し
が非推奨となったが、代替のActivityResult API
では、※リクエストコードを利用して、呼び出し元を
判別する仕組みはなかったので、自分でクラスを
作成して動作確認しました。

※requestCodeは、ActivityResult API側で作成し
インクリメントして利用している。

今回は、ボタン押下で、ギャラリー呼び出しをする。
そのとき、ユーザーは、リクエストコード(reqest_code)を
設定する。
ギャラリーから画像を選択し、呼び出し元に戻ったら、
トーストで、ユーザーが設定したリクエストコードを
表示するという流れです。


       

MainActivity.java
---------------------------------------
public class MainActivity extends AppCompatActivity {



    private Button btnStart;
    /** ギャラリー表示用リクエストコード */
    private static final int GALLARY_INTENT_CALLED = 302;

    /** アクティビティコール用のリクエストコード */
    private static final int ACTIVITY_CALLED = 201;


    private final ActivityResultLauncher<Intent> activityResultLauncher
            = registerForActivityResult(
                    new MyResultContract(),
                new ActivityResultCallback<ActivityResult>() {
                    @Override
                    public void onActivityResult(ActivityResult result) {
                        Intent intent=result.getData();
                        int code=intent.getIntExtra("request_code",0);

                        // --------------------------
                        // code(呼び出し先判別のコード)により処理を分岐する
                        // ---------------------------
                        switch (code){
                            case GALLARY_INTENT_CALLED:
                                // 処理 ①
                                break;
                            case ACTIVITY_CALLED:
                                // 処理 ②
                                break;
                            default:
                                break;
                        }


                        Toast.makeText(MainActivity.this, "onActivityResult requestCode = " +code, Toast.LENGTH_SHORT).show();
                    }

                });



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


        btnStart=findViewById(R.id.startBtn);
        btnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(Intent.ACTION_OPEN_DOCUMENT);
                //端末標準のギャラリーから取得
               intent.addCategory(Intent.CATEGORY_OPENABLE);
                // マイムタイプ
                intent.setType("image/jpeg");

                // ギャラリー起動
                Intent chooserIntent=Intent.createChooser(intent,getString(MainActivity.this,R.string.gallery_select)); // ギャラリーのタイトルが反映あされていませんが、原因は、後で調査します。

                chooserIntent.putExtra("recCode",GALLARY_INTENT_CALLED);

                activityResultLauncher.launch(chooserIntent);

            }
        });
    }
    public String getString(Context context, int resId) {
        return context.getResources().getString(resId);
    }
    /**
     * code(呼び出し元判別コード)を保持し、呼び出し元へ返却するクラス
     */
    class MyResultContract extends ActivityResultContract<Intent,ActivityResult> {

        private int callRequestCode;

        @NonNull
        @Override
         public Intent createIntent(@NonNull Context context, Intent input) {
            Intent intent=input;
           int code=intent.getIntExtra("request_code",0);
            callRequestCode=code;
            return intent;
        }


        @Override
        public ActivityResult parseResult(int resultCode, @Nullable Intent intent) {// intentは選択した画像のパスが入っている。
            if(resultCode==Activity.RESULT_OK){
               // 呼び出し元のリクエストコード
                Intent resIntent=intent;

                resIntent.putExtra("request_code",callRequestCode);
                ActivityResult activityResult=new ActivityResult(RESULT_OK,resIntent);

                return activityResult;

            }else{
                return null;
            }

        }

    }


}

10:56
2024/03/03

実行時のエラー対応につて

固定リンク | by:aramaki
エミュレータで、アプリを実行しようとすると、以下のようなエラーが出た。
-----------------------------------------------------------------------------------------------------------
Dependency 'androidx.appcompat:appcompat-resources:1.6.1' requires libraries and applications that
      depend on it to compile against version 33 or later of the
      Android APIs.
      
      
      略

対応)
build.gradleのcompileSdkとtargetSdkを32から34に変更

これで実行するとできた。
変更時は。「Sync Now」をクリックするのをわすれないように!
21:30
2024/01/28

サービスの起動確認をする。

固定リンク | by:aramaki
裏方で動くサービスが起動しているか確認するため使用していた「ActivityManager#getRunningServices」が非推奨となったため、LocalBroadcastManager
を利用して確認するサンプルを作ってみた。

以下がその実装コードです。MainActivityから、TimerService(ユーザーが、指定した時間(分)このサービスが10秒経過するごとに「〇分〇秒経過しました」とトースト表示する。その時、サービス起動確認ボタンを押下すると、「サービス起動中」とトースト表示する。)サービスをコールする。
------------------------------------------------------------------------------------
1・マニフェストにサービスを登録
<service android:name=".TimerService"></service>

2.strings.xmlに以下を追加
<string name="confirm">何分後に終了しますか</string>
 <string name="minute">分</string>
 <string name="service_start">サービス開始</string>
  <string name="service_check">サービス起動確認</string>

3.サービスクラス TimerService.java

public class TimerService extends Service {

    /** タイマー */
    private Timer mTimer=null;

    /** 経過時間 */
    private int countTime;

    /**  終了時間 */
    private int stopTime;

    /** サービスへブロードキャストするためのマネージャー */
    public static LocalBroadcastManager localBroadcastManager=null;

    /** レシーバー */
    private BroadcastReceiver broadcastReceiver=null;

    private static Context self;


    /** 経過時刻表示ハンドラー */
    // myLooperは、現在のスレッドに関連付けられている Looper オブジェクトを返します
    private Handler handler=new Handler(Looper.myLooper()){
        @Override
        public void handleMessage(@NonNull Message msg) {

            Toast.makeText(TimerService.this, (String)msg.obj, Toast.LENGTH_SHORT).show();
        }
    };


    /** タイマータスクを生成する */
    private TimerTask task=new TimerTask() {
        @Override
        public void run() {
            // 10秒ごとにカウントアップ
            countTime+=10;

            if(countTime/60==stopTime){
                // サービス終了
                stopSelf();
            }else{
                // 時間経過を表示するハンドリング設定
                // handlerにメッセージ送信
                handler.sendMessage(Message.obtain(handler,0,countTime/60+"分"+countTime % 60 +"秒経過しました!"));

            }
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Toast.makeText(this, "サービスを初期起動します", Toast.LENGTH_SHORT).show();
        //タイマーと経過時間初期化
        mTimer=new Timer();

        localBroadcastManager=LocalBroadcastManager.getInstance(getApplicationContext());

        self=getApplicationContext();

        countTime=0;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
       // super.onStartCommand(intent, flags, startId);
       // --------------------------
       // サービス開始
        Toast.makeText(this,"サービスを開始します",Toast.LENGTH_SHORT).show();

        // タイマーを設定する。
        //第1パラメータのタスクを起動後1秒後、10秒間隔で実行
        mTimer.schedule(task,1000,10000);

        // 終了時間を呼び出し元発行のIntentから取得する。
        Bundle bundle=intent.getExtras();
        stopTime=Integer.parseInt(bundle.getString("STOPTIME"));

        // レシーバーを生成する
        if(broadcastReceiver==null){
            broadcastReceiver=new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    Toast.makeText(self,"ブロードキャスト受信しました",Toast.LENGTH_SHORT).show();

                }
            };
        }
        // 受信対象サービスを判別するフィルターを生成する。
        final IntentFilter filter=new IntentFilter();
        filter.addAction("MESSAGE_SEND");
        localBroadcastManager.registerReceiver(broadcastReceiver,filter);


        return Service.START_STICKY;

    }

    @Override
    public void onDestroy() {
        // サービス通信用のレシーバー解除
        localBroadcastManager.unregisterReceiver(broadcastReceiver);

        super.onDestroy();

        //Toast表示
        Toast.makeText(this, "サービスを終了します", Toast.LENGTH_SHORT).show();

        // Timer設定解除
        mTimer.cancel();;
        mTimer.purge(); // タスクキューから不要なタスクを削除したい
    }

    public static Boolean isActiveService(){
        boolean result=false;
        if(localBroadcastManager==null){
            return result;
        }
        Intent intent=new Intent();
        intent.setAction("MESSAGE_SEND");
        result=localBroadcastManager.sendBroadcast(intent);
        String isActive=null;
        if(result==true){
            isActive="サービスは起動中です。";
        }else if(result==false){
            isActive="サービスは停止中です。";
        }
        // Toastの表示レイヤーは通常のアプリより優先度が高いため、サービスから、直接表示する。
        Toast.makeText(self,isActive,Toast.LENGTH_SHORT).show();
        return result;
    }
}



22:57
2023/06/17

PreferenceManagerの@Deprecated対応

固定リンク | by:aramaki
PreferenceManagerの@Deprecated対応
----------------------------------------------------------
PreferenceManagerがAndroid10で非推奨になっていたので、その
対応をメモする。

対応)
build.gradleに、以下を追加

implementation 'androidx.preference:preference:1.2.0'

ソースコードで以下を追加
import androidx.preference.PreferenceManager;
18:59
2023/05/22

MediaStoreから、保存した画像を読み込む

固定リンク | by:aramaki
 /**
     * MediaStoreから、保存した画像の情報を読み込む
     * @param context 呼び出し元のcontext
     */
    public static void readContent(Context context){
        ContentResolver resolver=context.getContentResolver();
        Cursor cursor=null;
        StringBuilder stringBuilder = null;
        stringBuilder=new StringBuilder();
        try {
            cursor=resolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,null,null,null,null);

            if(cursor!=null && cursor.moveToFirst()){
                String strCount=String.format("MediaStore.Images = %s\n\n", cursor.getCount());
                stringBuilder.append(strCount);
                do{
                   stringBuilder.append("ID: ");
                   stringBuilder.append(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)));
                   stringBuilder.append("\n\n");
                   stringBuilder.append("Title: ");
                   stringBuilder.append(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.TITLE)));
                   stringBuilder.append("\n\n");
                   stringBuilder.append("Path: ");
                   stringBuilder.append(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)));
                   stringBuilder.append("\n\n");
                }while (cursor.moveToNext());
               // cursor.close();

            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(cursor!=null){
                cursor.close();
            }
        }
        //showToast(context,stringBuilder.toString());
        String res=stringBuilder.toString();
        Log.i("Image ====== ",res);

    }
22:14
2023/05/22

画像をバイト配列にしたデータで、MediaStoreに保存する

固定リンク | by:aramaki
画像の保存パスと、画像をバイト配列にしたデータを指定して、MediaStoreに保存する
@param context 呼び出し元コンテキスト
@param imgPath 画像ファイルの絶対パス
@param data     画像データのバイト配列
@return boolean 保存されているか 保存されたtrue 保存されていないfalse

public static boolean save2sdcard(Context context, String imgPath, byte[] data) {
FileOutputStream fileOutputStream = null;

OutputStream outputStream = null;
ContentValues values = null;
ContentResolver resolver = null;
Uri uri = null;
// 処理結果
boolean result = false;

if (!hasSDCard()) {
    return false;
}
if(!isExternalStorageWritable()){
    return false;
}

try {
    final boolean is10 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
    // Android9以前の場合はfalseを返す
    // Android9以前の場合
    if (!is10) {
        Log.e("Android Version Error", "Your OS IS under Androi10");
        //   return false;
    } else {
        // Android10の場合
        String fileName = getSaveFileName(imgPath);
        values = new ContentValues();
        values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
        //values.put("data",imgPath);
        values.put(MediaStore.Images.Media.DATA, imgPath);

        // 書込み時にメディア ファイルに排他的にアクセスする
        values.put(MediaStore.Images.Media.IS_PENDING, 1);

        resolver = context.getContentResolver();
        Uri collection=MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);

        // 外部メディア参照
        Uri item = resolver.insert(collection, values);

        outputStream = resolver.openOutputStream(item);
        outputStream.write(data);

        values.clear();
        // 排他的にアクセスの解除
        values.put(MediaStore.Images.Media.IS_PENDING, 0);
        resolver.update(item, values, null, null);

        result = true;

    }

} catch (FileNotFoundException e) {
    if(Build.VERSION.SDK_INT < Build.VERSION_CODES.Q){
        resolver.delete(uri,null,null);
    }
    e.printStackTrace();
    throw new IllegalStateException(e);
} catch (IOException e) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
        resolver.delete(uri, null, null);
    }
    e.printStackTrace();
}catch (Exception e){
    e.printStackTrace();

}finally {
    if(outputStream!=null){
        try {
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

return result;
}
public static boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
return (Environment.MEDIA_MOUNTED.equals(state));
}
22:07
2023/03/03

戻るボタン表示

固定リンク | by:aramaki
戻るボタン表示
----------------------------
onCreate()内で以下を実装

 // アクションバーを取得する
 ActionBar actionBar=getSupportActionBar();
 //アクションバーの戻るメニューを有効に設定
 actionBar.setDisplayHomeAsUpEnabled(true);

クリックされたとき
@Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        //戻り値用の変数を初期値trueで用意。
        boolean result = true;
        //選択されたメニューのIDを取得。
        int itemId = item.getItemId();
        //選択されたメニューが[戻る]の場合、アクティビティを終了。
        if(itemId == android.R.id.home) {
            finish();
        }else {
            //親クラスの同名メソッドを呼び出し、その戻り値をreturnValとする。
            result = super.onOptionsItemSelected(item);
        }
        return result;
    }
09:42
2022/09/19

Android 10では非推奨になった、getExternalStorageDirectory()対応

固定リンク | by:aramaki
Android 10では非推奨になった、getExternalStorageDirectory()対応
-------------------------------------------------------------------------------------------

getExternalStorageDirectory()で取得したパスは、アプリから直接アクセスできなくなった Android 10では非推奨。
しかし、microSDカードを使用せず、内部共有ストレージに保存するようにし、
どうしても「/strage/emulated/0/」以下にアクセスしたいので、以下のように対応した。
備忘録としてメモする。

Androd10 までは、マニフェストに以下の対応をする
------------------------------
<application
        android:requestLegacyExternalStorage="true"

外部ストレージの場合は
File[] folderList=getExternalFilesDirs(null);
for(int i=0;i<folderList.length;i++){
if (Environment.isExternalStorageRemovable(folderList[i])) {
取り外し可能なのでSDカード
String SDPath=folderList[i].getPath().split("Android")[0];
}
}

14:53
2022/06/03

assetsフォルダーres配下ではxmlエラー

固定リンク | by:aramaki
assetsフォルダーres配下ではxmlエラー
--------------------------------------------------------
Android10にアプリをインストールすると、xmlファイルにしろとエラー

どうもこれまでのようにres配下にassetsを作成するとtxtファイルは、読み込みエラー
となる模様。

そこで対応は、File→New→Folderの中にAssets Folderで「assets」を作成し、そこに
txtファイルを置く。


assets内のテキストファイルを読み込む
--------------------------------------------------------------
public static String assetsToString(Context context, String fileName){
AssetManager assetManager=context.getResources().getAssets();
InputStream inputStream=null;
StringBuilder stringBuilder=new StringBuilder();
BufferedReader bufferedReader=null;


try {
    inputStream=assetManager.open(fileName);
    // 読みこむ文字コードを指定する。
    bufferedReader=new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
    String str;
    while ((str=bufferedReader.readLine())!=null){
        stringBuilder.append(str+"\n");
    }
} catch (IOException e) {
    e.printStackTrace();
    Log.i("assetsToString>>>>>>","文字列取得失敗");
} finally {
    if(bufferedReader!=null){
        try {
            bufferedReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

-----------------
Tabぺージのレイアウト
※gradleに追加
implementation 'com.google.android.material:material:1.6.0'


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical">
   <com.google.android.material.tabs.TabLayout
       android:id="@+id/tabLayout"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       app:tabIndicatorColor="@android:color/holo_blue_bright"/>

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>
</LinearLayout>

--------------------------------------------------------------------------]

public class HelpActivity extends FragmentActivity {

    private String[] tabTitles=new String[]{"仕様準備","使い方","動作環境"};
    private int[] icons=new int[]{R.drawable.clapboard,R.drawable.photos_polaroid,R.drawable.arrow_right};

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

        // ViewPagerの取得
        ViewPager2 pager2=findViewById(R.id.viewPager);

        // TabLaoutの取得
        TabLayout layout=findViewById(R.id.tabLayout);

        pager2.setAdapter(new ViewPagerAdapter(getSupportFragmentManager(),getLifecycle()));

        // TabLayout と ViewPager2 を連動させる
        new TabLayoutMediator(layout, pager2,
                new TabLayoutMediator.TabConfigurationStrategy() {
                    @Override
                    public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
                        // tabのタイトル設定
                        tab.setText(tabTitles[position]);
                        // アイコン設定
                        tab.setIcon(icons[position]);

                    }
                }).attach();

    }
}
------------
Adapter

public class ViewPagerAdapter extends FragmentStateAdapter {

    private static final int NUM_TABS = 3;

    public ViewPagerAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
        super(fragmentManager, lifecycle);
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        switch (position) {
            case 0:
                return new PrepareFragment();
            case 1:
                return new HowToFragment();
            case 2:
                return new KankyouFragment();
            default:
                break;

        }
        return null;
    }

    @Override
    public int getItemCount() {
        return NUM_TABS;
    }
}





11:44
2022/05/30

assets内のテキストファイルを読み込む

固定リンク | by:aramaki
assets内のテキストファイルを読み込む
assetsフォルダー内は、端末の環境によって影響を受けないもの
たとえばテキストファイル、画像等を格納している
-----------------------------------------------------------
public static String assetsToString(Context context, String fileName){
    AssetManager assetManager=context.getResources().getAssets();
    InputStream inputStream=null;
    StringBuilder stringBuilder=new StringBuilder();
    BufferedReader bufferedReader=null;


    try {
        inputStream=assetManager.open(fileName);
        // 読みこむ文字コードを指定する。
        bufferedReader=new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
        String str;
        while ((str=bufferedReader.readLine())!=null){
            stringBuilder.append(str+"\n");
        }
    } catch (IOException e) {
        e.printStackTrace();
        Log.i("assetsToString>>>>>>","文字列取得失敗");
    } finally {
        if(bufferedReader!=null){
            try {
                bufferedReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }


    return stringBuilder.toString();
}
10:51
123456