楽天アフィリエイト

2022年5月12日木曜日

PreferenceDataStore での起動判定

 Androidでの起動判定をDataStoreを使用して実施

忘備録としてプログラムの一部を記載


DataStoreが安定版の1.0.0

// Preferences DataStore
implementation 'androidx.datastore:datastore-preferences:1.0.0'
implementation 'androidx.datastore:datastore-preferences-core:1.0.0'


DataStoreSetting.kt


val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "DataStoreSetting")

class DataStoreSetting {
object PreferenceKey {
val START_FLAG = booleanPreferencesKey("StartFlag")
}

// 起動フラグの取得
suspend fun getFlag(context: Context): Boolean {

// DataStoreからフラグを取得
val flagFlow: Flow<Boolean> = context.dataStore.data
.catch { exception ->
if (exception is IOException) {
emit(emptyPreferences())
} else {
throw exception
}
}.map { preferences ->
// デフォルト値:false
preferences[PreferenceKey.START_FLAG] ?: false
}

// 起動フラグ取得
val flag = flagFlow.first()

return flag
}

// 起動フラグの設定
fun setFlag(context: Context) {
CoroutineScope(Dispatchers.Main).launch {
context.dataStore.edit { setting ->
setting[PreferenceKey.START_FLAG] = true
}
}
}

}


Fragment

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

// 非同期処理
CoroutineScope(Dispatchers.Main).launch {

val intent: Intent
// 起動フラグ
val startFlag = dataStoreSetting.getSetupCompletedFlag(requireContext())

intent = if (startFlag) {
Intent(activity, SecondActivity::class.java)
} else {
Intent(activity, FirstActivity::class.java)
}
    }
    dataStoreSetting.setFlag(requireContext())
startActivity(intent)
}


参考サイト:

https://developer.android.com/topic/libraries/architecture/datastore?hl=ja

https://developer.android.com/codelabs/android-preferences-datastore?hl=ja#0

https://zenn.dev/slowhand/articles/455aa5cd244e90

https://akira-watson.com/android/sharedpreferences.html

https://stackoverflow.com/questions/67265984/is-it-possible-to-read-write-primitive-types-from-datastore-without-flows



2022年4月5日火曜日

RecycleViewでの選択表示

 RecycleViewでの選択表示ができたのでメモ


環境

AndroidStudio:2021.1.1 Patch2

TargetSDK:31


やりたいこと

・リストを1つ選択状態で表示し,クリックした部分を選択にし,ほか選択部分は解除


参考にしたサイトで継承などしてましたが,継承するとクリックイベントが取れなくなったりしたのでとりあえずadapterに直書き

あと画面外に流れた際におかしくなっていましたが,画面外のリストをアタッチするタイミングでも判定すれば問題なく動作しました。

Adapter.kt

class KindAdapter(private val kindList: ArrayList<KindListData>, var defaultCode: String = "") :
RecyclerView.Adapter<KindAdapter.ViewHolder>() {

// 表示中のリスト
private var attachedItems = mutableSetOf<RecyclerView.ViewHolder>()
// 選択アイテムのID(ポジション)
private var checkedItemId: Int = 0


class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
// 表示するコードと名前をセット
val code: TextView = view.findViewById(R.id.kindCode)
val name: TextView = view.findViewById(R.id.kindName)
// 選択表示する用のTextView
val row: CheckedTextView = view.findViewById(R.id.kindRow)
}

override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(viewGroup.context)
.inflate(R.layout.layout_kind_list_item, viewGroup, false)

// デフォルト表示
repeat(kindList.size) {
if (kindList[it].code == defaultCode) {
checkedItemId = it
}
}

return ViewHolder(view)
}

override fun onBindViewHolder(viewholder: ViewHolder, position: Int) {
val kind = kindList[position]

Timber.i("onBindViewHolder-position:${position}:${viewholder}")
viewholder.code.text = kind.code
viewholder.name.text = kind.name
viewholder.itemView.setOnClickListener {
choiceSingleItem(viewholder, position)
listener.onItemClick(kind)
}
}

override fun getItemCount() = kindList.size

private fun choiceSingleItem(viewHolder: ViewHolder, position: Int) {
checkedItemId = position
Timber.i("choiceSingleItem:${checkedItemId}| position:${position}")
// クリック時に表示されているリスト項目を処理
attachedItems.forEach {
//クリックしたポジションと同じが判定(画面の背景色はdrawbleで実施)
(it as ViewHolder).row.isChecked = it.absoluteAdapterPosition == checkedItemId
notifyItemChanged(position)
}
}

// 画面外のリストが表示されるとき
override fun onViewAttachedToWindow(holder: ViewHolder) {
Timber.i("onViewAttachedToWindow:${holder.absoluteAdapterPosition}| checked=${checkedItemId}")
attachedItems.add(holder)
// 画面表示外のリストを処理
holder.row.isChecked = holder.absoluteAdapterPosition == checkedItemId
}

// 画面のリストが画面外で破棄?されるとき
override fun onViewDetachedFromWindow(holder: ViewHolder) {
Timber.i("onViewDetachedFromWindow:${holder}| checked=${checkedItemId}")
attachedItems.remove(holder)
}
}



参考:

https://android.suzu-sd.com/2021/04/recyclerview_choice_mode/

https://qiita.com/CAIOS/items/d7675a12b1345bf180d1





2022年4月1日金曜日

年金について

 年金について話題になっているので軽く調べてみた。


2022年4月からの変更点は色々あるけど大きく影響があるのが下記の2点

・繰下げ受給の上限年齢引上げ

現状70歳まで繰下げできたのが75歳まで繰下げ受給を選択できるようになった。

繰り上げの増額率は月0.7%増は変わらず

・繰上げ受給の減額率見直し

減額率が月0.5%から月0.4%になった


そこで繰上げ,繰下げした場合,どれくらいの差になっていつ給付したほうが得なのか

基準である65歳を100%とした場合

60歳での受給は76%(減額24%)

70歳での受給は142%(増額42%)

75歳での受給は184%(増額84%)

これを見ただけでは単純に75歳受給が強そうですが

75歳の給付が一番もらえるようになるのは95歳からです。

(男性ならだいぶ厳しそうですね。)


給付開始年齢:給付額が一番高くなる年齢

60歳:75歳までは一番給付額が高い

61歳:76~77

62歳:78~79

67歳:80

68歳:81~82

69歳:83~84

70歳:85~86

71歳:87~88

72歳:89~90

73歳:91~92

74歳:93~94

75歳:95歳~


63~66歳での給付を選択するのは間違いな気がしますね。


平均寿命:男性81歳,女性87歳とした場合

男性:68歳での給付を選択

女性:71歳での給付を選択

が一番もらえます

100歳まで生きるなら75歳一択!

(給付までどうするのなどは考慮していませんのであしからず。)


細かい部分は下記リンクの表で確認してください。

(本来は月ごとなのであくまで年単位のざっくりしたものです)

https://1drv.ms/x/s!AqK7Xkys8oqPuxVel-NactGVdZ51?e=rffBVb



2022年3月15日火曜日

Android でログのファイル出力(Timber+Log4J)

Androidアプリのログをファイル出力したい
Timber+Log4Jを使ったのでメモ

Kotlinで作成
Log4Jは脆弱性の対象となる前の(1.2.17)を使用
対応した現状の最新版(2.17.1)を使用したかったが,
まだ対応されていない?のかできなかった。

1.buile.gradleに追加
// Log
implementation 'com.jakewharton.timber:timber:4.7.1'
implementation 'de.mindpipe.android:android-logging-log4j:1.0.3'
implementation 'log4j:log4j:1.2.17'
2.application.ktの作成
var sLog : Logger? = null
override fun onCreate() {
super.onCreate()

initLog4J() // log4jの初期設定
initTimber() // Timberの初期設定

}
private fun initLog4J() {
val f = getFileStreamPath("${getString(R.string.app_name)}.log")
val logConfigurator = LogConfigurator()
logConfigurator.
fileName = f.path //ファイル名
logConfigurator.rootLevel = Level.ALL
logConfigurator.setLevel("org.apache", Level.ALL)
logConfigurator.
filePattern = "%d %-5p %m%n"         //メッセージ内容
logConfigurator.maxFileSize = (512 * 1024).toLong() //ファイルサイズ
logConfigurator.isImmediateFlush = true
logConfigurator.isUseLogCatAppender = false
logConfigurator.maxBackupSize = 1 //ファイル数
logConfigurator.configure()
sLog = Logger.getLogger(Application::class.java)
}
private fun initTimber() {
Timber.plant(Log4JTree())
}
private class Log4JTree : Timber.Tree() {
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
// Error,Warning,Infoのみ出力する
when (priority) {
Log.INFO -> {
sLog?.info(message)
}
Log.WARN-> {
sLog?.warn(message)
}
Log.ERROR -> {
sLog?.error(message)
}
}
}
}

3.使用方法
Timber.d("debug")   // ←これは出力されない
Timber.i("info")
Timber.w("warning")
Timber.e("error")






2022年3月2日水曜日

Bluetooth Adapterの取得

 Bluetooth使用時にAdapterを取得しているが

いろいろなサイトで下記の取得方法となっています。

val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()

google developer(https://developer.android.com/guide/topics/connectivity/bluetooth?hl=ja)でも現状変更されていない。


しかしAPI31で非推奨となり下記の警告が表示されます。

「'getDefaultAdapter(): BluetoothAdapter!' is deprecated. Deprecated in Java」


下記のように変更すれば,OKです。

val bluetoothManager = context?.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
val bluetoothAdapter: BluetoothAdapter = bluetoothManager.adapter



参考:https://stackoverflow.com/questions/69122978/what-do-i-use-now-that-bluetoothadapter-getdefaultadapter-is-deprecated