Commit 4328de38 authored by Ishankha K.C's avatar Ishankha K.C

Merge branch 'feature/chamod_dev' into 'master'

Feature/chamod dev

See merge request !9
parents 3155d413 6e7b5305
......@@ -18,6 +18,12 @@
android:supportsRtl="true"
android:theme="@style/Theme.BabyCare"
tools:targetApi="31">
<activity
android:name=".activities.BabyDevControlListActivity"
android:exported="false" />
<activity
android:name=".activities.BabyCamListActivity"
android:exported="false" />
<activity
android:name=".activities.LiveVocalActivity"
android:exported="false" />
......
package com.kaluwa.enterprises.babycare.activities;
import static com.kaluwa.enterprises.babycare.config.TokenSaver.clearToken;
import static com.kaluwa.enterprises.babycare.config.TokenSaver.getToken;
import static com.kaluwa.enterprises.babycare.utils.Utils.animationChanger;
import static com.kaluwa.enterprises.babycare.utils.Utils.loader;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.PopupMenu;
import androidx.appcompat.widget.Toolbar;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.github.ybq.android.spinkit.SpinKitView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.gson.Gson;
import com.kaluwa.enterprises.babycare.MainActivity;
import com.kaluwa.enterprises.babycare.R;
import com.kaluwa.enterprises.babycare.adapter.BabyCamListAdapter;
import com.kaluwa.enterprises.babycare.adapter.BabyDashboardAdapter;
import com.kaluwa.enterprises.babycare.config.ApiConfig;
import com.kaluwa.enterprises.babycare.dto.BabyDto;
import com.kaluwa.enterprises.babycare.dto.responseDto.AuthenticationDto;
import com.kaluwa.enterprises.babycare.error.ErrorDto;
import com.kaluwa.enterprises.babycare.service.BabyApiService;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class BabyCamListActivity extends AppCompatActivity {
private final static String TAG = "BabyCamListActivity";
private SwipeRefreshLayout swipeContainer;
private AuthenticationDto authDto;
private RecyclerView recyclerView;
private TextView tvNoContent;
private View overlay;
private SpinKitView progressbar;
private BabyApiService babyApiService;
private BabyCamListAdapter adapter;
private List<BabyDto> babyDtoList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_baby_cam_list);
// define actionbar
defineActionbar();
swipeToRefresh();
// initialize user api service
try {
authDto = getToken(getApplicationContext());
} catch (JsonProcessingException e) {
Log.e(TAG, "Error: "+e.getMessage());
Toast.makeText(this, "Error getting token, Please refresh", Toast.LENGTH_SHORT).show();
}
babyApiService = ApiConfig.getInstance().getBabyApi(authDto.getTokenDto().getToken());
// assign ids here
recyclerView = findViewById(R.id.bcl_baby_dash_rv);
tvNoContent = findViewById(R.id.no_content_message);
overlay = findViewById(R.id.overlay);
progressbar = findViewById(R.id.progress_bar);
// load data
loadData();
adapter = new BabyCamListAdapter(this, babyDtoList);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
}
private void loadData() {
Call<List<BabyDto>> call = babyApiService.getAllBabies();
loader(overlay, progressbar, true);
noContentCall();
call.enqueue(new Callback<List<BabyDto>>() {
@Override
public void onResponse(Call<List<BabyDto>> call, Response<List<BabyDto>> response) {
if (response.isSuccessful()) {
List<BabyDto> newBabyDtoList = new ArrayList<>();
if (response.body() != null) {
newBabyDtoList = response.body();
}
// Assuming babyDtoList is a class member
// Check for changes and update the list accordingly
if (babyDtoList == null) {
babyDtoList = newBabyDtoList;
adapter.notifyItemRangeInserted(0, newBabyDtoList.size());
} else {
// Here, you might want to implement a diffing algorithm
// For simplicity, let's assume you replace the entire list
babyDtoList.clear();
babyDtoList.addAll(newBabyDtoList);
adapter.notifyDataSetChanged();
}
noContentCall();
loader(overlay, progressbar, false);
} else {
try {
Gson gson = new Gson();
assert response.errorBody() != null;
String errorBodyString = response.errorBody().string();
// Check if the error body is in JSON format
if (errorBodyString.startsWith("{")) {
ErrorDto errorDto = gson.fromJson(errorBodyString, ErrorDto.class);
Toast.makeText(BabyCamListActivity.this, errorDto.getMessage(), Toast.LENGTH_LONG).show();
} else {
// If the error body is not in JSON format, display a generic error message
Log.e(TAG, errorBodyString);
Toast.makeText(BabyCamListActivity.this, "An unexpected error occurred", Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
Log.e(TAG, "else-error: " + e.getMessage());
Toast.makeText(BabyCamListActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
}
noContentCall();
loader(overlay, progressbar, false);
}
}
@Override
public void onFailure(Call<List<BabyDto>> call, Throwable throwable) {
noContentCall();
Log.e(TAG, throwable.getMessage());
Toast.makeText(BabyCamListActivity.this, "Error to Failure", Toast.LENGTH_LONG).show();
loader(overlay, progressbar, false);
}
});
}
private void defineActionbar() {
Toolbar toolbar = findViewById(R.id.b_care_action_bar);
setSupportActionBar(toolbar);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayShowTitleEnabled(false);
}
toolbar.setNavigationIcon(R.drawable.ico_menu_32);
toolbar.setNavigationOnClickListener(v -> {
// Initializing the popup menu and giving the reference as current context
PopupMenu popupMenu = new PopupMenu(this, toolbar);
popupMenu.setGravity(Gravity.BOTTOM);
popupMenu.getMenuInflater().inflate(R.menu.menu_main, popupMenu.getMenu());
popupMenu.setOnMenuItemClickListener(item -> {
int id = item.getItemId();
if (id == R.id.mm_dashboard) {
Intent intent = new Intent(this, DashboardActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
animationChanger(this);
finish();
} else if (id == R.id.mm_app_setting) {
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
animationChanger(this);
} else if (id == R.id.mm_logout) {
clearToken(getApplicationContext());
Toast.makeText(this, "Logout successful.", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
animationChanger(this);
}
return true;
});
popupMenu.show();
});
}
private void swipeToRefresh() {
// Look up for the swipe container
swipeContainer = findViewById(R.id.swipeContainer);
// Setup Refresh Listener which triggers new data loading
swipeContainer.setOnRefreshListener(() -> {
// Code to refresh goes here. Make sure to call swipeContainer.setRefresh(false) once the refreshed.
startActivity(getIntent());
finish();
overridePendingTransition(0,0);
swipeContainer.setRefreshing(false);
});
// Configure refresh colors
swipeContainer.setColorSchemeResources(android.R.color.holo_blue_bright, android.R.color.holo_green_light, android.R.color.holo_orange_light, android.R.color.holo_red_light);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.user_action_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
int id = item.getItemId();
if (id == R.id.user) {
Intent intent = new Intent(this, UserProfileActivity.class);
startActivity(intent);
animationChanger(this);
} else {
Toast.makeText(this, "No item.", Toast.LENGTH_SHORT).show();
}
return super.onOptionsItemSelected(item);
}
@Override
public void onBackPressed() {
super.onBackPressed();
animationChanger(this);
}
void noContentCall() {
if (babyDtoList.isEmpty()) {
tvNoContent.setVisibility(View.VISIBLE);
} else {
tvNoContent.setVisibility(View.GONE);
}
}
}
\ No newline at end of file
package com.kaluwa.enterprises.babycare.activities;
import static com.kaluwa.enterprises.babycare.config.TokenSaver.clearToken;
import static com.kaluwa.enterprises.babycare.config.TokenSaver.getToken;
import static com.kaluwa.enterprises.babycare.utils.Utils.animationChanger;
import static com.kaluwa.enterprises.babycare.utils.Utils.loader;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.PopupMenu;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.github.ybq.android.spinkit.SpinKitView;
import com.google.gson.Gson;
import com.kaluwa.enterprises.babycare.MainActivity;
import com.kaluwa.enterprises.babycare.R;
import com.kaluwa.enterprises.babycare.adapter.BabyCamListAdapter;
import com.kaluwa.enterprises.babycare.adapter.BabyDevControlListAdapter;
import com.kaluwa.enterprises.babycare.config.ApiConfig;
import com.kaluwa.enterprises.babycare.dto.BabyDto;
import com.kaluwa.enterprises.babycare.dto.responseDto.AuthenticationDto;
import com.kaluwa.enterprises.babycare.error.ErrorDto;
import com.kaluwa.enterprises.babycare.service.BabyApiService;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@SuppressLint("LongLogTag")
public class BabyDevControlListActivity extends AppCompatActivity {
private final static String TAG = "BabyDevControlListActivity";
private SwipeRefreshLayout swipeContainer;
private AuthenticationDto authDto;
private RecyclerView recyclerView;
private TextView tvNoContent;
private View overlay;
private SpinKitView progressbar;
private BabyApiService babyApiService;
private BabyDevControlListAdapter adapter;
private List<BabyDto> babyDtoList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_baby_dev_control_list);
// define actionbar
defineActionbar();
swipeToRefresh();
// initialize user api service
try {
authDto = getToken(getApplicationContext());
} catch (JsonProcessingException e) {
Log.e(TAG, "Error: "+e.getMessage());
Toast.makeText(this, "Error getting token, Please refresh", Toast.LENGTH_SHORT).show();
}
babyApiService = ApiConfig.getInstance().getBabyApi(authDto.getTokenDto().getToken());
// assign ids here
recyclerView = findViewById(R.id.bdcl_baby_dash_rv);
tvNoContent = findViewById(R.id.no_content_message);
overlay = findViewById(R.id.overlay);
progressbar = findViewById(R.id.progress_bar);
// load data
loadData();
adapter = new BabyDevControlListAdapter(this, babyDtoList);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
}
private void loadData() {
Call<List<BabyDto>> call = babyApiService.getAllBabies();
loader(overlay, progressbar, true);
noContentCall();
call.enqueue(new Callback<List<BabyDto>>() {
@Override
public void onResponse(Call<List<BabyDto>> call, Response<List<BabyDto>> response) {
if (response.isSuccessful()) {
List<BabyDto> newBabyDtoList = new ArrayList<>();
if (response.body() != null) {
newBabyDtoList = response.body();
}
// Assuming babyDtoList is a class member
// Check for changes and update the list accordingly
if (babyDtoList == null) {
babyDtoList = newBabyDtoList;
adapter.notifyItemRangeInserted(0, newBabyDtoList.size());
} else {
// Here, you might want to implement a diffing algorithm
// For simplicity, let's assume you replace the entire list
babyDtoList.clear();
babyDtoList.addAll(newBabyDtoList);
adapter.notifyDataSetChanged();
}
noContentCall();
loader(overlay, progressbar, false);
} else {
try {
Gson gson = new Gson();
assert response.errorBody() != null;
String errorBodyString = response.errorBody().string();
// Check if the error body is in JSON format
if (errorBodyString.startsWith("{")) {
ErrorDto errorDto = gson.fromJson(errorBodyString, ErrorDto.class);
Toast.makeText(BabyDevControlListActivity.this, errorDto.getMessage(), Toast.LENGTH_LONG).show();
} else {
// If the error body is not in JSON format, display a generic error message
Log.e(TAG, errorBodyString);
Toast.makeText(BabyDevControlListActivity.this, "An unexpected error occurred", Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
Log.e(TAG, "else-error: " + e.getMessage());
Toast.makeText(BabyDevControlListActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
}
noContentCall();
loader(overlay, progressbar, false);
}
}
@Override
public void onFailure(Call<List<BabyDto>> call, Throwable throwable) {
noContentCall();
Log.e(TAG, throwable.getMessage());
Toast.makeText(BabyDevControlListActivity.this, "Error to Failure", Toast.LENGTH_LONG).show();
loader(overlay, progressbar, false);
}
});
}
private void defineActionbar() {
Toolbar toolbar = findViewById(R.id.b_care_action_bar);
setSupportActionBar(toolbar);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayShowTitleEnabled(false);
}
toolbar.setNavigationIcon(R.drawable.ico_menu_32);
toolbar.setNavigationOnClickListener(v -> {
// Initializing the popup menu and giving the reference as current context
PopupMenu popupMenu = new PopupMenu(this, toolbar);
popupMenu.setGravity(Gravity.BOTTOM);
popupMenu.getMenuInflater().inflate(R.menu.menu_main, popupMenu.getMenu());
popupMenu.setOnMenuItemClickListener(item -> {
int id = item.getItemId();
if (id == R.id.mm_dashboard) {
Intent intent = new Intent(this, DashboardActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
animationChanger(this);
finish();
} else if (id == R.id.mm_app_setting) {
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
animationChanger(this);
} else if (id == R.id.mm_logout) {
clearToken(getApplicationContext());
Toast.makeText(this, "Logout successful.", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
animationChanger(this);
}
return true;
});
popupMenu.show();
});
}
private void swipeToRefresh() {
// Look up for the swipe container
swipeContainer = findViewById(R.id.swipeContainer);
// Setup Refresh Listener which triggers new data loading
swipeContainer.setOnRefreshListener(() -> {
// Code to refresh goes here. Make sure to call swipeContainer.setRefresh(false) once the refreshed.
startActivity(getIntent());
finish();
overridePendingTransition(0,0);
swipeContainer.setRefreshing(false);
});
// Configure refresh colors
swipeContainer.setColorSchemeResources(android.R.color.holo_blue_bright, android.R.color.holo_green_light, android.R.color.holo_orange_light, android.R.color.holo_red_light);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.user_action_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
int id = item.getItemId();
if (id == R.id.user) {
Intent intent = new Intent(this, UserProfileActivity.class);
startActivity(intent);
animationChanger(this);
} else {
Toast.makeText(this, "No item.", Toast.LENGTH_SHORT).show();
}
return super.onOptionsItemSelected(item);
}
@Override
public void onBackPressed() {
super.onBackPressed();
animationChanger(this);
}
void noContentCall() {
if (babyDtoList.isEmpty()) {
tvNoContent.setVisibility(View.VISIBLE);
} else {
tvNoContent.setVisibility(View.GONE);
}
}
}
\ No newline at end of file
package com.kaluwa.enterprises.babycare.activities;
import static com.kaluwa.enterprises.babycare.config.TokenSaver.clearToken;
import static com.kaluwa.enterprises.babycare.config.TokenSaver.getToken;
import static com.kaluwa.enterprises.babycare.constants.LogTypes.C_WET;
import static com.kaluwa.enterprises.babycare.utils.Utils.animationChanger;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuInflater;
......@@ -17,12 +23,35 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.PopupMenu;
import androidx.appcompat.widget.Toolbar;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.gson.Gson;
import com.kaluwa.enterprises.babycare.MainActivity;
import com.kaluwa.enterprises.babycare.R;
import com.kaluwa.enterprises.babycare.config.ApiConfig;
import com.kaluwa.enterprises.babycare.dto.ActivityLogDto;
import com.kaluwa.enterprises.babycare.dto.BabyDto;
import com.kaluwa.enterprises.babycare.dto.responseDto.AuthenticationDto;
import com.kaluwa.enterprises.babycare.error.ErrorDto;
import com.kaluwa.enterprises.babycare.service.ActivityLogsApiService;
import com.kaluwa.enterprises.babycare.service.BabyApiService;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class DashboardActivity extends AppCompatActivity {
LinearLayout btn1, btn2, btn3, btn4, btn5, btn6;
private static final String TAG = "DashboardActivity";
private static final long CHECK_INTERVAL_MS = 5000; // 5 seconds interval
private LinearLayout btn1, btn2, btn3, btn4, btn5, btn6;
private AuthenticationDto authDto;
private BabyApiService babyApiService;
private Handler handler = new Handler(Looper.getMainLooper());
private MediaPlayer warningSoundPlayer;
private boolean isWarningPlaying = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
......@@ -32,6 +61,15 @@ public class DashboardActivity extends AppCompatActivity {
// define actionbar
defineActionbar();
// initialize user api service
try {
authDto = getToken(getApplicationContext());
} catch (JsonProcessingException e) {
Log.e(TAG, "Error: "+e.getMessage());
Toast.makeText(this, "Error getting token, Please refresh", Toast.LENGTH_SHORT).show();
}
babyApiService = ApiConfig.getInstance().getBabyApi(authDto.getTokenDto().getToken());
btn1 = findViewById(R.id.btn_camera);
btn2 = findViewById(R.id.btn_vocal);
btn3 = findViewById(R.id.btn_notifications);
......@@ -40,7 +78,7 @@ public class DashboardActivity extends AppCompatActivity {
btn6 = findViewById(R.id.btn_settings);
btn1.setOnClickListener(v -> {
Intent intent = new Intent(this, LiveFeedActivity.class);
Intent intent = new Intent(this, BabyCamListActivity.class);
startActivity(intent);
animationChanger(this);
});
......@@ -55,7 +93,7 @@ public class DashboardActivity extends AppCompatActivity {
animationChanger(this);
});
btn4.setOnClickListener(v -> {
Intent intent = new Intent(this, DeviceControlsActivity.class);
Intent intent = new Intent(this, BabyDevControlListActivity.class);
startActivity(intent);
animationChanger(this);
});
......@@ -69,6 +107,89 @@ public class DashboardActivity extends AppCompatActivity {
startActivity(intent);
animationChanger(this);
});
// Start periodic check for wet status
startCheckingWetStatus();
}
private void startCheckingWetStatus() {
handler.post(checkWetStatusRunnable);
}
private final Runnable checkWetStatusRunnable = new Runnable() {
@Override
public void run() {
// Call the API to get the list of babies and check wet status
babyApiService.getAllBabiesByUserId(authDto.getUserId()).enqueue(new Callback<List<BabyDto>>() {
@Override
public void onResponse(Call<List<BabyDto>> call, Response<List<BabyDto>> response) {
if (response.isSuccessful() && response.body() != null) {
List<BabyDto> babies = response.body();
boolean anyBabyWet = false;
for (BabyDto baby : babies) {
if (baby.isWet() && !baby.getDeviceUid().isEmpty()) {
Log.i(TAG, "Baby " + baby.getFirstName() + " is wet.");
Toast.makeText(DashboardActivity.this, "Baby " + baby.getFirstName() + " is wet.", Toast.LENGTH_SHORT).show();
anyBabyWet = true;
}
}
if (anyBabyWet) {
startWarningSound();
} else {
stopWarningSound();
}
} else {
try {
Gson gson = new Gson();
assert response.errorBody() != null;
String errorBodyString = response.errorBody().string();
// Check if the error body is in JSON format
if (errorBodyString.startsWith("{")) {
ErrorDto errorDto = gson.fromJson(errorBodyString, ErrorDto.class);
Toast.makeText(DashboardActivity.this, errorDto.getMessage(), Toast.LENGTH_LONG).show();
} else {
// If the error body is not in JSON format, display a generic error message
Log.e(TAG, errorBodyString);
Toast.makeText(DashboardActivity.this, "An unexpected error occurred", Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
Log.e(TAG, "else-error: " + e.getMessage());
}
}
}
@Override
public void onFailure(Call<List<BabyDto>> call, Throwable t) {
Log.e(TAG, "API call failed: " + t.getMessage());
}
});
// Schedule the next check
handler.postDelayed(this, CHECK_INTERVAL_MS);
}
};
private void startWarningSound() {
if (!isWarningPlaying) {
if (warningSoundPlayer == null) {
warningSoundPlayer = MediaPlayer.create(this, R.raw.wet_warning); // Replace with your sound file
warningSoundPlayer.setLooping(true); // Set to loop
}
warningSoundPlayer.start();
isWarningPlaying = true;
}
}
private void stopWarningSound() {
if (isWarningPlaying) {
warningSoundPlayer.stop();
warningSoundPlayer.release();
warningSoundPlayer = null;
isWarningPlaying = false;
}
}
private void defineActionbar() {
......@@ -135,4 +256,11 @@ public class DashboardActivity extends AppCompatActivity {
super.onBackPressed();
animationChanger(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacks(checkWetStatusRunnable); // Stop status checking when activity is destroyed
stopWarningSound(); // Ensure sound stops if activity is closed
}
}
\ No newline at end of file
package com.kaluwa.enterprises.babycare.activities;
import static com.kaluwa.enterprises.babycare.config.TokenSaver.clearToken;
import static com.kaluwa.enterprises.babycare.config.TokenSaver.getToken;
import static com.kaluwa.enterprises.babycare.utils.Utils.animationChanger;
import android.app.AlertDialog;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.Toast;
import android.widget.ToggleButton;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.PopupMenu;
import androidx.appcompat.widget.Toolbar;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.gson.Gson;
import com.kaluwa.enterprises.babycare.MainActivity;
import com.kaluwa.enterprises.babycare.R;
import com.kaluwa.enterprises.babycare.config.ApiConfig;
import com.kaluwa.enterprises.babycare.config.TokenSaver;
import com.kaluwa.enterprises.babycare.dto.SwingRequestDto;
import com.kaluwa.enterprises.babycare.dto.responseDto.AuthenticationDto;
import com.kaluwa.enterprises.babycare.dto.responseDto.ResponseDto;
import com.kaluwa.enterprises.babycare.error.ErrorDto;
import com.kaluwa.enterprises.babycare.service.BabyApiService;
import com.kaluwa.enterprises.babycare.service.DeviceApiService;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class DeviceControlsActivity extends AppCompatActivity {
private final static String TAG = "DeviceControlsActivity";
private AuthenticationDto authDto;
private DeviceApiService deviceApiService;
private String deviceUid = "";
private LinearLayout btnSwing;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
......@@ -28,6 +58,123 @@ public class DeviceControlsActivity extends AppCompatActivity {
// define actionbar
defineActionbar();
// initialize user api service
try {
authDto = getToken(getApplicationContext());
} catch (JsonProcessingException e) {
Log.e(TAG, "Error: "+e.getMessage());
Toast.makeText(this, "Error getting token, Please refresh", Toast.LENGTH_SHORT).show();
}
deviceApiService = ApiConfig.getInstance().getDeviceApi(authDto.getTokenDto().getToken());
// get extra
if (getIntent().hasExtra("deviceUid")) {
deviceUid = getIntent().getStringExtra("deviceUid");
}
// initialize views
btnSwing = findViewById(R.id.btnSwing);
// set click listeners
onClickers();
}
private void onClickers() {
btnSwing.setOnClickListener(v -> {
// open swing dialog
showCradleSwingDialog();
});
}
private void showCradleSwingDialog() {
// Inflate the dialog layout
LayoutInflater inflater = getLayoutInflater();
View dialogView = inflater.inflate(R.layout.dialog_swing_control_layout, null);
// Find views in the dialog layout
Spinner speedControlSpinner = dialogView.findViewById(R.id.scl_spinner_speed_control);
ToggleButton toggleButton = dialogView.findViewById(R.id.scl_toggle_swing);
// Set up the speed options for the spinner
String[] speedOptions = {"Low", "Medium", "High"};
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, speedOptions);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
speedControlSpinner.setAdapter(adapter);
// Load and set previous swing status if available
boolean previousSwingStatus = TokenSaver.getSwingStatus(this);
toggleButton.setChecked(previousSwingStatus);
// Load and set previously selected speed
String previousSpeed = TokenSaver.getSpeed(this);
int speedIndex = adapter.getPosition(previousSpeed);
speedControlSpinner.setSelection(speedIndex); // Set the previous speed selection
// Create the dialog
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(dialogView)
.setTitle("Cradle Swing Control") // Set dialog title
.setPositiveButton("OK", (dialog, which) -> {
// Handle OK button click
// Retrieve selected speed and toggle state
String selectedSpeed = speedControlSpinner.getSelectedItem().toString();
boolean isSwingOn = toggleButton.isChecked();
// Save the swing status and speed
TokenSaver.setSwingStatus(this, isSwingOn);
TokenSaver.setSpeed(this, selectedSpeed);
// call api to enable swing
Call<ResponseDto> callSwing = deviceApiService.swingDevice(deviceUid, new SwingRequestDto(isSwingOn, selectedSpeed));
callSwing.enqueue(new Callback<ResponseDto>() {
@Override
public void onResponse(Call<ResponseDto> call, Response<ResponseDto> response) {
if (response.isSuccessful()) {
ResponseDto responseDto = response.body();
if (responseDto != null) {
Toast.makeText(DeviceControlsActivity.this, responseDto.getMessage(), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(DeviceControlsActivity.this, "An unexpected error occurred", Toast.LENGTH_LONG).show();
}
} else {
try {
Gson gson = new Gson();
assert response.errorBody() != null;
String errorBodyString = response.errorBody().string();
// Check if the error body is in JSON format
if (errorBodyString.startsWith("{")) {
ErrorDto errorDto = gson.fromJson(errorBodyString, ErrorDto.class);
Toast.makeText(DeviceControlsActivity.this, errorDto.getMessage(), Toast.LENGTH_LONG).show();
} else {
// If the error body is not in JSON format, display a generic error message
Log.e(TAG, errorBodyString);
Toast.makeText(DeviceControlsActivity.this, "An unexpected error occurred", Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
Log.e(TAG, "else-error: " + e.getMessage());
Toast.makeText(DeviceControlsActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
}
}
}
@Override
public void onFailure(Call<ResponseDto> call, Throwable throwable) {
Log.e(TAG, throwable.getMessage());
Toast.makeText(DeviceControlsActivity.this, "Error to Failure", Toast.LENGTH_LONG).show();
}
});
// Log or send to the backend
Log.d("CradleSwingControl", "Selected Speed: " + selectedSpeed + ", Swing On: " + isSwingOn);
})
.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss()); // Handle Cancel button
// Show the dialog
AlertDialog dialog = builder.create();
dialog.show();
}
private void defineActionbar() {
......
......@@ -2,6 +2,7 @@ package com.kaluwa.enterprises.babycare.activities;
import static com.kaluwa.enterprises.babycare.config.BabyEmotionWebSocketListener.NORMAL_CLOSURE_STATUS;
import static com.kaluwa.enterprises.babycare.config.TokenSaver.clearToken;
import static com.kaluwa.enterprises.babycare.config.TokenSaver.getToken;
import static com.kaluwa.enterprises.babycare.constants.Configs.EMOTIONAL_VIDEO_PROCESS_WS_URL;
import static com.kaluwa.enterprises.babycare.constants.Configs.LIVE_FEED_URL;
import static com.kaluwa.enterprises.babycare.constants.Configs.REFRESH_INTERVAL;
......@@ -29,9 +30,16 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.PopupMenu;
import androidx.appcompat.widget.Toolbar;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.gson.Gson;
import com.kaluwa.enterprises.babycare.MainActivity;
import com.kaluwa.enterprises.babycare.R;
import com.kaluwa.enterprises.babycare.config.ApiConfig;
import com.kaluwa.enterprises.babycare.config.BabyEmotionWebSocketListener;
import com.kaluwa.enterprises.babycare.dto.responseDto.AuthenticationDto;
import com.kaluwa.enterprises.babycare.dto.responseDto.ResponseDto;
import com.kaluwa.enterprises.babycare.error.ErrorDto;
import com.kaluwa.enterprises.babycare.service.DeviceApiService;
import java.io.IOException;
import java.io.InputStream;
......@@ -41,6 +49,9 @@ import java.net.URL;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.WebSocket;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class LiveFeedActivity extends AppCompatActivity {
......@@ -53,6 +64,10 @@ public class LiveFeedActivity extends AppCompatActivity {
private Handler handler = new Handler();
private Runnable updateRunnable;
private WebSocket webSocket;
private String deviceUid = "";
private AuthenticationDto authDto;
private DeviceApiService deviceApiService;
@Override
protected void onCreate(Bundle savedInstanceState) {
......@@ -60,6 +75,22 @@ public class LiveFeedActivity extends AppCompatActivity {
setContentView(R.layout.activity_live_feed);
// define actionbar
defineActionbar();
// initialize user api service
try {
authDto = getToken(getApplicationContext());
} catch (JsonProcessingException e) {
Log.e(TAG, "Error: "+e.getMessage());
Toast.makeText(this, "Error getting token, Please refresh", Toast.LENGTH_SHORT).show();
}
deviceApiService = ApiConfig.getInstance().getDeviceApi(authDto.getTokenDto().getToken());
// get extra
if (getIntent().hasExtra("deviceUid")) {
deviceUid = getIntent().getStringExtra("deviceUid");
}
// Replace the device UID in the URLs
LIVE_FEED_URL = LIVE_FEED_URL.replace("{device_uid}", deviceUid);
ivLiveFeed = findViewById(R.id.ivLiveFeed);
lfFlashBtn = findViewById(R.id.lfFlashBtn);
......@@ -84,30 +115,47 @@ public class LiveFeedActivity extends AppCompatActivity {
String url = isFlashOn ? LIVE_FEED_URL + "/flashlight/off" : LIVE_FEED_URL + "/flashlight/on";
int iconResId = isFlashOn ? R.drawable.ico_flash_off : R.drawable.ico_flash_on; // Icons for flash on/off
// Call the API in a background thread
new Thread(() -> {
try {
URL apiUrl = new URL(url);
HttpURLConnection connection = (HttpURLConnection) apiUrl.openConnection();
connection.setRequestMethod("GET");
connection.connect();
String isFlashOnStatus = isFlashOn ? "off" : "on";
int responseCode = connection.getResponseCode();
if (responseCode == 200) {
// Successfully called the API, update the ImageView and flash status on the main thread
runOnUiThread(() -> {
Call<ResponseDto> callToggle = deviceApiService.toggleFlashlight(deviceUid, isFlashOnStatus);
callToggle.enqueue(new Callback<ResponseDto>() {
@Override
public void onResponse(Call<ResponseDto> call, Response<ResponseDto> response) {
if (response.isSuccessful()) {
ResponseDto responseDto = response.body();
if (responseDto != null) {
Toast.makeText(LiveFeedActivity.this, responseDto.getMessage(), Toast.LENGTH_SHORT).show();
}
lfFlashBtn.setImageResource(iconResId); // Update the ImageView icon
isFlashOn = !isFlashOn; // Toggle the flash status
});
} else {
Log.e(TAG, "Failed to call API: " + responseCode);
try {
Gson gson = new Gson();
assert response.errorBody() != null;
String errorBodyString = response.errorBody().string();
// Check if the error body is in JSON format
if (errorBodyString.startsWith("{")) {
ErrorDto errorDto = gson.fromJson(errorBodyString, ErrorDto.class);
Toast.makeText(LiveFeedActivity.this, errorDto.getMessage(), Toast.LENGTH_LONG).show();
} else {
// If the error body is not in JSON format, display a generic error message
Log.e(TAG, errorBodyString);
Toast.makeText(LiveFeedActivity.this, "An unexpected error occurred", Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
Log.e(TAG, "else-error: " + e.getMessage());
Toast.makeText(LiveFeedActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
}
}
}
connection.disconnect();
} catch (IOException e) {
Log.e(TAG, "Error calling flashlight API", e);
@Override
public void onFailure(Call<ResponseDto> call, Throwable throwable) {
Log.e(TAG, throwable.getMessage());
Toast.makeText(LiveFeedActivity.this, "Error to Failure", Toast.LENGTH_LONG).show();
}
}).start();
});
}
});
......@@ -232,8 +280,19 @@ public class LiveFeedActivity extends AppCompatActivity {
}
private void connectWebSocket() {
EMOTIONAL_VIDEO_PROCESS_WS_URL = EMOTIONAL_VIDEO_PROCESS_WS_URL.replace("{device_uid}", deviceUid);
Log.d(TAG, "Connecting to WebSocket: " + EMOTIONAL_VIDEO_PROCESS_WS_URL);
// Create OkHttpClient instance
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(EMOTIONAL_VIDEO_PROCESS_WS_URL).build();
// Build request with Authorization header for Bearer token
Request request = new Request.Builder()
.url(EMOTIONAL_VIDEO_PROCESS_WS_URL)
.addHeader("Authorization", "Bearer " + authDto.getTokenDto().getToken())
.build();
// Create WebSocket with the request
webSocket = client.newWebSocket(request, new BabyEmotionWebSocketListener(this, tvLlStatusValue));
}
......
package com.kaluwa.enterprises.babycare.adapter;
import static com.kaluwa.enterprises.babycare.utils.Utils.animationChanger;
import static com.kaluwa.enterprises.babycare.utils.Utils.babyAgeCalculate;
import static com.kaluwa.enterprises.babycare.utils.Utils.convertByteArrayToBitmap;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.fragment.app.FragmentActivity;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.imageview.ShapeableImageView;
import com.kaluwa.enterprises.babycare.R;
import com.kaluwa.enterprises.babycare.activities.BabyCamListActivity;
import com.kaluwa.enterprises.babycare.activities.LiveFeedActivity;
import com.kaluwa.enterprises.babycare.dialogs.EditBabyDialog;
import com.kaluwa.enterprises.babycare.dto.BabyDto;
import java.time.LocalDate;
import java.util.List;
public class BabyCamListAdapter extends RecyclerView.Adapter<BabyCamListAdapter.BabyCamListItemHolder> {
private final static String TAG = "BabyCamListAdapter";
private Context context;
private List<BabyDto> babyList;
public BabyCamListAdapter(BabyCamListActivity context, List<BabyDto> babyList) {
this.context = context;
this.babyList = babyList;
}
@NonNull
@Override
public BabyCamListAdapter.BabyCamListItemHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.baby_rv_item, parent, false);
return new BabyCamListItemHolder(view);
}
@Override
public void onBindViewHolder(@NonNull BabyCamListAdapter.BabyCamListItemHolder holder, int position) {
BabyDto babyDto = babyList.get(position);
String firstname = babyDto.getFirstName();
String lastname = babyDto.getLastName();
LocalDate dob = babyDto.getDob();
String sex = babyDto.getSex();
String notes = babyDto.getNotes();
Boolean isActive = babyDto.getIsActive();
byte[] imageData = babyDto.getImageData();
if (lastname != null) {
holder.tvBabyName.setText(firstname + " " + lastname);
} else {
holder.tvBabyName.setText(firstname);
}
String age = babyAgeCalculate(dob);
holder.tvBabyAge.setText(age != null ? age : "Not Available");
holder.tvBabySex.setText(sex);
if (!TextUtils.isEmpty(notes)) {
holder.tvBabyDDespContent.setText(notes);
} else {
holder.tvBabyDDespTitle.setText("Status >");
holder.tvBabyDDespContent.setTextColor(isActive ? context.getResources().getColor(R.color.success_green) : context.getResources().getColor(R.color.cancel_red));
holder.tvBabyDDespContent.setText(isActive ? "Active" : "Inactive");
}
if (imageData != null) {
try {
Bitmap bitmap = convertByteArrayToBitmap(imageData);
holder.ivBabyImage.setImageBitmap(bitmap);
} catch (Exception e) {
Log.e(TAG, "Error occurred: " + e.getMessage());
Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
// Handle item click
holder.clBabyItem.setOnClickListener(v -> {
// Open live feed activity
if (babyDto.getDeviceUid() == null || babyDto.getDeviceUid().isEmpty()) {
Toast.makeText(context, "No device is associated with this baby.", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "Opening live feed for " + babyDto.getFirstName(), Toast.LENGTH_SHORT).show();
Intent intent = new Intent(context, LiveFeedActivity.class);
intent.putExtra("deviceUid", babyDto.getDeviceUid());
context.startActivity(intent);
animationChanger((Activity) context);
}
});
}
@Override
public int getItemCount() {
return babyList.size();
}
public static class BabyCamListItemHolder extends RecyclerView.ViewHolder {
public ConstraintLayout clBabyItem;
public TextView tvBabyName, tvBabyAge, tvBabySex, tvBabyDDespTitle, tvBabyDDespContent;
public ShapeableImageView ivBabyImage;
public BabyCamListItemHolder(@NonNull View view) {
super(view);
clBabyItem = view.findViewById(R.id.baby_item);
tvBabyName = view.findViewById(R.id.baby_item_name);
tvBabyAge = view.findViewById(R.id.baby_item_tv_age_content);
tvBabySex = view.findViewById(R.id.baby_item_tv_sex_content);
tvBabyDDespTitle = view.findViewById(R.id.baby_item_tv_desp_title);
tvBabyDDespContent = view.findViewById(R.id.baby_item_tv_desp_content);
ivBabyImage = view.findViewById(R.id.baby_item_iv_image);
}
}
}
package com.kaluwa.enterprises.babycare.adapter;
import static com.kaluwa.enterprises.babycare.utils.Utils.animationChanger;
import static com.kaluwa.enterprises.babycare.utils.Utils.babyAgeCalculate;
import static com.kaluwa.enterprises.babycare.utils.Utils.convertByteArrayToBitmap;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.imageview.ShapeableImageView;
import com.kaluwa.enterprises.babycare.R;
import com.kaluwa.enterprises.babycare.activities.BabyDevControlListActivity;
import com.kaluwa.enterprises.babycare.activities.DeviceControlsActivity;
import com.kaluwa.enterprises.babycare.activities.LiveFeedActivity;
import com.kaluwa.enterprises.babycare.dto.BabyDto;
import java.time.LocalDate;
import java.util.List;
@SuppressLint("LongLogTag")
public class BabyDevControlListAdapter extends RecyclerView.Adapter<BabyDevControlListAdapter.BabyDevControlListItemHolder> {
private final static String TAG = "BabyDevControlListAdapter";
private Context context;
private List<BabyDto> babyList;
public BabyDevControlListAdapter(Context context, List<BabyDto> babyList) {
this.context = context;
this.babyList = babyList;
}
@NonNull
@Override
public BabyDevControlListAdapter.BabyDevControlListItemHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.baby_rv_item, parent, false);
return new BabyDevControlListAdapter.BabyDevControlListItemHolder(view);
}
@Override
public void onBindViewHolder(@NonNull BabyDevControlListAdapter.BabyDevControlListItemHolder holder, int position) {
BabyDto babyDto = babyList.get(position);
String firstname = babyDto.getFirstName();
String lastname = babyDto.getLastName();
LocalDate dob = babyDto.getDob();
String sex = babyDto.getSex();
String notes = babyDto.getNotes();
Boolean isActive = babyDto.getIsActive();
byte[] imageData = babyDto.getImageData();
if (lastname != null) {
holder.tvBabyName.setText(firstname + " " + lastname);
} else {
holder.tvBabyName.setText(firstname);
}
String age = babyAgeCalculate(dob);
holder.tvBabyAge.setText(age != null ? age : "Not Available");
holder.tvBabySex.setText(sex);
if (!TextUtils.isEmpty(notes)) {
holder.tvBabyDDespContent.setText(notes);
} else {
holder.tvBabyDDespTitle.setText("Status >");
holder.tvBabyDDespContent.setTextColor(isActive ? context.getResources().getColor(R.color.success_green) : context.getResources().getColor(R.color.cancel_red));
holder.tvBabyDDespContent.setText(isActive ? "Active" : "Inactive");
}
if (imageData != null) {
try {
Bitmap bitmap = convertByteArrayToBitmap(imageData);
holder.ivBabyImage.setImageBitmap(bitmap);
} catch (Exception e) {
Log.e(TAG, "Error occurred: " + e.getMessage());
Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
// Handle item click
holder.clBabyItem.setOnClickListener(v -> {
// Open live feed activity
if (babyDto.getDeviceUid() == null || babyDto.getDeviceUid().isEmpty()) {
Toast.makeText(context, "No device is associated with this baby.", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "Opening device controls for " + babyDto.getFirstName(), Toast.LENGTH_SHORT).show();
Intent intent = new Intent(context, DeviceControlsActivity.class);
intent.putExtra("deviceUid", babyDto.getDeviceUid());
context.startActivity(intent);
animationChanger((Activity) context);
}
});
}
@Override
public int getItemCount() {
return babyList.size();
}
public class BabyDevControlListItemHolder extends RecyclerView.ViewHolder {
public ConstraintLayout clBabyItem;
public TextView tvBabyName, tvBabyAge, tvBabySex, tvBabyDDespTitle, tvBabyDDespContent;
public ShapeableImageView ivBabyImage;
public BabyDevControlListItemHolder(@NonNull View view) {
super(view);
clBabyItem = view.findViewById(R.id.baby_item);
tvBabyName = view.findViewById(R.id.baby_item_name);
tvBabyAge = view.findViewById(R.id.baby_item_tv_age_content);
tvBabySex = view.findViewById(R.id.baby_item_tv_sex_content);
tvBabyDDespTitle = view.findViewById(R.id.baby_item_tv_desp_title);
tvBabyDDespContent = view.findViewById(R.id.baby_item_tv_desp_content);
ivBabyImage = view.findViewById(R.id.baby_item_iv_image);
}
}
}
......@@ -10,6 +10,7 @@ import com.kaluwa.enterprises.babycare.service.ActivityLogsApiService;
import com.kaluwa.enterprises.babycare.service.AuthApiService;
import com.kaluwa.enterprises.babycare.service.BabyApiService;
import com.kaluwa.enterprises.babycare.service.ContactInfoService;
import com.kaluwa.enterprises.babycare.service.DeviceApiService;
import com.kaluwa.enterprises.babycare.service.DocumentApiService;
import com.kaluwa.enterprises.babycare.service.UserApiService;
......@@ -89,4 +90,9 @@ public class ApiConfig {
AUTH_TOKEN = JWTToken;
return retrofitOther.create(ContactInfoService.class);
}
public DeviceApiService getDeviceApi(String JWTToken) {
AUTH_TOKEN = JWTToken;
return retrofitOther.create(DeviceApiService.class);
}
}
......@@ -10,6 +10,8 @@ import com.kaluwa.enterprises.babycare.dto.responseDto.AuthenticationDto;
public class TokenSaver {
private final static String SHARED_PREF_NAME = "net.kaluwa.SHARED_PREF_NAME";
private final static String TOKEN_KEY = "net.kaluwa.TOKEN_KEY";
private final static String SWING_STATUS_KEY = "net.kaluwa.SWING_STATUS_KEY";
private final static String SPEED_KEY = "net.kaluwa.SPEED_KEY";
private static ObjectMapper objectMapper = new ObjectMapper();
public static AuthenticationDto getToken(Context c) throws JsonProcessingException {
......@@ -38,4 +40,32 @@ public class TokenSaver {
editor.remove(TOKEN_KEY);
editor.apply();
}
// New method to save swing status
public static void setSwingStatus(Context c, boolean swingStatus) {
SharedPreferences prefs = c.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(SWING_STATUS_KEY, swingStatus);
editor.apply();
}
// New method to get swing status
public static boolean getSwingStatus(Context c) {
SharedPreferences prefs = c.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
return prefs.getBoolean(SWING_STATUS_KEY, false); // Default to false if not found
}
// New method to save the selected speed
public static void setSpeed(Context c, String speed) {
SharedPreferences prefs = c.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString(SPEED_KEY, speed);
editor.apply();
}
// New method to get the selected speed
public static String getSpeed(Context c) {
SharedPreferences prefs = c.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
return prefs.getString(SPEED_KEY, "Medium"); // Default to "Medium" if not found
}
}
......@@ -4,8 +4,8 @@ public class Configs {
public static final String URL = "192.168.1.2:8080/api/v1/baby-care/";
public static final String BASE_URL = "http://"+URL;
public static final String LIVE_FEED_URL = "http://192.168.1.7";
public static final String EMOTIONAL_VIDEO_PROCESS_WS_URL = "ws://"+URL+"emotional/video-process";
public static String LIVE_FEED_URL = "http://192.168.1.7/{device_uid}";
public static String EMOTIONAL_VIDEO_PROCESS_WS_URL = "ws://"+URL+"emotional/video-process/{device_uid}";
public static final int REFRESH_INTERVAL = 100; // Refresh every 100 ms
......
package com.kaluwa.enterprises.babycare.constants;
public class LogTypes {
public static final String EMOTION = "EMOTION";
public static final String FLASH_LIGHT = "FLASH_LIGHT";
public static final String C_SWING = "C_SWING";
public static final String C_MOTION = "C_MOTION";
public static final String C_WET = "C_WET";
}
......@@ -35,7 +35,7 @@ import java.time.LocalDate;
public class AddBabyDialog extends AppCompatDialogFragment {
BabyDialogInterface babyDialogInterface;
private EditText etFirstname, etLastname, dateDob, etSex, etWeight, etHeight, etBloodType, etEyeColor, etHairColor, etAllergies, etMediConditions, etMedication, etVaccinateRecs, etDocName, etDocContact, etHealthInsuranceInfo, dateFirstSmile, dateFirstTooth, dateFirstWord, dateFirstStep, etFavFoods, etDisFoods, etPEmergConName, etPEmergRelation, etPEmergConNumber, etSEmergConName, etSEmergRelation, etSEmergConNumber, etNotes;
private EditText etFirstname, etLastname, dateDob, etSex, et_device_uid, etWeight, etHeight, etBloodType, etEyeColor, etHairColor, etAllergies, etMediConditions, etMedication, etVaccinateRecs, etDocName, etDocContact, etHealthInsuranceInfo, dateFirstSmile, dateFirstTooth, dateFirstWord, dateFirstStep, etFavFoods, etDisFoods, etPEmergConName, etPEmergRelation, etPEmergConNumber, etSEmergConName, etSEmergRelation, etSEmergConNumber, etNotes;
private ImageButton ibPhoto;
private SwitchCompat switchStatus;
private View overlay;
......@@ -113,12 +113,13 @@ public class AddBabyDialog extends AppCompatDialogFragment {
private BabyDto validateObjects() {
BabyDto baby = new BabyDto();
baby.setSys_validated(false);
String firstname, lastname, dob, sex, weight, height, bType, eyeColor, hairColor, allergies, medicalConditions, medications, vaccinateRecs, docName, docContact, healthInsuInfo, firstSmile, firstTooth, firstWord, firstStep, favFoods, disFoods, pEmergeConName, pEmergeRelationship, pEmergeContact, sEmergeConName, sEmergeRelationship, sEmergeContact, notes;
String firstname, lastname, dob, sex, device_uid, weight, height, bType, eyeColor, hairColor, allergies, medicalConditions, medications, vaccinateRecs, docName, docContact, healthInsuInfo, firstSmile, firstTooth, firstWord, firstStep, favFoods, disFoods, pEmergeConName, pEmergeRelationship, pEmergeContact, sEmergeConName, sEmergeRelationship, sEmergeContact, notes;
firstname = etFirstname.getText().toString();
lastname = etLastname.getText().toString();
dob = dateDob.getText().toString();
sex = etSex.getText().toString();
device_uid = et_device_uid.getText().toString();
weight = etWeight.getText().toString();
height = etHeight.getText().toString();
bType = etBloodType.getText().toString();
......@@ -159,6 +160,7 @@ public class AddBabyDialog extends AppCompatDialogFragment {
baby.setLastName(lastname);
baby.setDob(LocalDate.parse(dob, getDateTimeFormatter()));
baby.setSex(sex);
baby.setDeviceUid(device_uid);
baby.setIsActive(switchStatus.isChecked());
if (!TextUtils.isEmpty(weight)) {
baby.setWeight(Float.parseFloat(weight));
......@@ -215,6 +217,7 @@ public class AddBabyDialog extends AppCompatDialogFragment {
etLastname = view.findViewById(R.id.abv_et_last_name);
dateDob = view.findViewById(R.id.abv_et_dob);
etSex = view.findViewById(R.id.abv_et_sex);
et_device_uid = view.findViewById(R.id.abv_et_device_uid);
etWeight = view.findViewById(R.id.abv_et_weight);
etHeight = view.findViewById(R.id.abv_et_height);
etBloodType = view.findViewById(R.id.abv_dd_blood_type);
......
......@@ -72,7 +72,7 @@ public class EditBabyDialog extends AppCompatDialogFragment {
private static final String TAG = "EditBabyDialogTAG";
EditBabyDialogInterface babyDialogInterface;
private EditText etFirstname, etLastname, dateDob, etSex, etWeight, etHeight, etBloodType, etEyeColor, etHairColor, etAllergies, etMediConditions, etMedication, etVaccinateRecs, etDocName, etDocContact, etHealthInsuranceInfo, dateFirstSmile, dateFirstTooth, dateFirstWord, dateFirstStep, etFavFoods, etDisFoods, etPEmergConName, etPEmergRelation, etPEmergConNumber, etSEmergConName, etSEmergRelation, etSEmergConNumber, etNotes;
private EditText etFirstname, etLastname, dateDob, etSex, et_device_uid, etWeight, etHeight, etBloodType, etEyeColor, etHairColor, etAllergies, etMediConditions, etMedication, etVaccinateRecs, etDocName, etDocContact, etHealthInsuranceInfo, dateFirstSmile, dateFirstTooth, dateFirstWord, dateFirstStep, etFavFoods, etDisFoods, etPEmergConName, etPEmergRelation, etPEmergConNumber, etSEmergConName, etSEmergRelation, etSEmergConNumber, etNotes;
private ShapeableImageView ibPhoto;
private SwitchCompat switchStatus;
private View overlay;
......@@ -333,6 +333,7 @@ public class EditBabyDialog extends AppCompatDialogFragment {
etLastname.setText(babyDto.getLastName());
dateDob.setText(babyDto.getDob() != null ? babyDto.getDob().toString() : "");
etSex.setText(babyDto.getSex());
et_device_uid.setText(babyDto.getDeviceUid());
etWeight.setText(babyDto.getWeight() != null && babyDto.getWeight() != 0 ? String.valueOf(babyDto.getWeight()) : "");
etHeight.setText(babyDto.getHeight() != null && babyDto.getHeight() != 0 ? String.valueOf(babyDto.getHeight()) : "");
etBloodType.setText(babyDto.getBloodType());
......@@ -374,12 +375,13 @@ public class EditBabyDialog extends AppCompatDialogFragment {
BabyDto baby = new BabyDto();
baby.setBabyId(babyDto.getBabyId());
baby.setSys_validated(false);
String firstname, lastname, dob, sex, weight, height, bType, eyeColor, hairColor, allergies, medicalConditions, medications, vaccinateRecs, docName, docContact, healthInsuInfo, firstSmile, firstTooth, firstWord, firstStep, favFoods, disFoods, pEmergeConName, pEmergeRelationship, pEmergeContact, sEmergeConName, sEmergeRelationship, sEmergeContact, notes, uniqKey;
String firstname, lastname, dob, sex, device_uid, weight, height, bType, eyeColor, hairColor, allergies, medicalConditions, medications, vaccinateRecs, docName, docContact, healthInsuInfo, firstSmile, firstTooth, firstWord, firstStep, favFoods, disFoods, pEmergeConName, pEmergeRelationship, pEmergeContact, sEmergeConName, sEmergeRelationship, sEmergeContact, notes, uniqKey;
firstname = etFirstname.getText().toString();
lastname = etLastname.getText().toString();
dob = dateDob.getText().toString();
sex = etSex.getText().toString();
device_uid = et_device_uid.getText().toString();
weight = etWeight.getText().toString();
height = etHeight.getText().toString();
bType = etBloodType.getText().toString();
......@@ -420,6 +422,7 @@ public class EditBabyDialog extends AppCompatDialogFragment {
baby.setLastName(lastname);
baby.setDob(LocalDate.parse(dob, getDateTimeFormatter()));
baby.setSex(sex);
baby.setDeviceUid(device_uid);
baby.setIsActive(switchStatus.isChecked());
if (!TextUtils.isEmpty(weight)) {
baby.setWeight(Float.parseFloat(weight));
......@@ -477,6 +480,7 @@ public class EditBabyDialog extends AppCompatDialogFragment {
etLastname = view.findViewById(R.id.ebv_et_last_name);
dateDob = view.findViewById(R.id.ebv_et_dob);
etSex = view.findViewById(R.id.ebv_et_sex);
et_device_uid = view.findViewById(R.id.ebv_et_device_uid);
etWeight = view.findViewById(R.id.ebv_et_weight);
etHeight = view.findViewById(R.id.ebv_et_height);
etBloodType = view.findViewById(R.id.ebv_dd_blood_type);
......
......@@ -18,6 +18,7 @@ public class BabyDto {
private String lastName;
private LocalDate dob;
private String sex;
private String deviceUid;
private String status;
private Boolean isActive;
private Float weight;
......@@ -50,6 +51,7 @@ public class BabyDto {
private Long userId;
private Long documentId;
private byte[] imageData;
private boolean wet;
@JsonIgnore
private boolean sys_validated;
......
package com.kaluwa.enterprises.babycare.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SwingRequestDto {
private boolean start;
private String speed; // LOW, MEDIUM, HIGH
}
......@@ -8,6 +8,7 @@ import java.util.List;
import retrofit2.Call;
import retrofit2.http.DELETE;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface ActivityLogsApiService {
......@@ -17,4 +18,7 @@ public interface ActivityLogsApiService {
@DELETE("activity-logs")
Call<ResponseDto> deleteAllActivityLogs();
@GET("activity-logs")
Call<List<ActivityLogDto>> getTop5ActivityLogsByType(@Query("activityLogType") String activityLogType);
}
......@@ -10,6 +10,7 @@ import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Path;
import retrofit2.http.Query;
public interface BabyApiService {
......@@ -22,4 +23,7 @@ public interface BabyApiService {
@PUT("baby/{babyId}")
Call<BabyDto> updateBaby(@Path("babyId") Long babyId, @Body BabyDto baby);
@GET("baby")
Call<List<BabyDto>> getAllBabiesByUserId(@Query("userId") Long userId);
}
package com.kaluwa.enterprises.babycare.service;
import com.kaluwa.enterprises.babycare.dto.SwingRequestDto;
import com.kaluwa.enterprises.babycare.dto.responseDto.ResponseDto;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Path;
public interface DeviceApiService {
@GET("device/flashlight/{deviceUid}/{status}")
Call<ResponseDto> toggleFlashlight(@Path("deviceUid") String deviceUid, @Path("status") String status);
@POST("device/swing/{deviceUid}")
Call<ResponseDto> swingDevice(@Path("deviceUid") String deviceUid, @Body SwingRequestDto swingRequest);
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/swipeContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.BabyCamListActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
layout="@layout/appbar"/>
<ImageView
android:id="@+id/background"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/b_care_action_bar"
app:layout_constraintBottom_toBottomOf="parent"
android:src="@drawable/background"
android:contentDescription="background-image"
android:scaleType="centerCrop"
android:alpha="0.4"/>
<RelativeLayout
android:id="@+id/rl_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/rl_background"
android:backgroundTint="#62178F"
app:layout_constraintTop_toTopOf="@+id/background"
app:layout_constraintStart_toStartOf="@id/background"
app:layout_constraintEnd_toEndOf="@id/background"
android:padding="8dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Baby Cam List"
android:textAlignment="center"
android:textColor="@color/white"
android:textAllCaps="true"
android:fontFamily="@font/inknut_antiqua_regular"
android:textSize="20sp"
android:gravity="center"/>
</RelativeLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/bcl_baby_dash_rv"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/rl_header"
app:layout_constraintBottom_toBottomOf="@id/background"
app:layout_constraintEnd_toEndOf="@id/background"
app:layout_constraintStart_toStartOf="@id/background"
android:scrollbars="none"
android:paddingTop="6dp"
android:paddingStart="15dp"
android:paddingEnd="15dp"
android:paddingBottom="6dp"/>
<TextView
android:id="@+id/no_content_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="No content available"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:textStyle="bold"
android:visibility="gone"/>
<View
android:id="@+id/overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#99D5C5DF"
android:visibility="gone"
android:clickable="true"
android:focusable="true"/>
<com.github.ybq.android.spinkit.SpinKitView
android:id="@+id/progress_bar"
style="@style/SpinKitView.Large.DoubleBounce"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:SpinKit_Color="@color/purple"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:visibility="gone"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/swipeContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.BabyDevControlListActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
layout="@layout/appbar"/>
<ImageView
android:id="@+id/background"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/b_care_action_bar"
app:layout_constraintBottom_toBottomOf="parent"
android:src="@drawable/background"
android:contentDescription="background-image"
android:scaleType="centerCrop"
android:alpha="0.4"/>
<RelativeLayout
android:id="@+id/rl_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/rl_background"
android:backgroundTint="#62178F"
app:layout_constraintTop_toTopOf="@+id/background"
app:layout_constraintStart_toStartOf="@id/background"
app:layout_constraintEnd_toEndOf="@id/background"
android:padding="8dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Baby Device List"
android:textAlignment="center"
android:textColor="@color/white"
android:textAllCaps="true"
android:fontFamily="@font/inknut_antiqua_regular"
android:textSize="20sp"
android:gravity="center"/>
</RelativeLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/bdcl_baby_dash_rv"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/rl_header"
app:layout_constraintBottom_toBottomOf="@id/background"
app:layout_constraintEnd_toEndOf="@id/background"
app:layout_constraintStart_toStartOf="@id/background"
android:scrollbars="none"
android:paddingTop="6dp"
android:paddingStart="15dp"
android:paddingEnd="15dp"
android:paddingBottom="6dp"/>
<TextView
android:id="@+id/no_content_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="No content available"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:textStyle="bold"
android:visibility="gone"/>
<View
android:id="@+id/overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#99D5C5DF"
android:visibility="gone"
android:clickable="true"
android:focusable="true"/>
<com.github.ybq.android.spinkit.SpinKitView
android:id="@+id/progress_bar"
style="@style/SpinKitView.Large.DoubleBounce"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:SpinKit_Color="@color/purple"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:visibility="gone"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
\ No newline at end of file
......@@ -77,6 +77,15 @@
android:textSize="16sp"
android:fontFamily="@font/kanit_regular"/>
<EditText
android:id="@+id/abv_et_device_uid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/device_unique_id"
android:inputType="text"
android:textSize="16sp"
android:fontFamily="@font/kanit_regular"/>
<!-- <TextView-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
......
......@@ -24,8 +24,8 @@
android:layout_height="wrap_content"
android:text="Contact Information"
android:textColor="@color/purple"
android:textStyle="bold"
android:fontFamily="@font/jeju_gothic_regular"
android:textStyle="bold"
android:textSize="18sp"/>
<View
......
<?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="match_parent"
android:orientation="vertical"
android:padding="16dp"
android:background="@android:color/white">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/borders"
android:padding="24dp"
android:elevation="4dp"
android:layout_marginBottom="16dp">
<!-- Speed Control Spinner -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Select Speed"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="@color/purple"
android:fontFamily="@font/jeju_gothic_regular"
android:layout_marginBottom="8dp" />
<Spinner
android:id="@+id/scl_spinner_speed_control"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:padding="12dp"
android:layout_marginBottom="16dp" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/dark_purple"/>
<!-- Toggle Button -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Toggle Cradle Swing"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="@color/purple"
android:fontFamily="@font/jeju_gothic_regular"
android:layout_marginTop="16dp"
android:layout_marginBottom="8dp" />
<ToggleButton
android:id="@+id/scl_toggle_swing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textOn="Swing ON"
android:textOff="Swing OFF"
android:layout_gravity="center"
android:padding="16dp"
android:textSize="16sp"
android:layout_marginBottom="16dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Use the switch to control the cradle swing."
android:textSize="14sp"
android:layout_gravity="center"
android:layout_marginTop="8dp" />
</LinearLayout>
</LinearLayout>
......@@ -116,6 +116,15 @@
android:textSize="16sp"
android:fontFamily="@font/kanit_regular"/>
<EditText
android:id="@+id/ebv_et_device_uid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/device_unique_id"
android:inputType="text"
android:textSize="16sp"
android:fontFamily="@font/kanit_regular"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
......
......@@ -24,4 +24,5 @@
<string name="about">A child is a human being between the stages of birth and puberty, or between the developmental periods of infancy and puberty. It may also refer to an unborn human being. The legal definition of a child generally refers to a minor, which is a person younger than the age of majority.</string>
<string name="version">Version 0.1</string>
<string name="contact_name">Contact Name<font color="#FF0000"> *</font></string>
<string name="device_unique_id">Device Unique ID</string>
</resources>
\ No newline at end of file
......@@ -23,4 +23,5 @@
<string name="about">A child is a human being between the stages of birth and puberty, or between the developmental periods of infancy and puberty. It may also refer to an unborn human being. The legal definition of a child generally refers to a minor, which is a person younger than the age of majority.</string>
<string name="version">Version 0.1</string>
<string name="contact_name">Contact Name<font color="#FF0000"> *</font></string>
<string name="device_unique_id">Device Unique ID</string>
</resources>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment