ともちゃんのAndroid開発日記

組込みC言語プログラマだったともちゃんが、四苦八苦しながら、AndroidのJAVAとKotlinを習得して行きます。ともちゃんの備忘録も兼ねています。

Drawer内のNavigation Header内にSpinnerを置く

Drawer内のNavigation Header内にSpinnerを置き、正常に動作させるのに苦労しました。

やりたいこと

下記の様に、Drawer内のNavigation Header内にSpinnerを置きたい。

f:id:uchida001tmhr:20210221061844j:plain

DrawerのSpinner

Layout XML

activity_min.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
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:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">

<include
layout="@layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />

<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_main"
app:menu="@menu/activity_main_drawer" />

</androidx.drawerlayout.widget.DrawerLayout>

 

 nav_header_main.xml

<?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="@dimen/nav_header_height"
android:id="@+id/nav_header"
android:background="@drawable/side_nav_bar"
android:gravity="bottom"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:theme="@style/ThemeOverlay.AppCompat.Dark">

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="@dimen/nav_header_vertical_spacing"
android:src="@drawable/ic_health_log_48x48"
android:contentDescription="@string/app_name"/>

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/nav_header_vertical_spacing"
android:text="@string/nav_header_01"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />

<Spinner
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/spinnerSelectUser"
android:layout_marginTop="20dp" />

</LinearLayout>

 

 問題となったソースコード

DrawerLayout drawer = findViewById(R.id.drawer_layout);
drawer.addDrawerListener(new DrawerLayout.SimpleDrawerListener() {
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);

.......

Spinner spinner = drawer.findViewById(R.id.spinnerSelectUser);

.......

}
});

 Spinner spinner = drawer.findViewById(R.id.spinnerSelectUser);

でNull Pointer Exceptionで落ちることがありました。

うまくいったソースコード

これを、

DrawerLayout drawer = findViewById(R.id.drawer_layout);
drawer.addDrawerListener(new DrawerLayout.SimpleDrawerListener() {
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);

......

NavigationView navView = drawer.findViewById(R.id.nav_view);
View parentView = navView.getHeaderView(0);
Spinner spinner = parentView.findViewById(R.id.spinnerSelectUser);

......

}
});

のように直したら、クラッシュしなくなりました。

 

Firebaseを使用したPeer to Peerプッシュ通信(SMS代替)

背景

  1. Googleプレイストアでは、SMSに対する規制が強化されている。
  2. そのため、手軽なPush通知の手段が欲しい。
  3. Push通知のためのサーバを置きたくない。

イデア

f:id:uchida001tmhr:20200829143251p:plain

シンプルPush通知のアイデア



参照リンク

サンプルソースコード

謝辞

特に、AndroidのPush通知(FCM)をサーバー知識無しで試してみようの記事にはインスパイアされました。ありがとうございます。

 

 

Android 11 AsyncTask 非推奨 (Java)

Android 11でAsyncTaskが非推奨になりました。

なので、java.util.concurrentのExecutorを使って書き換えました。

 

Asyncタスクを使った例:

public static class AsyncExportProgress extends AsyncTask<Void, Void, String> {

ProgressBar progressBar;
Snackbar snackbar;

@Override
protected void onPreExecute() {
// ここに前処理を記述します
// 例) プログレスダイアログ表示
}

@Override
protected String doInBackground(Void... arg0) {
// バックグラウンド処理
}

@Override
protected void onPostExecute(String result) {
// バックグランド処理終了後の処理をここに記述します
// 例) プログレスダイアログ終了
}
}

 

呼び出し側:

AsyncExportProgress asyncExportProgress = new AsyncExportProgress();
asyncExportProgress.execute();

 

Executorを使用した例:

private static class AsyncExportProgress {

ProgressBar
progressBar;
Snackbar
snackbar;

private class AsyncRunnable implements Runnable {

private String strResult;

Handler
handler = new Handler(Looper.getMainLooper());
@Override
public void run() {
// ここにバックグラウンド処理を書く

handler.post(new Runnable() {
@Override
public void run() {
onPostExecute(
strResult);
}
});
}
}

void onPreExecute() {
 
// ここに前処理を記述します
// 例) プログレスダイアログ表示
}

void execute() {
onPreExecute();
ExecutorService executorService = Executors.
newSingleThreadExecutor();
executorService.submit(
new AsyncRunnable());
}

void onPostExecute(String result) {
// バックグランド処理終了後の処理をここに記述します
// 例) プログレスダイアログ終了
}
}

呼び出し側:

AsyncExportProgress asyncExportProgress = new AsyncExportProgress();
asyncExportProgress.execute();

Android 11 AsyncTask 非推奨 (Kotlin)

Android 11でAsyncTaskが非推奨になりました。

なので、java.util.concurrentのExecutorを使って書き換えました。

 

Asyncタスクを使った例:

private class TestAsyncTask : AsyncTask<Void, Void, Int>() {

    override fun onPreExecute() {
        super.onPreExecute()
        // 前処理
    }

    override fun doInBackground(vararg p0: Void): Int {
        // バックグラウンド処理
        return 0
    }

    override fun onPostExecute(response: Int) {
        super.onPostExecute(response)
        // 後処理
    }
}

 呼び出し側:

val testAsyncTask = TestAsyncTask()
testAsyncTask.execute()

 

Executorを使用した例:

private class TestRunnable(val onPostExecute:(Int) -> Unit) : Runnable {
    val handler = Handler(Looper.getMainLooper())

    override fun run() {
        // メイン処理

        // 後処理実行
        handler.post(Runnable {
            onPostExecute(1001)
        })
    }
}

private class TestAsyncTask {

    fun onPreExecute() {
        // 前処理

    }

    fun execute() {
        onPreExecute()
        val executorService : ExecutorService = Executors.newSingleThreadExecutor()
        executorService.submit(TestRunnable(context, ::onPostExecute)) }

    fun onPostExecute(response: Int) {
        // 後処理
    }

}

 呼び出し側:

val testAsyncTask = TestAsyncTask()
testAsyncTask.execute()

 

Android 11 IntentService 非推奨

Android 11でIntentServiceが非推奨になりました。

JobIntentServiceを使って書き換えました。

 

IntentServiceの例:

class TestService : IntentService("TestService") {
    override fun onHandleIntent(intent: Intent?) {
        // ここに処理を書く

    }

 呼び出し側:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    context.startForegroundService(intent)
} else {
    context.startService(intent)
}

 AndoidMenifest.xml

<service
    android:name=".TestService "
    android:enabled="true"
    android:exported="false" />

 

JobIntentServiceの例:

class TestService : JobIntentService() {

    private val JOB_ID = 1000;

    fun enqueueWork(context: Context, work: Intent) {
        enqueueWork(context, TestService::class.java, JOB_ID, work)
    }

    override fun onHandleWork(intent: Intent) {
        // ここに処理を書く
    }

 呼び出し側:

TestService().enqueueWork(context, intent)

AndoidMenifest.xml

<uses-permission android:name="android.permission.WAKE_LOCK"/>

 

<service
    android:name=".TestService "
    android:permission="android.permission.BIND_JOB_SERVICE"
    android:enabled="true"
    android:exported="false" />

 

ツータッチメール2で、Intentを使用してGmailアプリに、CC, BCC, 件名, 本文が渡せない。

ツータッチメール2では、Intentという内部処理を使用して、Gmailアプリに、CC, BCC, 件名, 本文を渡しています。

ある日突然、Gmailアプリが、CC, BCC, 件名, 本文を受け付けてくれなくなりました。

その時のソースコードはこうです。

// メールアドレス設定
val uri = Uri.parse("mailto:" + mailAddressTO)

// 件名、本文をメールツールに渡す
val intent = Intent(Intent.ACTION_SENDTO, uri)
intent.putExtra(Intent.EXTRA_CC, mailAddressCC)
intent.putExtra(Intent.EXTRA_BCC, mailAddressBCC)
intent.putExtra(Intent.EXTRA_SUBJECT, subject)
intent.putExtra(Intent.EXTRA_TEXT, mailText)
startActivity(context, intent, null)

 

 そして、改善後

val intent = Intent(Intent.ACTION_SEND)
intent.type= "message/rfc822"
intent.putExtra(Intent.EXTRA_EMAIL, mailAddressTO)
intent.putExtra(Intent.EXTRA_CC, mailAddressCC)
intent.putExtra(Intent.EXTRA_BCC, mailAddressBCC)
intent.putExtra(Intent.EXTRA_SUBJECT, subject)
intent.putExtra(Intent.EXTRA_TEXT, mailText)
context.startActivity(intent)

 

 です。

【参考】

https://stackoverflow.com/questions/8701634/send-email-intent

https://qiita.com/kitagry/items/e3b77061c31e388d152f

 

 

GoogleのCloud Pub/SubをREST APIを使って試使用してみた

GoogleのCloud Pub/SubをREST APIを使って試使用してみました。

f:id:uchida001tmhr:20191126192615p:plain

 

なぜ、REST APIか?

それは、Googleが推奨するClient Libraryは、Androidで動作しないためです。

Client Library(Java)はWindows環境変数を使用するように書いてあるのですが、ライブラリの中でSystem.getenv()を使用していて、実行するとWindows環境変数ではなく、Android環境変数が読めるんですよね…。当たり前といえば当たり前ですが…。

なので、REST APIとOkHttp3を使ってスクラッチでサンプルプログラム(Kotlin)を組みました。

 

ここからダウンロードできます。

 

  1. Assetsフォルダに、Google API Consoleからダウンロードしたjsonファイルを入れてください。
  2. また、MainActivity.ktの変数googleAccountに自分のGoogleアカウントを入れてください。
  3. Proxyの内側に端末がある場合には、PubSubHttps.ktでコメントアウトしてある"clientBuilder.proxy(Proxy(Proxy.Type.HTTP, InetSocketAddress("10.96.241.14", 9515)))"を有効にしてください。 

それだけ動くと思います。