Translate

BTemplates.com

Powered by Blogger.

2018年2月28日水曜日

2018-02-28 到達点メモ


ここ最近Udemyのフルスタックの講座をやっているのだが
その際にColorZillaというやつを入れました。
これは画面の色を取れる便利なアドオンなのですが、
どうもページによっては取れないものがあるようである
具体的に上げると、chromeで最初のページを開いたページとか
まぁ、よく見ればchromeはこの特別なページでは色の取得は許可されてないよ
と英語で警告してくれてはいるんですけどね……

参考サイト
ColorZillaのウェブストア
ソフトも技術も不要!「ColorZilla」を使ってカッコいいサイトから配色をパクる方法
ChromeのColorZillaを使ってWEBページのカラー情報をすばやく取得

あと、昨日からcssをいじって思ったのが最後のセミコロンは
たとえ一行でもつけるを忘れないようにしたい。

話は変わるがclassとidの違いが漸く分かった。
classは他でも使いまわしたい時に使うやつで、
idは一つだけに適応させたい時に使うものだった。
classは役割という意味であることを考えれば
あまりおかしくはない。

あと、今UdemyのJavaの方の講座もやっているのですが
前から微妙に疑問に思っていたcom.XXX.AAAという
パッケージ名だったのですが何でcomから始まるんだろうなと思っていたのですが
どうもユニークな名前にするためにそうしていたそうです。

動画講座をやっていると(当たり外れがあるという前置きが付きますが)
本って買う意味があるのかなという気分になる時がある
間違いがあれば訂正してくれるし、また、わからないことがあれば質問に答えてくれたり
他の人がした質問すら見ることができる。
定期的にアップデートすることは大変そうだが、
利用者からすると本より便利だなという印象を強く受ける。

前にWheelPickerの独立記事書かなかったっけ?と思っていたら
どうやらEvernoteに残していた模様。
サンプルまで作っていたがいつのまにかお蔵入りなっていた模様。

WheelPicker(GitHub)

使い方は、gradleで

compile 'cn.aigestudio.wheelpicker:WheelPicker:1.1.2'

を書けば使えるようになります。

使い方

レイアウトの場合。色合いはともかく基本的な設定は以下の設定がおすすめです。

            <!-- app:wheel_cyclic="true" デフォルトではfalse 最小値を下回った時に最大値、最大値を上回った時に最小値になる。 -->
            <!-- app:wheel_curved="true" デフォルトではfalse 見た目がドラムロール式になる。。 -->
            <!-- app:wheel_atmospheric="true" デフォルトではfalse 選択されていない値の色が薄くなる。 -->
            <!-- app:wheel_curtain="true" デフォルトではfalse 選択された部分にカーテンのように重ね掛けする。
            wheel_curtain_colorで色の指定ができる。指定がない場合は白色になる。指定する場合、透明度を設定しないと
            塗りつぶされて見えなくなるので注意。フォントの色も変わるので正直、背景色の設定を工夫したほうがいいと思う-->
            <!-- app:wheel_indicator="true"  デフォルトではfalse 選択された箇所に上下に線を引く。デフォルトの色は赤。
            wheel_indicator_colorで色を、wheel_indicator_sizeで太さを変更できる-->
            <com.aigestudio.wheelpicker.WheelPicker
                android:id="@+id/wheelPickerLeft"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:background="#146905"
                app:wheel_atmospheric="true"
                app:wheel_curved="true"
                app:wheel_cyclic="true"
                app:wheel_item_text_color="#000000"
                app:wheel_item_text_size="40sp"
                app:wheel_selected_item_position="0"
                app:wheel_selected_item_text_color="#ecf93c"
                app:wheel_visible_item_count="3" />


コードの設定。
*WheelPickerOnItemSelected を使わないならば、
多分WheelPickerOnItemSelectedの記述はいらない

private interface WheelPickerOnItemSelected : WheelPicker.OnItemSelectedListener{
    override fun onItemSelected(picker: WheelPicker?, data: Any?, position: Int) { }
}

class MainActivity : AppCompatActivity() , WheelPickerOnItemSelected {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val array0to9 = arrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
        val list0to9 = Arrays.asList(*array0to9)
        wheelPickerLeft.data = list0to9
        wheelPickerLeft.setOnItemSelectedListener(this)
        wheelPickerMiddle.data = list0to9
        wheelPickerMiddle.setOnItemSelectedListener(this)
        wheelPickerRight.data = list0to9
        wheelPickerRight.setOnItemSelectedListener(this)

    }

    override fun onItemSelected(picker: WheelPicker?, data: Any?, position: Int) {
        super.onItemSelected(picker, data, position)
        var num :Int = 0
        when(picker!!.id){
            R.id.wheelPickerLeft -> textViewNumLeft.text = position.toString()
            R.id.wheelPickerMiddle -> textViewNumMiddle.text = position.toString()
            R.id.wheelPickerRight -> textViewNumRight.text = position.toString()
            else -> 0
        }

    }

}

採用しようかと思ったが、表示の制御で出来なくて
(具体的には時間が25以上になった時に24に差し替える処理)
心が折れたので採用を取りやめます。
原因は検証しきれていないが、どうもダイアログで呼び出しているのが原因のように思える。

分単位の操作が多くて扱いづらいのだが、ひとまずTimePickerを採用します
このままだと現在時刻を取得するので初期化をしようと思ったのだが
hourやminuteを呼び出すとapi23以上しか対応していないと言われる
調べてみたところ、前はcurrentHourやcurrentMinuteを使っていたようである。

一先ず初期化ができたのは確認できたので
今日はここまで


2018-02-27 到達点メモ


今、転職活動中でSkype面談をしたのだが
その際に先方にメッセージが送信できなくてすごくあせった。

原因はいまいち掴み切れていないのだが
やったことを書いておく

1.再度メッセージを送った→意味をなさず
2.アプリ(恐らくWindowsストアにあるSkype)を×ボタンで一度終了し、再度立ち上げる→同じく意味をなさず
3.Windowsを再起動→同じく意味をなさず
*ここらへんで割とパニックの真骨頂に達する
4.調べてみたところ、どうもブラウザ版のSkypeで動くのか確かめたのかという言葉があったので起動してみて、メッセージを送信する→届いた!
*調べたところ相手側がオフラインだと届かないらしいが、送信した時はオフラインだった気がする
*あと、自分がつかっているパソコンのSkypeは何故か連絡帳の登録が表示されない。ブラウザ版だと表示されていたのでそれを登録してようやくオンラインの状態が分かるようになった。

皆も気を付けよう!

それはさておき少しだけ今日も開発を進めた

TimePickerのandroid:timePickerModeをclockで設定したところ
API21にしてくれという警告が出るようになった
少し調べてみたが、公式ドキュメントのTimePickerのandroid:timePickerModeの項目
AndroidのバージョンがLより前の時はspinnerしかなかったという話なので
多分、android:timePickerModeを使っているからそういうのが出ているのかもしれない。
公式ドキュメントのメソッドを見る限りでは、API1からのがあるので
何か抜け道がある気がする
多分、デザインで作っているからこういう風になっているのだと思われる。

実装したのだが、一分刻みをスクロールさせるの大変面倒である。
なので、5分刻みもしくは10分刻みにしようと思ったのだが……
[Android] 刻む「分」が指定できる拡張TimePickerを作る【解析編】
30分単位の時刻を選択するTimePicker
5分刻みのTimePicker

えっ、用意されてないの?
マジで????

前使ったライブラリを使うか。はぁ……。

独自レイアウト(独自ダイアログ)もしくはカスタムレイアウト(カスタムダイアログ)でやりたい場合は以下のようにする

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val builder = AlertDialog.Builder(activity)
        val view = LayoutInflater.from(activity).inflate(R.layout.time_limit_dialog, null)
        return builder.setTitle("timeSetting")
                        .setMessage("TEST")
                        .setView(view)
                        .create()
    }

    override fun onStart() {
        super.onStart()
    }

    override fun onPause() {
        super.onPause()
        dismiss()
    }

ある程度めどはついたので今日はここまで


2018年2月26日月曜日

2018-02-25 到達点メモ


一日で終わるかもと言っておきながら全然進まなかったが、
見直してみたら思ってたより進んでいた。
よく考えたらすぐできる実装なので実装した。できた。
そういう訳で、特定のアプリが立ち上がった時に割り込みで
立ち上がるようにすることができました。
やったぜ!!
他のアプリの状況を知りたい場合は、UsageStatsManagerを使うとよいらしい


下記のサイトを参考にした
[Android][Lollipop]UsageStatsManagerでgetRunningAppぽく取得する
Android 6.0 : UsageEvents method 'usageStatsManger.queryEvents()' is giving count 0 (zero)
【Android】getRunningTasksが使えなくなったLollipopでアプリ使用状況を取得する
[Android]自分のアプリが前面にいるかを判別する正しい方法(UsageStatsManager.queryEventsを使う(API21以降/要Permission)の項目)
ACTION_OUTSIDEが切り開くAndroidアプリ間連携の可能性 (2/4)


具体的に以下のように実装した

//履歴を取得の許可がでていなかったら、その画面に飛ぶ
if (isUsageStatsAllowed() == false){ startActivity( Intent("android.settings.USAGE_ACCESS_SETTINGS")) }
val userInfoString:String? = prefs.getString("PREF_KYE", null)
if (userInfoString != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ){
    var userInfo = gson.fromJson<UserInfo>(userInfoString, UserInfo::class.java!!)
    val stats = getSystemService(Service.USAGE_STATS_SERVICE) as UsageStatsManager
    val endTime = System.currentTimeMillis()
    val startTime = endTime - (10*1000).toLong()
    val nameList = ArrayList<ComponentName>()
    val usageEvents = stats.queryEvents(startTime, endTime)
    while (usageEvents.hasNextEvent()){
        Log.d(TAG,"usageEvents.hasNextEvent()")
        val event = android.app.usage.UsageEvents.Event()
        usageEvents.getNextEvent(event)
        if(event.eventType == UsageEvents.Event.MOVE_TO_FOREGROUND){
            val packageName = event.packageName
            val name = ComponentName(packageName, event.className)
            // リストにの先頭に追加する
            nameList.add(0, name)
            //普通にFOREGROUNDは複数あるようなのでbreakしない
        }
    }
    if(nameList.isEmpty() == false) {
        Log.d(TAG, "nameList[0].packageName:" + nameList[0].packageName)
        for (limitAppInfos: MutableMap.MutableEntry<String, LimitAppInfo> in userInfo.limitAppInfos) {
            if (limitAppInfos.value.limitFlag == false) {
                continue
            }
            val appPackageName = limitAppInfos.key
            if (nameList[0].packageName.equals(appPackageName)) {
                Log.d(TAG, appPackageName + " Looked")
                break
            }
        }
    }
}


◎此処までたどり着く際に詰まったところ


・uses-permission の設定

最初は以下のように設定していたのだが、エラー(赤の波線)が出ていた
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
で、以下のように設定しないとまずいらしい
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
        tools:ignore="ProtectedPermissions" />
理由は後日調べます


・stats.queryEvents(startTime, endTime)で、どっちが最近の時間?

startTimeは昔の時間。endTimeは最近の時間


・getSystemService("usagestats")で書くと赤い波線が出る。

getSystemService(Service.USAGE_STATS_SERVICE)と指定してあげるとよい


・コード的に正しいはずなのに、現在アクティブなアプリのデータを取得しない

使用するアプリが履歴を読み取ることを許可していますか?

大体こんなところだと思う。
一時はどうなるかと思ったが無事今月の最低限の目標は達成できたので
今日はここまで。

2018年2月24日土曜日

2018-02-24 到達点メモ


時間を置くと実装方法を忘れてしまう・・・。
そういう訳で前にしたServiceの実装方法を再度調べる
以前のブログに記録こそは残っていたが肝心の実装方法はあまり載っていなかった
はぁ・・・。
2017-12-03 到達点メモ
2017-12-07、08 到達点メモ
2017-12-12,13 到達点メモ
2017-12-14、15、19~21、27 到達点メモ

そういう訳でサービスの作成の際は以下二つの内容を参考にした
[Android] Service の使い方
Androidで死なないServiceを実装してみた

忘れがちな事項としてKotlinで継承いわゆる
Javaでいうextendをやる場合は以下のように書けばよい
class AppMonitoringService : Service(){}

あと、どうもなんか思い込みか
Service=何度も起動し続けるみたいなイメージがある
定期的に処理したい場合はServiceの中でスレッドを作成しないといけない

アプリを落としても一秒ごとにログを吐き続けるサービスの作り方

1.MainActivityで以下のように宣言する
        val serviceIntent= Intent(applicationContext, AppMonitoringService::class.java)
        startService(serviceIntent)

2.AndroidManifest.xmlに<application>タグの中に<service android:name=".AppMonitoringService"/>を宣言する

3.Serviceのクラスで以下のように宣言する
class AppMonitoringService : Service(){
    val TAG:String = "AppMonitoringService"
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

        val handlerThread = HandlerThread("other")
        handlerThread.start()
        val handler = Handler(handlerThread.looper)
        val runnable = object : Runnable {
            override fun run() {
                Log.d(TAG,"actingt!!")
                handler.postDelayed(this, 1000)
            }
        }
        handler.post(runnable)
        return START_STICKY//super.onStartCommand(intent, flags, startId)
    }

    override fun onBind(intent: Intent?): IBinder? {
        return  null
    }
}

以上。今回はサービスが生きてさえすれば
スリープによる停止は気にしなくてよいので
一先ずはこれで行けるのではないかと

あぁ、でも重くなるとサービスが削除される恐れがあるので
startForegroundはしないとまずいかもしれない。という訳で実装はした。

しかし、意外だったのはアプリを終了させると
どうやらサービスの方が生きていてもDeployGateでログが取れなくなるようである。
厳密には普通ログが取れない場合は、ログが取れないと出るところを
表示が変わらない状態になっていた。回避する方法があるのかもしれないが
少なくとも現段階での自分の設定ではそうだった。
ただ、AndroidStudioのほうではログが取れるので
なんだかんだ言って本当の意味でAndroidStudio無しでのテストは
まだ難しいのかもしれない。

あと、結構自分での意外な気付きだったのは
分からない作業は細かく分解すると案外難しくなさそうに見えることを実感した
最初は「アプリの常時を監視するサービスを作る」ぐらいだったタスクがあった。
そのタスクは今月いっぱいで終われる気がしなかった
しかし、以下のように分解したところ。見ての通り半分くらいは片付いた。
なお、これは今日の作業である。

項番 作業内容 優先度 実装状況
48 アプリの状態を常時監視するサービスのファイルを作成する 必須 作業済
52 serviceの作り方を確認する 必須 作業済
53 アプリが起動した際に立ち上がるサービスを作成する 必須 作業済
54 アプリが起動した際に立ち上がるサービスをログで確認する 必須 作業済
55 アプリが起動した際に一秒ごとにメッセージログを出すサービスを作成する 必須 作業済
56 アプリが起動した際に一秒ごとにメッセージログを出すサービスをログで確認する 必須 作業済
57 アプリが終了しても動き続ける機能を作成する。 必須 作業済
58 アプリが終了しても動き続ける機能をログで確認する 必須 作業済
59 現在アクティブになっているアプリを取得する機能を作成する 必須 未着手
60 現在アクティブになっているアプリを取得する機能をログで確認する 必須 未着手
61 特定のアクティビティが起動しているかを確認する機能を作成する 必須 未着手
62 特定のアクティビティが起動しているかを確認する機能を確認する 必須 未着手
63 特定のアクティビティが起動している時、別のアクティビティを立ち上げる機能を作成する 必須 未着手
64 特定のアクティビティが起動している時、別のアクティビティを立ち上げる機能が動くか確認する 必須 未着手

このように細かく分解したことでやることが明確になり
正直明日でも終わりそうな気がする。
国語の教科書で「困難は分割せよ」とあったがまさしくその通りだった。
最初に15分くらいかけて作ったかいがあった。
なお、これは最初からこう作れたわけではなく、途中で追加したタスクもある。
寧ろ詰まったらどんどん分解していくのがいいのかもしれない。

自分的には目的を達成したので今日はここまで。

2018年2月21日水曜日

20187-02-20 到達点メモ


今日は簡単に曜日の保存の機能の実装をした。

同じような処理は纏めたほうがやはりコード的にもすっきりする。
しかし、onCreate部分が長くなると、ローカル用のメソッドを作った際に
うっかりonCreate部分に書いてしまい、宣言したはずなのに
エディタ―上だと使えないのでどうなってるんだ?となってしまう。
*無論、使用する前より上の方でメソッドの宣言をすれば使えるが
それが目的ではないのでそうしない。

セーブデータがある場合、オブジェクトデータを事前に読み込む処理をいれていなかったので一部のデータが保存されていたりいなかったりして
一時期混乱した。
コメントできっちり残しておけば、この混乱は防げたかもしれないので
もう少し豆に着ける癖は付けた方がよいだろう
それはそれとして、クラスの中にクラスを作ってそれを実質データ型として使う
というのはあまり自分にとってはなじみがないのでそこも影響が出ている気はする。
*今までRailsを使って開発してきたが全部データベースでデータを管理してきたので
あまり必要性が無かった。

今までmapのデータをgetメソッドで呼び出していたが
[]で代用できるとエディタ―が警告していたのでそれも修正。

あんまり進んではいないが今日はここまで


2018年2月19日月曜日

2018-02-18 到達点メモ


トグルボタンのデザインのカスタム方法を見つけたが
今のところ、必要なくなるかも?
少なくとも優先度は下がったが、検証はしていないが調べておいたので
URLだけは貼っておく

参考サイト:Buttonの状態に応じて、text,textColor,BackgroundColorを変えるには
参考サイト:Android: トグルボタンのスタイル変更
参考サイト:トグルボタンの画像を変更する

ただ、On/Offでの動作はいるのでそれの実装はした

参考サイト:トグルボタンの実装


Gsonというライブラリを使うとスマートにデータ管理ができるらしい
確かにちょっと使ってみたが、色々読み書き用のデータを宣言しなくて楽だわ
これでやればキー一つで管理できる


あとlongからDateに変える方法は
var date:Date = Date(longData)
でできる



今回、コレクションのMapを使いたかったのでそれを実装
初期状態で宣言のやり方が見つからなくて
苦労したが以下のようにやればいけそう

class UserInfo {
    var limitAppInfos:MutableMap<String,LimitAppInfo> = mutableMapOf()
}

class LimitAppInfo{
    var limitFlag:Boolean = false
    var limitStartDate:Date? = null
    var limitEndDate:Date? = null
}

さて、今凄い問題なのがデータの受け渡しができない。
Activity間のデータの受け渡しってなんか特別な宣言はいらなかった気がするのだが・・・?

どうも本来intentと宣言してデータを取るところを
getIntent()と書いていたのが原因のように見える
this.intentでも動いているようなのだが
 testToggleButton.setOnCheckedChangeListener{}
の中でもintentを宣言してきちんと動くか確かめてみたが
自分のコード上では動くことが確認できた。

動かない場合は、特にJavaのコピペを所々やっている場合は
intentで取得しているかどうかを確認したほうが良い。

あと、しょうもないは話なのだが
マップデータを保存していたのだが
何度やってもデータが一つしか保存されないと思っていたら
データがある場合は、そのデータを使用するという設定をやっていなかった。
そりゃあ、何度やっても一つしか入らない訳だよ。

あと、今回リストViewからパッケージ名は表示したくないが
パッケージ名を取るという事をやりたかったので
テキストViewのvisibilityをgoneに設定してみたところ
狙い通りレイアウトに影響を出さないで
テキストViewに保存したデータを取得するということができた。

各アプリごとにOn/Offの保存と反映ができるようになったので
今日はここまで

2018年2月17日土曜日

2018-02-17 到達点メモ


*Kotlinで開発しています

今日はインストール日時と最終アップデートの日時の取得と
並び替えの機能を実装した。

val dataList = mutableListOf<ListItem>()

for (appInfo in appInfoList) {
//無関係部分を一部省略
     var data:ListItem = ListItem()
     val pkgInfo:PackageInfo = packageManager.getPackageInfo(appInfo.packageName,PackageManager.GET_META_DATA)
     data.installTime = pkgInfo.firstInstallTime
     data.lastUpdateTime = pkgInfo.lastUpdateTime
     dataList.add(data)
}

インストール日時と最終アップデート日時は、
ApplicationInfo(コード上ではappInfoを指す)から直接取ることができないので
PackageInfoでApplicationInfoでpackageNameを使ってアプリを特定し
そこから、firstInstallTimeとlastUpdateTimeを使ってそれぞれデータを取得する。
なお、返り値はLong型である。

参考サイト:アプリのサイズとインストール日を調べる方法
参考サイト:Android インストール日時、アップデート日時取得
参考サイト:アプリケーションのインストール日時、更新日時を取得する


並び替えに至っては思っていた以上に簡単だった
自分の場合は、ListItemを定義したファイルに以下のように書いた

internal class NameComparator : Comparator<ListItem> {
    override fun compare(lhs: ListItem, rhs: ListItem): Int {
        return lhs.label!!.compareTo(rhs.label!!)
    }
}

そしてMainActivityで以下のように実装。

Collections.sort(dataList, LastUpdateTimeComparator())
val adapter = ImageArrayAdapter(applicationContext, R.layout.list_view_image_item, dataList)

これで実装完了である。
少し注意する点があるとすれば,
昇順でなく降順に設定したい場合は以下のように書き換えればよい

internal class NameComparator : Comparator<ListItem> {
    override fun compare(lhs: ListItem, rhs: ListItem): Int {
        return rhs.label!!.compareTo(lhs.label!!)
    }
}

参考サイト:ListView内のアイテムの並び替え
参考サイト:【Java入門】compareToで大小を比較をする方法総まとめ(文字列/日付)

調子がいいので、そのままスピナーの並び替えも実装。
引数を取るようにし、引数によって昇順、降順の並び替えを変更。
で、色々やった結果が下の画像です。


  

正直、調べたら普通にできたので特に書きたい事は無い。
ただ個人的に思ったのがプログラム的にどうこうではないが
古いインストール順と古いアップデートはいらないということと
場合によっては、ツールバーのアイコンにどうさせる必要があるかもしれない。

参考サイト:Androidアプリ開発でSpinnerを追加する方法【初心者向け】
参考サイト:スピナー(Spinner)を使用するには
参考サイト:スピナー(公式ドキュメント)
参考サイト:Resourcesクラスを使ったリソースの参照

ついでにListViewの項目をタップすると
スケジュール画面に遷移するように設定。

参考サイト:ListViewにクリックイベントを追加してみよう

きりが良いので今日はここまで。

2018年2月16日金曜日

2018-02-16 到達点メモ


*Kotlinで開発しています

以下の二つのサイトを参考にして、インストールされているアプリ一覧を表示するようにする

参考サイト:端末にインストールされているアプリを一覧で取得してみる
参考サイト:ListViewに画像サムネイル付きテキストを表示してみる

原因がいまいちわかり切っていないのだが、
別ファイルでコンストラクタ付きのクラスをインポートできない時があった。
今回は一先ず、MainActivityのクラス内に書いて解決した

参考サイト:Kotlinでコンストラクタを書く
参考サイト:Kotlinでunresolved referenceが出ます。

あと、作成中に酷いレイアウト崩れが起きた。
原因は以下のように、理由は二つあり、一つは
ImageViewとTextViewの両方の大きさの設定がwrap_contentだったこと。
二つ目はImageViewとTextViewの両方にandroid:layout_weightを設定したからである

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/app_icon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:srcCompat="@android:drawable/btn_star_big_on"
            android:layout_weight="1"
            tools:padding="2dp" />

        <TextView
            android:id="@+id/app_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="テスト" />
    </LinearLayout>

今回に限って言えば、アイコンの大きさはある程度固定でよいので、
ImageViewの大きさを固定にし、android:layout_weight="1"を削除した。

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/app_icon"
            android:layout_width="64dp"
            android:layout_height="64dp"
            app:srcCompat="@android:drawable/btn_star_big_on"
            tools:padding="2dp" />

        <TextView
            android:id="@+id/app_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="テスト" />
    </LinearLayout>

また、表示するアプリに関してもそのままやるとラベル名がcom.×××.◎◎◎という風になっているのでそれをうまい感じに排除したい。
そこで考えたのが、基本的に使うアプリはプレインストールされていてもアップデートされるので、プレインストールされているアプリの中でもアップデートされるアプリは排除の対象外にすれば行けるのではないかと思って以下のようにやりました
*ApplicationInfoの中にFLAG_UPDATED_SYSTEM_APPというプレインストールされているアプリでアップデートされたアプリというフラグがある


if ((appInfo.flags and ApplicationInfo.FLAG_SYSTEM) ==  ApplicationInfo.FLAG_SYSTEM){
       if ((appInfo.flags and ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) !=  ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) continue
}

そうすると以下のような感じになりました。



今日の目標は無事達成できたので、今日はここまで。



2018-02-15 到達点メモ


Kotlinでの話です。
端末にインストールされているアプリを一覧で取得してみる(Javaでの話)
上記のサイトを参考に実装していたのですが、
val dataList:List<AppData> = ArrayList<AppData>()
dataList.add()
で、何故かaddがメソッドで見つからないと思っていたら

Kotlinコレクション入門(Kotlin の List は読み取り専用)によると
ArrayListは読み取り専用のだそうです。
マジかよ・・・

色々調べた結果、どうもmutableListOfが使えそうな気がする
Kotlin公式ドキュメント(mutableListOf)
Kotlinの小技

実際の実装はこっちが参考になりそう。
というか調べるときにきちんとKotlinを入れないとまずい。
思ったより、Kotlin独自の仕様が多く
自動翻訳をしても漏れることが多い
Kotlin:ListViewの基本的な実装方法!
KotlinでData Bindingをする - 基本編
KotlinでData Bindingをする - ListView編

そもそもDataBindingってなんじゃらほい?
意味はいまいち読み取れないのだが、
データとレイアウトXMLを接続し、データを変更すると
それに合わせてレイアウトXMLを変える機能らしい?
今まででも似たようなことがしたことがある気がするが
もっと効率的にやれるようになるのかも??
ちげぇ、これどうやらXMLでオブジェクトのデータが直接使えるようになるやつだ。
というかRailsで似たようなのがあった。

具体的にいうとプログラムのところで以下のように設定します
public class User {
   public final String firstName;
   public final String lastName;
   public User(String firstName, String lastName) {
       this.firstName = firstName;
       this.lastName = lastName;
   }
}
他にいろいろごにょごにょ設定すると
XML上で赤字のように直接モデルのメンバ変数を使えるようになる

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="handlers" type="com.example.Handlers"/>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.firstName}"
           android:onClick="@{handlers::onClickFriend}"/>
   </LinearLayout>
</layout>

ただ、正直今の俺には使い道がちょっと思いつかないなぁ……。

参考サイト:データ バインディング ライブラリ
参考サイト:Android Databinding 〜超入門〜
参考サイト:Android M の新機能!データバインディングを使ってみた
参考サイト:【Android】AndroidのData Bindingでできること(基本編)

あと、検証中に以下のようなエラーが出た。
具体的には起動してすぐ落ちた。
  java.lang.RuntimeException: Unable to start activity ComponentInfo{com.nononagainfo.www.applimiter/com.nononagainfo.www.applimiter.MainActivity}: android.view.InflateException: Binary XML file line #0: Binary XML file line #0: You must supply a layout_width attribute.

幅の調整の関係でチェックボックスのlayout_widthをデザイン画面でnoneにしていため
*xml上で見た場合は、layout_width自体が無い状態になっている
上記のエラーが起きた。データを入れれば解決した。


さて、下記の画像の通り何とか作ることができたのだが
よく考えたらChromeとかプレインストールアプリなので
((appInfo.flags and ApplicationInfo.FLAG_SYSTEM) ==  ApplicationInfo.FLAG_SYSTEM) で弾くのはまずい。
ので、何らかの方法を考えるなり調べる必要がある。




次回は、ListViewに画像サムネイル付きテキストを表示してみるを参考に
アイコンを表示できるように取り組みたいと思う。

今日はここまで。

2018年2月12日月曜日

2018-02-12 到達点メモ


*Kotlinで開発しています

今日は、ダイアログにプリインストールされているアプリを除く
インストールされているアプリ一覧を表示する機能を作った。



ついでにスクロール機能も付けた。
アプリ一覧の取得はさほど難しくはなかったが
プリインストールを排除する方法が
引っかかった。

Androidでプリインストールを排除する方法を調べると
以下のような方法が良く見つかるのだが
Kotlinでは以下の方法はそのままでは使えない

if((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == ApplicationInfo.FLAG_SYSTEM)

Kotlinでビット演算を行う場合は
&ではなくandなので以下の内容が正しい。

if ((appInfo.flags and ApplicationInfo.FLAG_SYSTEM) ==  ApplicationInfo.FLAG_SYSTEM)

参考サイト:端末にインストールされているアプリを一覧で取得してみる
参考サイト:アプリの一覧を取得する
参考サイト:[Android] ScrollView 画面の縦スクロール


また、GradleでDeployGateのアップロードの仕方の方法が
分かったのでそれについて書く

前は公式ドキュメントに掲載してあるGradleプラグインを基に
進めていたのだがこれだけだと上手く行かなかった。

冷静に考えれば、何処にもログイン情報の設定が書いてないので
当たり前なのだが、一番下の方にgradle-deploygate-pluginへの
リンクが張ってあるのでそこを見に行く必要がある

で、基本はそこの通りに設定すればよいのだが
一つ注意が必要な部分がある。
リンク先では、noAssemble = trueとなっているのだが
どうも、これをtrueのままだとビルド自体をスキップするようなので
ここはfalseにした方がよい。

userNameとtokenは、アカウント設定→プロフィールにいけば、
ユーザー名とアイコンにuserNameがあり、
tokenは一番下の方にスクロールさせるとAPI keyという項目があるので
それを代入すればよい。

これで動くはずである。AndroidStudioの右の項目のGradleからの
uploadDeployDebugを一度動作させれば、▶ボタンをクリックするだけで
一括で動くようになるので凄く便利。

もう接続したコネクタを抜き差ししたり、
ビルドしたファイルをわざわざアップロードをしなくてもできるようになるぞ!!

参考サイト:gradleからdeploygateへアップロードする

……今、トークンを直書きしているから後で直さなきゃ
作業はそんなに進んでいないが、前からの懸念事項は解決できたので
今日はここまで。

2018年2月11日日曜日

2018‐02‐11 到達点メモ


今日は、自作ダイアログを適応させて
コード上でボタンを追加させるという事を行った。
*実装はKotlinです。

結果は以下のような感じ




ダイアログにカスタムレイアウト適応するのはさほど難しくなかったが
追加するのがちょっと面倒だった。

具体的には以下のような感じに実装

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    // Use the Builder class for convenient dialog construction    val builder = AlertDialog.Builder(activity)
    val view:View = LayoutInflater.from(activity).inflate(R.layout.applimit_dialog,null)
    builder.setTitle("test")
           .setView(view)
    // Create the AlertDialog object and return it    return builder.create()
}

override fun onStart() {
    super.onStart()
    var textView:TextView = dialog.findViewById(R.id.copyTextView)
    textView.text = "ChangeText"    val button:Button = Button(activity)
    val layout = dialog.findViewById<ConstraintLayout>(R.id.applimit_dialog)
    layout.addView(button)

}

メモとして、Builderの中身の変更はonStartで行う。
また、onStartに処理を書けば
ダイアログを呼び出す度に内容を変更することができる

あと、微妙につまったのがレイアウトを取りたい時に
dialog.findViewById<>()の<>の中身の指定の仕方が
最初わからなかった。
以下のように指定すればいいと分かるとそんなに難しくはないが。
dialog.findViewById<ConstraintLayout>(R.id.applimit_dialog)

あと、複数を動的に表示したくて調べたが
とりあえず、今回の件ではView.OnLayoutChangeListenerは使わない。

参考サイト:Kotlin(Java)コード上でConstraintLayoutの制約を編集する
参考サイト:DialogFragmentにカスタムビューを設定

現状だと、大きさの変更をする必要があるので
そこの対策?がいるがやりたいことや確認したいことは大体確認できたので
今日はここまで。

2018年2月10日土曜日

2018-02-10 到達点メモ


KotlinでDialogFlagmentをつかってダイアログを表示する機能を作っていたのだが
ダイアログのボタンを押すと以下のようなエラーが出た。

java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter savedInstanceState
 at com.nononagainfo.www.applimiter.FireMissilesDialogFragment.onCreateDialog(FireMissilesDialogFragment.kt)
 at android.app.DialogFragment.getLayoutInflater(DialogFragment.java:406)
 at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:995)
 at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1171)
 at android.app.BackStackRecord.run(BackStackRecord.java:815)
 at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1578)
 at android.app.FragmentManagerImpl$1.run(FragmentManager.java:483)
 at android.os.Handler.handleCallback(Handler.java:751)
 at android.os.Handler.dispatchMessage(Handler.java:95)
 at android.os.Looper.loop(Looper.java:154)
 at android.app.ActivityThread.main(ActivityThread.java:6184)
 at java.lang.reflect.Method.invoke(Native Method)
 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)

なんじゃこりゃーといって、
DialogFragmentの定義部分で
val builder = AlertDialog.Builder(activity)
var builder = AlertDialog.Builder(activity)に変えたり
もしくは、ダイアログの呼び出し先で
val dialog =  SelectAppLimitDialogFragment()
var dialog =  SelectAppLimitDialogFragment()に変えたり
と無意味な事をしました。
そして、nullと呼び出されているから
dialog.show(fragmentManager,"test")を
dialog.show(fragmentManager!!,"test")に変更したりなど
冷静に考えれば意味が無いと気が付くような変な修正をしていました。

結局のところどう直せばよいかというと
override fun onCreateDialog(savedInstanceState: Bundle): Dialog を
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog に
修正すればよいという訳でした。
ようは、継承元がnullを許容する引数なのに、継承先がnullを許容しておらず
型が一致しないため、エラーが起きたのだと思われます。

前も同じような修正をしていた気がしたので
調べなおしたら見事にやらかしていた。
2017-11-10  到達点メモ
原因を特定するために参考にしたサイトも前と同じだったのは笑った。

このようなケースのミスはJavaからソースをKotlinに変換すると
起こりやすいミスな気がします。
前の失敗は、Javaのコードを変換した直後ですし
今回の件は、Javaのコードをコピペしたので可能性は高いと思っています。

参考サイト:KotlinでParameter specified as non-null is null

なお、DialogFragmentを作る際は以下のサイトを参考にした
参考サイト:ダイアログ(公式ドキュメント)
参考サイト:[Android] DialogFragmentを使ってダイアログを表示する
参考サイト:KotlinでDialogFragmentを使用する【Android】
参考サイト:コピペしてすぐ使えるアラートダイアログ集

あんまり進んでいないが、今日はここまで

2018年2月4日日曜日

2018-02-03 到達点メモ


デザイン画面で、同じ横幅でボタンなどを並べたい場合は
LinearLayout(horizontal)を使う。
一時期、TableLayoutで設定していたが、面倒なだけなので
特に理由が無いのであればやらない方がよい

ただ、デザイン画面で設定しているとよく起こるのだが
layout_heightの縦幅がデフォルトだと、match_parentになっており
一番下まで大きさを取ってしまう
その為、必要なものを追加した後に、wrap_contentに変更するとよい。

参考サイト:AndroidでButtonを並べる時の注意点

今回、一応画面設計を書いたうえで作ったのだが
実際にデザインしてみると、画面設計の配置がおかしいと感じたり
わざわざ採用しなかったのを採用しそうになったりした。

画面設計の配置に関しては、完成させることを優先させていたので
もっと丁寧にやるくらいしか対処が思いつかないのだが
機能の不採用の理由ぐらいは、何処かに書いておいた方がいいなと改めて思った。

一度作業を打ち切ると再開が難しい。
お蔭で全然進んでいないが、今日はここまで。

2018年2月2日金曜日

2018-02-02 到達点メモ


二月中に簡単な追加機能を除けば、VoiceTimerもぼちぼち落ち着いてきたはずなのでいい加減新しいアプリの開発を始める。

*言語は全部Kotlinで書いています

で、xmlファイルをデザイン画面でいじっていたら
警告マークが名前の横に表示される部分が出て以下の内容になっている
This is 'TableLayout' layout or its 'LinearLayout' parent is possibly useless

で、調べてみたところ以下の内容を見つけた
ScrollView layout or its RelativeLayout parent is possibly useless

Google翻訳で翻訳した上で、上の回答に限って言えば
RelativeLayoutが1つのビュー(ScrollView)しか含まないため、無用であることを意味します」とのことだった。

それを踏まえてみるに、自分のコードはLinearLayoutの中にTableLayoutを一つだけしか宣言していない。本来、LinearLayoutは、複数での宣言を前提にしているので、LinearLayoutを宣言している意味が無いよという事なのだと思う。

実際に、LinearLayoutの中に、もう一つTableLayoutを入れたところ、
警告マークの表示がなくなった。

あと、遷移の仕方とか度忘れしていて調べなおして初めて知ったのだが

           val intent = Intent(this@MainActivity, ScheduleSettingActivity::class.java)
           startActivity(intent)

のような宣言をすると、何度も画面遷移した場合、参照され続けて
Out of Memoryになるらしいです。
なので以下のように変更したほうが良いらしい

            val intent = Intent(application, ScheduleSettingActivity::class.java)
            startActivity(intent)

参考サイト:[Android] アプリの画面を遷移させる

あと、以下のような書き方をしていた場合

            val intent:Intent = Intent(application, ScheduleSettingActivity::class.java)
            startActivity(intent)


以下のような警告が出た。
Explicitly given type is redundant here less... (Ctrl+F1)
This inspection reports local variables' explicitly given types which are obvious and thus redundant, like
  val f: Foo = Foo()

ざっくりとした意訳だが、「明らかに型の明示がわかるから、型宣言をしなくてもいいよ」という意味らしい。
という訳で以下のように変更した

            val intent = Intent(application, ScheduleSettingActivity::class.java)
            startActivity(intent)

今日はここまで。