Translate

BTemplates.com

Powered by Blogger.

2017年12月27日水曜日

2017-12-14、15、19~21、27 到達点メモ


タイマーが終了した時に画面を点灯するように修正した

                val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
                wakeLock = powerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK
                        or PowerManager.ACQUIRE_CAUSES_WAKEUP, "disableLock")
                wakeLock.acquire()
                wakeLock.release()

PowerManager.SCREEN_BRIGHT_WAKE_LOCKは非推奨ではあるが
使用できない訳ではない。
というか、これそのものを実装する機能が見つからなかったので
これを使うしかない。
設定的には、wakeLock を取得した時に点灯するという設定の為
設定は全部近くでやる必要がある。
また、wakeLock.release()してもすぐに点灯が消えるわけではなく
単に灯りをつけるだけなら、この方がバクとかが発生しないのでこの方がよい。


ライフサイクル形式だと他のアクティビティがある場合はうまくいかない。
        ClassNoticeManager.instance.unregisterActivityLifecycleCallbacks(MyLifecycleListener())
ClassNoticeManager.instance.registerActivityLifecycleCallbacks(MyLifecycleListener())
的なやり方も試してみたが、再登録される様子はなかった。

あと、どうもデストロイされた後はずっとデストロイ扱いのようである。
すくなくともresumeとかは呼ばれなかったのは確認した。

参考サイト:Application registerActivityLifecycleCallbacks - top ranked examples from Open Source projects
参考サイト:アクティビティのライフサイクル


最終的にふと思ったのが、ようはアプリケーションが終了するときに処理をやれればいいので、onTerminateを使えばできるのではないだろうか?と思っていたが
実機では使うことができないらしい。
Android でアプリケーション開始時と終了時にごにょごにょする
アプリケーション開始時・終了時の処理を定義する
Application#onTerminateは、実機では呼ばれない。

副産物
メニューアイテムを隠す方法
 override fun onCreateOptionsMenu(menu: Menu): Boolean
でtestmenu = menuのような形で値を保持するようにし

必要な場所で
testmenu.findItem(R.id.(ボタン名)).setVisible(false)
と書けばよい

参考サイト:メニューアイテムを隠す

代案
存在しているActivityの名前を取る方法が見つかったので
そこから取得。getRunningTasksを使うのだが
これは例によって非推奨である。しかし、代案が見つからないため
一先ずこれを採用


私の場合は以下のように書いた
val manager:ActivityManager = getSystemService(Service.ACTIVITY_SERVICE) as ActivityManager
var activityFlag:Boolean = false
for (task:ActivityManager.RunningTaskInfo in manager.getRunningTasks(Int.MAX_VALUE)){
                    if(MainActivity.name.equals(task.baseActivity.className)){
                        activityFlag = true
                        break
                    }
}

代案が見つかりました。appTaskを使う。

for (tasks:ActivityManager.AppTask in manager.appTasks){ if(MainActivity.name.equals(tasks.taskInfo.baseIntent.component.className)){
                        activityFlag = true
                        break
                    }
}

なお、getRunningTasksを使った場合
他のアクティビティが前面に出ているときに
メインのアクティビティを呼び出すという処理を行った場合
サービスが止まるという事が起きた。(厳密にはサービスでタイマー処理を行っているが
それの動作が止まった)
なお、appTasksの場合は、その現象は起きなかった


参考サイト:Android L getTasksの挙動変更
参考サイト:Androidで表示しているActivity名を取得(非推奨のやり方)
参考サイト:Androidでアプリやサービスが起動しているかどうか確認する方法(非推奨のやり方)

どうも検証した結果、電源ボタンを押してスリープをすると
ほおっておくと強制的にDozeモードに入るらしい?


参考サイト:Android 画面を切るとTimerの間隔が遅くなる問題


ひとまず、電源を抜いた状態でどういう動作をするかを見ないと何とも言えなさそう。
駄目でした。

で、ふと思いついたのですが、特定の秒数ごとにWaleLockで強制的に
点灯させればよいのではと気が付いたので以下のように実装

if(now_sec % 3 == 0) {
    val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
    var wakeLock: PowerManager.WakeLock = powerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK
            or PowerManager.ACQUIRE_CAUSES_WAKEUP
            or PowerManager.ON_AFTER_RELEASE, "disableLock")
    wakeLock.acquire()
    wakeLock.release()
}

PowerManager.ON_AFTER_RELEASEは、このフラグを含むWakeLockがリリースされた時にthe user activity timerがリセットされるらしい。
調べてもいまいち意味が分かっていない為、推測になるがアクティビティごとにタイマーを取得しており、それが一定の値になるごとにライトの明るさを下げているのだと思われる。また、明るさを下げるたびにthe user activity timerがリセットされているのかもしれない。何故かというと、今、30分経つと自動的にスリープになるように設定しているが、now_sec % 10 == 0にすると、明るくなったり暗くなったりするからである。

なお今の設定だと電源ボタンでスリープを入れても、一度は明るく点灯するがほっておくと暗いままの点灯になる。

多分完全に消灯しても動くのが一番良いのだろうが、正直現状では実装できる範囲内でのこれ以上良い実装方法が思いつかないので一度これでリリースしました。


関係ない話になるが、GooglePlayで他の言語での順位などを調べたい場合は
アドレスの最後に、hl=(対応言語)つけると見ることができるのを初めて知った。
PC上で他の言語だとどのように表示されるか確認する際に便利。
また、言語によってアプリの表示順番も違うので、それを調べる際に参考になるかもしれない。
例:https://play.google.com/store/apps?hl=zh_HK

最近、Deploygateという存在を知り、ちょっと使い始めている状況である。
現状では、リモートテストぐらいしか使わなそうだが、わざわざPCにUSBをつながなくても使えるのは便利。

また、Deploygateはリモートログが取れるのだが、その際に
Releaseビルドだとリモートログは特殊な設定をしないと取れないので
リモートログを取りたい場合は、深い理由が無ければDebugビルドをすること。

ただ、使ってみた感想としては、なんだかんだ言ってAndroidStudioの方が見やすい。
ただし、自分のPCのせいかもしれないが、使っていて偶に接続が切れるので
そういう意味合いでは、リモートログの方が便利に感じる。

参考サイト:DeployGate SDKの設定方法
参考サイト:DeployGate SDK does not work or Cannot get DeployGate log or remote logcat

今日はここまで。

2017年12月16日土曜日

Android6.0以上の場合、以下の動作をする。という書きたい場合


 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) {}

以上。

なお、Mはマシュマロの略である。

他のバージョンコードを知りたい場合は、以下のサイトを参考にすること
参考サイト:Build.VERSION_CODES
参考サイト:[Android] API レベルと OSプラットフォーム(2017/12/16日現在、1.0から8.0まで記載してある)

2017年12月14日木曜日

2017-12-12,13 到達点メモ


対処方法を探している途中で使い道はないが
面白いものを発見したのでメモしておく

参考サイト:[Android/Kotlin] アプリの画面をスリープさせない/動画再生中はスリープを無効にする

具体的には、layout.xml に android:keepScreenOn=”true” を書くことで
画面をスリープさせないことができるのですが、通知画面でレイアウトを作りそこに先程の設定をすると通知画面が見える状態だとアプリがスリープしない状態になります。

スゴイ雑なやり方だけど、何処かに小さい透過ビューを作って
それのレイアウトに先程の設定をすれば、メインアクティビティは表示されていないくてもずっとスリープさせない状態にできるかも??

検証自体は面白そうですが、変な所で問題が起きそう気がするので実装はしません。

最終的には、サービスをフォアグラウンドで実行することにしたところ
スリープ中でも動くことは確認できた。
ただし、前の日記でも書いたが、この状態だとアプリが終了した時に
サービスが残ったままなので表示がおかしくなる。
そのため、サービス自体にアクティビティが終了しているかどうかを調べる機能が必要だった。
具体的には、アプリ起動中に特定のActivityを開いているかどうか判定するに書いてある内容をほぼそのまま使う。
ただし、自分の場合は、Activityがあるかどうかを是非としているので
onActivityCreatedにアクティビティのクラス名を代入するようにし、
(別のアクティビティへ遷移し、その後にMainActivtyに復帰した時に、
値が空のままになるのでこれだけでは使えない)
onActivityDestroyedで空にするようにする。
そうすることで、アプリをスライドして呼び出した場合
アプリが終了したことを察知できるようになる。

参考サイト:Androidで死なないServiceを実装してみた
参考サイト:アプリ起動中に特定のActivityを開いているかどうか判定する
参考サイト:[Android]自分のアプリが前面にいるかを判別する正しい方法
参考サイト:Checking if an Android application is running in the background


ただし、Kotlinでしか確認していないが、MainActivtyでクラス名を代入する際に以下のように実装すると問題が発生する

companion object {
        var name:String = MainActivity.javaClass.name
}

上記のように実装した場合、
nameの中身は(アプリの設定アドレス).MainActivityではなく
(アプリの設定アドレス).MainActivity$Companionになる。
検証した結果、companion object内でクラス名を呼び出した際につくようである。

対策自体は簡単で
companion object {
        var name:String = ""
}
にし、
onCreateで

name = this.javaClass.name

と呼び出せばよい。

なお、以下の面倒な文字列置換を行う必要はない。
companion object {
        var name:String = MainActivity.javaClass.name.replace(Regex("\\.MainActivity.*"),".MainActivity")
}

文字列置換とかエスケープとか正規表現などで参考にしたサイト一覧
参考サイト:特殊な文字の入力(エスケープシーケンス)
参考サイト:【Java】正規表現って何?
参考サイト:Kotlin の正規表現を使う
参考サイト:Kotlin公式ドキュメント-replace

メモ書きとして残しておくが、開発者設定できる機能の中に
充電中はスリープしないという項目があるので、充電中にスリープする場合は
その機能がONになっていないか確認すること。

テストしているが、幾つか動作不良を発見して修正している状態である。
バージョン違いのテストもそうだが、多分これはきちんとテスト仕様書を書いて
確認しないと★1の嵐になりそうである。

明日こそいい加減バージョンアップさせたい。

これ以上は集中できないので今日はここまで。

2017年12月12日火曜日

2017-12-09、11 到達点メモ



今までの動作不順は、Doze モードに入ったためと思っていたので
以下のサイトのようにテストを行った。
Android Marshmallow の Doze テストの正しい手順

現状ではサービスに普通に書いただけだとすぐには止まったりしない事は確認できた。
ただし、現バージョンのアプリでも同様だったのでidelに入る=すぐ止まるという訳ではないように見える。
別の話になるが、今までだとタイマー中にアプリを終了しても通知が残ったままだったのがサービスの方を採用したら、消えるようになったので今後はサービスを採用することになると思われる。


一通りテストしてみたが、問題なさそうである。
一方で、理由はわからないがReleaseビルドのパスワードが消えていたので
復旧するのに大分手間取った。
実際に実行した感じだと、一番手っ取り早いのは
C\ユーザー\ユーザー名\.AndroidStudioX.X\system\logの中にある
idea.logフォルダの中身を開いて、android.injected.signing.storeで検索をかけて
store.passwordとkey.passwordを調べたほうが早い。
注意事項として、***で隠されているのとそのまま表示されているのが二種類あるので
***に気を取られて見つからないという事が無いように注意しないといけない

参考サイト:忘れてしまったAndroidの署名情報は見つかるかもしれない
参考サイト:Android Studio: cannot recover key

Android7.0だと問題ないようだが、まだアプリがアクティブになっていない状態でしかテストしていないが、タブレットのAndroid5.0.2だとスリープに入り動作が止まっていた。
(厳密には、通知領域に表示されている時間が途中で止まっており、アプリを立ち上げるとタイマー設定画面になっていた。)
15秒ぐらいでスリープになるように設定したところ、止まった時間は大体1分10秒くらいだった。他のアプリで検証したところそのアプリも通知の更新が止まっており、アプリを立ち上げたらアラームが鳴った。
(*内部的には、時刻で管理していて立ち上げたらアラームがなるという仕組みなのかもしれない。)
Android7.0なら大丈夫だとは思うんですが、せめて5.0.2ぐらいはまともに動くことを確認しないとこのままでは出せない。

以下のサイトが参考になりそうなので明日試す。
Androidで端末がスリープ中でも定期的にバックグラウンド処理を実行する

今日はここまで

2017年12月8日金曜日

2017-12-07、08 到達点メモ


JobScheduleについて

恐らく何らかの制限をかけることを前提で作っているので
何の制限が掛かっていない状態で動くことは想定されていない

具体的にいうと以下のようにバッテリーの状態関係無し、ネットワークが繋がっていてもいなくても関係が無い、アイドル状態を無視する、というような設定をしていると、
JobScheduleを動かしたときに、java.lang.IllegalArgumentException: You're trying to build a job with no constraints, this is not allowed.というようなエラーが出る。

            builder.setBackoffCriteria(10000, JobInfo.BACKOFF_POLICY_LINEAR)
            val bundle = PersistableBundle()
            builder.setExtras(bundle)

            builder.setPersisted(false)
            builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE)
            scheduler.schedule(builder.build())


また、JobInfo.Builderでjobのサービス名を指定する際に、正しいパッケージ名とサービス名を指定しないとエラーになるので、注意すること。

恐らく今回の目的には、JobScheduleは使えないと思われる。
なので公式ドキュメントのバックグラウンド実行制限を読み直したところ
>>アプリがアイドル状態にある場合、バックグラウンド サービスの使用を制限します。 >>これは、ユーザーが認識しやすいフォアグラウンド サービスには適用されません。
とのことなので、フォアグラウンド サービスで動かせばいけそうな気がする

参考サイト:公式ドキュメント サービス
参考サイト:Service が Foreground で動いているか確認する
参考サイト:Serviceの挙動について
参考サイト:Android Marshmallow の Doze テストの正しい手順
参考サイト:[Android] Service の使い方
参考サイト:Android Oからのバックグラウンド・サービスの制限事項を実演する。
参考サイト:AndroidのServiceについて

情報を整理したいので今日はここまで。


2017年12月5日火曜日

2017-12-05、06 到達点メモ


Java → Kotlinの翻訳一覧

MainActivity.this → this@MainMainActivity
MyJobService.class.getName() → MyJobService::class.java.name


*下記は全部、基本的にKotlinに書き直して書いています。

Example of JobScheduler and JobService
探して見つけた一番簡単なサンプル。
ただし、これだとjobschedulerが本当動いているかわからない
やり方が悪い可能性もあるが、onStartJobが動いているようには見えない


How to use Android's Job Scheduler
上記のサイトのサンプル
同じように試してみたがどうも動いているように見えない

JobSchedulerの使い方
Kotlinで書き換えたところ一部でうまく書き換えができないところが出てきた。
具体的にいうとstatic部分。
一方で、そのままJavaだと動くことが確認できた。
で、調べなおしたところ、やはりstatic部分が原因だった。
対処方法的な話をするとstaticにしたい場所を
 companion object {}
で囲めばよい。

参考サイト:Kotlinにstaticが無いのなんで?(Why doesn't Kotlin have static members inside a class?)

jobFinished()でスケジュールを終了させることができる
scheduler.cancel()を呼ぶことで、onStopJobが呼ばれる。
スケジュールを終了させた後でscheduler.cancel()してもonStopJobは呼ばれない。

で、色々試した結果、JobSchedulerを使ってタイマー処理を行えることを確認できた。
自分にとって盲点だったのが、タイマー処理をする際に定期的に処理をしなくてはいけないので、JobSchedulerを使う際にJobSchedulerそのものを定期的に呼ばないといけないと勘違いしていた。
実際は、JobSchedulerで定期的に自身(JobSchedulerの事ではない)を呼ぶスレッドを呼ぶように設定すればよいと気が付いた。
もしかするとAlarmManagerとかでも同じように応用できるかもしれない。

個人的につまずいた部分として、MainActivtyのメソッドを別のクラスで読んでいたため
それを明示的に宣言しないといけないのだが
VoiceTimerで使っていた以下のような書き方では使えなかった。

 runOnUiThread { MainActivity.instace!!.updateUI(now_sec) }

*MainActivtyで、以下のように宣言しており、また、onCreateでinstace=thisを宣言している。
    companion object {
        var instace: MainActivity? = null
    }

理由は、JobSchedulerを読んでいるクラスでActivtyを継承していなかったため。
すでに、JobService()を継承しているため、Activtyは継承できない。
その為、別の方法を取る必要がある。具体的には、以下のように書けばActivtyを継承していなくても動くことが確認できた。
MainActivity.instace!!.runOnUiThread{ MainActivity.instace!!.updateUI(now_sec) }

参考サイト:Cannot resolve method .runOnUiThread

いやぁ、何とかなりそうで本当に良かった。
明日、VoiceTimerの方に実装してテストしてみて問題が無さそうであれば
近日実装できるかと思います。

今日はここまで



2017年12月3日日曜日

2017-12-03 到達点メモ


現バージョンのVoiceTimerだと、アプリがフォアグラウンドに表示されている場合、スリープ機能は設定がONになっていれば機能しているのですが、そうでない場合だとスリープが働くみたいです。

そういう訳で、今対処方法を調べているのですが一番あてにしていた方法が
微妙みたいです。
具体的には、WakefulBroadcastReceiverを使おうかと考えていたのですが
どうやらAPI26.1.0で廃止されるようです。
グーグル翻訳で読んだ感じだと、どうやらandroid.app.job.JobSchedulerを使えという話らしいのですが、どうしたもんだか……。
前にも同じような結論になったみたいなのだが、詳しく検証していないようだった。


参考サイト
AndroidでService内で発行したTimerが端末スリープ状態で勝手に止まる

いや、寧ろ如何にしてスリープモードが起こらないかを考えるべきかも
権限をあまり増やしたくはないんだが、これをとりあえず試してみる


あと、それはそれとしてSDKのバージョン一覧が分からなくなったので
サイトを見つけたのでメモしておく

公式サイトで何かないのか調べたところこんなのを見つけた。
APIのSDKのバージョンで言うところの幾つであるのかがわからないのが問題だが
何も無いよりはマシかと

あと、SDKによるif文の書き方を忘れやすいのでメモ
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) {}

参考サイト
自分が使っているAndroidでテストした時だと20分超えるとダメだったので
他の実機でも試してどうなるのかを確認する必要がある。

試してみましたが20分持たずにディープスリープに入りました。
Dozeは一先ず現時点では関係ないみたいですね。

そうなると、本格的にJobScheduler周りを調べるしかないかも

一先ず使えそうなサイト一覧

久しぶりにKotlinを書くと何をどう書くんだっけになるので
下記のサイトを参考にするとよいかも

今日はここまで