Compare commits

...

No commits in common. "main" and "master" have entirely different histories.
main ... master

336 changed files with 39 additions and 12917 deletions

48
.gitignore vendored
View File

@ -1,15 +1,35 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
# ---> Android
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Log/OS Files
*.log
# Android Studio generated files and folders
captures/
.externalNativeBuild/
.cxx/
*.apk
output.json
# IntelliJ
*.iml
.idea/
misc.xml
deploymentTargetDropDown.xml
render.experimental.xml
# Keystore files
*.jks
*.keystore
# Google Services (e.g. APIs or Firebase)
google-services.json
# Android Profiling
*.hprof

3
.idea/.gitignore vendored
View File

@ -1,3 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
</component>
</project>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<value>
<entry key="app">
<State />
</entry>
</value>
</component>
</project>

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
<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" />
</set>
</option>
<option name="resolveExternalAnnotations" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>

View File

@ -1,9 +0,0 @@
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

20
LICENSE
View File

@ -1,21 +1,9 @@
MIT License
Copyright (c) 2024 グループワークチーム「シフトメイト」メンバー
Copyright (c) 2024 kidshift
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:
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 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.
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,38 +1,2 @@
# WIP
# KidShift
~~## メモ
- リリース前(=提出前)には`DEBUG_ONLY`で検索してチェック(念のため)
## リリース前チェック
- 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機種で使用されるテーマの適用が中途半端なので完全に適用するように

1
app/.gitignore vendored
View File

@ -1 +0,0 @@
/build

View File

@ -1,69 +0,0 @@
plugins {
alias(libs.plugins.androidApplication)
id 'com.google.dagger.hilt.android'
}
android {
namespace 'one.nem.kidshift'
compileSdk 34
defaultConfig {
applicationId "one.nem.kidshift"
minSdk 28
targetSdk 34
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
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.activity
implementation libs.constraintlayout
implementation project(':model')
testImplementation libs.junit
androidTestImplementation libs.ext.junit
androidTestImplementation libs.espresso.core
// Module
implementation project(':feature:debug')
implementation project(':feature:parent')
implementation project(':feature:child')
implementation project(':feature:setting')
implementation project(':feature:common')
implementation project(':feature:wallet')
implementation project(':data')
implementation project(':shared')
implementation project(':utils')
// Hilt (DI)
implementation libs.com.google.dagger.hilt.android
annotationProcessor libs.com.google.dagger.hilt.compiler
// Navigation
implementation libs.navigation.fragment
implementation libs.navigation.ui
implementation libs.navigation.dynamic.features.fragment
// Retrofit
implementation libs.retrofit
implementation libs.converter.gson
}

View File

@ -1,21 +0,0 @@
# 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

@ -1,26 +0,0 @@
package one.nem.kidshift;
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", appContext.getPackageName());
}
}

View File

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".KidShiftApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
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" />
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

View File

@ -1,154 +0,0 @@
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,45 +0,0 @@
package one.nem.kidshift;
import android.app.Application;
import com.google.android.material.color.DynamicColors;
import javax.inject.Inject;
import dagger.hilt.android.HiltAndroidApp;
import one.nem.kidshift.utils.FeatureFlag;
import one.nem.kidshift.utils.KSLogger;
import one.nem.kidshift.utils.factory.KSLoggerFactory;
@HiltAndroidApp
public class KidShiftApplication extends Application {
@Inject
FeatureFlag featureFlag;
@Inject
KSLoggerFactory loggerFactory;
private KSLogger logger;
@Override
public void onCreate() {
super.onCreate();
this.logger = loggerFactory.create("KidShiftApplication");
logger.info("super.onCreate() completed");
if(featureFlag.isEnabled("dynamicColorEnable")) {
if (DynamicColors.isDynamicColorAvailable()) {
DynamicColors.applyToActivitiesIfAvailable(this);
logger.info("DynamicColors is available and applied to activities.");
} else {
logger.info("DynamicColors is not available.");
}
} else {
logger.info("DynamicColors feature is disabled by feature flag.");
}
}
}

View File

@ -1,98 +0,0 @@
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.io.IOException;
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;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
@AndroidEntryPoint
public class LoginActivity extends AppCompatActivity {
@Inject
KSLoggerFactory loggerFactory;
private KSLogger logger;
@Inject
UserSettings userSettings;
@Inject
KidShiftApiService kidShiftApiService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_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("LoginActivity");
EditText emailEditText = findViewById(R.id.parentLoginEmailEditText);
EditText passwordEditText = findViewById(R.id.parentLoginPasswordEditText);
findViewById(R.id.parentLoginButton).setOnClickListener(v -> {
String email = emailEditText.getText().toString(); // TODO: メールアドレスのバリデーション
String password = passwordEditText.getText().toString();
CompletableFuture.runAsync(() -> {
Call<ParentAuthResponse> call = kidShiftApiService.parentLogin(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.toRegisterButton).setOnClickListener(v -> {
startActivity(new Intent(this, RegisterActivity.class));
});
findViewById(R.id.toChildLoginButton).setOnClickListener(v -> {
startActivity(new Intent(this, ChildLoginActivity.class));
});
}
}

View File

@ -1,293 +0,0 @@
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 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) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, 0); // 0にしないとボトムナビゲーションが埋まるため
return insets;
});
logger = ksLoggerFactory.create("MainActivity");
// 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);
// Init navigation
try {
FragmentManager supportFragmentManager = getSupportFragmentManager();
NavHostFragment navHostFragment = (NavHostFragment) supportFragmentManager.findFragmentById(R.id.fragmentContainerView);
assert navHostFragment != null;
NavController navController = navHostFragment.getNavController();
NavigationUI.setupWithNavController(bottomNavigationView, navController);
} catch (Exception e) {
e.printStackTrace();
}
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);
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);
}
/**
* 起動時にバックグラウンドで行う各種更新処理とか
*/
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

@ -1,97 +0,0 @@
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,170 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@ -1,15 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
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="@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

@ -1,160 +0,0 @@
<?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

@ -1,122 +0,0 @@
<?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=".LoginActivity">
<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/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: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/parentLoginPasswordEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<Button
android:id="@+id/parentLoginButton"
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/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

@ -1,76 +0,0 @@
<?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=".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"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@+id/bottom_nav"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar"
app:navGraph="@navigation/main_nav" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav"
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: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

@ -1,122 +0,0 @@
<?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

@ -1,16 +0,0 @@
<?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

@ -1,52 +0,0 @@
<?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,31 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<!-- TODO: アイコン再検討 -->
<!-- 保護者モード / 保護者目線 -->
<item
android:id="@+id/feature_common_parent_parent_navigation"
android:icon="@drawable/home_24px"
android:title="ホーム" />
<!-- 保護者モード / 子供目線 -->
<item
android:id="@+id/feature_common_parent_child_navigation"
android:icon="@drawable/child_care_24px"
android:title="こども" />
<!-- 子供モード / 子供目線 -->
<item
android:id="@+id/feature_common_child_child_navigation"
android:icon="@drawable/home_24px"
android:title="ホーム" />
<item
android:id="@+id/feature_wallet_parent_navigation"
android:icon="@drawable/wallet_24px"
android:title="ウォレット" />
<item
android:id="@+id/feature_wallet_child_navigation"
android:icon="@drawable/wallet_24px"
android:title="ウォレット" />
</menu>

View File

@ -1,23 +0,0 @@
<?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

@ -1,5 +0,0 @@
<?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

@ -1,5 +0,0 @@
<?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

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 936 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 716 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

View File

@ -1,14 +0,0 @@
<?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"
android:id="@+id/main_nav"
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_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

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

View File

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

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample backup rules file; uncomment and customize as necessary.
See https://developer.android.com/guide/topics/data/autobackup
for details.
Note: This file is ignored for devices older that API 31
See https://developer.android.com/about/versions/12/backup-restore
-->
<full-backup-content>
<!--
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="device.xml"/>
-->
</full-backup-content>

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample data extraction rules file; uncomment and customize as necessary.
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
for details.
-->
<data-extraction-rules>
<cloud-backup>
<!-- TODO: Use <include> and <exclude> to control what is backed up.
<include .../>
<exclude .../>
-->
</cloud-backup>
<!--
<device-transfer>
<include .../>
<exclude .../>
</device-transfer>
-->
</data-extraction-rules>

View File

@ -1,17 +0,0 @@
package one.nem.kidshift;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}

View File

@ -1,12 +0,0 @@
// 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
alias(libs.plugins.androidLibrary) apply false
}

1
data/.gitignore vendored
View File

@ -1 +0,0 @@
/build

View File

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

View File

@ -1,54 +0,0 @@
plugins {
alias(libs.plugins.androidLibrary)
id 'com.google.dagger.hilt.android'
}
android {
namespace 'one.nem.kidshift.data'
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
testImplementation libs.junit
androidTestImplementation libs.ext.junit
androidTestImplementation libs.espresso.core
// Hilt (DI)
implementation libs.com.google.dagger.hilt.android
annotationProcessor libs.com.google.dagger.hilt.compiler
// Java Faker
implementation libs.javafaker
implementation project(':model')
implementation project(':utils')
// Retrofit
implementation libs.retrofit
implementation libs.converter.gson
// Room
implementation libs.androidx.room.runtime
annotationProcessor libs.androidx.room.compiler
}

View File

@ -1,21 +0,0 @@
# 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

@ -1,26 +0,0 @@
package one.nem.kidshift.data;
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.data.test", appContext.getPackageName());
}
}

View File

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

View File

@ -1,54 +0,0 @@
package one.nem.kidshift.data;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import one.nem.kidshift.model.ChildModel;
import one.nem.kidshift.model.callback.ChildModelCallback;
public interface ChildData {
/**
* 子ユーザー情報取得
* @param childId 子ID
* @return ChildModel 子ユーザー情報
*/
CompletableFuture<ChildModel> getChild(String childId);
/**
* 子ユーザー一覧取得
* @return List<ChildModel> 子ユーザー一覧
*/
CompletableFuture<List<ChildModel>> getChildList(ChildModelCallback callback);
/**
* 子ユーザー一覧をサーバーから直接取得(キャッシュ無視)
* @return List<ChildModel> 子ユーザー一覧
*/
CompletableFuture<List<ChildModel>> getChildListDirect();
/**
* 子ユーザー情報更新
* @param child 子ユーザー情報
*/
CompletableFuture<ChildModel> updateChild(ChildModel child);
/**
* 子ユーザー追加
* @param child 子ユーザー情報
*/
CompletableFuture<ChildModel> addChild(ChildModel child);
/**
* 子ユーザー削除
* @param childId 子ID
*/
CompletableFuture<Void> removeChild(String childId);
/**
* 子ユーザーログインコード発行
* @param childId 子ID
* @return int ログインコード
*/
CompletableFuture<Integer> issueLoginCode(String childId);
}

View File

@ -1,34 +0,0 @@
package one.nem.kidshift.data;
import java.util.List;
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 {
/**
* タスク, 子供情報を同期してTaskItemModelを取得
*/
CompletableFuture<List<TaskItemModel>> syncTasks();
CompletableFuture<List<ChildModel>> syncChildList();
/**
* 親ユーザー情報同期
*/
CompletableFuture<ParentModel> syncParent();
/**
* 履歴情報同期
*/
CompletableFuture<List<HistoryModel>> syncHistory(String childId);
}

View File

@ -1,35 +0,0 @@
package one.nem.kidshift.data;
import java.util.concurrent.CompletableFuture;
import one.nem.kidshift.model.ParentModel;
import one.nem.kidshift.model.callback.ParentModelCallback;
public interface ParentData {
/**
* 親ユーザー情報取得
* @return 親ユーザー情報
*/
CompletableFuture<ParentModel> getParent(ParentModelCallback callback);
/**
* 親ユーザー情報取得
* @return 親ユーザー情報
*/
CompletableFuture<ParentModel> getParentDirect();
/**
* 親ユーザー情報取得
* @return 親ユーザー情報
*/
CompletableFuture<ParentModel> getParentCache();
/**
* 親ユーザー情報更新
* @param parent 親ユーザー情報
*/
CompletableFuture<Void> updateParent(ParentModel parent);
}

View File

@ -1,22 +0,0 @@
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(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

@ -1,59 +0,0 @@
package one.nem.kidshift.data;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import one.nem.kidshift.model.callback.TaskItemModelCallback;
import one.nem.kidshift.model.tasks.TaskItemModel;
public interface TaskData {
// 親側
/**
* 存在する全てのタスクを取得する
* @return CompletableFuture<List<TaskItemModel>> タスクリスト
*/
CompletableFuture<List<TaskItemModel>> getTasks(TaskItemModelCallback callback);
/**
* アタッチされている全てのタスクを取得する
* @param childId 子ID
* @return CompletableFuture<List<TaskItemModel>> タスクリスト
*/
CompletableFuture<List<TaskItemModel>> getTasks(String childId, TaskItemModelCallback callback);
/**
* タスクを追加する
* @param task タスク
*/
CompletableFuture<TaskItemModel> addTask(TaskItemModel task);
/**
* タスクを削除する
* @param taskId タスクID
*/
CompletableFuture<Void> removeTask(String taskId);
/**
* タスクを更新する
* @param task タスク
*/
CompletableFuture<Void> updateTask(TaskItemModel task);
// 子側
/**
* タスクの詳細を取得する
* @param taskId タスクID
* @return TaskItemModel タスク
*/
CompletableFuture<TaskItemModel> getTask(String taskId);
/**
* タスクの完了を記録する
* @param taskId タスクID
* @param childId 子ID
*/
CompletableFuture<Void> recordTaskCompletion(String taskId, String childId);
}

View File

@ -1,62 +0,0 @@
package one.nem.kidshift.data;
import one.nem.kidshift.model.ParentModel;
public interface UserSettings {
/**
* API設定のインスタンスを取得
* @return ApiSetting API設定
*/
ApiSetting getApiSetting();
/**
* タスク設定のインスタンスを取得
* @return TaskSetting タスク設定
*/
TaskSetting getTaskSetting();
/**
* アプリ共通設定のインスタンスを取得
* @return AppCommonSetting アプリ共通設定
*/
AppCommonSetting getAppCommonSetting();
/**
* キャッシュのインスタンスを取得
* @return SharedPrefCache キャッシュ
*/
SharedPrefCache getCache();
interface AppCommonSetting {
boolean isLoggedIn();
void setLoggedIn(boolean loggedIn);
String getAccessToken();
void setAccessToken(String token);
boolean isChildMode();
void setChildMode(boolean childMode);
String getChildId();
void setChildId(String childId);
}
interface SharedPrefCache {
ParentModel getParent();
void setParent(ParentModel parent);
}
interface ApiSetting {
String getApiBaseUrl();
void setApiBaseUrl(String url);
}
interface TaskSetting {
int getDefaultIconColor();
void setDefaultIconColor(int color);
String getDefaultIconEmoji();
void setDefaultIconEmoji(String emoji);
}
}

View File

@ -1,183 +0,0 @@
package one.nem.kidshift.data.impl;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
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.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;
import one.nem.kidshift.model.callback.ChildModelCallback;
import one.nem.kidshift.utils.KSLogger;
import one.nem.kidshift.utils.factory.KSLoggerFactory;
import retrofit2.Call;
import retrofit2.Response;
public class ChildDataImpl implements ChildData {
private final KidShiftApiService kidShiftApiService;
private final KSActions ksActions;
private final CacheWrapper cacheWrapper;
private final KSLogger logger;
@Inject
public ChildDataImpl(KidShiftApiService kidShiftApiService, KSActions ksActions, CacheWrapper cacheWrapper, KSLoggerFactory loggerFactory) {
this.kidShiftApiService = kidShiftApiService;
this.ksActions = ksActions;
this.cacheWrapper = cacheWrapper;
this.logger = loggerFactory.create("ChildDataImpl");
}
@Override
public CompletableFuture<ChildModel> getChild(String childId) {
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
public CompletableFuture<List<ChildModel>> getChildList(ChildModelCallback callback) { // TODO: リファクタリング
return CompletableFuture.supplyAsync(() -> {
logger.debug("子供リスト取得開始");
AtomicReference<List<ChildModel>> childListTmp = new AtomicReference<>();
Thread thread = new Thread(() -> ksActions.syncChildList().thenAccept(childList -> {
if (childListTmp.get() == null || childListTmp.get().isEmpty()) {
logger.debug("子供リスト取得完了: キャッシュよりはやく取得完了 or キャッシュ無し");
if (childList == null || childList.isEmpty()) {
callback.onUnchanged();
} else {
callback.onUpdated(childList);
}
} else {
boolean isChanged =
childList.size() != childListTmp.get().size() ||
childList.stream().anyMatch(child -> childListTmp.get().stream().noneMatch(childTmp -> child.getId().equals(childTmp.getId())));
if (isChanged) {
logger.debug("子供リスト取得完了: キャッシュと比較して変更あり");
callback.onUpdated(childList);
} else {
logger.debug("子供リスト取得完了: キャッシュと比較して変更なし");
callback.onUnchanged();
}
}
}).exceptionally(e -> {
logger.error("子供リスト取得失敗: " + e.getMessage());
callback.onFailed(e.getMessage());
return null;
}));
thread.start();
return cacheWrapper.getChildList().thenApply(childList -> {
if (childList == null || childList.isEmpty()) {
try {
logger.debug("キャッシュ無: 子供リスト取得スレッド待機");
thread.join();
return cacheWrapper.getChildList().join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} else {
logger.debug("キャッシュ有 (子供数: " + childList.size() + ")");
childListTmp.set(childList);
return childList;
}
}).join();
});
}
@Override
public CompletableFuture<List<ChildModel>> getChildListDirect() {
return ksActions.syncChildList();
}
@Override
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 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
public CompletableFuture<Integer> issueLoginCode(String childId) {
return CompletableFuture.supplyAsync(() -> {
Call<ChildLoginCodeResponse> call = kidShiftApiService.issueLoginCode(childId);
try {
Response<ChildLoginCodeResponse> response = call.execute();
if (response.isSuccessful()) {
assert response.body() != null;
return response.body().getCode();
} else {
throw new RuntimeException("HTTP Status: " + response.code());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
}

View File

@ -1,185 +0,0 @@
package one.nem.kidshift.data.impl;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import javax.inject.Inject;
import one.nem.kidshift.data.KSActions;
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;
import one.nem.kidshift.utils.factory.KSLoggerFactory;
import retrofit2.Call;
import retrofit2.Response;
public class KSActionsImpl implements KSActions {
private final UserSettings userSettings;
private final KidShiftApiService kidShiftApiService;
private final KSLogger logger;
private final CacheWrapper cacheWrapper;
@Inject
public KSActionsImpl(UserSettings userSettings, KidShiftApiService kidShiftApiService, KSLoggerFactory ksLoggerFactory, CacheWrapper cacheWrapper) {
this.userSettings = userSettings;
this.kidShiftApiService = kidShiftApiService;
this.logger = ksLoggerFactory.create("KSActionsImpl");
this.cacheWrapper = cacheWrapper;
}
@Override
public CompletableFuture<List<TaskItemModel>> syncTasks() {
return doSyncTaskChild().thenApply(result -> result.taskList);
}
@Override
public CompletableFuture<List<ChildModel>> syncChildList() {
return doSyncTaskChild().thenApply(result -> result.childList);
}
private static class TaskSyncResult {
public List<TaskItemModel> taskList;
public List<ChildModel> childList;
}
/**
* タスクと子供リストを同期する(ラッパー)
* @return CompletableFuture<TaskSyncResult> タスクと子供リスト
*/
private CompletableFuture<TaskSyncResult> doSyncTaskChild() {
return fetchChildListAsync().thenCombine(fetchTaskListAsync(), (childListResponse, taskListResponse) -> {
Thread cacheThread = new Thread(() -> {
logger.debug("キャッシュ更新スレッド開始(スレッドID: " + Thread.currentThread().getId() + ")");
cacheWrapper.updateChildTaskCache(ChildModelConverter.childListResponseToChildModelList(childListResponse),
TaskModelConverter.taskListResponseToTaskItemModelList(taskListResponse)).join();
logger.info("キャッシュ更新完了");
});
cacheThread.start();
try {
// Workaround: DBオペレーションを待たずに結果を先に返したいが
// (現状(呼び出し元が)戻り値をそのままCallbackに渡しているために)
// キャッシュが空の場合のキャッシュ再取得のタイミングが調整できなくなる.
cacheThread.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return new TaskSyncResult() {
{
taskList = TaskModelConverter.taskListResponseToTaskItemModelList(taskListResponse);
childList = ChildModelConverter.childListResponseToChildModelList(childListResponse);
}
};
});
}
/**
* バックエンドからタスクリストをFetchする
* @return CompletableFuture<TaskListResponse> バックエンドから取得したタスクリスト
*/
private CompletableFuture<TaskListResponse> fetchTaskListAsync() {
return CompletableFuture.supplyAsync(() -> {
Call<TaskListResponse> call = kidShiftApiService.getTasks();
try {
Response<TaskListResponse> response = call.execute();
if (!response.isSuccessful()) {
logger.error("Error fetching task list: " + response.errorBody().string());
throw new RuntimeException("Error fetching task list: " + response.errorBody().string());
}
TaskListResponse responseBody = response.body();
return responseBody;
} catch (Exception e) {
logger.error("Error fetching task list");
throw new RuntimeException(e);
}
});
}
/**
* バックエンドから子供リストをFetchする
* @return CompletableFuture<ChildListResponse> バックエンドから取得した子供リスト
*/
private CompletableFuture<ChildListResponse> fetchChildListAsync() {
return CompletableFuture.supplyAsync(() -> {
Call<ChildListResponse> call = kidShiftApiService.getChildList();
try {
Response<ChildListResponse> response = call.execute();
if (!response.isSuccessful()) {
logger.error("Error fetching child list: " + response.errorBody().string());
throw new RuntimeException("Error fetching child list: " + response.errorBody().string());
}
ChildListResponse responseBody = response.body();
return responseBody;
} catch (Exception e) {
logger.error("Error fetching child list");
throw new RuntimeException(e);
}
});
}
@Override
public CompletableFuture<ParentModel> syncParent() {
logger.info("syncParent called and started");
return CompletableFuture.supplyAsync(() -> {
logger.info("fetching...");
Call<ParentInfoResponse> call = kidShiftApiService.getParentInfo();
try {
Response<ParentInfoResponse> response = call.execute();
if (!response.isSuccessful()) {
logger.error("Error fetching parent info: " + response.errorBody().string());
throw new RuntimeException("Error fetching parent info: " + response.errorBody().string());
}
ParentInfoResponse responseBody = response.body();
ParentModel parent = ParentModelConverter.parentInfoResponseToParentModel(responseBody);
// Save to cache
userSettings.getCache().setParent(parent);
logger.info("Parent saved to cache");
return parent;
} catch (Exception e) {
logger.error("Error fetching parent info");
throw new RuntimeException(e);
}
});
}
@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

@ -1,77 +0,0 @@
package one.nem.kidshift.data.impl;
import java.util.concurrent.CompletableFuture;
import javax.inject.Inject;
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) {
this.kidShiftApiService = kidShiftApiService;
this.userSettings = userSettings;
this.logger = ksLoggerFactory.create("ParentDataImpl");
this.ksActions = ksActions;
}
// 一旦キャッシュを返して, その後非同期でAPIから取得更新があればコールバックで通知
@Override
public CompletableFuture<ParentModel> getParent(ParentModelCallback callback) {
// Start thread to fetch parent info
new Thread(() -> {
logger.info("Fetching parent info...");
ParentModel refreshedParent = ksActions.syncParent().join();
if (refreshedParent == null) {
callback.onFailed("Failed to fetch parent info");
} else {
// Workaround, TODO: Compare with existing parent
callback.onUpdated(refreshedParent);
}
}).start();
return CompletableFuture.supplyAsync(() -> userSettings.getCache().getParent());
}
@Override
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,85 +0,0 @@
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

@ -1,174 +0,0 @@
package one.nem.kidshift.data.impl;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
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;
import one.nem.kidshift.utils.KSLogger;
import one.nem.kidshift.utils.factory.KSLoggerFactory;
import retrofit2.Call;
import retrofit2.Response;
public class TaskDataImpl implements TaskData {
private final KSActions ksActions;
private final KidShiftApiService kidShiftApiService;
private final CacheWrapper cacheWrapper;
private final KSLogger logger;
@Inject
public TaskDataImpl(KSActions ksActions, KidShiftApiService kidShiftApiService, CacheWrapper cacheWrapper, KSLoggerFactory loggerFactory) {
this.ksActions = ksActions;
this.kidShiftApiService = kidShiftApiService;
this.cacheWrapper = cacheWrapper;
this.logger = loggerFactory.create("TaskDataImpl");
}
@Override
public CompletableFuture<List<TaskItemModel>> getTasks(TaskItemModelCallback callback) {
return CompletableFuture.supplyAsync(() -> {
logger.debug("タスク取得開始");
AtomicReference<List<TaskItemModel>> taskListTmp = new AtomicReference<>();
Thread thread = new Thread(() -> ksActions.syncTasks().thenAccept(taskList -> {
if (taskListTmp.get() == null || taskListTmp.get().isEmpty()) {
logger.debug("タスク取得完了: キャッシュよりはやく取得完了 or キャッシュ無し");
if (taskList == null || taskList.isEmpty()) {
callback.onUnchanged();
} else {
callback.onUpdated(taskList);
}
} else {
// キャッシュと比較して変更の有無を確認
boolean isChanged =
taskList.size() != taskListTmp.get().size() ||
taskList.stream().anyMatch(task -> taskListTmp.get().stream().noneMatch(taskTmp -> task.getId().equals(taskTmp.getId())));
if (isChanged) {
logger.debug("タスク取得完了: キャッシュと比較して変更あり");
callback.onUpdated(taskList);
} else {
logger.debug("タスク取得完了: キャッシュと比較して変更なし");
callback.onUnchanged();
}
}
}).exceptionally(e -> {
logger.error("タスク取得失敗: " + e.getMessage());
callback.onFailed(e.getMessage());
return null;
}));
thread.start();
return cacheWrapper.getTaskList().thenApply(taskList -> {
if (taskList == null || taskList.isEmpty()) {
try {
logger.debug("キャッシュ無: タスク取得スレッド待機");
thread.join();
return cacheWrapper.getTaskList().join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} else {
logger.debug("キャッシュ有 (タスク数: " + taskList.size() + ")");
taskListTmp.set(taskList);
return taskList;
}
}).join();
});
}
@Override
public CompletableFuture<List<TaskItemModel>> getTasks(String childId, TaskItemModelCallback callback) {
return null;
}
@Override
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 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 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
public CompletableFuture<TaskItemModel> getTask(String taskId) {
return CompletableFuture.completedFuture(null);
}
@Override
public CompletableFuture<Void> recordTaskCompletion(String taskId, String childId) {
return CompletableFuture.supplyAsync(() -> {
Call<Void> call = kidShiftApiService.completeTask(taskId, childId);
try {
Response<Void> response = call.execute();
if (response.isSuccessful()) {
logger.info("タスク完了処理成功(taskId: " + taskId + ", childId: " + childId + ")");
return null;
} else {
logger.error("タスク完了処理失敗: HTTP Status: " + response.code());
throw new RuntimeException("HTTP Status: " + response.code());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
}

View File

@ -1,228 +0,0 @@
package one.nem.kidshift.data.impl;
import javax.inject.Inject;
import one.nem.kidshift.data.UserSettings;
import one.nem.kidshift.model.ParentModel;
import one.nem.kidshift.utils.SharedPrefUtils;
import one.nem.kidshift.utils.factory.SharedPrefUtilsFactory;
public class UserSettingsImpl implements UserSettings {
SharedPrefUtilsFactory sharedPrefUtilsFactory;
@Inject
public UserSettingsImpl(SharedPrefUtilsFactory sharedPrefUtilsFactory) {
this.sharedPrefUtilsFactory = sharedPrefUtilsFactory;
}
@Override
public ApiSetting getApiSetting() {
return new ApiSettingImpl();
}
@Override
public TaskSetting getTaskSetting() {
return new TaskSettingImpl();
}
@Override
public AppCommonSetting getAppCommonSetting() {
return new AppCommonSettingImpl();
}
@Override
public SharedPrefCache getCache() {
return new SharedPrefCacheImpl();
}
public class SharedPrefCacheImpl implements UserSettings.SharedPrefCache {
transient
SharedPrefUtils sharedPrefUtils;
ParentModel parent;
SharedPrefCacheImpl() {
sharedPrefUtils = sharedPrefUtilsFactory.create("user_settings");
SharedPrefCacheImpl sharedPrefCache = sharedPrefUtils.getObject("shared_pref_cache", SharedPrefCacheImpl.class);
if (sharedPrefCache != null) {
parent = sharedPrefCache.getParent();
} else {
parent = null;
}
}
private void save() {
sharedPrefUtils.saveObject("shared_pref_cache", this);
}
@Override
public ParentModel getParent() {
return parent;
}
@Override
public void setParent(ParentModel parent) {
this.parent = parent;
save();
}
}
public class AppCommonSettingImpl implements UserSettings.AppCommonSetting {
transient
SharedPrefUtils sharedPrefUtils;
boolean loggedIn;
String accessToken;
boolean childMode;
String childId;
AppCommonSettingImpl() {
sharedPrefUtils = sharedPrefUtilsFactory.create("user_settings");
AppCommonSettingImpl appCommonSetting = sharedPrefUtils.getObject("app_common_setting", AppCommonSettingImpl.class);
if (appCommonSetting != null) {
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 = "";
}
}
private void save() {
sharedPrefUtils.saveObject("app_common_setting", this);
}
@Override
public boolean isLoggedIn() {
return loggedIn;
}
@Override
public void setLoggedIn(boolean loggedIn) {
this.loggedIn = loggedIn;
save();
}
@Override
public String getAccessToken() {
return accessToken;
}
@Override
public void setAccessToken(String token) {
accessToken = token;
save();
}
@Override
public boolean isChildMode() {
return childMode;
}
@Override
public void setChildMode(boolean childMode) {
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 {
transient
SharedPrefUtils sharedPrefUtils;
String apiBaseUrl;
ApiSettingImpl() {
sharedPrefUtils = sharedPrefUtilsFactory.create("user_settings");
ApiSettingImpl apiSetting = sharedPrefUtils.getObject("api_setting", ApiSettingImpl.class);
// TODO: リフレクションつかって一括でやる(プロパティ数があまりにも増えるなら?), 三項演算子やめる?, デフォルト値の設定方法を改善する
if (apiSetting != null) {
apiBaseUrl = apiSetting.apiBaseUrl.isEmpty() ? "https://kidshift-beta.nem.one/" : apiSetting.apiBaseUrl;
} else {
apiBaseUrl = "https://kidshift-beta.nem.one/";
}
}
private void save() {
sharedPrefUtils.saveObject("api_setting", this);
}
@Override
public String getApiBaseUrl() {
return apiBaseUrl;
}
@Override
public void setApiBaseUrl(String url) {
apiBaseUrl = url;
save();
}
}
public class TaskSettingImpl implements UserSettings.TaskSetting {
transient
SharedPrefUtils sharedPrefUtils;
int defaultIconColor;
String defaultIconEmoji;
TaskSettingImpl() {
sharedPrefUtils = sharedPrefUtilsFactory.create("user_settings");
TaskSettingImpl taskSetting = sharedPrefUtils.getObject("task_setting", TaskSettingImpl.class);
if (taskSetting != null) {
defaultIconColor = taskSetting.getDefaultIconColor() == 0 ? 0 : taskSetting.getDefaultIconColor();
defaultIconEmoji = taskSetting.getDefaultIconEmoji().isEmpty() ? "" : taskSetting.getDefaultIconEmoji();
} else {
defaultIconColor = 0;
defaultIconEmoji = "";
}
}
private void save() {
sharedPrefUtils.saveObject("task_setting", this);
}
@Override
public int getDefaultIconColor() {
return defaultIconColor;
}
@Override
public void setDefaultIconColor(int color) {
defaultIconColor = color;
save();
}
@Override
public String getDefaultIconEmoji() {
return defaultIconEmoji;
}
@Override
public void setDefaultIconEmoji(String emoji) {
defaultIconEmoji = emoji;
save();
}
}
}

View File

@ -1,16 +0,0 @@
package one.nem.kidshift.data.modules;
import dagger.Binds;
import dagger.Module;
import dagger.hilt.InstallIn;
import dagger.hilt.components.SingletonComponent;
import one.nem.kidshift.data.ChildData;
import one.nem.kidshift.data.impl.ChildDataImpl;
@Module
@InstallIn(SingletonComponent.class)
public abstract class ChildDataModule {
@Binds
abstract ChildData provideChildData(ChildDataImpl childDataImpl);
}

View File

@ -1,16 +0,0 @@
package one.nem.kidshift.data.modules;
import dagger.Binds;
import dagger.Module;
import dagger.hilt.InstallIn;
import dagger.hilt.components.SingletonComponent;
import one.nem.kidshift.data.KSActions;
import one.nem.kidshift.data.impl.KSActionsImpl;
@Module
@InstallIn(SingletonComponent.class)
public abstract class KSActionsModule {
@Binds
public abstract KSActions bindKSActions(KSActionsImpl impl);
}

View File

@ -1,17 +0,0 @@
package one.nem.kidshift.data.modules;
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(SingletonComponent.class)
public abstract class ParentDataModule {
@Binds
public abstract ParentData bindParentData(ParentDataImpl parentDataImpl);
}

View File

@ -1,16 +0,0 @@
package one.nem.kidshift.data.modules;
import dagger.Binds;
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.RewardDataImpl;
@Module
@InstallIn(FragmentComponent.class)
public abstract class RewardDataModule {
@Binds
public abstract RewardData bindRewardData(RewardDataImpl rewardDataImpl);
}

View File

@ -1,16 +0,0 @@
package one.nem.kidshift.data.modules;
import dagger.Binds;
import dagger.Module;
import dagger.hilt.InstallIn;
import dagger.hilt.android.components.FragmentComponent;
import one.nem.kidshift.data.TaskData;
import one.nem.kidshift.data.impl.TaskDataImpl;
@Module
@InstallIn(FragmentComponent.class)
public abstract class TaskDataModule {
@Binds
public abstract TaskData bindTaskData(TaskDataImpl taskDataImpl);
}

View File

@ -1,15 +0,0 @@
package one.nem.kidshift.data.modules;
import dagger.Binds;
import dagger.Module;
import dagger.hilt.InstallIn;
import dagger.hilt.components.SingletonComponent;
import one.nem.kidshift.data.UserSettings;
import one.nem.kidshift.data.impl.UserSettingsImpl;
@Module
@InstallIn(SingletonComponent.class)
public abstract class UserSettingsModule {
@Binds
public abstract UserSettings bindUserSettings(UserSettingsImpl userSettingsImpl);
}

View File

@ -1,193 +0,0 @@
package one.nem.kidshift.data.retrofit;
import one.nem.kidshift.data.retrofit.interceptor.AuthorizationInterceptor;
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.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;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Path;
import retrofit2.http.Query;
public interface KidShiftApiService {
// Parent APIs
// Auth
/**
* 保護者ログイン処理
* @param request ParentLoginRequest
* @return ParentLoginResponse
*/
@POST("/parent/auth/login")
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);
/**
* 保護者アカウント情報取得処理
* @return ParentInfoResponse
*/
@GET("/parent/account")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<ParentInfoResponse> getParentInfo();
// Task APIs
/**
* タスク一覧取得
* @return TaskListResponse
*/
@GET("/task")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<TaskListResponse> getTasks();
/**
* タスク追加
* @param request TaskAddRequest
* @return TaskResponse
*/
@POST("/task")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<TaskResponse> addTask(@Body TaskAddRequest request);
/**
* タスク更新
* @param request TaskAddRequest
* @param id タスクID
* @return TaskResponse
*/
@PUT("/task/{id}")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<TaskResponse> updateTask(@Body TaskAddRequest request, @Path("id") String id);
/**
* タスク削除
* @param id タスクID
* @return Void
*/
@DELETE("/task/{id}")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<Void> removeTask(@Path("id") String id); // TODO-rca: OK responseをパース
/**
* タスク詳細取得
* @param id タスクID
* @return TaskResponse
*/
@GET("/task/{id}")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<TaskResponse> getTask(@Path("id") String id);
/**
* タスク完了処理
* @param id タスクID
* @return Void
*/
@POST("/task/{id}/complete")
@Headers(AuthorizationInterceptor.HEADER_PLACEHOLDER)
Call<Void> completeTask(@Path("id") String id, @Query("childId") String childId);
// Child APIs
/**
* 子供一覧取得
* @return ChildListResponse
*/
@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("/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("/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

@ -1,61 +0,0 @@
package one.nem.kidshift.data.retrofit;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import dagger.hilt.InstallIn;
import dagger.hilt.components.SingletonComponent;
import okhttp3.OkHttpClient;
import one.nem.kidshift.data.UserSettings;
import one.nem.kidshift.data.retrofit.interceptor.AuthorizationInterceptor;
import one.nem.kidshift.utils.KSLogger;
import one.nem.kidshift.utils.factory.KSLoggerFactory;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
@Module
@InstallIn(SingletonComponent.class)
public class KidShiftApiServiceModule {
@Inject
KSLoggerFactory ksLoggerFactory;
@Provides
@Singleton
public AuthorizationInterceptor provideAuthorizationInterceptor(UserSettings userSettings, KSLoggerFactory ksLoggerFactory) {
return new AuthorizationInterceptor(userSettings, ksLoggerFactory);
}
// Gson
@Provides
@Singleton
public Gson provideGson() {
return new GsonBuilder()
.create();
}
@Provides
@Singleton
public OkHttpClient provideOkHttpClient(AuthorizationInterceptor authorizationInterceptor) {
return new OkHttpClient.Builder()
.addInterceptor(authorizationInterceptor)
.build();
}
@Provides
@Singleton
public KidShiftApiService provideKidShiftApiService(OkHttpClient okHttpClient) {
return new Retrofit.Builder()
// .baseUrl(userSettings.getApiSetting().getApiBaseUrl())
.baseUrl("https://kidshift-beta.nem.one/")
.addConverterFactory(GsonConverterFactory.create(provideGson()))
.client(okHttpClient)
.build()
.create(KidShiftApiService.class);
}
}

View File

@ -1,55 +0,0 @@
package one.nem.kidshift.data.retrofit.interceptor;
import android.util.Log;
import androidx.annotation.NonNull;
import java.io.IOException;
import javax.inject.Inject;
import okhttp3.Interceptor;
import okhttp3.Response;
import one.nem.kidshift.data.UserSettings;
import one.nem.kidshift.utils.KSLogger;
import one.nem.kidshift.utils.factory.KSLoggerFactory;
/**
* Authorization placeholderが指定されている場合にアクセストークンで置換するインターセプター
*/
public class AuthorizationInterceptor implements Interceptor {
private static final String HEADER_NAME = "Authorization";
private static final String HEADER_VALUE = "VALUE";
public static final String HEADER_PLACEHOLDER = HEADER_NAME + ": " + HEADER_VALUE;
private final UserSettings userSettings;
private final KSLogger logger;
public AuthorizationInterceptor(UserSettings userSettings, KSLoggerFactory loggerFactory) {
this.userSettings = userSettings;
this.logger = loggerFactory.create("Auth_Interceptor");
}
@NonNull
@Override
public Response intercept(@NonNull Chain chain) throws IOException {
logger.debug("intercept");
try {
if (chain.request().header(HEADER_NAME) == null) {
logger.debug("Authorization header is null");
return chain.proceed(chain.request());
}
if (!HEADER_VALUE.equals(chain.request().header(HEADER_NAME))) {
logger.debug("Authorization header is invalid");
return chain.proceed(chain.request());
}
return chain.proceed(chain.request().newBuilder()
.header(HEADER_NAME, "Barer " + userSettings.getAppCommonSetting().getAccessToken())
.build());
} catch (Exception e) {
return chain.proceed(chain.request());
}
}
}

View File

@ -1,29 +0,0 @@
package one.nem.kidshift.data.retrofit.model.child;
// Request to add a child
public class ChildAddRequest {
private String name;
/**
* コンストラクタ (全プロパティ)
* @param name 子供の名前
*/
public ChildAddRequest(String name) {
this.name = name;
}
/**
* コンストラクタ ()
*/
public ChildAddRequest() {
}
// Getters and setters
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -1,40 +0,0 @@
package one.nem.kidshift.data.retrofit.model.child;
// Base class for children
public class ChildBaseItem {
private String id;
private String name;
/**
* コンストラクタ (全プロパティ)
* @param id 子供ID
* @param name 子供の名前
*/
public ChildBaseItem(String id, String name) {
this.id = id;
this.name = name;
}
/**
* コンストラクタ ()
*/
public ChildBaseItem() {
}
// Getters and setters
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -1,45 +0,0 @@
package one.nem.kidshift.data.retrofit.model.child;
import java.util.Date;
// Response for detailed information about a child
public class ChildDetailsResponse extends ChildBaseItem {
private Date createdAt;
private String homeGroupId;
/**
* コンストラクタ (全プロパティ)
* @param id 子供ID
* @param name 子供の名前
* @param createdAt 作成日時
* @param homeGroupId ホームグループID
*/
public ChildDetailsResponse(String id, String name, Date createdAt, String homeGroupId) {
super(id, name);
this.createdAt = createdAt;
this.homeGroupId = homeGroupId;
}
/**
* コンストラクタ ()
*/
public ChildDetailsResponse() {
}
// Getters and setters
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public String getHomeGroupId() {
return homeGroupId;
}
public void setHomeGroupId(String homeGroupId) {
this.homeGroupId = homeGroupId;
}
}

View File

@ -1,31 +0,0 @@
package one.nem.kidshift.data.retrofit.model.child;
import java.util.List;
// Response for a list of children
public class ChildListResponse {
private List<ChildResponse> list;
/**
* コンストラクタ (全プロパティ)
* @param list 子供リスト
*/
public ChildListResponse(List<ChildResponse> list) {
this.list = list;
}
/**
* コンストラクタ ()
*/
public ChildListResponse() {
}
// Getters and setters
public List<ChildResponse> getList() {
return list;
}
public void setList(List<ChildResponse> list) {
this.list = list;
}
}

View File

@ -1,20 +0,0 @@
package one.nem.kidshift.data.retrofit.model.child;
public class ChildLoginCodeResponse {
private int code;
public ChildLoginCodeResponse(int code) {
this.code = code;
}
public ChildLoginCodeResponse() {
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}

View File

@ -1,4 +0,0 @@
package one.nem.kidshift.data.retrofit.model.child;
public class ChildRequest extends ChildBaseItem {
}

View File

@ -1,4 +0,0 @@
package one.nem.kidshift.data.retrofit.model.child;
public class ChildResponse extends ChildBaseItem {
}

View File

@ -1,17 +0,0 @@
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

@ -1,30 +0,0 @@
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

@ -1,43 +0,0 @@
package one.nem.kidshift.data.retrofit.model.converter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
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.ChildResponse;
import one.nem.kidshift.model.ChildModel;
public class ChildModelConverter {
/**
* ChildModelをChildAddRequestに変換する
* @param childModel ChildModel
* @return ChildAddRequest
*/
public static ChildAddRequest childModelToChildAddRequest(ChildModel childModel) {
ChildAddRequest request = new ChildAddRequest();
request.setName(childModel.getName());
return request;
}
/**
* ChildListResponseをChildModelリストに変換する
* @param childListResponse ChildListResponse
* @return ChildModelリスト
*/
public static List<ChildModel> childListResponseToChildModelList(ChildListResponse childListResponse) {
return childListResponse.getList().stream().map(ChildModelConverter::childResponseToChildModel).collect(Collectors.toList());
}
/**
* ChildResponseをChildModelに変換する
* @param childResponse ChildResponse
* @return ChildModel
*/
public static ChildModel childResponseToChildModel(ChildResponse childResponse) {
return new ChildModel(childResponse.getId(), childResponse.getName());
}
}

View File

@ -1,79 +0,0 @@
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

@ -1,26 +0,0 @@
package one.nem.kidshift.data.retrofit.model.converter;
import one.nem.kidshift.data.retrofit.model.parent.ParentInfoResponse;
import one.nem.kidshift.model.ParentModel;
public class ParentModelConverter {
/**
* ParentInfoResponseをParentModelに変換する
* @param parentInfoResponse ParentInfoResponse
* @return ParentModel
*/
public static ParentModel parentInfoResponseToParentModel(ParentInfoResponse parentInfoResponse) {
return new ParentModel(parentInfoResponse.getId(), parentInfoResponse.getDisplay_name(), parentInfoResponse.getEmail());
}
/**
* ParentModelをParentInfoResponseに変換する
* @param parentModel ParentModel
* @return ParentInfoResponse
*/
public static ParentInfoResponse parentModelToParentInfoResponse(ParentModel parentModel) {
return new ParentInfoResponse(parentModel.getId(), parentModel.getName(), parentModel.getEmail());
}
}

View File

@ -1,60 +0,0 @@
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;
public class TaskModelConverter {
/**
* TaskResponseをTaskItemModelに変換する
* @param taskResponse TaskResponse
* @return TaskItemModel
*/
public static TaskItemModel taskResponseToTaskItemModel(TaskResponse taskResponse) {
TaskItemModel model = new TaskItemModel();
model.setId(taskResponse.getId());
model.setName(taskResponse.getName());
model.setReward(taskResponse.getReward());
model.setBgColor(taskResponse.getBgColor() == null ? "" : taskResponse.getBgColor());
model.setIconEmoji(taskResponse.getIconEmoji() == null ? "" : taskResponse.getIconEmoji());
return model;
}
/**
* TaskItemModelをTaskResponseに変換する
* @param taskItemModel TaskItemModel
* @return TaskResponse
*/
public static TaskResponse taskItemModelToTaskResponse(TaskItemModel taskItemModel) {
TaskResponse response = new TaskResponse();
response.setId(taskItemModel.getId());
response.setName(taskItemModel.getName());
response.setReward(taskItemModel.getReward());
response.setBgColor(taskItemModel.getBgColor());
response.setIconEmoji(taskItemModel.getIconEmoji());
return response;
}
/**
* TaskListResponseをTaskItemModelリストに変換する
* @param taskListResponse TaskListResponse
* @return TaskItemModelリスト
*/
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

@ -1,44 +0,0 @@
package one.nem.kidshift.data.retrofit.model.parent;
public class ParentInfoResponse {
private String id;
private String email;
private String display_name;
/**
* コンストラクタ (全プロパティ)
* @param id 親ID
* @param email メールアドレス
* @param display_name 表示名
*/
public ParentInfoResponse(String id, String email, String display_name) {
this.id = id;
this.email = email;
this.display_name = display_name;
}
public String getId() {
return id;
}
public String getEmail() {
return email;
}
public String getDisplay_name() {
return display_name;
}
public void setId(String id) {
this.id = id;
}
public void setEmail(String email) {
this.email = email;
}
public void setDisplay_name(String display_name) {
this.display_name = display_name;
}
}

View File

@ -1,21 +0,0 @@
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,32 +0,0 @@
package one.nem.kidshift.data.retrofit.model.parent.auth;
public class ParentAuthRequest {
private String email;
private String password;
/**
* コンストラクタ (全プロパティ)
* @param email メールアドレス
* @param password パスワード
*/
public ParentAuthRequest(String email, String password) {
this.email = email;
this.password = password;
}
public String getEmail() {
return email;
}
public String getPassword() {
return password;
}
public void setEmail(String email) {
this.email = email;
}
public void setPassword(String password) {
this.password = password;
}
}

View File

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

View File

@ -1,62 +0,0 @@
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

@ -1,22 +0,0 @@
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

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

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