Compare commits

...

568 Commits

Author SHA1 Message Date
4dab0f33f0 Merge pull request 'ライセンス追加' (#172) from add_license into main
Reviewed-on: #172
2024-07-25 20:24:58 +00:00
d055c5fa14 ライセンス追加 2024-07-26 05:24:13 +09:00
e712a84564 Merge pull request 'update readme' (#171) from doc into main
Reviewed-on: #171
2024-07-24 07:30:04 +00:00
81dfdc1fe2
update readme 2024-07-24 16:29:50 +09:00
ad0ad3685b Merge pull request 'refactor/2' (#170) from refactor/2 into main
Reviewed-on: #170
2024-07-24 07:27:18 +00:00
f192876b4c
子供モード時チェックボックスが表示されないように 2024-07-24 14:47:43 +09:00
764beff222
アイコンリソース追加 2024-07-23 14:39:28 +09:00
21df151e65
RecyclerViewAnimation追加 2024-07-23 14:10:26 +09:00
b3bb61e43b
月修正 2024-07-23 14:10:17 +09:00
8da7b26d72
区切りを月に戻した 2024-07-23 13:56:49 +09:00
aece6909ad Merge pull request 'refactor/refactor1' (#169) from refactor/refactor1 into main
Reviewed-on: #169
2024-07-23 04:45:48 +00:00
bba2751704
判定ミス修正 2024-07-23 13:44:47 +09:00
7794d664ba
カレンダーダイアログ修正 2024-07-23 13:43:52 +09:00
bef4d02dae Merge pull request 'feature/improve-history' (#168) from feature/improve-history into main
Reviewed-on: #168
2024-07-23 04:32:58 +00:00
3000fe9989
自動更新するように 2024-07-23 13:31:18 +09:00
ecafb0c13a
UI微修正 2024-07-23 13:30:39 +09:00
c9e1706374
UI微修正 2024-07-23 13:29:45 +09:00
bad3b72c91
実行後更新するように 2024-07-23 13:20:57 +09:00
53614cf033
キャッシュありに戻した 2024-07-23 13:16:39 +09:00
2d9c1a10c9
WIP 2024-07-23 13:15:52 +09:00
fbfb02ad09
起動時処理修正 2024-07-23 13:12:21 +09:00
6213acc2b0
取得処理修正 2024-07-23 13:08:59 +09:00
7d01ebc018
Paidを集計に含めないように 2024-07-23 13:05:18 +09:00
3d222cafe0
レイアウト修正 2024-07-23 13:05:10 +09:00
c6ee2dc75e
チェックボックスを全部隠すフラグを追加 2024-07-23 13:02:29 +09:00
6a4ad4de05
支払い済みアイテムも取得対象に 2024-07-23 12:56:23 +09:00
942e7b561b
支払い済みの場合はチェックボックスを非表示に 2024-07-23 12:54:24 +09:00
03337612a7
テスト実装 2024-07-23 12:50:18 +09:00
0421d84bd6
PayReward実装 2024-07-23 12:49:49 +09:00
fc7e709cf6
API追加 wip 2024-07-23 12:45:15 +09:00
1ae8335a4a
Revert "Rename"
This reverts commit cbf7f843b2.
2024-07-23 12:44:08 +09:00
cbf7f843b2
Rename 2024-07-23 12:33:42 +09:00
870b8be7f6
リフレッシュ処理改善 2024-07-23 12:28:42 +09:00
f932b935b9
WIP 2024-07-23 12:28:20 +09:00
ede51b124f
WIP 2024-07-23 12:22:22 +09:00
4ba2677a15
WIP 2024-07-23 12:19:28 +09:00
f2c4e43b98
Setter 2024-07-23 12:15:33 +09:00
8129b83e16
いろいろ 2024-07-23 12:15:04 +09:00
62d298ebfd
とりあえず廃止 2024-07-23 12:11:44 +09:00
4916c05b08
効率化, リファクタ, 修正 2024-07-23 12:00:10 +09:00
161d9d7602
ID修正 2024-07-23 11:50:07 +09:00
a4986cffbc
全部✓ボタン挙動 WIP 2024-07-23 11:49:54 +09:00
f4b116cd27
例外workaround 2024-07-23 11:47:15 +09:00
86093a1e13
総額表示実装 WIP 2024-07-23 11:42:16 +09:00
7ecd46fb98
総額表示実装 WIP 2024-07-23 11:41:58 +09:00
7f8c9d74fa
レイアウト修正 WIP 2024-07-23 11:39:08 +09:00
98cacfd561
レイアウト修正 WIP 2024-07-23 11:37:00 +09:00
9a8d9ef631
TextView書き換えテスト 2024-07-23 11:17:28 +09:00
dc83d541e2
test wip 2024-07-23 11:16:28 +09:00
3a6fc8cdd7
ID修正 2024-07-23 11:16:21 +09:00
73e2c0f1d9
ViewHolder追加 2024-07-23 11:13:49 +09:00
696486623e
リファクタ 2024-07-23 11:09:49 +09:00
70db920060
方向修正 2024-07-23 11:08:54 +09:00
7ea5e143d6
暫定ヘッダーを挿入するように 2024-07-23 11:07:01 +09:00
8ea083cdf4
レイアウト微修正 2024-07-23 11:06:27 +09:00
9173db1f88
レイアウトパラメータ追加 2024-07-23 11:05:40 +09:00
8f77c73439
デバッグ用に日付を用いるように 2024-07-23 11:04:55 +09:00
20a24b4ea3
暫定実装 2024-07-23 11:03:37 +09:00
fc0091b24a
WIP debug 2024-07-23 11:00:13 +09:00
06c392af2e
テスト用レイアウト作成 2024-07-23 10:48:15 +09:00
e2a75ba6d7
Checkedなアイテムを一括取得できるように 2024-07-23 10:43:01 +09:00
fb3b15450d
回避 2024-07-23 10:42:29 +09:00
36ed1a0ab9
確実に元データを書き換えるように? 2024-07-23 10:35:55 +09:00
7c2496c373
コンストラクタで初期化するように 2024-07-23 10:34:48 +09:00
8095095ce8
コンストラクタで処理するように 2024-07-23 10:33:49 +09:00
6496e218af
呼び出し変更 2024-07-23 10:33:39 +09:00
9f1a9b64a9
チェックボックス挙動実装 WIP 2024-07-23 10:17:18 +09:00
dc21fee2cd
レイアウト修正 2024-07-23 10:15:28 +09:00
3959cbba5f
エラー修正 2024-07-23 10:12:48 +09:00
69689e7247
Streamにした 2024-07-23 10:12:19 +09:00
f73bfeeb03
WIP 2024-07-23 10:12:10 +09:00
a6612c5ea9
test 2024-07-22 12:23:42 +09:00
43d00c90c3
リファクタ 2024-07-22 12:18:45 +09:00
5d5804a1f6
iroiro 2024-07-22 12:01:16 +09:00
b08b1f54e4
View 2024-07-22 11:59:30 +09:00
aa9b997117
代入追加 2024-07-22 11:42:10 +09:00
805cb93d59
static 2024-07-22 11:41:58 +09:00
49e8acbe2f
Adapterひな形実装 2024-07-22 11:40:27 +09:00
3da941e0ae
仮レイアウト作成 2024-07-22 11:37:48 +09:00
c79f9e5469
isPaidを追加 2024-07-22 11:33:05 +09:00
a0cf1f3ab3
isPaidを追加 2024-07-22 11:32:25 +09:00
21ec30ef19
isPaidを追加 2024-07-22 11:31:32 +09:00
05e8d15a20 Merge pull request 'fix/fix_child_uinfo' (#167) from fix/fix_child_uinfo into main
Reviewed-on: #167
2024-07-12 02:57:10 +00:00
d33a454ecc
子供モード時編集ボタンをいったん非表示に 2024-07-12 11:56:45 +09:00
aace66143f
文言修正 2024-07-12 11:54:59 +09:00
e90fd8a759
実装改善 2024-07-12 11:54:09 +09:00
9b0b710c73
getChild実装 WIP 2024-07-12 11:54:03 +09:00
16f2ed9c20 Merge pull request 'fix/fix_icon' (#166) from fix/fix_icon into main
Reviewed-on: #166
2024-07-12 02:49:24 +00:00
7039271e45
アイコン変更 2024-07-12 11:48:32 +09:00
81ebc36c98
アイコンリソース追加 2024-07-12 11:47:32 +09:00
a37dbc9445 Merge pull request 'ウォレット画面のスワイプリフレッシュを修正' (#165) from fix/fix_pull_to_refresh into main
Reviewed-on: #165
2024-07-12 02:19:22 +00:00
4293b477f5 Merge pull request '子供がタスクを完了できない不具合を修正' (#164) from fix/fix_child_complete into main
Reviewed-on: #164
2024-07-12 02:18:57 +00:00
a5dcde0d54
スワイプしてリフレッシュを修正 2024-07-12 11:18:19 +09:00
6160eba7ac
修正 2024-07-12 11:13:29 +09:00
48949d3c60 Merge pull request 'AGPバージョンをダウングレード' (#163) from fix/support_flamingo into main
Reviewed-on: #163
2024-07-09 10:34:16 +00:00
d9cb4ecfb2
AGPバージョンをダウングレード 2024-07-09 19:32:49 +09:00
00ccb3a5f0 Merge pull request 'improve/all' (#162) from improve/all into main
Reviewed-on: #162
2024-07-09 09:35:20 +00:00
eb2e2e5b6d
ヘッダーレイアウト修正 2024-07-09 16:25:43 +09:00
94f47982f7
rename 2024-07-09 16:22:23 +09:00
cc5560745f
cache取得追加 2024-07-09 16:18:39 +09:00
358116811f
暫定対応 2024-07-09 16:12:17 +09:00
73bcefa99f
parentRenameRequest 2024-07-09 16:05:05 +09:00
c846659e4b
Singleton化 2024-07-09 16:00:43 +09:00
10374d2486
不要な引数を削除 2024-07-09 15:58:18 +09:00
2963ffec7e
実装 WIP 2024-07-09 15:58:07 +09:00
e714b26921
getParentDirect追加 2024-07-09 15:43:14 +09:00
57b22b8ec4
レイアウト調整 2024-07-09 15:39:52 +09:00
555e0d3862
アイコンリソース追加 2024-07-09 15:38:18 +09:00
c183d43643
レイアウト仮作成 2024-07-09 15:37:11 +09:00
793ff62b41
アイコン変更 2024-07-09 15:29:59 +09:00
bb2298598d
メニューリソース追加 2024-07-09 15:28:51 +09:00
00febce148
toolBar, Fabの明示追加 2024-07-09 15:09:42 +09:00
a033a3888e
引数指定 2024-07-09 15:01:35 +09:00
6769397484
挙動修正 2024-07-09 15:01:26 +09:00
c43ec08318
挙動修正 2024-07-09 14:21:57 +09:00
10ebcae612
マージン削除 2024-07-09 14:21:50 +09:00
38e9277e9b
addTaskDialog暫定再実装 2024-07-09 14:18:46 +09:00
b9fed3ab71
2024-07-09 13:53:12 +09:00
e01a28b1c4
テキスト変更 2024-07-09 13:52:37 +09:00
97ff8da8d8 Merge pull request 'improve/all' (#161) from improve/all into main
Reviewed-on: #161
2024-07-09 04:43:15 +00:00
39f4316345
fix 2024-07-09 13:42:46 +09:00
42a972855d
fix 2024-07-09 13:09:29 +09:00
15426d358c
更新に対応 2024-07-09 13:08:38 +09:00
c01f3012dd
アニメーション時間調整 2024-07-09 13:06:36 +09:00
4d2f177a88
一時的に消していた処理を復元 2024-07-09 13:06:29 +09:00
364f580fbc
WIP 2024-07-09 12:59:38 +09:00
97e6bc072e
アニメーション実装 WIP 2024-07-09 12:44:52 +09:00
8495e8021e
アニメーション修正 2024-07-09 12:44:44 +09:00
f052710cd8
アニメーション追加 2024-07-09 12:37:51 +09:00
4fa39cb49b
MenuHostで代替した関数を削除 2024-07-09 12:36:00 +09:00
f82fcf090f
アクション実装 WIP 2024-07-09 12:35:19 +09:00
434e52bf4a
Workaround 2024-07-09 12:29:37 +09:00
c8c3215c3f
WIP 2024-07-09 12:07:19 +09:00
34e5085b63
改善 2024-07-09 12:06:54 +09:00
4974bfced6
アニメーションでラグをごまかせたので遅延を削除 2024-07-09 11:49:16 +09:00
fbf49bbe43
実験実装削除 2024-07-09 11:48:31 +09:00
dd32a82b7b
toolbarManagerTest 2024-07-09 11:47:33 +09:00
f1c181a755
setToolbar追加 2024-07-09 11:46:10 +09:00
0993e46d48
ToolbarManager実装 WIP 2024-07-09 11:45:32 +09:00
5b01c048f9
アニメーション付与 WIP 2024-07-09 11:41:53 +09:00
a63deb01ac
ユーティリティでアニメーションを付与するように 2024-07-09 11:30:43 +09:00
6fe88d9ebf
戻り値を廃止 2024-07-09 11:29:50 +09:00
160be606ae
WIP 2024-07-09 11:27:58 +09:00
23993285af
実装削除 2024-07-09 11:25:32 +09:00
47134fcde6
depend移動 2024-07-09 11:25:27 +09:00
dcec3c2e9e
ユーティリティ作成 WIP 2024-07-09 11:24:37 +09:00
6fad20c34a
WIP 2024-07-09 11:24:29 +09:00
bdcd30c6f8
アニメーションを適用するために実装を修正 2024-07-09 11:19:43 +09:00
e6c8a85c95
Animatorsを依存に追加 2024-07-09 11:10:14 +09:00
61eead89c6
テスト用アニメーション追加 2024-07-09 11:03:27 +09:00
e70bfb17cd
遅延で回避 2024-07-09 10:52:56 +09:00
dde0edfad7
menuアイテム作成 2024-07-09 10:49:35 +09:00
1b1e57fb49
アイコンリソース追加 2024-07-09 10:49:03 +09:00
07c7f1c6fb
menuリソース作成 2024-07-09 10:47:47 +09:00
cc13bdd37f
コメント 2024-07-09 10:46:49 +09:00
8761e5ef81
toolBarの参照を外から取れるように 2024-07-09 10:46:21 +09:00
04fd81a547
toolBarをインスタンス変数に 2024-07-09 10:45:39 +09:00
7142a5d30f
Inject整理, リネーム 2024-07-09 10:45:07 +09:00
a4d838c781
初回もカレンダーのタイトルを設定するように 2024-07-09 10:42:00 +09:00
dc9b0a080f
ボタン, タイトルを設定 2024-07-09 10:41:17 +09:00
91b07b4aae
レイアウト WIP 2024-07-09 10:36:35 +09:00
cfdc160cad
レイアウト WIP 2024-07-09 10:33:59 +09:00
43cd603fcc
WIP 2024-07-09 10:28:50 +09:00
9b8fa59f9a
アイコンリソース追加 2024-07-09 10:28:40 +09:00
5f029a3a4f
色修正 2024-07-09 10:19:04 +09:00
34497196d5
カレンダーデザイン変更, WIP 2024-07-09 10:13:10 +09:00
43a20fe409
明示的にFabを隠すように 2024-07-09 10:09:02 +09:00
e4c36e6971 Merge pull request 'feature/calendar' (#160) from feature/calendar into main
Reviewed-on: #160
2024-07-09 01:04:56 +00:00
9efe6e12d4
仮ダイアログ設置 2024-07-09 10:02:27 +09:00
bb64622cc3
暫定表示処理実装 2024-07-09 10:00:05 +09:00
6c7f00249a
暫定取得メソッド実装 2024-07-09 09:56:53 +09:00
c0f371a892
インタフェース追加 2024-07-09 09:53:21 +09:00
731c2b8033 Merge pull request 'feature/parent_child' (#159) from feature/parent_child into main
Reviewed-on: #159
2024-07-08 20:37:30 +00:00
rca
94807508ee 子供を選んで飛べるように 2024-07-09 05:36:07 +09:00
rca
c1244accca Add 2024-07-09 05:32:50 +09:00
rca
4b163f6202 順序修正 2024-07-09 05:32:40 +09:00
rca
075bae4d2d SafeArgs追加 WIP 2024-07-09 05:32:23 +09:00
rca
9f930438ff id訂正忘れ修正, リネーム 2024-07-09 05:19:17 +09:00
rca
0df125ba2f レイアウト指定ミス修正 2024-07-09 05:18:18 +09:00
rca
e252f557d0 UIスレッドで実行するように 2024-07-09 05:17:26 +09:00
rca
9f07171543 セット忘れ修正 2024-07-09 05:16:30 +09:00
rca
722a66e1f0 loggerのinitを一番最初に行うように 2024-07-09 05:15:11 +09:00
rca
fa1bdfce4b 不要なログを削除 2024-07-09 05:14:45 +09:00
rca
6969e841b9 ボタンリスナー追加 2024-07-09 05:11:57 +09:00
rca
70f530a8d3 arg追加 2024-07-09 05:11:08 +09:00
rca
1960a83185 navigation修正 2024-07-09 05:09:48 +09:00
rca
a18a9eaf1c 暫定 2024-07-09 05:08:29 +09:00
rca
e8f4a5d66c レイアウト移植, 微修正 2024-07-09 05:06:36 +09:00
rca
ab8d309328 Adapter移植 2024-07-09 05:06:26 +09:00
rca
b025fc3abb id修正 2024-07-09 05:02:17 +09:00
rca
24c40d05e3 暫定レイアウト作成 2024-07-09 05:01:50 +09:00
rca
f0182b25c2 init 2024-07-09 04:58:49 +09:00
rca
6433a0d09a cleanup 2024-07-09 04:57:43 +09:00
rca
b416e93ab3 SelectChildFragment WIP 2024-07-09 04:57:22 +09:00
b433c8e7ed Merge pull request 'feature/child_mode_2' (#158) from feature/child_mode_2 into main
Reviewed-on: #158
2024-07-08 19:55:26 +00:00
rca
dfc159fec0 アクティビティをfinishではなく新しくインテントするように 2024-07-09 04:54:28 +09:00
rca
6e49695551 処理順序変更 2024-07-09 04:51:28 +09:00
rca
3d6914fa36 子供モードのとき子供管理画面は開けないように 2024-07-09 04:50:44 +09:00
rca
22f6f48bb1 メモ追加 2024-07-09 04:39:48 +09:00
rca
833b3613e8 レスポンスから記録するように 2024-07-09 04:23:36 +09:00
rca
96b1a5ea6d レスポンスモデル更新 2024-07-09 04:22:45 +09:00
rca
b29a98ee3e UserSettingsでChildIDを保持できるように 2024-07-09 04:18:22 +09:00
rca
c312e2ce29 デバッグメニューの呼び出しを廃止 2024-07-09 04:13:19 +09:00
rca
4307283d40 Revert "上限数にぶつかったので一つに統一"
This reverts commit 9ce71dbd64.
2024-07-09 04:12:20 +09:00
rca
e3d97b96c5 Revert "上限数にぶつかったので一つに統一"
This reverts commit 8bf4ed9c21.
2024-07-09 04:12:20 +09:00
rca
8bf4ed9c21 上限数にぶつかったので一つに統一 2024-07-09 04:11:19 +09:00
rca
9ce71dbd64 上限数にぶつかったので一つに統一 2024-07-09 04:11:14 +09:00
rca
4d23b91c2d 処理追加 2024-07-09 04:07:48 +09:00
rca
9426988a2e ナビゲーションに追加 2024-07-09 04:04:58 +09:00
rca
9fcd0ba1aa Child側Include 2024-07-09 04:04:17 +09:00
rca
c1d125b4f0 子供モード暫定実装 2024-07-09 04:03:03 +09:00
rca
354fac1659 ナビゲーション修正 2024-07-09 04:01:23 +09:00
rca
01220b933c StartDest 2024-07-09 03:58:22 +09:00
rca
19e99ad4f4 include 2024-07-09 03:58:02 +09:00
rca
08945533db fix id 2024-07-09 03:57:57 +09:00
rca
3c3f19c727 delete 2024-07-09 03:56:14 +09:00
rca
4cc80f9857 NavGraph 2024-07-09 03:55:56 +09:00
rca
70d12bbc59 エンドポイント変更 2024-07-09 03:50:28 +09:00
41a3fa3b54 Merge pull request 'エンドポイント変更' (#157) from feature/migrate_endpoint into main
Reviewed-on: #157
2024-07-08 17:13:36 +00:00
rca
da98941ed4 エンドポイント変更 2024-07-09 02:12:10 +09:00
e98cbefcad Merge pull request 'feature/child_mode' (#156) from feature/child_mode into main
Reviewed-on: #156
2024-07-08 17:10:15 +00:00
rca
85b8067f20 finishするように 2024-07-09 02:07:04 +09:00
rca
0a3eb19f6e 実行を非同期に変更 2024-07-09 02:04:06 +09:00
db09196303 Merge pull request 'feature/child_mode' (#155) from feature/child_mode into main
Reviewed-on: #155
2024-07-08 17:01:42 +00:00
rca
e25177a65b wrap_contentに変更 2024-07-09 01:59:29 +09:00
rca
254214253f 文字サイズ変更 2024-07-09 01:59:13 +09:00
rca
a0cb825336 一旦コメントアウト 2024-07-09 01:57:42 +09:00
rca
c91406bd3f 逆方向にも対応するように 2024-07-09 01:51:27 +09:00
rca
a9587b3917 逆方向にも対応するように 2024-07-09 01:50:00 +09:00
rca
94f10f7ce2 ログ追加 2024-07-09 01:47:35 +09:00
rca
7c4a93a4e7 インスタンスの取得を1回に 2024-07-09 01:46:51 +09:00
rca
520b4701c9 setChildModeを操作するように 2024-07-09 01:45:58 +09:00
rca
e04d29b0bb APIコール実装 WIP 2024-07-09 01:45:27 +09:00
rca
dac26806c3 APIをサービスに追加 2024-07-09 01:41:20 +09:00
rca
8a874a027f モデル作成 2024-07-09 01:40:45 +09:00
8406ac6e36 Merge pull request 'feature/login_register_ui' (#154) from feature/login_register_ui into main
Reviewed-on: #154
2024-07-08 16:34:58 +00:00
rca
6d3e53ec72 ChildLoginActivityへの推移を追加 2024-07-09 01:33:54 +09:00
rca
cb15ec6568 ChildLoginActivityへの推移を追加 2024-07-09 01:33:47 +09:00
rca
53e9b9c588 ID修正 2024-07-09 01:33:18 +09:00
rca
f4cb5f5b3c テキスト, ID修正 2024-07-09 01:32:14 +09:00
rca
3b8b7cd12c コピペ 2024-07-09 01:29:51 +09:00
rca
f96189e3dc EditTextやめた 2024-07-09 01:22:44 +09:00
rca
58bbde92f5 ID修正 2024-07-09 01:12:55 +09:00
rca
ed0c30de60 レイアウト調整 2024-07-09 01:09:29 +09:00
rca
7e4f6815bb ボタン設置 2024-07-09 01:07:17 +09:00
rca
70293ee51f レイアウト作成 WIP 2024-07-09 00:58:28 +09:00
ed877771b2 Merge pull request 'feature/child_mode' (#153) from feature/child_mode into main
Reviewed-on: #153
2024-07-08 15:54:29 +00:00
rca
6b6d03d540 処理 WIP 2024-07-09 00:53:43 +09:00
rca
40465d526a レイアウト調整 2024-07-09 00:48:08 +09:00
rca
196ce53e03 レイアウト調整 WIP 2024-07-09 00:47:34 +09:00
rca
18b64d255c レイアウト調整 WIP 2024-07-09 00:44:43 +09:00
rca
a3e7191c3a レイアウト調整 2024-07-09 00:41:28 +09:00
rca
d7b5f4e14c レイアウト WIP 2024-07-09 00:29:16 +09:00
rca
7a364607bb ログインコードでログインするためのアクティビティ作成 2024-07-09 00:20:47 +09:00
45321ebdc3 Merge pull request 'refactor/refactor' (#152) from refactor/refactor into main
Reviewed-on: #152
2024-07-08 15:18:43 +00:00
rca
1ba551ccb5 メモ追加 2024-07-09 00:17:31 +09:00
rca
3fbbc7311f テキスト変更 2024-07-09 00:17:12 +09:00
rca
a9e6d16540 ログ追加 2024-07-09 00:16:24 +09:00
rca
640d094e34 警告回避 2024-07-09 00:15:09 +09:00
61c46b12f6 Merge pull request 'refactor/refactor' (#151) from refactor/refactor into main
Reviewed-on: #151
2024-07-08 15:14:18 +00:00
rca
c3dec40ac0 Padding追加 2024-07-09 00:13:20 +09:00
rca
89ce3b718f マージン修正 2024-07-09 00:09:21 +09:00
rca
17cdf4a384 レイアウト高さ修正 2024-07-09 00:08:27 +09:00
rca
59051ee517 回避 2024-07-09 00:07:56 +09:00
rca
081b469f24 余白修正 2024-07-09 00:07:09 +09:00
rca
483e638754 子供の数が多いときはスクロールするように 2024-07-09 00:02:41 +09:00
rca
81d708ebe3 起動時にドロワーを開く処理を削除 2024-07-08 23:59:38 +09:00
rca
2e9a6c5660 規定ページを変更 2024-07-08 23:58:59 +09:00
rca
5dd9072f6a 旧設定画面をナビゲーションから削除 2024-07-08 23:58:29 +09:00
rca
13b6922c6d 旧設定画面をナビゲーションから削除 2024-07-08 23:57:54 +09:00
rca
7b85eb41af FeatureFlagを適用するように 2024-07-08 23:56:28 +09:00
rca
649e63d668 Productionに変更 2024-07-08 23:53:52 +09:00
rca
3e48fb8795 フラグ追加 2024-07-08 23:53:31 +09:00
rca
9d134ab0cb アイコン変更 2024-07-08 23:43:04 +09:00
rca
cc6dcb84b9 アイコンリソース追加 2024-07-08 23:42:46 +09:00
rca
f0674c1a88 仮置きデバッグメニュー代替追加 2024-07-08 23:40:47 +09:00
rca
627fe279a5 デバッグ情報をドロワーに追加 2024-07-08 23:12:58 +09:00
d4a07d1b15 Merge pull request 'feature/manage_child' (#150) from feature/manage_child into main
Reviewed-on: #150
2024-07-08 14:08:49 +00:00
rca
016218d673 アイコン修正 2024-07-08 23:08:15 +09:00
rca
a7a3f0f3b1 アイコンリソース追加 2024-07-08 23:07:50 +09:00
rca
099b6a04d8 非同期対応 2024-07-08 22:15:16 +09:00
rca
c8b70ef635 removeChild実装 2024-07-08 22:15:00 +09:00
rca
1b40da9eb0 removeChild追加 2024-07-08 22:14:34 +09:00
rca
ef510dcea2 removeChild戻り値型変更 2024-07-08 22:14:14 +09:00
rca
9fbec35d20 メモ追加 2024-07-08 22:12:56 +09:00
rca
0561f7a623 呼び出し忘れ修正 2024-07-08 22:12:21 +09:00
rca
26dc837eb8 削除ボタン暫定? 2024-07-08 22:11:34 +09:00
rca
aec917a9cb 編集ダイアログ実装 WIP 2024-07-08 22:09:18 +09:00
rca
0aa152ac43 updateChild実装 2024-07-08 22:09:05 +09:00
rca
e296c445fe API追加 2024-07-08 22:08:23 +09:00
rca
31cdd0e9a8 戻り値型変更 2024-07-08 21:56:17 +09:00
rca
1c534adca4 直接サーバーから更新する方法を追加 2024-07-08 21:52:09 +09:00
rca
3650ca9b4d コメント削除 2024-07-08 21:50:39 +09:00
rca
c1e4f75769 コンテナとマージンを追加 2024-07-08 21:49:33 +09:00
rca
ef64d58c8e 子供追加ダイアログ実装 2024-07-08 21:43:08 +09:00
86c7a87f05 Merge pull request 'feature/manage_child' (#149) from feature/manage_child into main
Reviewed-on: #149
2024-07-08 12:28:40 +00:00
46dc7644cc
iroiro 2024-07-08 17:16:34 +09:00
1366e671ae
メニューリソース追加 2024-07-08 16:51:35 +09:00
5d5cb999f2
ToolBarセットアップ 2024-07-08 16:49:31 +09:00
ae75a04e9d
ログ追加 2024-07-08 16:42:55 +09:00
118361aff4
ログインコードを発行して表示できるように 2024-07-08 16:40:50 +09:00
15a02fe501
ログインコードダイアログのレイアウト追加 2024-07-08 16:30:55 +09:00
3d173b437e
マージン修正 2024-07-08 16:28:23 +09:00
3d0ea8525f
マージン修正 2024-07-08 16:26:00 +09:00
c5b2e9a186
マージン追加 2024-07-08 15:45:30 +09:00
c97575ee0f
メモ追加 2024-07-08 15:43:56 +09:00
f22c87011a
メモ追加 2024-07-08 15:42:49 +09:00
5cb26ddc12
子供編集画面を正常に表示できるように 2024-07-08 15:41:56 +09:00
d6ec8f16dc
子供編集画面をインテント出来るように WIP 2024-07-08 15:39:49 +09:00
94efb7ddb1
実装 WIP 2024-07-08 15:29:48 +09:00
1e6a206524
Adapter実装 WIP 2024-07-08 15:26:12 +09:00
553cf0db30
アイテムレイアウト作成 2024-07-08 15:23:52 +09:00
4d05b9e568
Adapter WIP 2024-07-08 15:18:41 +09:00
a531b0c5c6
Adapterひな形 2024-07-08 15:16:52 +09:00
93442fed23
RecyclerView追加 2024-07-08 15:12:09 +09:00
ffb60342fd
ID 2024-07-08 15:11:16 +09:00
b7fa1efe3c
ToolBar追加 2024-07-08 15:10:34 +09:00
56fd31b025
depend 2024-07-08 15:10:25 +09:00
cfcf11e557
アクティビティ作成 2024-07-08 14:05:35 +09:00
a8d1180aab Merge pull request 'feature/fix_fab' (#148) from feature/fix_fab into main
Reviewed-on: #148
2024-07-08 03:49:33 +00:00
208088e9b2
JavaDoc対応 2024-07-08 12:49:14 +09:00
d207f2e4d3
JavaDoc 2024-07-08 12:48:12 +09:00
cdca79818a Merge pull request 'feature/fix_fab' (#147) from feature/fix_fab into main
Reviewed-on: #147
2024-07-08 03:46:46 +00:00
5bc5930d5b
fabのセットアップをonResumeで呼び出し 2024-07-08 12:44:26 +09:00
4c1a342b58
暫定処理実装など 2024-07-08 12:43:28 +09:00
deec9301df
コメント修正 2024-07-08 12:40:09 +09:00
7e7c221ae1
Fabアクション WIP 2024-07-08 12:39:33 +09:00
e17d392af9 Merge pull request 'feature/wallet' (#146) from feature/wallet into main
Reviewed-on: #146
2024-07-08 03:36:09 +00:00
2ac14316b6
Fabを隠すように 2024-07-08 12:34:41 +09:00
6be673d9a0
アイコン変更 2024-07-08 12:32:47 +09:00
c6831df910
アイコンリソース追加 2024-07-08 12:31:16 +09:00
7b1e4dc698
単位付与 2024-07-08 12:22:53 +09:00
efec0512d2
暫定実装 2024-07-08 12:22:43 +09:00
ac29832038
対応 WIP 2024-07-08 12:11:46 +09:00
0579a1b45a
TotalRewardを表示するように WIP 2024-07-08 12:10:42 +09:00
d2c5e7e8b3
引数を受け取るように 2024-07-08 12:07:18 +09:00
aaae868114
削除漏れアイテム削除 2024-07-08 12:04:19 +09:00
f1e5b0b43c
ViewPager2に変更 2024-07-08 12:03:29 +09:00
a39872647b
ナビゲーション追加, ID修正 2024-07-08 12:02:15 +09:00
d9c1bf126b
リストに追加 2024-07-08 12:01:38 +09:00
09b4d4eb33
保護者側ナビゲーション作成 2024-07-08 12:01:30 +09:00
9e987f1994
add depend 2024-07-08 12:01:15 +09:00
f924e498c2
Debug 2024-07-08 11:58:29 +09:00
e00eb0b598
レイアウト暫定 2024-07-08 11:42:41 +09:00
e24e3d40a1
実装整理 2024-07-08 11:38:19 +09:00
df244ae6c7
親用ラッパーフラグメント作成 2024-07-08 11:38:03 +09:00
f8d03adb54
リネーム 2024-07-08 11:37:30 +09:00
3f5160b9ce
Bypass 2024-07-08 11:36:36 +09:00
3b52cb53d7
モジュール作成 2024-07-08 11:35:35 +09:00
4089c2ad86
Dummy削除 2024-07-08 11:34:39 +09:00
4a03cfa6ec
WIP 2024-07-08 11:34:06 +09:00
612ad9eb1b
メモ追加 2024-07-08 11:32:49 +09:00
67fa07a9e0
Impl init 2024-07-08 11:31:22 +09:00
1db94bc3b6
WIP 2024-07-08 11:29:20 +09:00
446ba17cca
リファクタリング 2024-07-08 11:27:43 +09:00
4d3b7ac6d2
Action実装 WIP 2024-07-08 11:25:33 +09:00
9d449aa575
Converter実装 2024-07-08 11:25:13 +09:00
02cd9b7aab
if作成 2024-07-08 11:13:54 +09:00
e09091f2fb
JavaDoc修正 2024-07-08 11:12:22 +09:00
0b49ee9b09
getHistory実装 2024-07-08 11:11:14 +09:00
0465f54aca
Converter追加 2024-07-08 11:10:31 +09:00
477152e40e
Dao追加, モデル追加 2024-07-08 11:08:48 +09:00
f78b17dfb5
Conveter実装 2024-07-08 10:56:31 +09:00
1cb47048e7
逆追加 2024-07-08 10:54:01 +09:00
51fe42ec05
rename 2024-07-08 10:52:51 +09:00
d4af873190
読込 2024-07-08 10:51:17 +09:00
c5a39836cc
Dao作成 2024-07-08 10:50:27 +09:00
3288b27cf0
Converter追加 2024-07-08 10:47:40 +09:00
5c6ea6c9cf
TypeConverter作成 2024-07-08 10:47:05 +09:00
67b7d3280a
Entity作成 2024-07-08 10:45:31 +09:00
ffaf8dd444
メモ追加 2024-07-08 10:41:20 +09:00
1824cfd7ce
Converter実装 2024-07-08 10:40:23 +09:00
369e65aecb
空コンストラクタ追加 2024-07-08 10:38:05 +09:00
24c3078637
Converterクラス作成 2024-07-08 10:23:42 +09:00
f7bf16cd19
ヒストリ取得APIをサービスに追加 2024-07-08 10:22:03 +09:00
c2297dee90
空行 2024-07-08 10:21:37 +09:00
5b726c5923
HistoryListresponse作成 2024-07-08 10:21:30 +09:00
f0349d0e1e
Response 2024-07-08 10:20:12 +09:00
29e367e79d
空コンストラクタ追加 2024-07-08 10:19:42 +09:00
d213541bc1
BaseModel作成 2024-07-08 10:19:35 +09:00
9ec4818d19
HistoryModel修正 2024-07-08 10:14:40 +09:00
9ae72b6d8e
HistoryModel作成 2024-07-08 10:12:39 +09:00
rca
ce49d93265 子供側ナビゲーション作成 2024-07-08 00:40:16 +09:00
rca
db2a00e37b 暫定レイアウト作成 2024-07-08 00:38:04 +09:00
rca
5973114924 Swipe to refresh 2024-07-08 00:30:44 +09:00
rca
298a7e4874 init 2024-07-08 00:29:00 +09:00
rca
8a29ec64cb 実装整理 2024-07-08 00:28:12 +09:00
rca
f3115ae951 フラグメント作成 2024-07-08 00:27:36 +09:00
rca
f63268406e 依存関係追加 2024-07-08 00:26:00 +09:00
rca
470852b1ec Merge branch 'main' into feature/wallet 2024-07-08 00:24:14 +09:00
edfc33b653 Merge pull request '仮置きログイン, 登録画面実装' (#145) from feature/auth_activity into main
Reviewed-on: #145
2024-07-07 15:23:10 +00:00
rca
02634ca94f モジュール作成 2024-07-08 00:22:45 +09:00
rca
9f3f34ed72 非同期実行するように 2024-07-08 00:19:34 +09:00
rca
bdb57f5412 printStackTrace追加 2024-07-08 00:17:03 +09:00
rca
db4710623c コピペミス修正 2024-07-08 00:15:40 +09:00
rca
95e2bcef9f Loggerイニシャライズ忘れ修正 2024-07-08 00:12:54 +09:00
rca
3e5b1f2626 相互にインテントできるように 2024-07-08 00:10:52 +09:00
rca
3bc38c4020 レイアウト修正 2024-07-08 00:09:58 +09:00
rca
2468800b03 レイアウト修正 2024-07-08 00:09:05 +09:00
rca
4f0e54ad23 コメント追加 2024-07-08 00:06:23 +09:00
rca
e6e23d6ef3 ログ挿入 2024-07-08 00:06:01 +09:00
rca
eeacc23a2e 移植 2024-07-08 00:03:55 +09:00
rca
f9aa29eefa 登録処理実装 2024-07-08 00:03:19 +09:00
rca
038ac102d1 JavaDoc 2024-07-07 23:54:25 +09:00
rca
5e92be0f98 リネーム 2024-07-07 23:54:14 +09:00
rca
a0eb13db8b リネーム 2024-07-07 23:53:57 +09:00
rca
d1d8af6b39 WIP 2024-07-07 23:53:33 +09:00
rca
dc78d6cd02 仮置きレイアウト作成 2024-07-07 23:52:01 +09:00
rca
09f0f6acfb アクティビティ作成 2024-07-07 23:50:45 +09:00
6b77025075 Merge pull request 'feature/add_drawer_test' (#144) from feature/add_drawer_test into main
Reviewed-on: #144
2024-07-07 14:48:45 +00:00
rca
470cf1ec6a ToolBarのテキスト変更 2024-07-07 20:43:18 +09:00
rca
8b1452a5ed ボタンでドロワーを開閉できるように 2024-07-07 20:41:05 +09:00
rca
e246712ab2 文字列リソース追加 2024-07-07 20:40:19 +09:00
rca
6e20d4564e ToolBar追加 2024-07-07 20:33:56 +09:00
rca
f510652848 ToolBar追加 2024-07-07 20:21:33 +09:00
376c7e38f3 Merge pull request '大規模リファクタ' (#143) from feature/change_module into main
Reviewed-on: #143
2024-07-07 06:44:07 +00:00
rca
86266e9311 Workaround追加 2024-07-07 15:42:37 +09:00
rca
d733a4c75b ラッパー関数を追加 2024-07-07 15:40:25 +09:00
rca
6efbfd914d WIP 2024-07-07 15:38:20 +09:00
rca
229382cc04 SwipeRefreshLayoutにIDを付与 2024-07-07 15:37:34 +09:00
rca
0681947478 データ取得完了前にダイアログが表示される問題を修正 2024-07-07 15:32:43 +09:00
rca
b34683f0ea メモ追加 2024-07-07 15:28:55 +09:00
rca
650e0ce591 凡ミス修正 2024-07-07 15:24:39 +09:00
rca
3151060bb4 引数渡し忘れ修正 2024-07-07 15:20:57 +09:00
rca
a76208f115 変換機構のミスを修正 2024-07-07 15:18:34 +09:00
rca
28b8f9e06b Workaround 2024-07-07 14:49:50 +09:00
rca
6c1fdbc974 呼び出し漏れ修正 2024-07-07 14:12:55 +09:00
rca
b3a1b32ba1 getItemCount修正 2024-07-07 14:08:34 +09:00
rca
1141580a44 自前のフォークに切り替え 2024-07-07 13:51:19 +09:00
rca
708ddf0490 自前のリポジトリを追加 2024-07-07 13:48:32 +09:00
rca
59164af22e WIP 2024-07-06 17:19:41 +09:00
rca
801b00e7ea 不要になった実装を削除 2024-07-06 16:36:05 +09:00
rca
2a35d18fe5 ナビゲーションに追加 2024-07-06 16:34:17 +09:00
rca
0fd977bb68 ナビゲーションに追加 2024-07-06 16:33:52 +09:00
rca
1c88c55e90 ナビゲーション作成 2024-07-06 16:33:32 +09:00
rca
7d916e4855 コメント追加 2024-07-06 16:32:09 +09:00
rca
512e096487 依存関係追加 2024-07-06 16:31:55 +09:00
rca
2854a97a24 ナビゲーション修正 2024-07-06 16:31:05 +09:00
rca
e4b5f32d16 SwipeToRefreshを追加 2024-07-06 16:19:50 +09:00
rca
7696ebee21 SwipeToRefreshを追加 2024-07-06 16:19:17 +09:00
rca
da4b436b78 コメント追加 2024-07-06 16:16:01 +09:00
rca
50b75709b7 コメント追加 2024-07-06 16:13:18 +09:00
rca
356643892e コメント追加 2024-07-06 16:12:18 +09:00
rca
31ba7dea75 親側の場合にダイアログを表示できるように 2024-07-06 16:11:34 +09:00
rca
a69bbcdec1 コンストラクタ追加 2024-07-06 16:09:30 +09:00
rca
5f66572569 ItemCount実装 2024-07-06 16:05:47 +09:00
rca
3e80c62cfd 命名規則変更追従 2024-07-06 16:02:58 +09:00
rca
30d35cf4f2 実装 2024-07-06 16:02:42 +09:00
rca
a7f5eee032 リストレイアウト作成 2024-07-06 15:57:58 +09:00
rca
c85f9edd72 命名規則修正 2024-07-06 15:50:16 +09:00
rca
e3068f1299 子供側ダイアログ実装 2024-07-06 15:46:39 +09:00
rca
db1f2582c4 ダイアログからの戻り地の処理を実装 2024-07-06 15:42:46 +09:00
rca
72efe84497 isChildModeに応じてchildIdの処理を決定するように 2024-07-06 15:40:02 +09:00
rca
3dde8dfa02 ChildIDを引数として取るように 2024-07-06 15:39:13 +09:00
rca
a167d8495b WIP 2024-07-06 15:38:07 +09:00
rca
ad8b388101 Logger追加 2024-07-06 15:37:30 +09:00
rca
bbeed80914 WIP 2024-07-06 15:36:26 +09:00
rca
70fecadaf1 WIP 2024-07-06 15:36:01 +09:00
rca
5ea8077e7c nameも渡すように 2024-07-06 15:35:41 +09:00
rca
95fb3b1aae WIP 2024-07-06 15:34:52 +09:00
rca
d1a58f93da WIP 2024-07-06 15:34:12 +09:00
rca
21549fb589 Adapter実装 2024-07-06 15:27:31 +09:00
rca
1306df3ee9 レイアウト調整 2024-07-06 15:21:18 +09:00
rca
f1f3a86f9f レイアウト 移植 2024-07-06 15:19:32 +09:00
rca
64d4d3d98b リストアイテムレイアウト作成 2024-07-06 15:16:57 +09:00
rca
165c0cd052 WIP 2024-07-06 15:15:26 +09:00
rca
7463606db4 Adapter作成 WIP 2024-07-06 15:15:19 +09:00
rca
735fab046c ID追加 2024-07-06 15:15:06 +09:00
rca
267bb87649 暫定レイアウト作成 2024-07-06 15:03:47 +09:00
rca
a57b8da206 カレンダー追加 テスト 2024-07-06 14:59:34 +09:00
rca
10be91ba19 依存関係修正 2024-07-06 14:58:53 +09:00
rca
1b8aa63e5d カレンダーを依存関係に追加 2024-07-06 14:50:04 +09:00
rca
963a6979bc 依存関係 2024-07-06 14:46:26 +09:00
rca
df9130a1bf 実装整理 2024-07-06 14:39:02 +09:00
rca
04fdae855f フラグメント作成 2024-07-06 14:37:12 +09:00
rca
eb03dca276 commonモジュール追加 2024-07-06 13:44:52 +09:00
rca
44ed718894 commonモジュール追加 2024-07-06 13:43:52 +09:00
e8ddf8702a Merge pull request 'Workaround' (#142) from refactor/parent into main
Reviewed-on: #142
2024-07-06 04:35:40 +00:00
rca
6663f92cf8 Workaround 2024-07-06 13:33:52 +09:00
e22f43f336 Merge pull request 'refactor/parent' (#141) from refactor/parent into main
Reviewed-on: #141
2024-07-06 04:22:07 +00:00
rca
92a5aa8210 リファクタ 2024-07-06 13:19:39 +09:00
rca
dc41cf6b3a Suppress 2024-07-06 13:18:49 +09:00
rca
2131adfe4a リファクタ 2024-07-06 13:18:27 +09:00
rca
99fb33c79d コメント修正 2024-07-06 13:16:26 +09:00
rca
9dce161666 リファクタ 2024-07-06 13:14:15 +09:00
rca
10e9e7ca6e リファクタ 2024-07-06 13:02:42 +09:00
rca
e03c9974c3 リファクタ WIP 2024-07-06 12:53:02 +09:00
rca
be0d7d1f20 README WIP 2024-07-06 12:50:00 +09:00
rca
b2df16a8a9 Impl 2024-07-06 12:45:56 +09:00
rca
044fed9784 インタフェース追加 2024-07-06 12:43:22 +09:00
rca
dbe706ccbf リファクタ WIP 2024-07-06 12:43:16 +09:00
rca
01c9f84a61 リネーム 2024-07-06 12:43:00 +09:00
rca
03d121af8a リネーム 2024-07-06 12:42:55 +09:00
rca
f991df9191 レイアウト修正 2024-07-06 12:42:43 +09:00
d1804877e5 Merge pull request 'refactor/1' (#140) from refactor/1 into main
Reviewed-on: #140
2024-07-06 03:11:15 +00:00
d1694d2f71 Merge branch 'main' into refactor/1 2024-07-06 03:11:07 +00:00
rca
172b6b5a3a Workaround 2024-07-06 12:10:29 +09:00
rca
f87ff116bc 暫定 2024-07-06 12:08:43 +09:00
rca
d27f04dade import整理 2024-07-06 12:04:07 +09:00
rca
ad5fbaf56f リファクタ 2024-07-06 12:03:55 +09:00
rca
9f8c9690b8 エラーキャッチ改善 2024-07-06 12:02:33 +09:00
rca
7ae8a5559f NonNullアノテーション追加 2024-07-06 12:00:49 +09:00
rca
a1dcc90afc 改行修正 2024-07-06 12:00:18 +09:00
rca
bb4fc8638b スコープ修正 2024-07-06 12:00:06 +09:00
rca
aa252669bc 改行整理 2024-07-06 11:59:40 +09:00
rca
c8f44bf581 全体的にリファクタリング 2024-07-06 11:59:17 +09:00
rca
705cbce5f5 未使用コードを削除 2024-07-06 11:34:42 +09:00
b55f1b776a Merge pull request 'hotfix/fix1' (#139) from hotfix/fix1 into main
Reviewed-on: #139
2024-07-06 02:17:36 +00:00
rca
1cde0d74af ロガー修正 2024-07-06 11:15:51 +09:00
rca
2cdc84d1bb ロガー修正 2024-07-06 11:15:33 +09:00
rca
a638319a03 workaround 2024-07-06 11:12:56 +09:00
7672c86d34 Merge pull request 'feature/updateupdate' (#138) from feature/updateupdate into main
Reviewed-on: #138
2024-07-06 02:11:16 +00:00
bb1196a26a Merge branch 'main' into feature/updateupdate 2024-07-06 02:11:09 +00:00
b2cca2df5e Merge pull request 'improve/cleanup_logger' (#137) from improve/cleanup_logger into main
Reviewed-on: #137
2024-07-06 02:07:48 +00:00
rca
d3614fc59f getHistoryを使用する機能を削除 2024-07-06 10:40:35 +09:00
rca
68321327ad import整理 2024-07-06 10:38:25 +09:00
rca
ee5861ad2c SharedPreference関係の機能を削除 2024-07-06 10:38:07 +09:00
30b906d284 Merge remote-tracking branch 'origin/feature/updateupdate' into feature/updateupdate
# Conflicts:
#	feature/parent/src/main/java/one/nem/kidshift/feature/parent/ParentMainFragment.java
2024-07-05 12:56:58 +09:00
7487d6aa1b 子供のチェックボックス左寄せしたよん 2024-07-05 12:55:14 +09:00
c25e9837f8 Merge pull request '子供を追加できるように' (#134) from feature/add_child_impl into main
Reviewed-on: #134
2024-07-05 02:42:10 +00:00
c5d4b5000f
非同期化対応 2024-07-05 11:38:52 +09:00
b625918138
型修正, APIリクエスト実装 2024-07-05 11:37:30 +09:00
bef159028d Merge pull request 'feature/support_add_child' (#133) from feature/support_add_child into main
Reviewed-on: #133
2024-07-05 02:34:07 +00:00
68642d5319
メッセージ修正 2024-07-05 11:32:42 +09:00
49a07a776c
リファクタリング 2024-07-05 11:31:10 +09:00
0808bbe69a
WIP ダイアログを表示し直せるように 2024-07-05 11:16:35 +09:00
77632af88d
暫定処理実装 2024-07-05 11:14:27 +09:00
5d8ffda2a4
id修正 2024-07-05 11:12:15 +09:00
1a9be01db1 Merge pull request '削除漏れ' (#132) from fix/remove_swipe into main
Reviewed-on: #132
2024-07-05 02:10:21 +00:00
ecc3f92f2c
削除漏れ 2024-07-05 11:10:02 +09:00
a0e114e25e Merge pull request 'onSwipeを削除' (#131) from fix/remove_swipe into main
Reviewed-on: #131
2024-07-05 02:09:13 +00:00
5d4a967585
onSwipeを削除 2024-07-05 11:08:43 +09:00
8d5605f8d3 Merge pull request 'Fabでダイアログを表示するように' (#130) from feature/setting_support_fab into main
Reviewed-on: #130
2024-07-05 02:07:39 +00:00
c1e9100df8
Fabでダイアログを表示するように 2024-07-05 11:07:01 +09:00
6e029fe802 Merge pull request 'Fab追加' (#129) from feature/add_fab into main
Reviewed-on: #129
2024-07-05 02:03:11 +00:00
de35b531fa
テスト用ボタン削除 2024-07-05 11:02:46 +09:00
61e571e70e
プロバイダを使ってインジェクトするように 2024-07-05 10:59:27 +09:00
0d1a9e2006
Singleton化忘れ修正 2024-07-05 10:48:55 +09:00
606aa12960
トグルするように 2024-07-05 10:46:15 +09:00
2a36f0c561
isShown追加 2024-07-05 10:46:01 +09:00
ae5310ab4c
CallbackをUtilsモジュールに移動 2024-07-05 10:45:39 +09:00
e661285428
WIP 2024-07-05 10:44:44 +09:00
16620e103c
utilsモジュールに移動 2024-07-05 10:43:57 +09:00
0d142b8f5f
utilsモジュールに移動 2024-07-05 10:43:44 +09:00
2d1db02cb0
デバッグ用ボタン追加 2024-07-05 10:42:22 +09:00
4f717c28fa
onCreateでfabをsetするように 2024-07-05 10:40:57 +09:00
47008d5c35
Fabの操作を独立させた 2024-07-05 10:40:30 +09:00
0caed0860f
fabControl実装 2024-07-05 10:35:47 +09:00
05e09f281d
add depend 2024-07-05 10:33:56 +09:00
2f1ab38a3a
コールバック整理, 修正 2024-07-05 10:32:15 +09:00
325ca7b96a
コールバック作成 2024-07-05 10:29:03 +09:00
520f28618d
rename 2024-07-05 10:25:55 +09:00
c3c3b1faea
Fabを設置 2024-07-05 10:25:09 +09:00
a086d94945 Merge pull request 'リファクタ' (#128) from refactor/refactor3 into main
Reviewed-on: #128
2024-07-03 14:40:49 +00:00
rca
21006ebd43 未使用のインポートを削除 2024-07-03 23:38:55 +09:00
rca
07ad7ae672 変換 2024-07-03 23:38:36 +09:00
rca
c53c27d392 変換 2024-07-03 23:38:17 +09:00
rca
b2848f0913 assert追加 2024-07-03 23:37:56 +09:00
f1b6f854ca Merge pull request 'タスク削除実装' (#127) from feature/remove_task into main
Reviewed-on: #127
2024-07-03 14:29:01 +00:00
rca
da226e4c6e 戻り値型修正, 実装 2024-07-03 23:28:26 +09:00
e83fe5ef5d Merge pull request 'タスク追加実装' (#126) from feature/add_task into main
Reviewed-on: #126
2024-07-03 14:24:55 +00:00
rca
5ded6d3621 戻り値型修正, 実装 2024-07-03 23:24:29 +09:00
0b2667e84a Merge pull request 'feature/update_task' (#125) from feature/update_task into main
Reviewed-on: #125
2024-07-03 14:21:47 +00:00
rca
8656093e09 updateTask実装 2024-07-03 23:21:32 +09:00
rca
c3667dc551 Converter追加 2024-07-03 23:19:32 +09:00
rca
dd73c237fa 戻り値型変更 2024-07-03 23:18:02 +09:00
d49ab35914 Merge pull request 'feature/complete_task' (#124) from feature/complete_task into main
Reviewed-on: #124
2024-07-03 14:13:14 +00:00
39f7056f30 子供のチェックボックス左寄せしたよん 2024-07-03 16:24:46 +09:00
164 changed files with 5453 additions and 642 deletions

View File

@ -13,9 +13,11 @@
<option value="$PROJECT_DIR$/data" />
<option value="$PROJECT_DIR$/feature" />
<option value="$PROJECT_DIR$/feature/child" />
<option value="$PROJECT_DIR$/feature/common" />
<option value="$PROJECT_DIR$/feature/debug" />
<option value="$PROJECT_DIR$/feature/parent" />
<option value="$PROJECT_DIR$/feature/setting" />
<option value="$PROJECT_DIR$/feature/wallet" />
<option value="$PROJECT_DIR$/model" />
<option value="$PROJECT_DIR$/shared" />
<option value="$PROJECT_DIR$/utils" />

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 グループワークチーム「シフトメイト」メンバー
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,8 +1,38 @@
# WIP
## メモ
~~## メモ
- リリース前(=提出前)には`DEBUG_ONLY`で検索してチェック(念のため)
## リリース前チェック
- DBの破壊的マイグレーションを許可するオプションを無効に
-
- DBの破壊的マイグレーションを許可するオプションを無効に~~
#### 補足
- カレンダーはCompactCalendarViewをそのまま使用する予定でしたがAndroidX環境で使用するとクラス重複でビルドできないためAndroidXに対応させるPRを取り込んだ物を専用Mavenリポジトリとして公開して使用しています
- https://github.com/r-ca/CompactCalendarView
## 補足
- 直前に大規模リファクタリングを始めていたため,未使用コード(ファイル)がいくつか残っています
- `:feature:setting`, `:feature:parent` は廃止されており, 現在は使用されていません
- `:feature:child` は子供管理画面のアクティビティのみ使用されています
- 親, 子供のタスク一覧画面はどちらも`:feature:common`の`CommonHomeFragment`を用いており, ナビゲーショングラフを切り替えることで表示モードを切り替えています
## 既知の問題
- 初回起動時, ウォレットの表示に失敗する場合がある
- 特定の操作を行った場合にナビゲーションが正常に動作しなくなる場合がある
- 特定の状況で子供モード時に追加ウィンドウが開けてしまう場合がある(APIの権限チェックではじかれるため, 実際に追加することは不可能)
- カレンダーの表示を切り替える際, RecyclerViewのアニメーションが一定範囲にしか反映されない
- お手伝い履歴がローカルキャッシュされておらず,毎回サーバーから全データを取得している
- ウォレット画面でPull-to-Refreshが動作しない
- ウォレット画面にて非UIスレッドでUI更新を行ってしまっている?
- オフライン時,ウォレットなど一部の画面でクラッシュする場合がある
- 資格情報が間違ったままログインできてしまう
- キャッシュとサーバーのマスターデータに差異があった場合, 再表示しないと表示に適応されない場合がある(コールバックの処理が適切に実装されていない画面がある)
## TODO
- 全体的なUX改善
- インメモリデータベースの活用(Related: 全体的なUX改善)
- ViewModelの本格導入
- タスクのアサイン機能への対応
- 親モードで子供画面を表示したとき, 親モードへの移動にロックをかけられるようにする
- 非DynamicColor機種で使用されるテーマの適用が中途半端なので完全に適用するように

View File

@ -35,6 +35,7 @@ dependencies {
implementation libs.material
implementation libs.activity
implementation libs.constraintlayout
implementation project(':model')
testImplementation libs.junit
androidTestImplementation libs.ext.junit
androidTestImplementation libs.espresso.core
@ -44,6 +45,8 @@ dependencies {
implementation project(':feature:parent')
implementation project(':feature:child')
implementation project(':feature:setting')
implementation project(':feature:common')
implementation project(':feature:wallet')
implementation project(':data')

View File

@ -15,6 +15,12 @@
android:supportsRtl="true"
android:theme="@style/Theme.KidShift"
tools:targetApi="31">
<activity
android:name=".ChildLoginActivity"
android:exported="false" />
<activity
android:name=".RegisterActivity"
android:exported="false" />
<activity
android:name=".LoginActivity"
android:exported="false" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

@ -0,0 +1,154 @@
package one.nem.kidshift;
import android.content.Intent;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import java.util.concurrent.CompletableFuture;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
import one.nem.kidshift.data.ChildData;
import one.nem.kidshift.data.UserSettings;
import one.nem.kidshift.data.retrofit.KidShiftApiService;
import one.nem.kidshift.data.retrofit.model.child.auth.ChildAuthRequest;
import one.nem.kidshift.data.retrofit.model.child.auth.ChildAuthResponse;
import one.nem.kidshift.utils.KSLogger;
import one.nem.kidshift.utils.factory.KSLoggerFactory;
import retrofit2.Call;
@AndroidEntryPoint
public class ChildLoginActivity extends AppCompatActivity {
@Inject
UserSettings userSettings;
@Inject
KSLoggerFactory loggerFactory;
@Inject
KidShiftApiService kidShiftApiService;
private KSLogger logger;
private EditText loginCode1, loginCode2, loginCode3, loginCode4, loginCode5, loginCode6, loginCode7, loginCode8;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_child_login);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
logger = loggerFactory.create("ChildLoginActivity");
// コードのフォーカスを自動で移動する
loginCode1 = findViewById(R.id.loginCode_1);
loginCode2 = findViewById(R.id.loginCode_2);
loginCode3 = findViewById(R.id.loginCode_3);
loginCode4 = findViewById(R.id.loginCode_4);
loginCode5 = findViewById(R.id.loginCode_5);
loginCode6 = findViewById(R.id.loginCode_6);
loginCode7 = findViewById(R.id.loginCode_7);
loginCode8 = findViewById(R.id.loginCode_8);
loginCode1.addTextChangedListener(new LoginCodeTextWatcher(loginCode1, loginCode2, null));
loginCode2.addTextChangedListener(new LoginCodeTextWatcher(loginCode2, loginCode3, loginCode1));
loginCode3.addTextChangedListener(new LoginCodeTextWatcher(loginCode3, loginCode4, loginCode2));
loginCode4.addTextChangedListener(new LoginCodeTextWatcher(loginCode4, loginCode5, loginCode3));
loginCode5.addTextChangedListener(new LoginCodeTextWatcher(loginCode5, loginCode6, loginCode4));
loginCode6.addTextChangedListener(new LoginCodeTextWatcher(loginCode6, loginCode7, loginCode5));
loginCode7.addTextChangedListener(new LoginCodeTextWatcher(loginCode7, loginCode8, loginCode6));
loginCode8.addTextChangedListener(new LoginCodeTextWatcher(loginCode8, null, loginCode7));
// ログインボタンを押したときの処理
findViewById(R.id.childLoginButton).setOnClickListener(v -> {
logger.debug("ログインボタンが押されました");
Call<ChildAuthResponse> call = kidShiftApiService.childLogin(new ChildAuthRequest(getLoginCode()));
CompletableFuture.runAsync(() -> {
try {
ChildAuthResponse childAuthResponse = call.execute().body();
if (childAuthResponse == null || childAuthResponse.getAccessToken() == null) {
// エラー処理
logger.error("ChildAuthResponseがnullまたはAccessTokenがnullです");
return;
}
UserSettings.AppCommonSetting appCommonSetting = userSettings.getAppCommonSetting();
appCommonSetting.setLoggedIn(true);
appCommonSetting.setAccessToken(childAuthResponse.getAccessToken());
appCommonSetting.setChildId(childAuthResponse.getChildId());
appCommonSetting.setChildMode(true);
} catch (Exception e) {
logger.error("リクエストに失敗しました");
Toast.makeText(this, "ログインに失敗しました", Toast.LENGTH_SHORT).show();
}
}).thenRun(() -> {
startActivity(new Intent(this, MainActivity.class));
});
});
}
private String getLoginCode() {
return loginCode1.getText().toString() +
loginCode2.getText().toString() +
loginCode3.getText().toString() +
loginCode4.getText().toString() +
loginCode5.getText().toString() +
loginCode6.getText().toString() +
loginCode7.getText().toString() +
loginCode8.getText().toString();
}
private static class LoginCodeTextWatcher implements TextWatcher, View.OnKeyListener {
private EditText currentView;
private final EditText nextView;
private final EditText previousView;
LoginCodeTextWatcher(EditText currentView, EditText nextView, EditText previousView) {
this.currentView = currentView;
this.nextView = nextView;
this.previousView = previousView;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) { }
@Override
public void afterTextChanged(Editable s) {
if (s.length() == 1 && nextView != null) {
nextView.requestFocus();
}
}
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// if (keyCode == KeyEvent.KEYCODE_DEL && event.getAction() == KeyEvent.ACTION_DOWN) { // TODO: バックスペースの処理
// EditText currentView = (EditText) v;
// if (currentView.getText().length() == 0 && previousView != null) {
// previousView.requestFocus();
// }
// }
// return false;
return true;
}
}
}

View File

@ -1,5 +1,6 @@
package one.nem.kidshift;
import android.content.Intent;
import android.os.Bundle;
import android.widget.EditText;
@ -17,10 +18,11 @@ import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
import one.nem.kidshift.data.UserSettings;
import one.nem.kidshift.data.retrofit.KidShiftApiService;
import one.nem.kidshift.data.retrofit.model.parent.auth.ParentLoginRequest;
import one.nem.kidshift.data.retrofit.model.parent.auth.ParentLoginResponse;
import one.nem.kidshift.data.retrofit.model.parent.auth.ParentAuthRequest;
import one.nem.kidshift.data.retrofit.model.parent.auth.ParentAuthResponse;
import one.nem.kidshift.utils.KSLogger;
import one.nem.kidshift.utils.factory.KSLoggerFactory;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
@ -36,6 +38,9 @@ public class LoginActivity extends AppCompatActivity {
@Inject
UserSettings userSettings;
@Inject
KidShiftApiService kidShiftApiService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -49,61 +54,45 @@ public class LoginActivity extends AppCompatActivity {
logger = loggerFactory.create("LoginActivity");
// Retrofit init
KidShiftApiService apiService = new Retrofit.Builder()
.baseUrl("https://kidshift-beta.nem.one/")
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(KidShiftApiService.class);
EditText emailEditText = findViewById(R.id.parentLoginEmailEditText);
EditText passwordEditText = findViewById(R.id.parentLoginPasswordEditText);
EditText emailEditText = findViewById(R.id.emailEditText);
EditText passwordEditText = findViewById(R.id.passwordEditText);
findViewById(R.id.parentLoginButton).setOnClickListener(v -> {
String email = emailEditText.getText().toString(); // TODO: メールアドレスのバリデーション
String password = passwordEditText.getText().toString();
findViewById(R.id.loginButton).setOnClickListener(v -> {
CompletableFuture.supplyAsync(() -> {
CompletableFuture.runAsync(() -> {
Call<ParentAuthResponse> call = kidShiftApiService.parentLogin(new ParentAuthRequest(email, password));
try {
Response<ParentLoginResponse> response = apiService.parentLogin(
new ParentLoginRequest(
emailEditText.getText().toString(),
passwordEditText.getText().toString()
)).execute();
return response;
} catch (IOException e) {
logger.error("IOException");
throw new RuntimeException(e);
}
}).thenAccept(response -> {
Response<ParentAuthResponse> response = call.execute();
if (response.isSuccessful()) {
logger.info("Login Success");
logger.debug("AccessToken: " + response.body().getAccessToken());
UserSettings.AppCommonSetting appCommonSetting = userSettings.getAppCommonSetting();
appCommonSetting.setLoggedIn(true);
appCommonSetting.setAccessToken(response.body().getAccessToken());
appCommonSetting.setChildMode(false);
finish();
ParentAuthResponse parentAuthResponse = response.body();
if (parentAuthResponse == null || parentAuthResponse.getAccessToken() == null) {
// エラー処理
logger.error("ParentAuthResponseがnullまたはAccessTokenがnullです");
return;
}
userSettings.getAppCommonSetting().setLoggedIn(true);
userSettings.getAppCommonSetting().setAccessToken(parentAuthResponse.getAccessToken());
} else {
logger.error("Login Failed");
try {
logger.debug("Response: " + response.errorBody().string());
} catch (IOException e) {
logger.error("IOException while reading error body");
logger.error("リクエストに失敗しました");
// エラー処理
}
// ログイン失敗時の処理
} catch (Exception e) {
logger.error("リクエストに失敗しました: " + e.getMessage());
e.printStackTrace();
}
}).exceptionally(e -> {
logger.error("Exception occurred: " + e.getMessage());
return null;
}).thenRun(() -> {
startActivity(new Intent(this, MainActivity.class));
});
});
// for Debug
findViewById(R.id.loginButton).setOnLongClickListener(v -> {
// ログイン画面をバイパスしてメイン画面に遷移
finish();
return true;
findViewById(R.id.toRegisterButton).setOnClickListener(v -> {
startActivity(new Intent(this, RegisterActivity.class));
});
findViewById(R.id.toChildLoginButton).setOnClickListener(v -> {
startActivity(new Intent(this, ChildLoginActivity.class));
});
}
}

View File

@ -1,37 +1,75 @@
package one.nem.kidshift;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.FragmentManager;
import androidx.navigation.NavController;
import androidx.navigation.fragment.NavHostFragment;
import androidx.navigation.ui.NavigationUI;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.chip.Chip;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.divider.MaterialDivider;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.navigation.NavigationView;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
import one.nem.kidshift.data.ChildData;
import one.nem.kidshift.data.ParentData;
import one.nem.kidshift.data.UserSettings;
import one.nem.kidshift.feature.child.ChildManageMainActivity;
import one.nem.kidshift.model.ParentModel;
import one.nem.kidshift.model.callback.ParentModelCallback;
import one.nem.kidshift.utils.FabManager;
import one.nem.kidshift.utils.FeatureFlag;
import one.nem.kidshift.utils.KSLogger;
import one.nem.kidshift.utils.ToolBarManager;
import one.nem.kidshift.utils.factory.KSLoggerFactory;
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
@Inject
KSLoggerFactory loggerFactory;
private KSLogger logger;
KSLoggerFactory ksLoggerFactory;
@Inject
FabManager fabManager;
@Inject
ToolBarManager toolBarManager;
@Inject
FeatureFlag featureFlag;
@Inject
UserSettings userSettings;
@Inject
ParentData parentData;
@Inject
ChildData childData;
private KSLogger logger;
private FloatingActionButton fab;
private Toolbar toolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -44,9 +82,52 @@ public class MainActivity extends AppCompatActivity {
return insets;
});
logger = loggerFactory.create("MainActivity");
logger = ksLoggerFactory.create("MainActivity");
logger.info("MainActivity started!");
// Check logged in
if (userSettings.getAppCommonSetting().isLoggedIn()) {
logger.info("User is logged in!");
} else {
logger.info("User is not logged in!");
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
}
toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
toolBarManager.setToolbar(toolbar);
DrawerLayout drawerLayout = findViewById(R.id.drawerLayout);
// アイテムが選択されたときの処理
NavigationView navigationView = findViewById(R.id.navigationView);
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
logger.debug("Item selected: " + item.getItemId());
if (item.getItemId() == R.id.manage_child_account) {
Intent intent = new Intent(MainActivity.this, ChildManageMainActivity.class);
startActivity(intent);
return true;
} else if (item.getItemId() == R.id.show_debug_dialog) {
showDebugDialog();
return true;
} else if (item.getItemId() == R.id.show_account_dialog) {
showAccountDialog();
return true;
} else {
logger.warn("不明なアイテム: " + item.getItemId());
}
return false;
}
});
ActionBarDrawerToggle actionBarDrawerToggle =
new ActionBarDrawerToggle(
this, drawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close);
drawerLayout.addDrawerListener(actionBarDrawerToggle);
actionBarDrawerToggle.syncState();
BottomNavigationView bottomNavigationView = findViewById(R.id.bottom_nav);
@ -62,15 +143,27 @@ public class MainActivity extends AppCompatActivity {
e.printStackTrace();
}
// Check logged in
if (userSettings.getAppCommonSetting().isLoggedIn()) {
logger.info("User is logged in!");
} else {
logger.info("User is not logged in!");
UserSettings.AppCommonSetting appCommonSetting = userSettings.getAppCommonSetting();
if (appCommonSetting.isChildMode()) {
logger.info("Child mode is enabled!");
// 保護者向けのナビゲーションを削除
bottomNavigationView.getMenu().removeItem(R.id.feature_common_parent_child_navigation);
bottomNavigationView.getMenu().removeItem(R.id.feature_common_parent_parent_navigation);
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
bottomNavigationView.getMenu().removeItem(R.id.feature_wallet_parent_navigation);
// startDestinationを変更
bottomNavigationView.setSelectedItemId(R.id.feature_common_child_child_navigation);
// manage_child_accountを削除
navigationView.getMenu().removeItem(R.id.manage_child_account);
} else {
logger.info("Child mode is disabled!");
bottomNavigationView.getMenu().removeItem(R.id.feature_common_child_child_navigation);
bottomNavigationView.getMenu().removeItem(R.id.feature_wallet_child_navigation);
}
fab = findViewById(R.id.mainFab);
fabManager.setFab(fab);
}
/**
@ -79,4 +172,122 @@ public class MainActivity extends AppCompatActivity {
private void startup() {
}
private void showAccountDialog() {
boolean isEditMode = false;
View view = getLayoutInflater().inflate(R.layout.user_info_dialog_layout, null);
if (userSettings.getAppCommonSetting().isChildMode()) {
childData.getChild(userSettings.getAppCommonSetting().getChildId()).thenAccept(childModel -> {
((TextView) view.findViewById(R.id.userNameTextView)).setText(childModel.getName());
logger.debug("ChildModel: " + childModel.getName());
((TextView) view.findViewById(R.id.emailTextView)).setText("子供ユーザーはメールアドレスを設定できません");
((Chip) view.findViewById(R.id.chip)).setText("子供/Child");
}).join();
} else {
parentData.getParentDirect().thenAccept(parentModel -> {
((TextView) view.findViewById(R.id.userNameTextView)).setText(parentModel.getName());
logger.debug("ParentModel: " + parentModel.getName() + ", " + parentModel.getEmail());
((TextView) view.findViewById(R.id.emailTextView)).setText(parentModel.getEmail());
((Chip) view.findViewById(R.id.chip)).setText("保護者/Parent");
}).join();
}
// Workaround
if (userSettings.getAppCommonSetting().isChildMode()) {
view.findViewById(R.id.userNameEditButton).setVisibility(View.GONE);
} else {
view.findViewById(R.id.userNameEditButton).setVisibility(View.VISIBLE);
}
view.findViewById(R.id.userNameEditButton).setOnClickListener(v -> {
EditText editText = new EditText(this);
editText.setText(((TextView) view.findViewById(R.id.userNameTextView)).getText());
editText.setHint("ユーザー名");
new MaterialAlertDialogBuilder(this)
.setTitle("ユーザー名の変更")
.setView(editText)
.setPositiveButton("OK", (dialog, which) -> {
((TextView) view.findViewById(R.id.userNameTextView)).setText(editText.getText());
if (userSettings.getAppCommonSetting().isChildMode()) {
// 子供モード
childData.getChild(userSettings.getAppCommonSetting().getChildId()).thenAccept(childModel -> {
childModel.setName(editText.getText().toString());
childData.updateChild(childModel);
});
} else {
// 保護者モード
parentData.getParentDirect().thenAccept(parentModel -> {
parentModel.setName(editText.getText().toString());
parentData.updateParent(parentModel);
});
}
})
.setNegativeButton("キャンセル", (dialog, which) -> {
// Do nothing
})
.show();
});
new MaterialAlertDialogBuilder(this)
.setTitle("アカウント情報")
.setView(view)
.setPositiveButton("OK", (dialog, which) -> {
// Do nothing
})
.show();
}
private void showDebugDialog() {
ScrollView scrollView = new ScrollView(this);
scrollView.setPadding(32, 16, 32, 16);
LinearLayout linearLayout = new LinearLayout(this);
TextView serverAddressTextView = new TextView(this);
serverAddressTextView.setText("サーバーアドレス: " + userSettings.getApiSetting().getApiBaseUrl());
serverAddressTextView.setTextSize(16);
TextView accessTokenTextView = new TextView(this);
accessTokenTextView.setText("アクセストークン: " + userSettings.getAppCommonSetting().getAccessToken());
accessTokenTextView.setTextSize(16);
TextView childModeTextView = new TextView(this);
childModeTextView.setText("子供モード: " + userSettings.getAppCommonSetting().isChildMode());
childModeTextView.setTextSize(16);
linearLayout.setOrientation(LinearLayout.VERTICAL);
linearLayout.addView(serverAddressTextView);
linearLayout.addView(createDivider(this));
linearLayout.addView(accessTokenTextView);
linearLayout.addView(createDivider(this));
linearLayout.addView(childModeTextView);
scrollView.addView(linearLayout);
new MaterialAlertDialogBuilder(this)
.setTitle("参考情報(評価用)")
.setView(scrollView)
.setPositiveButton("OK", (dialog, which) -> {
// Do nothing
})
.show();
}
private MaterialDivider createDivider(Context context) {
MaterialDivider divider = new MaterialDivider(context);
// Margin (48, 16, 48, 16)
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
params.setMargins(48, 16, 48, 16);
divider.setLayoutParams(params);
return divider;
}
public Toolbar getToolbar() { // TODO: toolbarのインスタンス自体を取得するのではなくfabのように操作できるようにする
return toolbar;
}
}

View File

@ -0,0 +1,97 @@
package one.nem.kidshift;
import android.content.Intent;
import android.os.Bundle;
import android.widget.EditText;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import java.util.concurrent.CompletableFuture;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
import one.nem.kidshift.data.UserSettings;
import one.nem.kidshift.data.retrofit.KidShiftApiService;
import one.nem.kidshift.data.retrofit.model.parent.auth.ParentAuthRequest;
import one.nem.kidshift.data.retrofit.model.parent.auth.ParentAuthResponse;
import one.nem.kidshift.utils.KSLogger;
import one.nem.kidshift.utils.factory.KSLoggerFactory;
import retrofit2.Call;
import retrofit2.Response;
@AndroidEntryPoint
public class RegisterActivity extends AppCompatActivity {
@Inject
KidShiftApiService kidShiftApiService;
@Inject
UserSettings userSettings;
@Inject
KSLoggerFactory loggerFactory;
private KSLogger logger;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_register);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
logger = loggerFactory.create("RegisterActivity");
EditText emailEditText = findViewById(R.id.parentRegisterEmailEditText); // TODO: メールアドレスのバリデーション
EditText passwordEditText = findViewById(R.id.parentRegisterPasswordEditText);
findViewById(R.id.parentRegisterButton).setOnClickListener(v -> {
String email = emailEditText.getText().toString();
String password = passwordEditText.getText().toString();
CompletableFuture.runAsync(() -> {
Call<ParentAuthResponse> call = kidShiftApiService.parentRegister(new ParentAuthRequest(email, password));
try {
Response<ParentAuthResponse> response = call.execute();
if (response.isSuccessful()) {
ParentAuthResponse parentAuthResponse = response.body();
if (parentAuthResponse == null || parentAuthResponse.getAccessToken() == null) {
// エラー処理
logger.error("ParentAuthResponseがnullまたはAccessTokenがnullです");
return;
}
userSettings.getAppCommonSetting().setLoggedIn(true);
userSettings.getAppCommonSetting().setAccessToken(parentAuthResponse.getAccessToken());
} else {
logger.error("リクエストに失敗しました");
// エラー処理
}
} catch (Exception e) {
logger.error("リクエストに失敗しました: " + e.getMessage());
e.printStackTrace();
}
}).thenRun(() -> {
startActivity(new Intent(this, MainActivity.class));
});
});
findViewById(R.id.toLoginButton).setOnClickListener(v -> {
startActivity(new Intent(this, LoginActivity.class));
});
findViewById(R.id.toChildLoginButton).setOnClickListener(v -> {
startActivity(new Intent(this, ChildLoginActivity.class));
});
}
}

View File

@ -1,30 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#333333">
<group android:scaleX="0.435"
android:scaleY="0.435"
android:translateX="6.78"
android:translateY="6.78">
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
android:fillColor="@android:color/white"
android:pathData="M22,8c0,-0.55 -0.45,-1 -1,-1h-7c-0.55,0 -1,0.45 -1,1s0.45,1 1,1h7C21.55,9 22,8.55 22,8zM13,16c0,0.55 0.45,1 1,1h7c0.55,0 1,-0.45 1,-1c0,-0.55 -0.45,-1 -1,-1h-7C13.45,15 13,15.45 13,16zM10.47,4.63c0.39,0.39 0.39,1.02 0,1.41l-4.23,4.25c-0.39,0.39 -1.02,0.39 -1.42,0L2.7,8.16c-0.39,-0.39 -0.39,-1.02 0,-1.41c0.39,-0.39 1.02,-0.39 1.41,0l1.42,1.42l3.54,-3.54C9.45,4.25 10.09,4.25 10.47,4.63zM10.48,12.64c0.39,0.39 0.39,1.02 0,1.41l-4.23,4.25c-0.39,0.39 -1.02,0.39 -1.42,0L2.7,16.16c-0.39,-0.39 -0.39,-1.02 0,-1.41s1.02,-0.39 1.41,0l1.42,1.42l3.54,-3.54C9.45,12.25 10.09,12.25 10.48,12.64L10.48,12.64z"/>
</group>
</vector>

View File

@ -0,0 +1,160 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ChildLoginActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/titleTextView"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:text="KidShiftにログイン"
android:textSize="32dp"
app:layout_constraintBottom_toTopOf="@+id/inputContainer"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<LinearLayout
android:id="@+id/inputContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="128dp"
android:gravity="center_horizontal"
android:orientation="horizontal"
android:weightSum="10"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/titleTextView">
<EditText
android:id="@+id/loginCode_1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:inputType="number"
android:maxWidth="48dp"
android:maxLength="1"
android:textSize="24sp"
tools:ignore="Autofill,LabelFor" />
<EditText
android:id="@+id/loginCode_2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:inputType="number"
android:maxWidth="48dp"
android:maxLength="1"
android:textSize="24sp"
tools:ignore="Autofill,LabelFor" />
<EditText
android:id="@+id/loginCode_3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:inputType="number"
android:maxWidth="48dp"
android:maxLength="1"
android:textSize="24sp"
tools:ignore="Autofill,LabelFor" />
<EditText
android:id="@+id/loginCode_4"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:inputType="number"
android:maxWidth="48dp"
android:maxLength="1"
android:textSize="24sp"
tools:ignore="Autofill,LabelFor" />
<TextView
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_weight="0"
android:gravity="center"
android:text="-"
android:textSize="34sp"
tools:ignore="Autofill,LabelFor" />
<EditText
android:id="@+id/loginCode_5"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:inputType="number"
android:maxWidth="48dp"
android:maxLength="1"
android:textSize="24sp"
tools:ignore="Autofill,LabelFor" />
<EditText
android:id="@+id/loginCode_6"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:inputType="number"
android:maxWidth="48dp"
android:maxLength="1"
android:textSize="24sp"
tools:ignore="Autofill,LabelFor" />
<EditText
android:id="@+id/loginCode_7"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:inputType="number"
android:maxWidth="48dp"
android:maxLength="1"
android:textSize="24sp"
tools:ignore="Autofill,LabelFor" />
<EditText
android:id="@+id/loginCode_8"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:inputType="number"
android:maxWidth="48dp"
android:maxLength="1"
android:textSize="24sp"
tools:ignore="Autofill,LabelFor" />
</LinearLayout>
<Button
android:id="@+id/childLoginButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="128dp"
android:layout_weight="1"
android:text="ログイン"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/inputContainer" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -7,47 +7,116 @@
android:layout_height="match_parent"
tools:context=".LoginActivity">
<LinearLayout
android:id="@+id/inputContainer"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintBottom_toTopOf="@+id/linearLayout3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<EditText
android:id="@+id/emailEditText"
<TextView
android:id="@+id/titleTextView"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginBottom="128dp"
android:text="KidShiftにログイン"
android:textSize="32dp"
app:layout_constraintBottom_toTopOf="@+id/inputContainer"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<LinearLayout
android:id="@+id/inputContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="128dp"
android:gravity="center_horizontal"
android:orientation="vertical"
android:weightSum="10"
app:layout_constraintBottom_toTopOf="@+id/parentLoginButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:hint="メールアドレス">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/parentLoginEmailEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textEmailAddress" />
</com.google.android.material.textfield.TextInputLayout>
<EditText
android:id="@+id/passwordEditText"
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPassword" />
android:layout_marginTop="8dp"
android:hint="パスワード"
app:passwordToggleEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/parentLoginPasswordEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<TextView
android:id="@+id/textView"
<Button
android:id="@+id/parentLoginButton"
style="@style/Widget.Material3.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="※Loginボタン長押しでBypass"
app:layout_constraintBottom_toTopOf="@+id/loginButton"
android:layout_marginTop="128dp"
android:layout_weight="1"
android:paddingHorizontal="48dp"
android:text="ログイン"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/inputContainer" />
</androidx.constraintlayout.widget.ConstraintLayout>
<Button
android:id="@+id/loginButton"
<LinearLayout
android:id="@+id/linearLayout3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="LOGIN"
android:layout_height="0dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
app:layout_constraintStart_toStartOf="parent">
<Button
android:id="@+id/toRegisterButton"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="12dp"
android:layout_marginBottom="8dp"
android:layout_weight="1"
android:padding="0dp"
android:paddingLeft="0dp"
android:paddingTop="0dp"
android:text="新規登録" />
<Button
android:id="@+id/toChildLoginButton"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="12dp"
android:layout_marginBottom="8dp"
android:layout_weight="1"
android:padding="0dp"
android:paddingLeft="0dp"
android:paddingTop="0dp"
android:text="子供ログイン" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -7,6 +7,28 @@
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.drawerlayout.widget.DrawerLayout
android:id="@+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorSurface"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarTheme"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:title="@string/app_name"
android:elevation="8dp" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragmentContainerView"
android:name="androidx.navigation.fragment.NavHostFragment"
@ -16,7 +38,7 @@
app:layout_constraintBottom_toTopOf="@+id/bottom_nav"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar"
app:navGraph="@navigation/main_nav" />
<com.google.android.material.bottomnavigation.BottomNavigationView
@ -27,4 +49,28 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="@menu/main_nav_menu" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/mainFab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:layout_marginBottom="24dp"
android:clickable="true"
app:layout_constraintBottom_toTopOf="@id/bottom_nav"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/add_24px" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/navigationView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/nav_header"
app:menu="@menu/nav_menu" />
</androidx.drawerlayout.widget.DrawerLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".RegisterActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/linearLayout3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/titleTextView"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginBottom="128dp"
android:text="KidShiftに新規登録"
android:textSize="32dp"
app:layout_constraintBottom_toTopOf="@+id/inputContainer"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<LinearLayout
android:id="@+id/inputContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="128dp"
android:gravity="center_horizontal"
android:orientation="vertical"
android:weightSum="10"
app:layout_constraintBottom_toTopOf="@+id/parentRegisterButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:hint="メールアドレス">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/parentRegisterEmailEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textEmailAddress" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:hint="パスワード"
app:passwordToggleEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/parentRegisterPasswordEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<Button
android:id="@+id/parentRegisterButton"
style="@style/Widget.Material3.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="128dp"
android:layout_weight="1"
android:paddingHorizontal="48dp"
android:text="新規登録"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/inputContainer" />
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:id="@+id/linearLayout3"
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<Button
android:id="@+id/toLoginButton"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="12dp"
android:layout_marginBottom="8dp"
android:layout_weight="1"
android:padding="0dp"
android:paddingLeft="0dp"
android:paddingTop="0dp"
android:text="ログイン" />
<Button
android:id="@+id/toChildLoginButton"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="12dp"
android:layout_marginBottom="8dp"
android:layout_weight="1"
android:padding="0dp"
android:paddingLeft="0dp"
android:paddingTop="0dp"
android:text="子供ログイン" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,16 @@
<?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="wrap_content"
android:background="@color/colorSecondary"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="48dp"
android:text="KidShift"
android:textColor="@android:color/white"
android:textSize="34sp" />
</LinearLayout>

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="32dp"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/userNameTextView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical"
android:text="UNAME_PHOLDER"
android:textSize="24sp" />
<ImageButton
android:id="@+id/userNameEditButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:background="@android:color/transparent"
android:padding="24px"
app:srcCompat="@drawable/edit_24px" />
</LinearLayout>
<TextView
android:id="@+id/emailTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="huga@example.com" />
<com.google.android.material.chip.Chip
android:id="@+id/chip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="保護者" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,23 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<!-- TODO: アイコン再検討 -->
<!-- 保護者モード / 保護者目線 -->
<item
android:id="@+id/feature_parent_navigation"
android:icon="@drawable/pending_24px"
android:title="Parent" />
android:id="@+id/feature_common_parent_parent_navigation"
android:icon="@drawable/home_24px"
android:title="ホーム" />
<!-- 保護者モード / 子供目線 -->
<item
android:id="@+id/feature_child_navigation"
android:id="@+id/feature_common_parent_child_navigation"
android:icon="@drawable/child_care_24px"
android:title="Child" />
android:title="こども" />
<!-- 子供モード / 子供目線 -->
<item
android:id="@+id/feature_common_child_child_navigation"
android:icon="@drawable/home_24px"
android:title="ホーム" />
<item
android:id="@+id/feature_debug_navigation"
android:icon="@drawable/developer_mode_24px"
android:title="Debug" />
android:id="@+id/feature_wallet_parent_navigation"
android:icon="@drawable/wallet_24px"
android:title="ウォレット" />
<item
android:id="@+id/feature_setting_navigation"
android:icon="@drawable/settings_24px"
android:title="Setting" />
android:id="@+id/feature_wallet_child_navigation"
android:icon="@drawable/wallet_24px"
android:title="ウォレット" />
</menu>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/show_account_dialog"
android:icon="@drawable/account_box_24px"
android:title="ユーザー情報" />
<item
android:id="@+id/manage_child_account"
android:icon="@drawable/manage_accounts_24px"
android:title="子供アカウントの管理" />
<!-- Divider -->
<item
android:id="@+id/divider"
android:title=""
android:enabled="false" />
<item
android:id="@+id/show_debug_dialog"
android:icon="@drawable/developer_mode_24px"
android:title="デバッグ情報" />
</menu>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 936 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 B

After

Width:  |  Height:  |  Size: 716 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@ -2,10 +2,13 @@
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_nav"
app:startDestination="@id/feature_debug_navigation">
app:startDestination="@id/feature_common_parent_parent_navigation">
<include app:graph="@navigation/feature_debug_navigation" />
<include app:graph="@navigation/feature_child_navigation" />
<include app:graph="@navigation/feature_parent_navigation" />
<include app:graph="@navigation/feature_setting_navigation" />
<include app:graph="@navigation/feature_common_parent_parent_navigation" />
<include app:graph="@navigation/feature_common_parent_child_navigation" />
<include app:graph="@navigation/feature_common_child_child_navigation" />
<include app:graph="@navigation/feature_wallet_parent_navigation" />
<include app:graph="@navigation/feature_wallet_child_navigation" />
</navigation>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#F1DEDE</color>
</resources>

View File

@ -1,3 +1,6 @@
<resources>
<string name="app_name">KidShift</string>
<string name="drawer_open">ドロワーを開く</string>
<string name="drawer_close">ドロワーを閉じる</string>
</resources>

View File

@ -1,4 +1,10 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
dependencies {
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.7.7" // TODO:
}
}
plugins {
alias(libs.plugins.androidApplication) apply false
id 'com.google.dagger.hilt.android' version '2.44' apply false

16
data/README.md Normal file
View File

@ -0,0 +1,16 @@
# :data モジュール
## 概要
UI側に対してバックエンドとの通信, キャッシュ処理を隠蔽するためのラッパーモジュール
## 内容
### KSActions
#### 概要
モジュール内で共通処理を切り出したモジュール
### ChildData
#### 概要
子データを取得するためのモジュール
#### 一覧
WIP
#### メモ

View File

@ -21,23 +21,29 @@ public interface ChildData {
*/
CompletableFuture<List<ChildModel>> getChildList(ChildModelCallback callback);
/**
* 子ユーザー一覧をサーバーから直接取得(キャッシュ無視)
* @return List<ChildModel> 子ユーザー一覧
*/
CompletableFuture<List<ChildModel>> getChildListDirect();
/**
* 子ユーザー情報更新
* @param child 子ユーザー情報
*/
void updateChild(ChildModel child);
CompletableFuture<ChildModel> updateChild(ChildModel child);
/**
* 子ユーザー追加
* @param child 子ユーザー情報
*/
void addChild(ChildModel child);
CompletableFuture<ChildModel> addChild(ChildModel child);
/**
* 子ユーザー削除
* @param childId 子ID
*/
void removeChild(String childId);
CompletableFuture<Void> removeChild(String childId);
/**
* 子ユーザーログインコード発行

View File

@ -5,11 +5,12 @@ import java.util.concurrent.CompletableFuture;
import one.nem.kidshift.data.retrofit.model.task.TaskListResponse;
import one.nem.kidshift.model.ChildModel;
import one.nem.kidshift.model.HistoryModel;
import one.nem.kidshift.model.ParentModel;
import one.nem.kidshift.model.tasks.TaskItemModel;
/**
* データの同期など, ユーザーからの操作に基づかない処理を行う
* データの同期などを提供する内部ユーティリティ
*/
public interface KSActions {
@ -25,4 +26,9 @@ public interface KSActions {
*/
CompletableFuture<ParentModel> syncParent();
/**
* 履歴情報同期
*/
CompletableFuture<List<HistoryModel>> syncHistory(String childId);
}

View File

@ -14,10 +14,22 @@ public interface ParentData {
*/
CompletableFuture<ParentModel> getParent(ParentModelCallback callback);
/**
* 親ユーザー情報取得
* @return 親ユーザー情報
*/
CompletableFuture<ParentModel> getParentDirect();
/**
* 親ユーザー情報取得
* @return 親ユーザー情報
*/
CompletableFuture<ParentModel> getParentCache();
/**
* 親ユーザー情報更新
* @param parent 親ユーザー情報
*/
void updateParent(ParentModel parent);
CompletableFuture<Void> updateParent(ParentModel parent);
}

View File

@ -1,11 +1,22 @@
package one.nem.kidshift.data;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import one.nem.kidshift.model.HistoryModel;
public interface RewardData {
/**
* 現時点の合計報酬額を取得する
* @return Integer 合計報酬額
*/
CompletableFuture<Integer> getTotalReward();
CompletableFuture<Integer> getTotalReward(String childId);
CompletableFuture<List<HistoryModel>> getRewardHistoryList();
CompletableFuture<List<HistoryModel>> getRewardHistoryList(String childId);
CompletableFuture<Void> payReward(String historyId);
CompletableFuture<Void> payReward(List<String> historyIds);
}

View File

@ -27,19 +27,19 @@ public interface TaskData {
* タスクを追加する
* @param task タスク
*/
void addTask(TaskItemModel task);
CompletableFuture<TaskItemModel> addTask(TaskItemModel task);
/**
* タスクを削除する
* @param taskId タスクID
*/
void removeTask(String taskId);
CompletableFuture<Void> removeTask(String taskId);
/**
* タスクを更新する
* @param task タスク
*/
void updateTask(TaskItemModel task);
CompletableFuture<Void> updateTask(TaskItemModel task);
// 子側

View File

@ -37,6 +37,9 @@ public interface UserSettings {
boolean isChildMode();
void setChildMode(boolean childMode);
String getChildId();
void setChildId(String childId);
}
interface SharedPrefCache {

View File

@ -1,18 +1,16 @@
package one.nem.kidshift.data.impl;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import javax.inject.Inject;
import one.nem.kidshift.data.ChildData;
import one.nem.kidshift.data.KSActions;
import one.nem.kidshift.data.retrofit.KidShiftApiService;
import one.nem.kidshift.data.retrofit.model.child.ChildListResponse;
import one.nem.kidshift.data.retrofit.model.child.ChildLoginCodeResponse;
import one.nem.kidshift.data.retrofit.model.child.ChildResponse;
import one.nem.kidshift.data.retrofit.model.converter.ChildModelConverter;
import one.nem.kidshift.data.room.utils.CacheWrapper;
import one.nem.kidshift.model.ChildModel;
@ -38,7 +36,21 @@ public class ChildDataImpl implements ChildData {
@Override
public CompletableFuture<ChildModel> getChild(String childId) {
return null;
return CompletableFuture.supplyAsync(() -> {
Call<ChildResponse> call = kidShiftApiService.getChild(childId);
try {
Response<ChildResponse> response = call.execute();
if (response.isSuccessful()) {
assert response.body() != null;
logger.info("子供取得成功(childId: " + response.body().getId() + ")");
return ChildModelConverter.childResponseToChildModel(response.body());
} else {
throw new RuntimeException("HTTP Status: " + response.code());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
@Override
@ -46,8 +58,7 @@ public class ChildDataImpl implements ChildData {
return CompletableFuture.supplyAsync(() -> {
logger.debug("子供リスト取得開始");
AtomicReference<List<ChildModel>> childListTmp = new AtomicReference<>();
Thread thread = new Thread(() -> {
ksActions.syncChildList().thenAccept(childList -> {
Thread thread = new Thread(() -> ksActions.syncChildList().thenAccept(childList -> {
if (childListTmp.get() == null || childListTmp.get().isEmpty()) {
logger.debug("子供リスト取得完了: キャッシュよりはやく取得完了 or キャッシュ無し");
if (childList == null || childList.isEmpty()) {
@ -71,8 +82,7 @@ public class ChildDataImpl implements ChildData {
logger.error("子供リスト取得失敗: " + e.getMessage());
callback.onFailed(e.getMessage());
return null;
});
});
}));
thread.start();
return cacheWrapper.getChildList().thenApply(childList -> {
if (childList == null || childList.isEmpty()) {
@ -93,18 +103,64 @@ public class ChildDataImpl implements ChildData {
}
@Override
public void updateChild(ChildModel child) {
public CompletableFuture<List<ChildModel>> getChildListDirect() {
return ksActions.syncChildList();
}
@Override
public void addChild(ChildModel child) {
public CompletableFuture<ChildModel> updateChild(ChildModel child) {
return CompletableFuture.supplyAsync(() -> {
Call<ChildResponse> call = kidShiftApiService.updateChild(ChildModelConverter.childModelToChildAddRequest(child), child.getId());
try {
Response<ChildResponse> response = call.execute();
if (response.isSuccessful()) {
assert response.body() != null;
logger.info("子供更新成功(childId: " + response.body().getId() + ")");
return ChildModelConverter.childResponseToChildModel(response.body());
} else {
throw new RuntimeException("HTTP Status: " + response.code());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
@Override
public void removeChild(String childId) {
public CompletableFuture<ChildModel> addChild(ChildModel child) {
return CompletableFuture.supplyAsync(() -> {
Call<ChildResponse> call = kidShiftApiService.addChild(ChildModelConverter.childModelToChildAddRequest(child));
try {
Response<ChildResponse> response = call.execute();
if (response.isSuccessful()) {
assert response.body() != null;
logger.info("子供追加成功(childId: " + response.body().getId() + ")");
return ChildModelConverter.childResponseToChildModel(response.body());
} else {
throw new RuntimeException("HTTP Status: " + response.code());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
@Override
public CompletableFuture<Void> removeChild(String childId) {
return CompletableFuture.supplyAsync(() -> {
Call<Void> call = kidShiftApiService.removeChild(childId);
try {
Response<Void> response = call.execute();
if (response.isSuccessful()) {
logger.info("子供削除成功(childId: " + childId + ")");
return null;
} else {
throw new RuntimeException("HTTP Status: " + response.code());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
@Override

View File

@ -10,12 +10,15 @@ import one.nem.kidshift.data.UserSettings;
import one.nem.kidshift.data.retrofit.KidShiftApiService;
import one.nem.kidshift.data.retrofit.model.child.ChildListResponse;
import one.nem.kidshift.data.retrofit.model.converter.ChildModelConverter;
import one.nem.kidshift.data.retrofit.model.converter.HistoryModelConverter;
import one.nem.kidshift.data.retrofit.model.converter.ParentModelConverter;
import one.nem.kidshift.data.retrofit.model.converter.TaskModelConverter;
import one.nem.kidshift.data.retrofit.model.parent.ParentInfoResponse;
import one.nem.kidshift.data.retrofit.model.task.HistoryListResponse;
import one.nem.kidshift.data.retrofit.model.task.TaskListResponse;
import one.nem.kidshift.data.room.utils.CacheWrapper;
import one.nem.kidshift.model.ChildModel;
import one.nem.kidshift.model.HistoryModel;
import one.nem.kidshift.model.ParentModel;
import one.nem.kidshift.model.tasks.TaskItemModel;
import one.nem.kidshift.utils.KSLogger;
@ -61,7 +64,7 @@ public class KSActionsImpl implements KSActions {
return fetchChildListAsync().thenCombine(fetchTaskListAsync(), (childListResponse, taskListResponse) -> {
Thread cacheThread = new Thread(() -> {
logger.debug("キャッシュ更新スレッド開始(スレッドID: " + Thread.currentThread().getId() + ")");
cacheWrapper.updateCache(ChildModelConverter.childListResponseToChildModelList(childListResponse),
cacheWrapper.updateChildTaskCache(ChildModelConverter.childListResponseToChildModelList(childListResponse),
TaskModelConverter.taskListResponseToTaskItemModelList(taskListResponse)).join();
logger.info("キャッシュ更新完了");
});
@ -152,4 +155,31 @@ public class KSActionsImpl implements KSActions {
}
});
}
@Override
public CompletableFuture<List<HistoryModel>> syncHistory(String childId) {
CompletableFuture<HistoryListResponse> callHistoryApi = CompletableFuture.supplyAsync(() -> {
Call<HistoryListResponse> call = kidShiftApiService.getHistory(childId, true); // TODO: containPaidを引数に
try {
Response<HistoryListResponse> response = call.execute();
if (!response.isSuccessful()) {
logger.error("Error fetching history list: " + response.errorBody().string());
throw new RuntimeException("Error fetching history list: " + response.errorBody().string());
}
return response.body();
} catch (Exception e) {
logger.error("Error fetching history list");
throw new RuntimeException(e);
}
});
CompletableFuture<TaskListResponse> callTaskApi = fetchTaskListAsync();
return CompletableFuture.allOf(callHistoryApi, callTaskApi).thenApplyAsync(result -> {
HistoryListResponse historyListResponse = callHistoryApi.join();
TaskListResponse taskListResponse = callTaskApi.join();
return HistoryModelConverter.historyListResponseAndTaskListResponseToHistoryModelList(historyListResponse, taskListResponse);
});
// TODO: キャッシュ更新
}
}

View File

@ -8,21 +8,26 @@ import one.nem.kidshift.data.KSActions;
import one.nem.kidshift.data.ParentData;
import one.nem.kidshift.data.UserSettings;
import one.nem.kidshift.data.retrofit.KidShiftApiService;
import one.nem.kidshift.data.retrofit.model.parent.ParentInfoResponse;
import one.nem.kidshift.data.retrofit.model.parent.ParentRenameRequest;
import one.nem.kidshift.model.ParentModel;
import one.nem.kidshift.model.callback.ParentModelCallback;
import one.nem.kidshift.utils.KSLogger;
import one.nem.kidshift.utils.factory.KSLoggerFactory;
import retrofit2.Call;
public class ParentDataImpl implements ParentData {
private final UserSettings userSettings;
private final KidShiftApiService kidShiftApiService;
private final KSLogger logger;
private final KSActions ksActions;
@Inject
public ParentDataImpl(KidShiftApiService kidshiftApiService, UserSettings userSettings, KSLoggerFactory ksLoggerFactory, KSActions ksActions) {
public ParentDataImpl(KidShiftApiService kidShiftApiService, UserSettings userSettings, KSLoggerFactory ksLoggerFactory, KSActions ksActions) {
this.kidShiftApiService = kidShiftApiService;
this.userSettings = userSettings;
this.logger = ksLoggerFactory.create("ParentDataImpl");
this.ksActions = ksActions;
@ -46,8 +51,27 @@ public class ParentDataImpl implements ParentData {
}
@Override
public void updateParent(ParentModel parent) {
public CompletableFuture<ParentModel> getParentDirect() {
return ksActions.syncParent();
}
@Override
public CompletableFuture<ParentModel> getParentCache() {
return CompletableFuture.supplyAsync(() -> userSettings.getCache().getParent());
}
@Override
public CompletableFuture<Void> updateParent(ParentModel parent) {
Call<ParentInfoResponse> call = kidShiftApiService.renameParent(new ParentRenameRequest(parent.getName()));
try {
ParentInfoResponse response = call.execute().body();
if (response == null) {
return CompletableFuture.completedFuture(null);
}
return CompletableFuture.completedFuture(null);
} catch (Exception e) {
return CompletableFuture.completedFuture(null);
}
}
}

View File

@ -1,27 +0,0 @@
package one.nem.kidshift.data.impl;
import com.github.javafaker.Faker;
import java.util.concurrent.CompletableFuture;
import javax.inject.Inject;
import one.nem.kidshift.data.RewardData;
import one.nem.kidshift.utils.KSLogger;
public class RewardDataDummyImpl implements RewardData {
private final Faker faker;
@Inject
public RewardDataDummyImpl() {
faker = new Faker();
// logger.setTag("RewardDataDummyImpl");
}
@Override
public CompletableFuture<Integer> getTotalReward() {
return CompletableFuture.supplyAsync(() -> faker.number().numberBetween(0, 1000));
}
}

View File

@ -0,0 +1,85 @@
package one.nem.kidshift.data.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import javax.inject.Inject;
import one.nem.kidshift.data.ChildData;
import one.nem.kidshift.data.KSActions;
import one.nem.kidshift.data.RewardData;
import one.nem.kidshift.data.UserSettings;
import one.nem.kidshift.data.retrofit.KidShiftApiService;
import one.nem.kidshift.data.room.utils.CacheWrapper;
import one.nem.kidshift.model.ChildModel;
import one.nem.kidshift.model.HistoryModel;
import one.nem.kidshift.utils.KSLogger;
import one.nem.kidshift.utils.factory.KSLoggerFactory;
import retrofit2.Call;
public class RewardDataImpl implements RewardData {
private final UserSettings userSettings;
private final KSActions ksActions;
private final CacheWrapper cacheWrapper;
private final KSLogger logger;
private final ChildData childData;
private final KidShiftApiService kidShiftApiService;
@Inject
public RewardDataImpl(KSLoggerFactory ksLoggerFactory, CacheWrapper cacheWrapper, UserSettings userSettings, KSActions ksActions, ChildData childData, KidShiftApiService kidShiftApiService) {
this.userSettings = userSettings;
this.ksActions = ksActions;
this.cacheWrapper = cacheWrapper;
this.childData = childData;
this.kidShiftApiService = kidShiftApiService;
this.logger = ksLoggerFactory.create("RewardDataImpl");
}
@Override
public CompletableFuture<Integer> getTotalReward(String childId) { // TODO: localCacheを使う
return CompletableFuture.supplyAsync(() -> ksActions.syncHistory(childId).join().stream().mapToInt(HistoryModel::getReward).sum());
}
@Override
public CompletableFuture<List<HistoryModel>> getRewardHistoryList() { // TODO: localCacheを使う
List<HistoryModel> historyModels = new ArrayList<>();
return childData.getChildListDirect().thenAccept(childModels -> {
childModels.forEach(childModel -> {
historyModels.addAll(ksActions.syncHistory(childModel.getId()).join());
});
}).thenApply(v -> historyModels);
}
@Override
public CompletableFuture<List<HistoryModel>> getRewardHistoryList(String childId) { // TODO: localCacheを使う
return CompletableFuture.supplyAsync(() -> ksActions.syncHistory(childId).join());
}
@Override
public CompletableFuture<Void> payReward(String historyId) {
return CompletableFuture.runAsync(() -> {
Call<Void> call = kidShiftApiService.payHistory(historyId, true);
try {
call.execute();
} catch (Exception e) {
logger.error("Failed to pay reward : " + e.getMessage());
}
});
}
@Override
public CompletableFuture<Void> payReward(List<String> historyIds) {
return CompletableFuture.runAsync(() -> {
historyIds.forEach(historyId -> { // TODO: API側でリストに対応する
Call<Void> call = kidShiftApiService.payHistory(historyId, true);
try {
call.execute();
} catch (Exception e) {
logger.error("Failed to pay reward : " + e.getMessage());
}
});
});
}
}

View File

@ -9,6 +9,8 @@ import javax.inject.Inject;
import one.nem.kidshift.data.KSActions;
import one.nem.kidshift.data.TaskData;
import one.nem.kidshift.data.retrofit.KidShiftApiService;
import one.nem.kidshift.data.retrofit.model.converter.TaskModelConverter;
import one.nem.kidshift.data.retrofit.model.task.TaskResponse;
import one.nem.kidshift.data.room.utils.CacheWrapper;
import one.nem.kidshift.model.callback.TaskItemModelCallback;
import one.nem.kidshift.model.tasks.TaskItemModel;
@ -37,8 +39,7 @@ public class TaskDataImpl implements TaskData {
return CompletableFuture.supplyAsync(() -> {
logger.debug("タスク取得開始");
AtomicReference<List<TaskItemModel>> taskListTmp = new AtomicReference<>();
Thread thread = new Thread(() -> {
ksActions.syncTasks().thenAccept(taskList -> {
Thread thread = new Thread(() -> ksActions.syncTasks().thenAccept(taskList -> {
if (taskListTmp.get() == null || taskListTmp.get().isEmpty()) {
logger.debug("タスク取得完了: キャッシュよりはやく取得完了 or キャッシュ無し");
if (taskList == null || taskList.isEmpty()) {
@ -63,8 +64,7 @@ public class TaskDataImpl implements TaskData {
logger.error("タスク取得失敗: " + e.getMessage());
callback.onFailed(e.getMessage());
return null;
});
});
}));
thread.start();
return cacheWrapper.getTaskList().thenApply(taskList -> {
if (taskList == null || taskList.isEmpty()) {
@ -90,18 +90,62 @@ public class TaskDataImpl implements TaskData {
}
@Override
public void addTask(TaskItemModel task) {
public CompletableFuture<TaskItemModel> addTask(TaskItemModel task) {
return CompletableFuture.supplyAsync(() -> {
Call<TaskResponse> call = kidShiftApiService.addTask(TaskModelConverter.taskItemModelToTaskAddRequest(task));
try {
Response<TaskResponse> response = call.execute();
if (response.isSuccessful()) {
assert response.body() != null;
logger.info("タスク追加成功(taskId: " + response.body().getId() + ")");
return TaskModelConverter.taskResponseToTaskItemModel(response.body());
} else {
logger.error("タスク追加失敗: HTTP Status: " + response.code());
throw new RuntimeException("HTTP Status: " + response.code());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
@Override
public void removeTask(String taskId) {
public CompletableFuture<Void> removeTask(String taskId) {
return CompletableFuture.supplyAsync(() -> {
Call<Void> call = kidShiftApiService.removeTask(taskId);
try {
Response<Void> response = call.execute();
if (response.isSuccessful()) {
logger.info("タスク削除成功(taskId: " + taskId + ")");
return null;
} else {
logger.error("タスク削除失敗: HTTP Status: " + response.code());
throw new RuntimeException("HTTP Status: " + response.code());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
@Override
public void updateTask(TaskItemModel task) {
public CompletableFuture<Void> updateTask(TaskItemModel task) {
return CompletableFuture.supplyAsync(() -> {
Call<TaskResponse> call = kidShiftApiService.updateTask(TaskModelConverter.taskItemModelToTaskAddRequest(task), task.getId());
try {
Response<TaskResponse> response = call.execute();
if (response.isSuccessful()) {
logger.info("タスク更新成功(taskId: " + task.getId() + ")");
// return response.body();
return null;
} else {
logger.error("タスク更新失敗: HTTP Status: " + response.code());
throw new RuntimeException("HTTP Status: " + response.code());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
@Override

View File

@ -77,6 +77,7 @@ public class UserSettingsImpl implements UserSettings {
boolean loggedIn;
String accessToken;
boolean childMode;
String childId;
AppCommonSettingImpl() {
sharedPrefUtils = sharedPrefUtilsFactory.create("user_settings");
@ -85,10 +86,12 @@ public class UserSettingsImpl implements UserSettings {
loggedIn = appCommonSetting.isLoggedIn();
accessToken = appCommonSetting.getAccessToken().isEmpty() ? "" : appCommonSetting.getAccessToken();
childMode = appCommonSetting.isChildMode();
childId = appCommonSetting.getChildId().isEmpty() ? "" : appCommonSetting.getChildId();
} else {
loggedIn = false;
accessToken = "";
childMode = false;
childId = "";
}
}
@ -128,6 +131,17 @@ public class UserSettingsImpl implements UserSettings {
this.childMode = childMode;
save();
}
@Override
public String getChildId() {
return childId;
}
@Override
public void setChildId(String childId) {
this.childId = childId;
save();
}
}
public class ApiSettingImpl implements UserSettings.ApiSetting {

View File

@ -4,11 +4,12 @@ import dagger.Binds;
import dagger.Module;
import dagger.hilt.InstallIn;
import dagger.hilt.android.components.FragmentComponent;
import dagger.hilt.components.SingletonComponent;
import one.nem.kidshift.data.ParentData;
import one.nem.kidshift.data.impl.ParentDataImpl;
@Module
@InstallIn(FragmentComponent.class)
@InstallIn(SingletonComponent.class)
public abstract class ParentDataModule {
@Binds

View File

@ -5,12 +5,12 @@ import dagger.Module;
import dagger.hilt.InstallIn;
import dagger.hilt.android.components.FragmentComponent;
import one.nem.kidshift.data.RewardData;
import one.nem.kidshift.data.impl.RewardDataDummyImpl;
import one.nem.kidshift.data.impl.RewardDataImpl;
@Module
@InstallIn(FragmentComponent.class)
abstract public class RewardDataDummyModule {
public abstract class RewardDataModule {
@Binds
public abstract RewardData bindRewardData(RewardDataDummyImpl rewardDataDummyImpl);
public abstract RewardData bindRewardData(RewardDataImpl rewardDataImpl);
}

View File

@ -5,9 +5,13 @@ import one.nem.kidshift.data.retrofit.model.child.ChildAddRequest;
import one.nem.kidshift.data.retrofit.model.child.ChildListResponse;
import one.nem.kidshift.data.retrofit.model.child.ChildLoginCodeResponse;
import one.nem.kidshift.data.retrofit.model.child.ChildResponse;
import one.nem.kidshift.data.retrofit.model.child.auth.ChildAuthRequest;
import one.nem.kidshift.data.retrofit.model.child.auth.ChildAuthResponse;
import one.nem.kidshift.data.retrofit.model.parent.ParentInfoResponse;
import one.nem.kidshift.data.retrofit.model.parent.auth.ParentLoginRequest;
import one.nem.kidshift.data.retrofit.model.parent.auth.ParentLoginResponse;
import one.nem.kidshift.data.retrofit.model.parent.ParentRenameRequest;
import one.nem.kidshift.data.retrofit.model.parent.auth.ParentAuthRequest;
import one.nem.kidshift.data.retrofit.model.parent.auth.ParentAuthResponse;
import one.nem.kidshift.data.retrofit.model.task.HistoryListResponse;
import one.nem.kidshift.data.retrofit.model.task.TaskAddRequest;
import one.nem.kidshift.data.retrofit.model.task.TaskListResponse;
import one.nem.kidshift.data.retrofit.model.task.TaskResponse;
@ -15,7 +19,6 @@ import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.PUT;
@ -33,7 +36,32 @@ public interface KidShiftApiService {
* @return ParentLoginResponse
*/
@POST("/parent/auth/login")
Call<ParentLoginResponse> parentLogin(@Body ParentLoginRequest request);
Call<ParentAuthResponse> parentLogin(@Body ParentAuthRequest request);
/**
* 保護者登録処理
* @param request ParentRegisterRequest
* @return ParentRegisterResponse
*/
@POST("/parent/auth/register")
Call<ParentAuthResponse> parentRegister(@Body ParentAuthRequest request);
/**
* 保護者情報更新処理
* @param request ParentRenameRequest
* @return ParentInfoResponse
*/
@PUT("/parent/account")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<ParentInfoResponse> renameParent(@Body ParentRenameRequest request);
/**
* 子供ログイン処理
* @param request ChildAuthRequest
* @return ChildAuthResponse
*/
@POST("/child/auth/login")
Call<ChildAuthResponse> childLogin(@Body ChildAuthRequest request);
/**
* 保護者アカウント情報取得処理
@ -49,7 +77,7 @@ public interface KidShiftApiService {
* タスク一覧取得
* @return TaskListResponse
*/
@GET("/parent/task")
@GET("/task")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<TaskListResponse> getTasks();
@ -58,7 +86,7 @@ public interface KidShiftApiService {
* @param request TaskAddRequest
* @return TaskResponse
*/
@POST("/parent/task")
@POST("/task")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<TaskResponse> addTask(@Body TaskAddRequest request);
@ -68,7 +96,7 @@ public interface KidShiftApiService {
* @param id タスクID
* @return TaskResponse
*/
@PUT("/parent/task/{id}")
@PUT("/task/{id}")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<TaskResponse> updateTask(@Body TaskAddRequest request, @Path("id") String id);
@ -77,7 +105,7 @@ public interface KidShiftApiService {
* @param id タスクID
* @return Void
*/
@DELETE("/parent/task/{id}")
@DELETE("/task/{id}")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<Void> removeTask(@Path("id") String id); // TODO-rca: OK responseをパース
@ -86,7 +114,7 @@ public interface KidShiftApiService {
* @param id タスクID
* @return TaskResponse
*/
@GET("/parent/task/{id}")
@GET("/task/{id}")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<TaskResponse> getTask(@Path("id") String id);
@ -95,7 +123,7 @@ public interface KidShiftApiService {
* @param id タスクID
* @return Void
*/
@POST("/parent/task/{id}/complete")
@POST("/task/{id}/complete")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<Void> completeTask(@Path("id") String id, @Query("childId") String childId);
@ -105,26 +133,61 @@ public interface KidShiftApiService {
* 子供一覧取得
* @return ChildListResponse
*/
@GET("/parent/child")
@GET("/child")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<ChildListResponse> getChildList();
/**
* 子供情報取得
* @param id 子供ID
* @return ChildResponse
*/
@GET("/child/{id}")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<ChildResponse> getChild(@Path("id") String id);
/**
* 子供追加
* @param request ChildAddRequest
* @return ChildResponse
*/
@POST("/parent/child")
@POST("/child")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<ChildResponse> addChild(@Body ChildAddRequest request);
/**
* 子供更新
* @param request ChildAddRequest
* @param id 子供ID
* @return ChildResponse
*/
@PUT("/child/{id}")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<ChildResponse> updateChild(@Body ChildAddRequest request, @Path("id") String id);
/**
* 子供削除
* @param id 子供ID
* @return Void
*/
@DELETE("/child/{id}")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<Void> removeChild(@Path("id") String id);
/**
* 子供ログインコード発行
* @param id 子供ID
* @return ChildLoginCodeResponse
*/
@GET("/parent/child/{id}/login")
@GET("/child/{id}/login")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<ChildLoginCodeResponse> issueLoginCode(@Path("id") String id);
@GET("/task/history/{childId}")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<HistoryListResponse> getHistory(@Path("childId") String childId, @Query("containPaid") boolean containPaid);
@POST("/task/history/{historyId}/paid")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<Void> payHistory(@Path("historyId") String historyId, @Query("isPaid") boolean isPaid);
}

View File

@ -0,0 +1,17 @@
package one.nem.kidshift.data.retrofit.model.child.auth;
public class ChildAuthRequest {
private String loginCode;
public ChildAuthRequest(String loginCode) {
this.loginCode = loginCode;
}
public String getLoginCode() {
return loginCode;
}
public void setLoginCode(String loginCode) {
this.loginCode = loginCode;
}
}

View File

@ -0,0 +1,30 @@
package one.nem.kidshift.data.retrofit.model.child.auth;
public class ChildAuthResponse {
private String accessToken;
private String childId;
public ChildAuthResponse() {
}
public ChildAuthResponse(String accessToken, String childId) {
this.accessToken = accessToken;
this.childId = childId;
}
public String getChildId() {
return childId;
}
public void setChildId(String childId) {
this.childId = childId;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
}

View File

@ -0,0 +1,79 @@
package one.nem.kidshift.data.retrofit.model.converter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import one.nem.kidshift.data.retrofit.model.task.HistoryListResponse;
import one.nem.kidshift.data.retrofit.model.task.HistoryResponse;
import one.nem.kidshift.data.retrofit.model.task.TaskListResponse;
import one.nem.kidshift.data.retrofit.model.task.TaskResponse;
import one.nem.kidshift.model.HistoryModel;
import one.nem.kidshift.model.tasks.TaskItemModel;
public class HistoryModelConverter { // TODO: JavaDoc
public static HistoryModel historyResponseToHistoryModel(HistoryResponse historyResponse) {
HistoryModel historyModel = new HistoryModel();
historyModel.setId(historyResponse.getId());
historyModel.setTaskId(historyResponse.getTaskId());
historyModel.setChildId(historyResponse.getChildId());
historyModel.setRegisteredAt(historyResponse.getRegisteredAt());
historyModel.setPaid(historyResponse.isPaid());
return historyModel;
}
public static HistoryResponse historyModelToHistoryResponse(HistoryModel historyModel) {
HistoryResponse historyResponse = new HistoryResponse();
historyResponse.setId(historyModel.getId());
historyResponse.setTaskId(historyModel.getTaskId());
historyResponse.setChildId(historyModel.getChildId());
historyResponse.setRegisteredAt(historyModel.getRegisteredAt());
historyResponse.setPaid(historyModel.isPaid());
return historyResponse;
}
public static List<HistoryModel> historyListResponseToHistoryModelList(HistoryListResponse historyListResponse) {
List<HistoryModel> historyModelList = new ArrayList<>();
for (HistoryResponse historyResponse : historyListResponse.getList()) {
historyModelList.add(historyResponseToHistoryModel(historyResponse));
}
return historyModelList;
}
public static HistoryListResponse historyModelListToHistoryListResponse(List<HistoryModel> historyModelList) {
HistoryListResponse historyListResponse = new HistoryListResponse();
List<HistoryResponse> historyResponseList = new ArrayList<>();
for (HistoryModel historyModel : historyModelList) {
historyResponseList.add(historyModelToHistoryResponse(historyModel));
}
historyListResponse.setList(historyResponseList);
return historyListResponse;
}
private static TaskResponse emptyTaskItemModel() {
TaskResponse taskResponse = new TaskResponse();
taskResponse.setId("");
taskResponse.setName("Critical Error occurred(Your data (on server) is may be corrupted.)");
taskResponse.setReward(0);
return taskResponse;
}
public static List<HistoryModel> historyListResponseAndTaskListResponseToHistoryModelList(HistoryListResponse historyListResponse, TaskListResponse taskListResponse) {
List<HistoryModel> historyModelList = new ArrayList<>();
for (HistoryResponse historyResponse : historyListResponse.getList()) {
HistoryModel historyModel = historyResponseToHistoryModel(historyResponse);
if (taskListResponse == null || taskListResponse.getList() == null || taskListResponse.getList().isEmpty()) {
continue;
}
TaskItemModel taskItemModel = TaskModelConverter.taskResponseToTaskItemModel(
Objects.requireNonNull(taskListResponse.getList().stream()
.filter(taskResponse -> taskResponse.getId().equals(historyModel.getTaskId()))
.findFirst().orElse(emptyTaskItemModel())));
historyModel.setTaskName(taskItemModel.getName());
historyModel.setReward(taskItemModel.getReward());
historyModelList.add(historyModel);
}
return historyModelList;
}
}

View File

@ -11,7 +11,7 @@ public class ParentModelConverter {
* @return ParentModel
*/
public static ParentModel parentInfoResponseToParentModel(ParentInfoResponse parentInfoResponse) {
return new ParentModel(parentInfoResponse.getId(), parentInfoResponse.getDisplayName(), parentInfoResponse.getEmail());
return new ParentModel(parentInfoResponse.getId(), parentInfoResponse.getDisplay_name(), parentInfoResponse.getEmail());
}
/**

View File

@ -3,6 +3,7 @@ package one.nem.kidshift.data.retrofit.model.converter;
import java.util.List;
import java.util.stream.Collectors;
import one.nem.kidshift.data.retrofit.model.task.TaskAddRequest;
import one.nem.kidshift.data.retrofit.model.task.TaskListResponse;
import one.nem.kidshift.data.retrofit.model.task.TaskResponse;
import one.nem.kidshift.model.tasks.TaskItemModel;
@ -47,4 +48,13 @@ public class TaskModelConverter {
public static List<TaskItemModel> taskListResponseToTaskItemModelList(TaskListResponse taskListResponse) {
return taskListResponse.getList().stream().map(TaskModelConverter::taskResponseToTaskItemModel).collect(Collectors.toList());
}
public static TaskAddRequest taskItemModelToTaskAddRequest(TaskItemModel taskItemModel) {
TaskAddRequest request = new TaskAddRequest();
request.setName(taskItemModel.getName());
request.setReward(taskItemModel.getReward());
request.setBgColor(taskItemModel.getBgColor());
request.setIconEmoji(taskItemModel.getIconEmoji());
return request;
}
}

View File

@ -4,18 +4,18 @@ public class ParentInfoResponse {
private String id;
private String email;
private String displayName;
private String display_name;
/**
* コンストラクタ (全プロパティ)
* @param id 親ID
* @param email メールアドレス
* @param displayName 表示名
* @param display_name 表示名
*/
public ParentInfoResponse(String id, String email, String displayName) {
public ParentInfoResponse(String id, String email, String display_name) {
this.id = id;
this.email = email;
this.displayName = displayName;
this.display_name = display_name;
}
public String getId() {
@ -26,8 +26,8 @@ public class ParentInfoResponse {
return email;
}
public String getDisplayName() {
return displayName;
public String getDisplay_name() {
return display_name;
}
public void setId(String id) {
@ -38,7 +38,7 @@ public class ParentInfoResponse {
this.email = email;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
public void setDisplay_name(String display_name) {
this.display_name = display_name;
}
}

View File

@ -0,0 +1,21 @@
package one.nem.kidshift.data.retrofit.model.parent;
public class ParentRenameRequest {
private String displayName;
/**
* コンストラクタ (全プロパティ)
* @param displayName 表示名
*/
public ParentRenameRequest(String displayName) {
this.displayName = displayName;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
}

View File

@ -1,6 +1,6 @@
package one.nem.kidshift.data.retrofit.model.parent.auth;
public class ParentLoginRequest {
public class ParentAuthRequest {
private String email;
private String password;
@ -9,7 +9,7 @@ public class ParentLoginRequest {
* @param email メールアドレス
* @param password パスワード
*/
public ParentLoginRequest(String email, String password) {
public ParentAuthRequest(String email, String password) {
this.email = email;
this.password = password;
}

View File

@ -1,13 +1,13 @@
package one.nem.kidshift.data.retrofit.model.parent.auth;
public class ParentLoginResponse {
public class ParentAuthResponse {
private String accessToken;
/**
* コンストラクタ (全プロパティ)
* @param accessToken アクセストークン
*/
public ParentLoginResponse(String accessToken) {
public ParentAuthResponse(String accessToken) {
this.accessToken = accessToken;
}

View File

@ -0,0 +1,62 @@
package one.nem.kidshift.data.retrofit.model.task;
import java.util.Date;
public class HistoryBaseItem {
private String id;
private String taskId;
private String childId;
private Date registeredAt;
private boolean isPaid;
public HistoryBaseItem(String id, String taskId, String childId, Date registeredAt, boolean isPaid) {
this.id = id;
this.taskId = taskId;
this.childId = childId;
this.registeredAt = registeredAt;
this.isPaid = isPaid;
}
public HistoryBaseItem() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTaskId() {
return taskId;
}
public void setTaskId(String taskId) {
this.taskId = taskId;
}
public String getChildId() {
return childId;
}
public void setChildId(String childId) {
this.childId = childId;
}
public Date getRegisteredAt() {
return registeredAt;
}
public void setRegisteredAt(Date registeredAt) {
this.registeredAt = registeredAt;
}
public boolean isPaid() {
return isPaid;
}
public void setPaid(boolean isPaid) {
this.isPaid = isPaid;
}
}

View File

@ -0,0 +1,22 @@
package one.nem.kidshift.data.retrofit.model.task;
import java.util.List;
public class HistoryListResponse {
List<HistoryResponse> list;
public HistoryListResponse(List<HistoryResponse> list) {
this.list = list;
}
public HistoryListResponse() {
}
public List<HistoryResponse> getList() {
return list;
}
public void setList(List<HistoryResponse> list) {
this.list = list;
}
}

View File

@ -0,0 +1,5 @@
package one.nem.kidshift.data.retrofit.model.task;
public class HistoryResponse extends HistoryBaseItem {
// Additional fields
}

View File

@ -4,13 +4,15 @@ import androidx.room.Database;
import androidx.room.RoomDatabase;
import one.nem.kidshift.data.room.dao.ChildCacheDao;
import one.nem.kidshift.data.room.dao.HistoryCacheDao;
import one.nem.kidshift.data.room.dao.TaskCacheDao;
import one.nem.kidshift.data.room.dao.TaskChildLinkageDao;
import one.nem.kidshift.data.room.entity.ChildCacheEntity;
import one.nem.kidshift.data.room.entity.HistoryCacheEntity;
import one.nem.kidshift.data.room.entity.TaskCacheEntity;
import one.nem.kidshift.data.room.entity.TaskChildLinkageEntity;
@Database(entities = {ChildCacheEntity.class, TaskCacheEntity.class, TaskChildLinkageEntity.class}, version = 1)
@Database(entities = {ChildCacheEntity.class, TaskCacheEntity.class, TaskChildLinkageEntity.class, HistoryCacheEntity.class}, version = 2)
public abstract class KidShiftDatabase extends RoomDatabase {
public abstract ChildCacheDao childCacheDao();
@ -19,4 +21,6 @@ public abstract class KidShiftDatabase extends RoomDatabase {
public abstract TaskChildLinkageDao taskChildLinkageDao();
public abstract HistoryCacheDao historyCacheDao();
}

View File

@ -0,0 +1,16 @@
package one.nem.kidshift.data.room.converter;
import androidx.room.TypeConverter;
public class DateTypeConverter {
@TypeConverter
public static java.util.Date fromTimestamp(Long value) {
return value == null ? null : new java.util.Date(value);
}
@TypeConverter
public static Long dateToTimestamp(java.util.Date date) {
return date == null ? null : date.getTime();
}
}

View File

@ -0,0 +1,48 @@
package one.nem.kidshift.data.room.dao;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import androidx.room.Transaction;
import java.util.List;
import one.nem.kidshift.data.room.entity.HistoryCacheEntity;
import one.nem.kidshift.data.room.model.HistoryWithTask;
@Dao
public interface HistoryCacheDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertHistory(HistoryCacheEntity history);
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertHistoryList(List<HistoryCacheEntity> historyList);
@Query("SELECT * FROM history_cache WHERE id = :historyId")
HistoryCacheEntity getHistory(String historyId);
@Query("SELECT * FROM history_cache")
List<HistoryCacheEntity> getHistoryList();
@Query("SELECT * FROM history_cache WHERE child_id = :childId")
List<HistoryCacheEntity> getHistoryListByChildId(String childId);
@Transaction
@Query("SELECT * FROM history_cache")
List<HistoryWithTask> getHistoryWithTasks();
@Transaction
@Query("SELECT * FROM history_cache WHERE child_id = :childId")
List<HistoryWithTask> getHistoryWithTasksByChildId(String childId);
@Query("SELECT * FROM history_cache WHERE task_id = :taskId")
List<HistoryCacheEntity> getHistoryListByTaskId(String taskId);
@Query("DELETE FROM history_cache WHERE id = :historyId")
void deleteHistory(String historyId);
@Query("DELETE FROM history_cache WHERE child_id = :childId")
void deleteHistoryByChildId(String childId);
}

View File

@ -0,0 +1,30 @@
package one.nem.kidshift.data.room.entity;
import androidx.annotation.NonNull;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import androidx.room.TypeConverters;
import java.util.Date;
import one.nem.kidshift.data.room.converter.DateTypeConverter;
@Entity(tableName = "history_cache")
@TypeConverters({DateTypeConverter.class})
public class HistoryCacheEntity {
@PrimaryKey
@ColumnInfo(name = "id")
@NonNull
public String id;
@ColumnInfo(name = "child_id")
public String childId;
@ColumnInfo(name = "task_id")
public String taskId;
@ColumnInfo(name = "registered_at")
public Date registeredAt;
}

View File

@ -0,0 +1,18 @@
package one.nem.kidshift.data.room.model;
import androidx.room.Embedded;
import androidx.room.Relation;
import one.nem.kidshift.data.room.entity.HistoryCacheEntity;
import one.nem.kidshift.data.room.entity.TaskCacheEntity;
public class HistoryWithTask {
@Embedded
public HistoryCacheEntity history;
@Relation(
parentColumn = "task_id",
entityColumn = "id"
)
public TaskCacheEntity task;
}

View File

@ -9,13 +9,17 @@ import javax.inject.Inject;
import dagger.Module;
import dagger.hilt.InstallIn;
import dagger.hilt.components.SingletonComponent;
import one.nem.kidshift.data.retrofit.model.converter.HistoryModelConverter;
import one.nem.kidshift.data.room.KidShiftDatabase;
import one.nem.kidshift.data.room.entity.ChildCacheEntity;
import one.nem.kidshift.data.room.entity.TaskCacheEntity;
import one.nem.kidshift.data.room.entity.TaskChildLinkageEntity;
import one.nem.kidshift.data.room.model.HistoryWithTask;
import one.nem.kidshift.data.room.utils.converter.ChildCacheConverter;
import one.nem.kidshift.data.room.utils.converter.HistoryCacheConverter;
import one.nem.kidshift.data.room.utils.converter.TaskCacheConverter;
import one.nem.kidshift.model.ChildModel;
import one.nem.kidshift.model.HistoryModel;
import one.nem.kidshift.model.tasks.TaskItemModel;
import one.nem.kidshift.utils.KSLogger;
import one.nem.kidshift.utils.factory.KSLoggerFactory;
@ -39,7 +43,7 @@ public class CacheWrapper {
* @param taskList タスクリスト
* @return CompletableFuture
*/
public CompletableFuture<Void> updateCache(List<ChildModel> childList, List<TaskItemModel> taskList) {
public CompletableFuture<Void> updateChildTaskCache(List<ChildModel> childList, List<TaskItemModel> taskList) {
return CompletableFuture.runAsync(() -> {
logger.debug("Updating cache");
insertChildList(childList).join();
@ -65,6 +69,12 @@ public class CacheWrapper {
});
}
public CompletableFuture<Void> updateHistoryCache(List<HistoryModel> historyList) {
return CompletableFuture.runAsync(() -> {
kidShiftDatabase.historyCacheDao().insertHistoryList(HistoryCacheConverter.historyModelListToHistoryCacheEntityList(historyList));
});
}
/**
* 子供リストをDBに挿入する
* @param childList 子供リスト
@ -109,5 +119,12 @@ public class CacheWrapper {
});
}
public CompletableFuture<List<HistoryModel>> getHistoryList(String childId) {
return CompletableFuture.supplyAsync(() -> {
List<HistoryWithTask> result = kidShiftDatabase.historyCacheDao().getHistoryWithTasksByChildId(childId);
return HistoryCacheConverter.historyWithTaskListToHistoryModelList(result);
});
}
}

View File

@ -0,0 +1,60 @@
package one.nem.kidshift.data.room.utils.converter;
import java.util.ArrayList;
import java.util.List;
import one.nem.kidshift.data.room.entity.HistoryCacheEntity;
import one.nem.kidshift.data.room.model.HistoryWithTask;
import one.nem.kidshift.model.HistoryModel;
public class HistoryCacheConverter {
public static HistoryCacheEntity historyModelToHistoryCacheEntity(HistoryModel historyModel) {
HistoryCacheEntity historyCacheEntity = new HistoryCacheEntity();
historyCacheEntity.id = historyModel.getId();
historyCacheEntity.taskId = historyModel.getTaskId();
historyCacheEntity.childId = historyModel.getChildId();
historyCacheEntity.registeredAt = historyModel.getRegisteredAt();
return historyCacheEntity;
}
public static HistoryModel historyCacheEntityToHistoryModel(HistoryCacheEntity historyCacheEntity) {
HistoryModel historyModel = new HistoryModel();
historyModel.setId(historyCacheEntity.id);
historyModel.setTaskId(historyCacheEntity.taskId);
historyModel.setChildId(historyCacheEntity.childId);
historyModel.setRegisteredAt(historyCacheEntity.registeredAt);
return historyModel;
}
public static List<HistoryCacheEntity> historyModelListToHistoryCacheEntityList(List<HistoryModel> historyModelList) {
List<HistoryCacheEntity> historyCacheEntityList = new ArrayList<>();
for (HistoryModel historyModel : historyModelList) {
historyCacheEntityList.add(historyModelToHistoryCacheEntity(historyModel));
}
return historyCacheEntityList;
}
public static List<HistoryModel> historyCacheEntityListToHistoryModelList(List<HistoryCacheEntity> historyCacheEntityList) {
List<HistoryModel> historyModelList = new ArrayList<>();
for (HistoryCacheEntity historyCacheEntity : historyCacheEntityList) {
historyModelList.add(historyCacheEntityToHistoryModel(historyCacheEntity));
}
return historyModelList;
}
public static List<HistoryModel> historyWithTaskListToHistoryModelList(List<HistoryWithTask> result) {
List<HistoryModel> historyModelList = new ArrayList<>();
for (HistoryWithTask historyWithTask : result) {
HistoryModel historyModel = new HistoryModel();
historyModel.setId(historyWithTask.history.id);
historyModel.setTaskId(historyWithTask.history.taskId);
historyModel.setChildId(historyWithTask.history.childId);
historyModel.setRegisteredAt(historyWithTask.history.registeredAt);
historyModel.setTaskName(historyWithTask.task.name);
historyModel.setReward(historyWithTask.task.reward);
historyModelList.add(historyModel);
}
return historyModelList;
}
}

View File

@ -37,7 +37,14 @@ public class TaskCacheConverter {
* @return TaskItemModel
*/
public static TaskItemModel taskCacheEntityToTaskModel(TaskCacheEntity entity) {
return new TaskItemModel(entity.id, entity.name, entity.iconEmoji, entity.reward, null);
TaskItemModel model = new TaskItemModel();
model.setId(entity.id);
model.setName(entity.name);
model.setReward(entity.reward);
model.setIconEmoji(entity.iconEmoji == null ? "" : entity.iconEmoji); // Workaround
model.setBgColor(null); // TODO: 実装
model.setAttachedChildren(null); // TODO: 実装
return model;
}
/**

View File

@ -32,6 +32,8 @@ dependencies {
implementation libs.material
implementation libs.navigation.fragment
implementation libs.navigation.ui
implementation libs.activity
implementation libs.constraintlayout
testImplementation libs.junit
androidTestImplementation libs.ext.junit
androidTestImplementation libs.espresso.core
@ -48,4 +50,5 @@ dependencies {
implementation project(':model')
implementation project(':utils')
implementation project(':data')
implementation project(':shared')
}

View File

@ -1,4 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity
android:name=".ChildManageMainActivity"
android:exported="false" />
</application>
</manifest>

View File

@ -90,24 +90,24 @@ public class ChildMainFragment extends Fragment {
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
logger.addTag("ChildMainFragment");
Integer reward = rewardData.getTotalReward().join();
logger.debug("取得したデータ: " + reward);
Calendar cl = Calendar.getInstance();
TextView tr = view.findViewById(R.id.totalReward);
TextView dv = view.findViewById(R.id.dateView);
Date date = new Date();
NumberFormat nf = NumberFormat.getNumberInstance();
SimpleDateFormat sdf = new SimpleDateFormat();
sdf.applyPattern("yyyy年MM月");
dv.setText(sdf.format(cl.getTime()) + " お小遣い総額");
tr.setText("¥" + nf.format(reward).toString());
//
// logger.addTag("ChildMainFragment");
//
// Integer reward = rewardData.getTotalReward().join();
//
// logger.debug("取得したデータ: " + reward);
//
// Calendar cl = Calendar.getInstance();
// TextView tr = view.findViewById(R.id.totalReward);
// TextView dv = view.findViewById(R.id.dateView);
// Date date = new Date();
//
//
// NumberFormat nf = NumberFormat.getNumberInstance();
// SimpleDateFormat sdf = new SimpleDateFormat();
// sdf.applyPattern("yyyy年MM月");
//
// dv.setText(sdf.format(cl.getTime()) + " お小遣い総額");
// tr.setText("¥" + nf.format(reward).toString());
}
}

View File

@ -0,0 +1,201 @@
package one.nem.kidshift.feature.child;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.print.PrintAttributes;
import android.view.View;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.List;
import java.util.Objects;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
import one.nem.kidshift.data.ChildData;
import one.nem.kidshift.feature.child.adapter.ChildListAdapter;
import one.nem.kidshift.model.ChildModel;
import one.nem.kidshift.model.callback.ChildModelCallback;
import one.nem.kidshift.utils.KSLogger;
import one.nem.kidshift.utils.factory.KSLoggerFactory;
@AndroidEntryPoint
public class ChildManageMainActivity extends AppCompatActivity {
@Inject
ChildData childData;
@Inject
KSLoggerFactory loggerFactory;
private KSLogger logger;
ChildListAdapter childListAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_child_manage_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
logger = loggerFactory.create("ChildManageMainActivity");
// ToolBarのセットアップ
Toolbar toolbar = findViewById(R.id.toolBar);
// タイトル
toolbar.setTitle("子供アカウント管理");
// 閉じる
toolbar.setNavigationIcon(one.nem.kidshift.shared.R.drawable.close_24px);
toolbar.setNavigationOnClickListener(v -> finish());
// 追加ボタン
toolbar.inflateMenu(R.menu.child_manage_main_toolbar_item);
toolbar.setOnMenuItemClickListener(item -> {
if (item.getItemId() == R.id.add_child_account) {
showAddChildDialog();
return true;
}
return false;
});
RecyclerView recyclerView = findViewById(R.id.childListRecyclerView);
childListAdapter = new ChildListAdapter();
childListAdapter.setButtonEventCallback(new ChildListAdapter.ButtonEventCallback() {
@Override
public void onEditButtonClick(ChildModel childModel) {
showEditChildDialog(childModel);
}
@Override
public void onLoginButtonClick(ChildModel childModel) {
childData.issueLoginCode(childModel.getId()).thenAccept(loginCode -> {
logger.debug("ログインコード発行完了: " + loginCode);
runOnUiThread(() -> showLoginCodeDialog(loginCode));
});
}
});
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(childListAdapter);
updateList();
}
private void showLoginCodeDialog(Integer loginCode) {
View view = getLayoutInflater().inflate(R.layout.child_login_code_dialog_layout, null);
TextView loginCodeTextView = view.findViewById(R.id.loginCode);
// loginCodeをStringに変換して4桁 2つに分割してハイフンで繋げる
loginCodeTextView.setText(loginCode.toString().substring(0, 4) + "-" + loginCode.toString().substring(4));
new MaterialAlertDialogBuilder(this)
.setTitle("ログインコード")
.setView(view)
.setPositiveButton("閉じる", (dialog, which) -> dialog.dismiss())
.show();
}
private void showAddChildDialog() {
// EditTextを作成
EditText childNameEditText = new EditText(this);
childNameEditText.setHint("子供の名前");
// FrameLayoutに入れる
FrameLayout container = new FrameLayout(this);
container.addView(childNameEditText);
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) childNameEditText.getLayoutParams();
params.setMargins(32, 16, 32, 16);
childNameEditText.setLayoutParams(params);
new MaterialAlertDialogBuilder(this)
.setTitle("子供アカウント追加")
.setView(container)
.setPositiveButton("追加", (dialog, which) -> {
String childName = Objects.requireNonNull(childNameEditText.getText()).toString();
if (childName.isEmpty()) {
Toast.makeText(this, "名前を入力してください", Toast.LENGTH_SHORT).show();
}
childData.addChild(new ChildModel(childName))
.thenRun(this::updateListDirectly);
})
.setNegativeButton("キャンセル", (dialog, which) -> dialog.dismiss())
.show();
}
private void showEditChildDialog(ChildModel childModel) {
// EditTextを作成
EditText childNameEditText = new EditText(this);
childNameEditText.setHint("子供の名前");
childNameEditText.setText(childModel.getName());
// FrameLayoutに入れる
FrameLayout container = new FrameLayout(this);
container.addView(childNameEditText);
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) childNameEditText.getLayoutParams();
params.setMargins(32, 16, 32, 16);
childNameEditText.setLayoutParams(params);
new MaterialAlertDialogBuilder(this)
.setTitle("子供アカウント編集")
.setView(container)
.setPositiveButton("保存", (dialog, which) -> {
String childName = Objects.requireNonNull(childNameEditText.getText()).toString();
if (childName.isEmpty()) {
Toast.makeText(this, "名前を入力してください", Toast.LENGTH_SHORT).show();
}
childModel.setName(childName);
childData.updateChild(childModel)
.thenRun(this::updateListDirectly);
})
.setNegativeButton("キャンセル", (dialog, which) -> dialog.dismiss())
// 削除ボタン
.setNeutralButton("削除", (dialog, which) -> { // TODO: 確認ダイアログを表示する
childData.removeChild(childModel.getId())
.thenRun(this::updateListDirectly);
})
.show();
}
@SuppressLint("NotifyDataSetChanged")
private void updateList() {
childData.getChildList(new ChildModelCallback() {
@Override
public void onUnchanged() {
}
@Override
public void onUpdated(List<ChildModel> childModelList) {
}
@Override
public void onFailed(String message) {
}
}).thenAccept(childListAdapter::setChildList).thenRun(() -> {
runOnUiThread(() -> childListAdapter.notifyDataSetChanged());
});
}
@SuppressLint("NotifyDataSetChanged")
private void updateListDirectly() {
childData.getChildListDirect().thenAccept(childListAdapter::setChildList).thenRun(() -> {
runOnUiThread(() -> childListAdapter.notifyDataSetChanged());
});
}
}

View File

@ -0,0 +1,79 @@
package one.nem.kidshift.feature.child.adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import one.nem.kidshift.feature.child.R;
import one.nem.kidshift.model.ChildModel;
public class ChildListAdapter extends RecyclerView.Adapter<ChildListAdapter.ViewHolder>{
private List<ChildModel> childList;
private ButtonEventCallback buttonEventCallback;
public ChildListAdapter() {
}
public void setChildList(List<ChildModel> childList) {
this.childList = childList;
}
public void setButtonEventCallback(ButtonEventCallback buttonEventCallback) {
this.buttonEventCallback = buttonEventCallback;
}
@NonNull
@Override
public ChildListAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_child_manage_main, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ChildListAdapter.ViewHolder holder, int position) {
ChildModel childModel = childList.get(position);
holder.childNameTextView.setText(childModel.getName());
holder.editButton.setOnClickListener(v -> {
if (buttonEventCallback != null) {
buttonEventCallback.onEditButtonClick(childModel);
}
});
holder.loginButton.setOnClickListener(v -> {
if (buttonEventCallback != null) {
buttonEventCallback.onLoginButtonClick(childModel);
}
});
}
@Override
public int getItemCount() {
return childList == null ? 0 : childList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView childNameTextView;
Button editButton;
Button loginButton;
public ViewHolder(@NonNull View itemView) {
super(itemView);
childNameTextView = itemView.findViewById(R.id.childNameTextView);
editButton = itemView.findViewById(R.id.editButton);
loginButton = itemView.findViewById(R.id.loginButton);
}
}
public interface ButtonEventCallback {
void onEditButtonClick(ChildModel childModel);
void onLoginButtonClick(ChildModel childModel);
}
}

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ChildManageMainActivity">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarTheme"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/childListRecyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="1dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="1dp"
android:layout_marginBottom="1dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolBar" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<TextView
android:id="@+id/loginCode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="30dp"
android:paddingBottom="25dp"
android:textAppearance="@style/TextAppearance.AppCompat.Display3" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="12dp"
android:layout_marginHorizontal="16dp">
<TextView
android:id="@+id/childNameTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Placeholder"
android:textAppearance="@style/TextAppearance.AppCompat.Display1"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/linearLayout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<Button
android:id="@+id/editButton"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="編集"
android:layout_marginRight="8px"/>
<Button
android:id="@+id/loginButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="ログイン"
android:layout_marginLeft="8px"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/add_child_account"
android:icon="@drawable/add_24px"
app:showAsAction="ifRoom"
android:title="子供アカウントの追加" />
</menu>

1
feature/common/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

View File

@ -0,0 +1,52 @@
plugins {
alias(libs.plugins.androidLibrary)
id 'com.google.dagger.hilt.android'
id 'androidx.navigation.safeargs'
}
android {
namespace 'one.nem.kidshift.feature.common'
compileSdk 34
defaultConfig {
minSdk 28
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation libs.appcompat
implementation libs.material
implementation libs.navigation.fragment
implementation libs.navigation.ui
testImplementation libs.junit
androidTestImplementation libs.ext.junit
androidTestImplementation libs.espresso.core
implementation libs.androidx.swiperefreshlayout
// Hilt (DI)
implementation libs.com.google.dagger.hilt.android
annotationProcessor libs.com.google.dagger.hilt.compiler
implementation 'one.nem:compact-calendar-view:1.0.0'
// Project modules
implementation project(':model')
implementation project(':utils')
implementation project(':data')
implementation project(':shared')
}

View File

21
feature/common/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,26 @@
package one.nem.kidshift.feature.common;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("one.nem.kidshift.feature.common.test", appContext.getPackageName());
}
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>

View File

@ -0,0 +1,470 @@
package one.nem.kidshift.feature.common;
import android.annotation.SuppressLint;
import android.graphics.Color;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.core.view.MenuHost;
import androidx.core.view.MenuProvider;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Lifecycle;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import com.github.sundeepk.compactcalendarview.CompactCalendarView;
import com.github.sundeepk.compactcalendarview.domain.Event;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
import one.nem.kidshift.data.ChildData;
import one.nem.kidshift.data.RewardData;
import one.nem.kidshift.data.TaskData;
import one.nem.kidshift.data.UserSettings;
import one.nem.kidshift.feature.common.adapter.ChildListItemAdapter;
import one.nem.kidshift.feature.common.adapter.TaskListItemAdapter;
import one.nem.kidshift.model.HistoryModel;
import one.nem.kidshift.model.callback.TaskItemModelCallback;
import one.nem.kidshift.model.tasks.TaskItemModel;
import one.nem.kidshift.utils.FabManager;
import one.nem.kidshift.utils.KSLogger;
import one.nem.kidshift.utils.RecyclerViewAnimUtils;
import one.nem.kidshift.utils.ToolBarManager;
import one.nem.kidshift.utils.factory.KSLoggerFactory;
import one.nem.kidshift.utils.models.FabEventCallback;
@AndroidEntryPoint
public class CommonHomeFragment extends Fragment {
private static final String ARG_IS_CHILD_MODE = "isChildMode";
private static final String ARG_CHILD_ID = "childId";
@Inject
KSLoggerFactory ksLoggerFactory;
@Inject
TaskData taskData;
@Inject
ChildData childData;
@Inject
FabManager fabManager;
@Inject
ToolBarManager toolBarManager;
@Inject
RewardData rewardData;
@Inject
RecyclerViewAnimUtils recyclerViewAnimUtils;
@Inject
UserSettings userSettings;
private boolean isChildMode;
private String childId;
private KSLogger logger;
CompactCalendarView compactCalendarView;
View calendarContainer;
SwipeRefreshLayout swipeRefreshLayout;
TaskListItemAdapter taskListItemAdapter;
TextView calendarTitleTextView;
ImageButton calendarPrevButton;
ImageButton calendarNextButton;
public CommonHomeFragment() {
// Required empty public constructor
}
// TODO: SwipeToRef
public static CommonHomeFragment newInstance(boolean isChildMode, String childId) {
CommonHomeFragment fragment = new CommonHomeFragment();
Bundle args = new Bundle();
args.putBoolean(ARG_IS_CHILD_MODE, isChildMode);
args.putString(ARG_CHILD_ID, childId);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
logger = ksLoggerFactory.create("CommonHomeFragment");
if (userSettings.getAppCommonSetting().isChildMode()) {
logger.debug("子供モードで起動(子供ログイン)");
isChildMode = true;
childId = userSettings.getAppCommonSetting().getChildId();
logger.debug("childId: " + childId);
} else {
if (getArguments() != null) {
isChildMode = getArguments().getBoolean(ARG_IS_CHILD_MODE) && getArguments().getBoolean(ARG_IS_CHILD_MODE);
childId = getArguments().getString(ARG_CHILD_ID) != null ? getArguments().getString(ARG_CHILD_ID) : "";
}
if (childId != null && !childId.isEmpty()) {
isChildMode = true;
}
if (isChildMode) {
logger.debug("子供モードで起動");
logger.debug("childId: " + childId);
} else {
logger.debug("保護者モードで起動");
}
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_common_home, container, false);
compactCalendarView = view.findViewById(R.id.calendar);
taskListItemAdapter = new TaskListItemAdapter();
taskListItemAdapter.setCallback((taskId, taskName) -> {
if (isChildMode) {
showConfirmDialog(taskId, taskName);
} else {
showChildSelectDialog(taskId, taskName);
}
});
RecyclerView taskListRecyclerView = view.findViewById(R.id.taskListRecyclerView);
taskListRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
taskListRecyclerView.setAdapter(taskListItemAdapter);
taskListRecyclerView.setItemViewCacheSize(10);
recyclerViewAnimUtils.setSlideUpAnimation(taskListRecyclerView);
swipeRefreshLayout = view.findViewById(R.id.swipeRefreshLayout);
swipeRefreshLayout.setOnRefreshListener(this::updateData);
calendarTitleTextView = view.findViewById(R.id.calendarTitleTextView);
calendarPrevButton = view.findViewById(R.id.calendarPrevButton);
calendarNextButton = view.findViewById(R.id.calendarNextButton);
calendarContainer = view.findViewById(R.id.calendarContainer);
initCalender();
updateData();
return view;
}
private void recyclerViewRefresh() {
requireActivity().runOnUiThread(() -> {
taskListItemAdapter.notifyItemRangeRemoved(0, taskListItemAdapter.getItemCount());
taskListItemAdapter.notifyItemRangeInserted(0, taskListItemAdapter.getItemCount());
});
}
@Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
@Override
public void onResume() {
super.onResume();
if (isChildMode) {
setupFabChild();
} else {
setupFabParent();
}
setupToolBar();
}
/**
* 親モードの場合(=子供モードではない場合)のFABの設定
*/
private void setupFabParent() {
fabManager.show();
fabManager.setFabEventCallback(new FabEventCallback() {
@Override
public void onClicked() {
showAddTaskDialog();
}
@Override
public void onLongClicked() {
// Do nothing
}
});
}
/**
* 子供モードの場合のFABの設定
*/
private void setupFabChild() {
fabManager.hide();
}
private void setupToolBar() {
if (isChildMode) {
toolBarManager.setTitle("ホーム");
toolBarManager.setSubtitle("子供ビュー");
} else {
toolBarManager.setTitle("ホーム");
toolBarManager.setSubtitle("保護者ビュー");
}
MenuHost menuHost = requireActivity();
menuHost.invalidateMenu();
menuHost.addMenuProvider(new MenuProvider() {
@Override
public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) {
logger.debug("onCreateMenu, インフレート");
menu.clear();
menuInflater.inflate(R.menu.common_home_toolbar_menu, menu);
}
@Override
public boolean onMenuItemSelected(@NonNull MenuItem menuItem) {
if (menuItem.getItemId() == R.id.toggle_calendar) {
if (calendarContainer.getVisibility() == View.VISIBLE) {
Animation slideUp = AnimationUtils.loadAnimation(getContext(), one.nem.kidshift.shared.R.anim.slide_up);
calendarContainer.startAnimation(slideUp);
slideUp.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
recyclerViewRefresh();
}
@Override
public void onAnimationEnd(Animation animation) {
calendarContainer.setVisibility(View.GONE);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
} else {
Animation slideDown = AnimationUtils.loadAnimation(getContext(), one.nem.kidshift.shared.R.anim.slide_down);
calendarContainer.startAnimation(slideDown);
slideDown.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
calendarContainer.setVisibility(View.VISIBLE);
recyclerViewRefresh();
}
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
return true;
}
return false;
}
}, this.getViewLifecycleOwner(), Lifecycle.State.RESUMED);
}
/**
* タスク完了確認ダイアログを表示 (子供モード用)
*
* @param taskId タスクID
* @param taskName タスク名
*/
private void showConfirmDialog(String taskId, String taskName) {
new MaterialAlertDialogBuilder(requireContext())
.setTitle("タスクを完了しますか?")
.setMessage(taskName + "を完了しますか?")
.setPositiveButton("はい", (dialog, which) -> {
dialog.dismiss();
taskData.recordTaskCompletion(taskId, childId);
})
.setNegativeButton("いいえ", (dialog, which) -> {
dialog.dismiss();
taskData.recordTaskCompletion(taskId, childId);
})
.show();
}
/**
* タスク完了ダイアログ(子供選択画面)を表示 (親モード用)
*
* @param taskId タスクID
* @param taskName タスク名
*/
private void showChildSelectDialog(String taskId, String taskName) { // TODO: Assignされている子供かどうかを考慮するように
RecyclerView childListRecyclerView = new RecyclerView(requireContext());
childListRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
childListRecyclerView.setPadding(0, 48, 0, 0);
// TODO: キャッシュから取得する方にする
childData.getChildListDirect().thenAccept(childModelList -> {
ChildListItemAdapter childListItemAdapter = new ChildListItemAdapter(childModelList);
childListItemAdapter.setCallback(childId -> {
taskData.recordTaskCompletion(taskId, childId);
});
childListRecyclerView.setAdapter(childListItemAdapter);
}).thenRun(() -> {
requireActivity().runOnUiThread(() -> {
new MaterialAlertDialogBuilder(requireContext())
.setTitle(taskName + "を完了したお子様を選択")
.setView(childListRecyclerView)
.setNeutralButton("閉じる", (dialog, which) -> dialog.dismiss())
.show();
});
});
}
/**
* タスク情報を更新
* @return CompletableFuture<Void>
*/
@SuppressLint("NotifyDataSetChanged")
private CompletableFuture<Void> updateTaskInfo() { // TODO: updatedの場合の処理など実装
return taskData.getTasks(new TaskItemModelCallback() {
@Override
public void onUnchanged() {
// Do nothing
}
@Override
public void onUpdated(List<TaskItemModel> taskItem) { // Workaround
taskListItemAdapter.notifyItemRangeRemoved(0, taskListItemAdapter.getItemCount());
taskListItemAdapter.setTaskItemModelList(taskItem);
taskListItemAdapter.notifyItemRangeInserted(0, taskItem.size());
}
@Override
public void onFailed(String message) {
// TODO: ユーザーに丁寧に通知
Toast.makeText(requireContext(), "タスク情報の取得に失敗しました", Toast.LENGTH_SHORT).show(); // Workaround
}
}).thenAccept(taskItemModel -> {
requireActivity().runOnUiThread(() -> {
// taskListItemAdapter.notifyItemRangeRemoved(0, taskListItemAdapter.getItemCount());
// taskListItemAdapter.setTaskItemModelList(taskItemModel);
// taskListItemAdapter.notifyItemRangeInserted(0, taskItemModel.size());
taskListItemAdapter.setTaskItemModelList(taskItemModel);
taskListItemAdapter.notifyItemRangeChanged(0, taskItemModel.size());
});
});
}
/**
* カレンダーを更新
* @return CompletableFuture<Void>
*/
private CompletableFuture<Void> updateCalender() {
return rewardData.getRewardHistoryList().thenAccept(historyModels -> {
historyModels.forEach(historyModel -> {
compactCalendarView.addEvent(new Event(Color.RED, historyModel.getRegisteredAt().getTime(), historyModel)); // debug
});
});
}
private void initCalender() {
compactCalendarView.setListener(new CompactCalendarView.CompactCalendarViewListener() {
@Override
public void onDayClick(Date date) { // Test
List<Event> events = compactCalendarView.getEvents(date);
ScrollView scrollView = new ScrollView(requireContext());
LinearLayout linearLayout = new LinearLayout(requireContext());
linearLayout.setOrientation(LinearLayout.VERTICAL);
linearLayout.setPadding(96, 24, 96, 24);
scrollView.addView(linearLayout);
events.forEach(event -> {
TextView textView = new TextView(requireContext());
textView.setText(((HistoryModel) event.getData()).getTaskName() + " @ " + ((HistoryModel) event.getData()).getChildId());
textView.setPadding(0, 0, 0, 24);
linearLayout.addView(textView);
});
new MaterialAlertDialogBuilder(requireContext())
.setTitle("タスク一覧 (DEBUG)")
.setMessage(events.size() + "件のタスクが登録されています")
.setView(scrollView)
.setNeutralButton("閉じる", (dialog, which) -> dialog.dismiss())
.show();
}
@Override
public void onMonthScroll(Date date) {
// 0000年00月の形式に変換 getYear/getMonthは非推奨
calendarTitleTextView.setText(String.format("%d年%d月", date.getYear() + 1900, date.getMonth() + 1)); // 統合
}
});
// 初回
Date date = new Date();
calendarTitleTextView.setText(String.format("%d年%d月", date.getYear() + 1900, date.getMonth() + 1)); // 統合
calendarPrevButton.setOnClickListener(v -> {
compactCalendarView.scrollLeft();
});
calendarNextButton.setOnClickListener(v -> {
compactCalendarView.scrollRight();
});
}
/**
* データを更新 (updateTaskInfoとupdateCalenderを並列実行)
*/
private void updateData() {
requireActivity().runOnUiThread(() -> {
swipeRefreshLayout.setRefreshing(true);
});
CompletableFuture.allOf(updateTaskInfo(), updateCalender()).thenRun(() -> {
requireActivity().runOnUiThread(() -> {
swipeRefreshLayout.setRefreshing(false);
});
});
}
/**
* タスク追加ダイアログを表示
*/
private void showAddTaskDialog() {
View view = getLayoutInflater().inflate(R.layout.common_task_add_dialog_layout, null);
view.setPadding(48, 24, 48, 24);
new MaterialAlertDialogBuilder(requireContext())
.setTitle("タスクを追加")
.setView(view)
.setPositiveButton("追加", (dialog, which) -> {
EditText taskNameEditText = view.findViewById(R.id.addTaskNameEditText);
EditText taskRewardEditText = view.findViewById(R.id.addTaskRewardEditText);
TaskItemModel taskItemModel = new TaskItemModel();
taskItemModel.setName(taskNameEditText.getText().toString());
taskItemModel.setReward(Integer.parseInt(taskRewardEditText.getText().toString()));
taskData.addTask(taskItemModel).thenRun(this::updateData);
})
.setNegativeButton("キャンセル", (dialog, which) -> dialog.dismiss())
.show();
}
}

View File

@ -0,0 +1,90 @@
package one.nem.kidshift.feature.common;
import static androidx.navigation.Navigation.findNavController;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.concurrent.CompletableFuture;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
import one.nem.kidshift.data.ChildData;
import one.nem.kidshift.feature.common.adapter.SelectShowChildListItemAdapter;
import one.nem.kidshift.utils.FabManager;
import one.nem.kidshift.utils.KSLogger;
import one.nem.kidshift.utils.RecyclerViewAnimUtils;
import one.nem.kidshift.utils.ToolBarManager;
import one.nem.kidshift.utils.factory.KSLoggerFactory;
@AndroidEntryPoint
public class CommonSelectChildFragment extends Fragment {
@Inject
KSLoggerFactory loggerFactory;
@Inject
ChildData childData;
@Inject
FabManager fabManager;
@Inject
ToolBarManager toolBarManager;
@Inject
RecyclerViewAnimUtils recyclerViewAnimUtils;
private KSLogger logger;
private SelectShowChildListItemAdapter adapter;
public CommonSelectChildFragment() {
// Required empty public constructor
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
logger = loggerFactory.create("CommonSelectChildFragment");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_common_select_child, container, false);
RecyclerView childListRecyclerView = view.findViewById(R.id.selectShowChildListRecyclerView);
childListRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerViewAnimUtils.setSlideUpAnimation(childListRecyclerView);
adapter = new SelectShowChildListItemAdapter();
adapter.setCallback(childId -> {
// 静的解析エラーが発生するのになぜか実行はできる
findNavController(view).navigate(CommonSelectChildFragmentDirections.actionCommonSelectChildFragmentToCommonHomeFragmentParentChild(childId));
});
CompletableFuture.runAsync(() -> childListRecyclerView.setAdapter(adapter)).thenRun(() -> childData.getChildListDirect().thenAccept(childList -> {
requireActivity().runOnUiThread(() -> {
adapter.setChildDataList(childList);
adapter.notifyItemRangeChanged(0, childList.size());
});
}).exceptionally(e -> {
logger.error("Failed to load child list");
return null;
}));
return view;
}
@Override
public void onResume() {
super.onResume();
fabManager.hide();
toolBarManager.setTitle("子供選択");
toolBarManager.setSubtitle(null);
}
}

View File

@ -0,0 +1,75 @@
package one.nem.kidshift.feature.common.adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import one.nem.kidshift.feature.common.R;
import one.nem.kidshift.model.ChildModel;
public class ChildListItemAdapter extends RecyclerView.Adapter<ChildListItemAdapter.ViewHolder> {
private List<ChildModel> childDataList;
private CompleteButtonClickedCallback callback;
public ChildListItemAdapter() {
// Empty constructor
}
public ChildListItemAdapter(List<ChildModel> childDataList) {
this.childDataList = childDataList;
}
public void setChildDataList(List<ChildModel> childDataList) {
this.childDataList = childDataList;
}
public void setCallback(CompleteButtonClickedCallback callback) {
this.callback = callback;
}
@NonNull
@Override
public ChildListItemAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_task_completion_child, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ChildListItemAdapter.ViewHolder holder, int position) {
ChildModel childData = childDataList.get(position);
holder.childName.setText(childData.getName());
holder.completedButton.setOnClickListener(v -> {
if (callback != null) {
callback.onClicked(childData.getId());
}
});
}
@Override
public int getItemCount() {
return childDataList == null ? 0 : childDataList.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView childName;
Button completedButton;
public ViewHolder(@NonNull android.view.View itemView) {
super(itemView);
childName = itemView.findViewById(R.id.childNameTextView);
completedButton = itemView.findViewById(R.id.completeButton);
}
}
public interface CompleteButtonClickedCallback {
void onClicked(String taskId);
}
}

View File

@ -0,0 +1,75 @@
package one.nem.kidshift.feature.common.adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import one.nem.kidshift.feature.common.R;
import one.nem.kidshift.model.ChildModel;
public class SelectShowChildListItemAdapter extends RecyclerView.Adapter<SelectShowChildListItemAdapter.ViewHolder> {
private List<ChildModel> childDataList;
private CompleteButtonClickedCallback callback;
public SelectShowChildListItemAdapter() {
// Empty constructor
}
public SelectShowChildListItemAdapter(List<ChildModel> childDataList) {
this.childDataList = childDataList;
}
public void setChildDataList(List<ChildModel> childDataList) {
this.childDataList = childDataList;
}
public void setCallback(CompleteButtonClickedCallback callback) {
this.callback = callback;
}
@NonNull
@Override
public SelectShowChildListItemAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_select_show_child, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull SelectShowChildListItemAdapter.ViewHolder holder, int position) {
ChildModel childData = childDataList.get(position);
holder.childName.setText(childData.getName());
holder.selectButton.setOnClickListener(v -> {
if (callback != null) {
callback.onClicked(childData.getId());
}
});
}
@Override
public int getItemCount() {
return childDataList == null ? 0 : childDataList.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView childName;
Button selectButton;
public ViewHolder(@NonNull android.view.View itemView) {
super(itemView);
childName = itemView.findViewById(R.id.childNameTextView);
selectButton = itemView.findViewById(R.id.selectButton);
}
}
public interface CompleteButtonClickedCallback {
void onClicked(String taskId);
}
}

View File

@ -0,0 +1,80 @@
package one.nem.kidshift.feature.common.adapter;
import android.annotation.SuppressLint;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import one.nem.kidshift.feature.common.R;
import one.nem.kidshift.model.tasks.TaskItemModel;
public class TaskListItemAdapter extends RecyclerView.Adapter<TaskListItemAdapter.ViewHolder> {
private List<TaskItemModel> taskItemModelList;
private CompleteButtonClickedCallback callback;
public TaskListItemAdapter() {
}
public void setTaskItemModelList(List<TaskItemModel> taskItemModelList) {
this.taskItemModelList = taskItemModelList;
}
public void setCallback(CompleteButtonClickedCallback callback) {
this.callback = callback;
}
@NonNull
@Override
public TaskListItemAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// View view = View.inflate(parent.getContext(), R.layout.list_item_common_task, null);
// Workaround? by https://stackoverflow.com/questions/30691150/match-parent-width-does-not-work-in-recyclerview
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_common_task, parent, false);
return new ViewHolder(view);
}
@SuppressLint("SetTextI18n")
@Override
public void onBindViewHolder(@NonNull TaskListItemAdapter.ViewHolder holder, int position) {
TaskItemModel taskItemModel = taskItemModelList.get(position);
holder.taskTitle.setText(taskItemModel.getName());
holder.taskContents.setText(taskItemModel.getReward() + ""); // TODO: ハードコードやめる
holder.completedButton.setOnClickListener(v -> {
if (callback != null) {
callback.onClicked(taskItemModel.getId(), taskItemModel.getName());
}
});
}
@Override
public int getItemCount() {
return taskItemModelList == null ? 0 : taskItemModelList.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView taskTitle;
TextView taskContents;
Button completedButton;
public ViewHolder(@NonNull View itemView) {
super(itemView);
taskTitle = itemView.findViewById(R.id.task_title_text_view);
taskContents = itemView.findViewById(R.id.task_contents_text_view);
completedButton = itemView.findViewById(R.id.actbutton);
}
}
public interface CompleteButtonClickedCallback {
void onClicked(String taskId, String taskName);
}
}

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="0dp">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/addTaskNameTextInputLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="タスク名"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/addTaskNameEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/addTaskRewardTextInputLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:hint="金額"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/addTaskNameTextInputLayout">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/addTaskRewardEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CommonHomeFragment"
android:id="@+id/swipeRefreshLayout">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:id="@+id/calendarContainer">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorSurface">
<ImageButton
android:id="@+id/calendarPrevButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:src="@drawable/arrow_back_24px"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tint="@color/colorOnSurface"
android:padding="8dp"/>
<TextView
android:id="@+id/calendarTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/colorOnSurface"
android:textSize="20sp"
android:text="2000年1月"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/calendarNextButton"
app:layout_constraintStart_toEndOf="@+id/calendarPrevButton"
app:layout_constraintTop_toTopOf="parent"/>
<ImageButton
android:id="@+id/calendarNextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:src="@drawable/arrow_forward_24px"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tint="@color/colorOnSurface"
android:padding="8dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<com.github.sundeepk.compactcalendarview.CompactCalendarView
android:id="@+id/calendar"
android:layout_width="fill_parent"
android:layout_height="300dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
app:compactCalendarBackgroundColor="@color/colorSurface"
app:compactCalendarCurrentDayBackgroundColor="@color/colorSurfaceVariant"
app:compactCalendarCurrentSelectedDayBackgroundColor="@color/colorSurfaceVariant"
app:compactCalendarMultiEventIndicatorColor="@color/colorOnSurface"
app:compactCalendarTextColor="@color/colorOnSurface"
app:compactCalendarTextSize="12sp"/>
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/taskListRecyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/calendarContainer" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CommonSelectChildFragment" >
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="128dp"
android:text="使用するお子様を選択"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/selectShowChildListRecyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="32dp"
android:layout_marginBottom="128dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView2" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginVertical="8dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/textContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1">
<TextView
android:id="@+id/task_title_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="お手伝い名"
android:textAppearance="@style/TextAppearance.AppCompat.Display1"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/task_contents_text_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="13dp"
android:text="円/回"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/task_title_text_view" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="0">
<Button
android:id="@+id/actbutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="完了"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginVertical="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="horizontal"
android:padding="8px"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/childNameTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="PLACEHOLDER"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
<!-- ImageButtonに変更? -->
<Button
android:id="@+id/selectButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="表示" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginVertical="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="horizontal"
android:padding="8px"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/childNameTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="PLACEHOLDER"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
<!-- ImageButtonに変更? -->
<Button
android:id="@+id/completeButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="完了" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/toggle_calendar"
android:icon="@drawable/calendar_month_24px"
app:showAsAction="ifRoom"
android:title="カレンダー表示切り替え" />
</menu>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation 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/feature_common_child_child_navigation"
app:startDestination="@id/commonHomeFragmentChildChild">
<fragment
android:id="@+id/commonHomeFragmentChildChild"
android:name="one.nem.kidshift.feature.common.CommonHomeFragment"
android:label="fragment_common_home"
tools:layout="@layout/fragment_common_home" >
<argument
android:name="childId"
app:argType="string" />
<argument
android:name="isChildMode"
app:argType="boolean"
android:defaultValue="true" />
</fragment>
</navigation>

Some files were not shown because too many files have changed in this diff Show More