Android Kotlin 通知サンプル

  • Android 8.0 以降で通知をまとめる(以前ではまとめない)
  • 通知クリックでアクティビティを起動する

Android 8.0

通知がまとまっている

android_notification_group01

通知を展開

android_notification_group02

Android5.0

通知はまとまらない

android_notification_group03

サンプル

private var mNotificationNumber = 0
/**
 * 通知
 * @see http://android.techblog.jp/archives/7976103.html
 * @see https://developer.android.com/guide/topics/ui/notifiers/notifications?hl=ja
 * @see https://developer.android.com/training/notify-user/build-notification
 * @see https://developer.android.com/training/notify-user/channels
 * @see https://developer.android.com/training/notify-user/group?hl=ja#create_a_group_and_add_a_notification_to_it
 * @see https://qiita.com/mstssk/items/14e1b94be6c52af3a0a6
 * @see https://qiita.com/naoi/items/367fc23e55292c50d459
 * @see https://www.gaprot.jp/pickup/old-tips/android-o/notification-channel
 */
private fun notification() {
    val channelId = "channelId"
    val channelName = "GcpApiTrial"
    val groupKey = "groupKey"
    val notifyId = ++mNotificationNumber;

    val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
        // 通知チャネルの登録
        // targetSdkVersion(26 Oreo) 以降の場合、通知チャネルを使用しないと通知が表示されない
        // 複数のチャンネルを作り、個々の通知を任意のチャンネルに割り振ることによって、
        // 重要度や通知音などの属性を一括で指定することができる
        val summaryId = 0
        if (manager.getNotificationChannel(channelId) == null) {
            val channel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT)
            channel.apply {
                description = "GcpApiTrial Notification"
            }
            manager.createNotificationChannel(channel)
        }
        val summary = NotificationCompat.Builder(this, channelId).run {
            setContentTitle("Summary Content Title")
            setContentText("New Message.")
            setSmallIcon(R.drawable.ic_menu_camera)
            setStyle(NotificationCompat.InboxStyle().addLine("New Message Add line"))
            setNumber(mNotificationNumber)
            setGroup(groupKey)
            setGroupSummary(true)
            build()
        }
        manager.apply {
            notify(summaryId, summary)
        }
    }

    // 通知クリックでアクティビティを起動
    val intent = Intent(this, MainActivity::class.java)
    val stackBuilder = TaskStackBuilder.create(this)
    stackBuilder.addParentStack(MainActivity::class.java)
    stackBuilder.addNextIntent(intent)
    val pendingIntent = stackBuilder.getPendingIntent(0,PendingIntent.FLAG_UPDATE_CURRENT)

    // 通知の作成
    val notification = NotificationCompat.Builder(this, channelId).run {
        setContentTitle("New Message")
        setContentText("You've received new message. No.$mNotificationNumber")
        setSmallIcon(R.drawable.ic_menu_camera)
        setNumber(mNotificationNumber)
        setContentIntent(pendingIntent)
        setAutoCancel(true) // 通知クリックでクリア
        setGroup(groupKey)
        build()
    }
    manager.apply {
        notify(notifyId, notification)
    }
}

参考

http://android.techblog.jp/archives/7976103.html
https://developer.android.com/guide/topics/ui/notifiers/notifications?hl=ja
https://developer.android.com/training/notify-user/build-notification
https://developer.android.com/training/notify-user/channels
https://developer.android.com/training/notify-user/group?hl=ja#create_a_group_and_add_a_notification_to_it
https://qiita.com/mstssk/items/14e1b94be6c52af3a0a6
https://qiita.com/naoi/items/367fc23e55292c50d459
https://www.gaprot.jp/pickup/old-tips/android-o/notification-channel

1.準備

1.1 参考

以前のインストールメモ、

および

DB2 Express-C 11.1 for Linux クイックインストール

を参考に、DB2 Express-C 環境を、Vagrant を利用して VirtualBox の仮想マシン上に作成する。

1.2 Vagrant

Vagrantを利用すると、仮想環境へのサーバーの構築が非常に簡単にできる。Vagrant入門ガイド Kindle版は、500円以下で購入できるので、全体像を把握するにはコストパフォーマンスが高いと思う。

Vagrantでは、Boxファイルという仮想マシンのベースとなるイメージファイルが必要になる。

Vagrantのリポジトリで、そのBoxファイルが公開されているのだが、Centos7を導入するには、Vagrant および VirtualBox をインストールしたのちに、

vagrant init centos/7 コマンドで初期化、vagrant up で仮想マシンの構築から起動まで行われるので、CentOSのインストール作業を行う必要がない。

ただ、DB2をインストールするには、GUI環境が必要なので、上記公式CentOSのBoxではなく、有志が作成し、公開されている、CentOS7にGUI環境を適用済みで公開されているBoxファイルを使用する。

Vagrant box カタログ

https://app.vagrantup.com/boxes/search

で、centos gui で検索、今回は検索で最上位に表示されたBoxを使用する。

vagrant_boxies

vagrant init pbarriscale/centos7-gui

上記コマンドで、環境の初期化を行うと、実行したフォルダに、Vagrantfile という設定ファイルが生成されるので、テキストエディタで、vb.gui = true に編集した後で、vagrant up コマンドを実行。CentOSがGUIモードで立ち上がる。停止時は、vagrant halt、vagrant ssh で SSH接続できる。

  config.vm.provider "virtualbox" do |vb|
     # Display the VirtualBox GUI when booting the machine
    vb.gui = true
        :
  end

1.3 CentOS設定

SELinux を無効化

/etc/selinux/config を編集

#SELINUX=enforcing
SELINUX=disabled

Firewall の設定

管理用523ポートと接続用デフォルト5000ポートを開放する。

db2_firewall

2.DB2インストール

2.1 ダウンロード

IBMのサイトが非常に分かりにくいので、URLを示せないが、ググって以下のファイル(本体と言語パック)をダウンロード、上記でVagrantfileがあるフォルダに配置すると、ゲストOSと共有される。

v11.1_linuxx64_expc.tar.gz
v11.1_linuxx64_nlpack.tar.gz

root でログイン(ログインするには passwd root でパスワードを設定)、vagrant ディレクトリに共有されたファイル(v11.1_linuxx64_expc.tar.gz)を展開する。

db2_extract

解凍されたディレクトリ、expc/の中に言語パックを展開。後で適用もできるが、ここで上書きしておけばそれでOK

2.2 インストールの実行

/db2/expc/db2setup を実行

ダブルクリックで、エディタが起動してしまう場合は、ファイルを選択状態で、メニュー - ファイル - 設定 - 動作 から、実行可能形式のファイルは実行するにチェックして実行(もしくはターミナルから実行)

centos7_shell_run_gui

あとは、ウィザードに従いインストール

db2_install01

新規インストール

db2_install02

製品を選択

db2_install03

標準で。

以下の設定を準備しておく。

http://typea.info/blg/glob/2012/01/db2-express-c-97-centos-6.html

DB2管理サーバーのユーザー名 デフォルト:dasadm1
DB2のインスタンス・オーナー・ユーザー名 デフォルト:db2inst1
fencedユーザーのユーザー名 デフォルト:db2fenc1
インスタンスのTCP/IPポート番号 50000番

db2_install04

準備した設定に従い、ユーザーおよびパスワードを設定。

db2_install05

db2_install06

準備完了、完了ボタン。

db2_install07

インストールが実行される。

db2_install08

インストール完了。

db2inst1でログインしなおし、db2level コマンドで状況を確認。OSを再起動するか、db2start でDB2を起動

db2_install09

2.3 サンプルDBをインストール

$ db2sampl –xml –sql

db2_after_install03

3.DataStudioのインストールと実行

DB2の管理ツール、DataStudioのダウンロードとインストールを実行

http://typea.info/blg/glob/2011/05/db2-ibm-data-studio-sql.html

3.1 トラブルシュート

4.4.2 だと、結構な確率で、以下のエラーが発生する。

datastudio_err

org.osgi.framework.BundleException: モジュールの状態変更ロックを取得できませんでした:osgi.identity; osgi.identity="org.eclipse.core.runtime"; type="osgi.bundle"; version:Version="3.10.0.v20140318-2214"; singleton:="true" [id=1902] STOPPED [STARTED]
	at org.eclipse.osgi.container.Module.lockStateChange(Unknown Source)
	at org.eclipse.osgi.container.Module.stop(Unknown Source)
	at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.decStartLevel(Unknown Source)
	at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.doContainerStartLevel(Unknown Source)
	at org.eclipse.osgi.container.SystemModule.stopWorker(Unknown Source)
	at org.eclipse.osgi.internal.framework.EquinoxBundle$SystemBundle$EquinoxSystemModule.stopWorker(Unknown Source)
	at org.eclipse.osgi.container.Module.doStop(Unknown Source)
	at org.eclipse.osgi.container.Module.stop(Unknown Source)
	at org.eclipse.osgi.container.SystemModule.stop(Unknown Source)
	at org.eclipse.osgi.internal.framework.EquinoxBundle$SystemBundle$EquinoxSystemModule$1.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)

インストールフォルダ(例えば以下)の、ecplise.ini ファイル

C:\Program Files\IBM\DS4.1.2\eclipse.ini

から、

-Xquickstart

を削除することで解決。OKできた。

db2_install_success

以降は、上記で設定した、Vagrantfile の gui を falseにしとけばいい。

4.再利用

DB2のインストールができたので、vagrant の Boxイメージを作成しておき、再利用する。

4.1 Boxファイルの作成

http://typea.info/tips/wiki.cgi?page=Vagrant

centos7db2 というBoxを作成する

vagrant package default --output centos7db2.box

centos7db2.box というファイルが作成されるので、取り込みたいPCにコピーし以下を実行

vagrant box add centos7db2 centos7db2.box

あとは、同様に初期化と実行

vagrant init centos7db2

以上。

DB2使用者必携の良書!

Kotlinスタートブック を参考に、Android および Java で利用できる、HTTP クライアント、Retrofit を使用して、Web API (JSON)を Kotlinのモデルとして扱う。

驚くほど簡単。KotlinスタートブックはAndroidアプリをKotlinで作成するための要点が端的にまとめられている。個人的にはもう少し文章密度が高いほうが好みだが、手法をシンプルに知るにはこちらのほうが良いかも。

1.build.gradle (Module app)

build.gradle に以下を追加、retrofitから、リアクティブプログラミングのRxJava、Googleが提供するJSONとJavaオブジェクトを相互変換ライブラリGSONの設定を行う

dependencies {
            :
    def retrofitVersion = '2.4.0'
    compile "com.squareup.retrofit2:retrofit:$retrofitVersion"
    compile "com.squareup.retrofit2:adapter-rxjava:$retrofitVersion"
    compile "com.squareup.retrofit2:converter-gson:$retrofitVersion"
}

2.Model の定義

data class Category(
        val id: String,
        val name: String,
        val subCategoryList: List,
        val priority: Int
)

data class SubCategory(
        val id: String,
        val name: String,
        val priority: Int
)

3.APIのインターフェースを定義

interface ApiClient {
    @GET("/category")
    fun category(@Query("query") query: String) : Observable<List<Category>>
}

4.API呼び出し例

下記で記述するJSONを取得する、Webサービスを呼び出し、取得出来たらLogcatに内容を吐き出す。

private fun apiCategoryFetch() {
    val gson = GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create()
    val retrofit = Retrofit.Builder()
            .baseUrl(CATEGORY_API_URL)
            .addConverterFactory(GsonConverterFactory.create(gson))
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .build()
    val apiClient = retrofit.create(ApiClient::class.java)
    apiClient.category("")
            .subscribeOn(Schedulers.io())
            .subscribe({
                for (test in it) {
                    Log.i(TAG, String.format("%s", test))
                }
            }, {
                Log.w(TAG, "")
            })
}

5.JSON サンプル

以下のJSONを取得する。

[
  {
    "id": "690d2606-27a3-4fc5-96e9-a8c614c891da", 
    "name": "Category[0]", 
    "priority": 0, 
    "sub_category_list": [
      {
        "id": "1ac76014-d5f5-489e-bb7d-692a24544fd9", 
        "name": "SubCategory[0,0]", 
        "priority": 0
      }, 
      {
        "id": "69b5c1ab-59f9-4d73-8b26-1de678aa6fa9", 
        "name": "SubCategory[0,2]", 
        "priority": 2
      }, 
      {
        "id": "a6be8f99-653d-4eea-8cf5-0add0133bbb2", 
        "name": "SubCategory[0,1]", 
        "priority": 1
      }
    ]
  }, 
  {
    "id": "067a1604-d9da-4676-9c2f-7a0d1dbed6ec", 
    "name": "Category[1]", 
    "priority": 1, 
    "sub_category_list": [
      {
        "id": "4f5c8387-4b5e-47b5-9a3f-7890ba7ea840", 
        "name": "SubCategory[1,0]", 
        "priority": 0
      }, 
      {
        "id": "e789a851-8b06-4612-9ff3-fa4a70b15df9", 
        "name": "SubCategory[1,2]", 
        "priority": 2
      }, 
      {
        "id": "c1d66227-22b0-4fab-8500-953c78bb7a84", 
        "name": "SubCategory[1,1]", 
        "priority": 1
      }
    ]
  }, 
  {
    "id": "c4824082-d727-4dcb-9341-8f734d83dcf2", 
    "name": "Category[2]", 
    "priority": 2, 
    "sub_category_list": [
      {
        "id": "7d4e1da9-604e-47e9-9d64-4d0831d3335d", 
        "name": "SubCategory[2,0]", 
        "priority": 0
      }, 
      {
        "id": "9ff87596-f129-4bc0-bfac-427546e58809", 
        "name": "SubCategory[2,2]", 
        "priority": 2
      }, 
      {
        "id": "2ad260c8-9d11-4188-b596-49bc98e4987f", 
        "name": "SubCategory[2,1]", 
        "priority": 1
      }
    ]
  }
]

6.取得結果のLog

いい感じで取得できた。

07-01 23:16:09.976 22795-22849/info.typea.hoge I/@@@@@ MainActivity: Category(id=690d2606-27a3-4fc5-96e9-a8c614c891da, name=Category[0], subCategoryList=[SubCategory(id=1ac76014-d5f5-489e-bb7d-692a24544fd9, name=SubCategory[0,0], priority=0), SubCategory(id=69b5c1ab-59f9-4d73-8b26-1de678aa6fa9, name=SubCategory[0,2], priority=2), SubCategory(id=a6be8f99-653d-4eea-8cf5-0add0133bbb2, name=SubCategory[0,1], priority=1)], priority=0)
    Category(id=067a1604-d9da-4676-9c2f-7a0d1dbed6ec, name=Category[1], subCategoryList=[SubCategory(id=4f5c8387-4b5e-47b5-9a3f-7890ba7ea840, name=SubCategory[1,0], priority=0), SubCategory(id=e789a851-8b06-4612-9ff3-fa4a70b15df9, name=SubCategory[1,2], priority=2), SubCategory(id=c1d66227-22b0-4fab-8500-953c78bb7a84, name=SubCategory[1,1], priority=1)], priority=1)
07-01 23:16:09.986 22795-22849/info.typea.sentence2 I/@@@@@ MainActivity: Category(id=c4824082-d727-4dcb-9341-8f734d83dcf2, name=Category[2], subCategoryList=[SubCategory(id=7d4e1da9-604e-47e9-9d64-4d0831d3335d, name=SubCategory[2,0], priority=0), SubCategory(id=9ff87596-f129-4bc0-bfac-427546e58809, name=SubCategory[2,2], priority=2), SubCategory(id=2ad260c8-9d11-4188-b596-49bc98e4987f, name=SubCategory[2,1], priority=1)], priority=2)