diff --git a/doc/01_行业和领域调研分析报告 .docx b/doc/01_行业和领域调研分析报告 .docx
new file mode 100644
index 0000000..31aeca9
Binary files /dev/null and b/doc/01_行业和领域调研分析报告 .docx differ
diff --git a/doc/02_软件系统的需求构思及描述.docx b/doc/02_软件系统的需求构思及描述.docx
new file mode 100644
index 0000000..48d6daa
Binary files /dev/null and b/doc/02_软件系统的需求构思及描述.docx differ
diff --git a/doc/03_软件需求规格说明书.docx b/doc/03_软件需求规格说明书.docx
new file mode 100644
index 0000000..b9da871
Binary files /dev/null and b/doc/03_软件需求规格说明书.docx differ
diff --git a/doc/04_软件设计规格说明书.docx b/doc/04_软件设计规格说明书.docx
new file mode 100644
index 0000000..31a5579
Binary files /dev/null and b/doc/04_软件设计规格说明书.docx differ
diff --git a/doc/1.txt b/doc/1.txt
deleted file mode 100644
index e69de29..0000000
diff --git a/model/2.txt b/model/2.txt
deleted file mode 100644
index e69de29..0000000
diff --git a/other/05_软件工程课程设计汇报_朱柯蓉.pptx b/other/05_软件工程课程设计汇报_朱柯蓉.pptx
new file mode 100644
index 0000000..9be378a
Binary files /dev/null and b/other/05_软件工程课程设计汇报_朱柯蓉.pptx differ
diff --git a/other/06_软件开发项目的个人自评报告.xlsx b/other/06_软件开发项目的个人自评报告.xlsx
new file mode 100644
index 0000000..567fe2d
Binary files /dev/null and b/other/06_软件开发项目的个人自评报告.xlsx differ
diff --git a/other/07_软件开发项目的团队自评报告.xlsx b/other/07_软件开发项目的团队自评报告.xlsx
new file mode 100644
index 0000000..a0f776f
Binary files /dev/null and b/other/07_软件开发项目的团队自评报告.xlsx differ
diff --git a/other/08_220340052王华雪-实践总结报告.docx b/other/08_220340052王华雪-实践总结报告.docx
new file mode 100644
index 0000000..02d0f1c
Binary files /dev/null and b/other/08_220340052王华雪-实践总结报告.docx differ
diff --git a/other/08_220340071龚奕霞-实践总结报告.docx b/other/08_220340071龚奕霞-实践总结报告.docx
new file mode 100644
index 0000000..074f657
Binary files /dev/null and b/other/08_220340071龚奕霞-实践总结报告.docx differ
diff --git a/other/08_220340076朱柯蓉-实践总结报告.docx b/other/08_220340076朱柯蓉-实践总结报告.docx
new file mode 100644
index 0000000..2f6e944
Binary files /dev/null and b/other/08_220340076朱柯蓉-实践总结报告.docx differ
diff --git a/other/08_220340077孟依琳-实践总结报告.docx b/other/08_220340077孟依琳-实践总结报告.docx
new file mode 100644
index 0000000..c34c055
Binary files /dev/null and b/other/08_220340077孟依琳-实践总结报告.docx differ
diff --git a/other/08_220340079刘悦-实践总结报告.docx b/other/08_220340079刘悦-实践总结报告.docx
new file mode 100644
index 0000000..9a5eaef
Binary files /dev/null and b/other/08_220340079刘悦-实践总结报告.docx differ
diff --git a/other/08_220340081刘涵鎔-实践总结报告.docx b/other/08_220340081刘涵鎔-实践总结报告.docx
new file mode 100644
index 0000000..1d6d94f
Binary files /dev/null and b/other/08_220340081刘涵鎔-实践总结报告.docx differ
diff --git a/other/09_演示运行视频.mp4 b/other/09_演示运行视频.mp4
new file mode 100644
index 0000000..5da62c0
Binary files /dev/null and b/other/09_演示运行视频.mp4 differ
diff --git a/other/10_项目宣传海报.png b/other/10_项目宣传海报.png
new file mode 100644
index 0000000..d87a22e
Binary files /dev/null and b/other/10_项目宣传海报.png differ
diff --git a/other/3.txt b/other/3.txt
deleted file mode 100644
index e69de29..0000000
diff --git a/src/FallDetectionmaster/.gitignore b/src/FallDetectionmaster/.gitignore
new file mode 100644
index 0000000..aa724b7
--- /dev/null
+++ b/src/FallDetectionmaster/.gitignore
@@ -0,0 +1,15 @@
+*.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
+local.properties
diff --git a/src/FallDetectionmaster/app/.gitignore b/src/FallDetectionmaster/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/src/FallDetectionmaster/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/.idea/.gitignore b/src/FallDetectionmaster/app/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/src/FallDetectionmaster/app/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/src/FallDetectionmaster/app/.idea/gradle.xml b/src/FallDetectionmaster/app/.idea/gradle.xml
new file mode 100644
index 0000000..89935b5
--- /dev/null
+++ b/src/FallDetectionmaster/app/.idea/gradle.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/.idea/migrations.xml b/src/FallDetectionmaster/app/.idea/migrations.xml
new file mode 100644
index 0000000..f8051a6
--- /dev/null
+++ b/src/FallDetectionmaster/app/.idea/migrations.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/.idea/misc.xml b/src/FallDetectionmaster/app/.idea/misc.xml
new file mode 100644
index 0000000..3040d03
--- /dev/null
+++ b/src/FallDetectionmaster/app/.idea/misc.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/build.gradle b/src/FallDetectionmaster/app/build.gradle
new file mode 100644
index 0000000..a78a88e
--- /dev/null
+++ b/src/FallDetectionmaster/app/build.gradle
@@ -0,0 +1,44 @@
+plugins {
+ id 'com.android.application'
+}
+
+android {
+ compileSdk 34
+ defaultConfig {
+ applicationId "edu.gatech.seclass.fall_detection_master"
+ minSdk 27
+ 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 files('libs\\AMap3DMap_10.0.900_AMapSearch_9.7.3_AMapLocation_6.4.7_20240816.jar')
+
+ implementation 'com.google.android.material:material:1.0.0'
+
+ implementation "androidx.drawerlayout:drawerlayout:1.1.1"
+ implementation 'com.github.zcweng:switch-button:0.0.3@aar'
+
+ implementation 'androidx.appcompat:appcompat:1.3.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/libs/AMap3DMap_10.0.900_AMapSearch_9.7.3_AMapLocation_6.4.7_20240816.jar b/src/FallDetectionmaster/app/libs/AMap3DMap_10.0.900_AMapSearch_9.7.3_AMapLocation_6.4.7_20240816.jar
new file mode 100644
index 0000000..5e2252a
Binary files /dev/null and b/src/FallDetectionmaster/app/libs/AMap3DMap_10.0.900_AMapSearch_9.7.3_AMapLocation_6.4.7_20240816.jar differ
diff --git a/src/FallDetectionmaster/app/proguard-rules.pro b/src/FallDetectionmaster/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/src/FallDetectionmaster/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/release/output-metadata.json b/src/FallDetectionmaster/app/release/output-metadata.json
new file mode 100644
index 0000000..6be2b0e
--- /dev/null
+++ b/src/FallDetectionmaster/app/release/output-metadata.json
@@ -0,0 +1,20 @@
+{
+ "version": 3,
+ "artifactType": {
+ "type": "APK",
+ "kind": "Directory"
+ },
+ "applicationId": "edu.gatech.seclass.fall_detection_master",
+ "variantName": "release",
+ "elements": [
+ {
+ "type": "SINGLE",
+ "filters": [],
+ "attributes": [],
+ "versionCode": 1,
+ "versionName": "1.0",
+ "outputFile": "app-release.apk"
+ }
+ ],
+ "elementType": "File"
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/androidTest/java/edu/gatech/seclass/fall_detection_master/ExampleInstrumentedTest.java b/src/FallDetectionmaster/app/src/androidTest/java/edu/gatech/seclass/fall_detection_master/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..b81edd2
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/androidTest/java/edu/gatech/seclass/fall_detection_master/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package edu.gatech.seclass.fall_detection_master;
+
+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 Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ assertEquals("edu.gatech.seclass.fall_detection_master", appContext.getPackageName());
+ }
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/AndroidManifest.xml b/src/FallDetectionmaster/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..0a9772a
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/AndroidManifest.xml
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/AlarmActivity.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/AlarmActivity.java
new file mode 100644
index 0000000..04f1874
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/AlarmActivity.java
@@ -0,0 +1,194 @@
+package edu.gatech.seclass.fall_detection_master;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.app.TimePickerDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TimePicker;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+import edu.gatech.seclass.fall_detection_master.adapter.AlarmAdapter;
+import edu.gatech.seclass.fall_detection_master.model.AlarmInfo;
+import edu.gatech.seclass.fall_detection_master.db.AlarmDao;
+import edu.gatech.seclass.fall_detection_master.service.AlarmService;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+
+public class AlarmActivity extends AppCompatActivity implements AlarmAdapter.OnItemClickListener {
+ private RecyclerView rvAlarms;
+ private FloatingActionButton fabAddAlarm;
+ private AlarmAdapter alarmAdapter;
+ private List alarmList;
+ private AlarmDao alarmDao;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_alarm);
+
+ initViews();
+ initData();
+ setupListeners();
+ }
+
+ private void initViews() {
+ rvAlarms = findViewById(R.id.rv_alarms);
+ fabAddAlarm = findViewById(R.id.fab_add_alarm);
+
+ rvAlarms.setLayoutManager(new LinearLayoutManager(this));
+ alarmList = new ArrayList<>();
+ alarmAdapter = new AlarmAdapter(alarmList);
+ alarmAdapter.setOnItemClickListener(this);
+ rvAlarms.setAdapter(alarmAdapter);
+ }
+
+ private void initData() {
+ alarmDao = new AlarmDao(this);
+ loadAlarms();
+ }
+
+ private void loadAlarms() {
+ alarmList.clear();
+ alarmList.addAll(alarmDao.getAllAlarms());
+ alarmAdapter.notifyDataSetChanged();
+ }
+
+ private void setupListeners() {
+ fabAddAlarm.setOnClickListener(v -> showAlarmDialog(null));
+ }
+
+ private void showAlarmDialog(AlarmInfo alarm) {
+ View dialogView = getLayoutInflater().inflate(R.layout.dialog_alarm, null);
+ TimePicker timePicker = dialogView.findViewById(R.id.time_picker);
+ EditText etDescription = dialogView.findViewById(R.id.et_description);
+
+ // 如果是编辑模式,设置已有的值
+ if (alarm != null) {
+ timePicker.setHour(alarm.getHour());
+ timePicker.setMinute(alarm.getMinute());
+ etDescription.setText(alarm.getDescription());
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setTitle(alarm == null ? "添加闹钟" : "编辑闹钟")
+ .setView(dialogView)
+ .setPositiveButton("确定", (dialog, which) -> {
+ int hour = timePicker.getHour();
+ int minute = timePicker.getMinute();
+ String description = etDescription.getText().toString().trim();
+
+ if (alarm == null) {
+ // 添加新闹钟
+ AlarmInfo newAlarm = new AlarmInfo(hour, minute);
+ newAlarm.setDescription(description);
+ long id = alarmDao.insert(newAlarm);
+ newAlarm.setId((int)id);
+ alarmList.add(newAlarm);
+ alarmAdapter.notifyItemInserted(alarmList.size() - 1);
+ scheduleAlarm(newAlarm);
+ } else {
+ // 更新现有闹钟
+ alarm.setHour(hour);
+ alarm.setMinute(minute);
+ alarm.setDescription(description);
+ alarmDao.update(alarm);
+ alarmAdapter.notifyDataSetChanged();
+ if (alarm.isEnabled()) {
+ cancelAlarm(alarm);
+ scheduleAlarm(alarm);
+ }
+ }
+ })
+ .setNegativeButton("取消", null);
+
+ builder.create().show();
+ }
+
+ private void scheduleAlarm(AlarmInfo alarm) {
+ Intent alarmIntent = new Intent(this, AlarmService.class);
+ alarmIntent.putExtra("alarm_id", alarm.getId());
+
+ PendingIntent pendingIntent = PendingIntent.getService(
+ this,
+ alarm.getId(),
+ alarmIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
+ );
+
+ AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
+
+ Calendar calendar = Calendar.getInstance();
+ calendar.set(Calendar.HOUR_OF_DAY, alarm.getHour());
+ calendar.set(Calendar.MINUTE, alarm.getMinute());
+ calendar.set(Calendar.SECOND, 0);
+
+ // 如果时间已过,设置为明天
+ if (calendar.getTimeInMillis() <= System.currentTimeMillis()) {
+ calendar.add(Calendar.DAY_OF_YEAR, 1);
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ alarmManager.setExactAndAllowWhileIdle(
+ AlarmManager.RTC_WAKEUP,
+ calendar.getTimeInMillis(),
+ pendingIntent
+ );
+ } else {
+ alarmManager.setExact(
+ AlarmManager.RTC_WAKEUP,
+ calendar.getTimeInMillis(),
+ pendingIntent
+ );
+ }
+ }
+
+ private void cancelAlarm(AlarmInfo alarm) {
+ Intent alarmIntent = new Intent(this, AlarmService.class);
+ PendingIntent pendingIntent = PendingIntent.getService(
+ this,
+ alarm.getId(),
+ alarmIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
+ );
+
+ AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
+ alarmManager.cancel(pendingIntent);
+ }
+
+ @Override
+ public void onItemClick(AlarmInfo alarm) {
+ showAlarmDialog(alarm);
+ }
+
+ @Override
+ public void onSwitchChanged(AlarmInfo alarm, boolean isChecked) {
+ alarm.setEnabled(isChecked);
+ alarmDao.update(alarm);
+
+ if (isChecked) {
+ scheduleAlarm(alarm);
+ } else {
+ cancelAlarm(alarm);
+ }
+ }
+
+ @Override
+ public void onDeleteClick(AlarmInfo alarm) {
+ int position = alarmList.indexOf(alarm);
+ if (position != -1) {
+ cancelAlarm(alarm);
+ alarmDao.delete(alarm.getId());
+ alarmList.remove(position);
+ alarmAdapter.notifyItemRemoved(position);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/Fall.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/Fall.java
new file mode 100644
index 0000000..02bebfe
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/Fall.java
@@ -0,0 +1,159 @@
+package edu.gatech.seclass.fall_detection_master;
+
+
+
+import android.util.Log;
+
+
+public class Fall {
+
+ private float highThresholdValue;
+ private float lowThresholdValue;
+ // private int SENSOR_RATE;
+ private boolean isFell;
+ public static float[] svmData;
+ public static float[] svmFilteringData;
+ public static int svmCount = 0;
+ public static final String TAG = "cauc";
+
+
+ public Fall(){
+ svmData = new float[150];
+ svmFilteringData = new float[150];
+ isFell = false;
+ }
+
+ /*
+ 设置阈值
+ */
+ public void setThresholdValue(float highThreshold, float lowThreshold){
+ this.highThresholdValue = highThreshold;
+ this.lowThresholdValue = lowThreshold;
+ Log.d(TAG, highThreshold + " " + lowThreshold);
+ }
+
+
+ /*
+ 设置传感器采样率
+ */
+// public void setSENSOR_RATE(int sensor_rate){
+// SENSOR_RATE = sensor_rate;
+// }
+ /*
+ 跌倒检测
+ */
+ public void fallDetection(){
+ Log.d(TAG, "Fall.fallDetection()");
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ boolean running = true;
+ while(running){
+ //阈值法
+ for(int i = 0; i < svmFilteringData.length; i++){
+ if(svmFilteringData[i] <= lowThresholdValue){
+ if(i < svmFilteringData.length-10){
+ for (int j = i; j < i + 10; j++) {
+ if (svmFilteringData[j] >= highThresholdValue) {
+ running = false;
+ setFell(true);
+ }
+ }
+ }else {
+ for (int j = i; j < svmFilteringData.length; j++) {
+ if (svmFilteringData[j] >= highThresholdValue) {
+ running = false;
+ setFell(true);
+ }
+ }
+ for (int k = 0; k < (10-(svmFilteringData.length - i)); k++){
+ if (svmFilteringData[k] >= highThresholdValue) {
+ running = false;
+ setFell(true);
+ }
+ }
+ }
+ }
+ }
+
+ }
+ }
+ }).start();
+ }
+
+ /*
+ 3s内svm原始数据收集
+ */
+ public static void svmCollector(float svm){
+
+ if(svmCount < svmData.length){
+ svmData[svmCount] = svm;
+ }else{
+ svmCount = 0;
+ svmData[svmCount] = svm;
+ }
+ svmCount++;
+// Log.d(TAG, "Fall.svmCollector" + svmData[svmCount]);
+ }
+
+ /*
+ svm中值滤波
+ */
+ public static void setSvmFilteringData(){
+ //中值滤波取的三个值
+ float s1, s2, s3, temp;
+ //冒泡排序
+ for (int i = 0; i < svmFilteringData.length-1; i++){
+ if(i == 0){
+ s1 = svmData[i];
+ s2 = svmData[i + 1];
+ s3 = svmData[i + 2];
+ }else if(i < svmFilteringData.length-2){
+ s1 = svmData[i - 1];
+ s2 = svmData[i];
+ s3 = svmData[i + 1];
+ }else{
+ s1 = svmData[i - 1];
+ s2 = svmData[i];
+ s3 = svmData[0];
+ }
+ if(s1 > s2){
+ temp = s1;
+ s1 = s2;
+ s2 = temp;
+ }
+ if(s2 > s3){
+ temp = s2;
+ s2 = s3;
+ s3 = temp;
+ }
+ svmFilteringData[i] = s2;
+ Log.d(TAG, s1 + " " + s2 + " " + s3);
+// Log.d(TAG, "Fall.setSvmFilteringData" + svmFilteringData[i]);
+ }
+
+ }
+
+ public boolean isFell() {
+// Log.e(TAG, "isFELL" + isFell);
+ return isFell;
+
+ }
+
+ public void setFell(boolean fell) {
+ isFell = fell;
+// Log.e(TAG, "setFELL" + isFell);
+ }
+
+ public void cleanData(){
+ Log.d(TAG , "Fall.clean()");
+ for (int i = 0; i < svmData.length; i++){
+ svmData[i] = 0;
+ }
+ //中值滤波
+ for (int i = 0; i < svmFilteringData.length; i++){
+ svmFilteringData[i] = 0;
+ }
+ }
+}
+
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/FallDetectionService.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/FallDetectionService.java
new file mode 100644
index 0000000..735caae
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/FallDetectionService.java
@@ -0,0 +1,236 @@
+package edu.gatech.seclass.fall_detection_master;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.BitmapFactory;
+import android.os.Build;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.util.Log;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import androidx.core.app.NotificationCompat;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+public class FallDetectionService extends Service {
+
+ private FallSensorManager fallSensorManager;
+ public Fall fall;
+ private final int FELL = 0;
+ private final int TIME = 1;
+ private boolean running = false;
+ private TextView countingView;
+ private Dialog dialog;
+ private Timer timer;
+ private final String TAG = "FallDetection";
+ private DetectThread detectThread;
+ private IntentFilter intentFilter;
+ private LocalBroadcastManager localBroadcastManager;
+ private FallLocalReceiver fallLocalReceiver;
+ private static final String CHANNEL_ID = "fall_detection_channel";
+ private static final String CHANNEL_NAME = "跌倒检测服务";
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.d(TAG, "FallDetectionService.onCreate()");
+
+ // 创建通知渠道
+ //createNotificationChannel();
+
+ fallSensorManager = new FallSensorManager(this);
+ fallSensorManager.initSensor();
+ fallSensorManager.registerSensor();
+ fall = new Fall();
+ fall.setThresholdValue(25, 5);
+ running = true;
+
+ localBroadcastManager = LocalBroadcastManager.getInstance(this);
+ intentFilter = new IntentFilter();
+ intentFilter.addAction("com.broadcast.FALL_LOCAL_BROADCAST");
+ fallLocalReceiver = new FallLocalReceiver();
+ localBroadcastManager.registerReceiver(fallLocalReceiver, intentFilter);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.d(TAG, "FallDetectionService.onStartCommand");
+ detectThread = new DetectThread();
+ detectThread.start();
+ return START_STICKY;
+ }
+
+ @Override
+ public void onDestroy() {
+ fallSensorManager.unregisterSensor();
+ localBroadcastManager.unregisterReceiver(fallLocalReceiver);
+ super.onDestroy();
+ }
+
+ class DetectThread extends Thread {
+ /*原有代码
+ @Override
+ public void run() {
+ fall.fallDetection();
+ Log.d(TAG, "DetectThread.start()");
+ while (running) {
+ if (fall.isFell()) {
+ Log.e(TAG, "跌倒了");
+ running = false;
+ Message msg = handler.obtainMessage();
+ msg.what = FELL;
+ handler.sendMessage(msg);
+ fall.setFell(false);
+ fall.cleanData();
+ stopSelf();
+ }
+ }
+ }
+
+ */
+ @Override
+ public void run() {
+ fall.fallDetection();
+ while (running) {
+ if (fall.isFell()) {
+ Log.e(TAG, "跌倒了");
+ running = false;
+ handler.obtainMessage(FELL).sendToTarget();
+ fall.setFell(false);
+ fall.cleanData();
+ stopSelf();
+ break;
+ }
+ try {
+ Thread.sleep(200); // 延时,减少循环的频率
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ }
+
+ private Handler handler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case FELL:
+ Log.e(TAG, "FELL");
+ showAlertDialog();
+ Intent intent = new Intent("com.broadcast.FALL_LOCAL_BROADCAST");
+ localBroadcastManager.sendBroadcast(intent);
+ break;
+
+ case TIME:
+ if (msg.arg1 > 0) {
+ countingView.setText(msg.arg1 + "秒后自动报警");
+ } else {
+ if (dialog != null) {
+ dialog.dismiss();
+ }
+ timer.cancel();
+ }
+ break;
+ }
+ }
+ };
+
+ private void showAlertDialog() {
+ countingView = new TextView(getApplicationContext());
+ AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext());
+ builder.setTitle("跌倒警报");
+ builder.setView(countingView);
+ builder.setMessage("检测到跌倒发生,是否发出警报?");
+ builder.setIcon(R.drawable.ic_warning);
+
+ builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ timer.cancel();
+ dialog.dismiss();
+ running = true;
+ detectThread.interrupt();
+ detectThread = null;
+ if (detectThread == null) {
+ detectThread = new DetectThread();
+ detectThread.start();
+ }
+ }
+ });
+ dialog = builder.create();
+ dialog.setCanceledOnTouchOutside(false);
+ // dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+ countDown();
+ dialog.show();
+ Log.d(TAG, "dialog.create()");
+ }
+
+ private void countDown() {
+ timer = new Timer();
+ TimerTask timerTask = new TimerTask() {
+ int countTime = 6;
+
+ @Override
+ public void run() {
+ if (countTime > 0) {
+ countTime--;
+ Message msgTime = handler.obtainMessage();
+ msgTime.what = TIME;
+ msgTime.arg1 = countTime;
+ handler.sendMessage(msgTime);
+ }
+ }
+ };
+ timer.schedule(timerTask, 100, 1000);
+ }
+
+ private void showInNotification() {
+ Intent intent = new Intent(this, MainActivity.class);
+ PendingIntent pi = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
+
+ Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
+ .setContentTitle("老人跌倒检测")
+ .setContentText("老人跌倒检测正在运行")
+ .setWhen(System.currentTimeMillis())
+ .setSmallIcon(R.drawable.ic_app)
+ .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_app))
+ .setContentIntent(pi)
+ .build();
+
+ startForeground(1, notification);
+ }
+
+ private void createNotificationChannel() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ NotificationChannel serviceChannel = new NotificationChannel(
+ CHANNEL_ID,
+ CHANNEL_NAME,
+ NotificationManager.IMPORTANCE_DEFAULT);
+ serviceChannel.setDescription("Channel description");
+ NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ if (manager != null) {
+ manager.createNotificationChannel(serviceChannel);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/FallLocalReceiver.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/FallLocalReceiver.java
new file mode 100644
index 0000000..157c233
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/FallLocalReceiver.java
@@ -0,0 +1,407 @@
+package edu.gatech.seclass.fall_detection_master;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.location.Address;
+import android.location.Geocoder;
+import android.location.Location;
+import android.location.LocationListener;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+import android.speech.tts.TextToSpeech;
+import android.telephony.SmsManager;
+import android.util.Log;
+import android.view.WindowManager;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.amap.api.location.AMapLocation;
+import com.amap.api.location.AMapLocationClient;
+import com.amap.api.location.AMapLocationClientOption;
+import com.amap.api.location.AMapLocationListener;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Timer;
+import java.util.TimerTask;
+
+public class FallLocalReceiver extends BroadcastReceiver implements AMapLocationListener, LocationListener, TextToSpeech.OnInitListener {
+
+ private TextView countingView;
+ private Dialog dialog;
+ private Timer timer;
+ private SharedPreferences sharedPreferences;
+ private Vibrator vibrator;
+ private boolean isVibrate;
+ private MediaPlayer mediaPlayer;
+
+ private AMapLocationClient locationClient;
+ private AMapLocationClientOption locationClientOption;
+ public String locationAddress;
+ public String locationTime;
+ private Context context;
+ private final String TAG = "FallDetection";
+ private TextToSpeech textToSpeech;
+
+ public FallLocalReceiver() {
+ }
+/*
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "FallLocalReceiver.onReceive()");
+ this.context = context;
+ showAlertDialog();
+
+ textToSpeech = new TextToSpeech(this, this);
+
+ sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
+ isVibrate = sharedPreferences.getBoolean("pre_key_vibrate", true);
+ if (isVibrate) {
+ startVibrate();
+ }
+ startAlarm();
+ try {
+ startLocation();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ */
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "FallLocalReceiver.onReceive()");
+ this.context = context;
+ showAlertDialog();
+
+ if (textToSpeech != null && textToSpeech.isSpeaking()) {
+ textToSpeech.stop();
+ }
+ /*
+ if (textToSpeech != null) {
+ textToSpeech.speak("检测到您已跌倒", TextToSpeech.QUEUE_ADD, null, "FallDetected");
+ }
+
+ */
+ if (textToSpeech != null) {
+ // 使用函数调用
+ speakFallDetected();
+ }
+
+
+ sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
+ isVibrate = sharedPreferences.getBoolean("pre_key_vibrate", true);
+ if (isVibrate) {
+ // startVibrate();
+ }
+ // startAlarm();
+ initializeTextToSpeech();
+
+
+ try {
+ startLocation();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void initializeTextToSpeech() {
+ textToSpeech = new TextToSpeech(context, this);
+ }
+/*
+ @Override
+ public void onInit(int status) {
+ if (status == TextToSpeech.SUCCESS) {
+ int result = textToSpeech.setLanguage(Locale.CHINESE);
+ if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
+ Log.e(TAG, "TextToSpeech: 语言不支持");
+ } else {
+ // 确保初始化成功后立即播报
+ speakFallDetected();
+ }
+ } else {
+ Log.e(TAG, "TextToSpeech 初始化失败");
+ }
+ }
+
+ */
+ @Override
+ public void onInit(int status) {
+ if (status == TextToSpeech.SUCCESS) {
+ int result = textToSpeech.setLanguage(Locale.CHINESE);
+
+ if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
+ //Toast.makeText(this, "TTS语音引擎不支持或缺少中文语音数据", Toast.LENGTH_LONG).show();
+ } else {
+ speakFallDetected(); // 当初始化成功后直接开始语音播报位置信息
+ }
+ } else {
+ // Toast.makeText(this, "TTS引擎初始化失败", Toast.LENGTH_LONG).show();
+ }
+ }
+
+ private void speakFallDetected() {
+ if (textToSpeech != null) {
+ textToSpeech.speak("检测到您已跌倒", TextToSpeech.QUEUE_ADD, null, "FallDetected");
+ }
+ }
+
+ /*
+ private void speakFallDetected() {
+ if (textToSpeech != null) {
+ textToSpeech.speak("检测到您已跌倒", TextToSpeech.QUEUE_FLUSH, null, "FallDetected");
+ }
+ }
+
+
+ */
+ private void showAlertDialog() {
+ //检查权限
+ if (!Settings.canDrawOverlays(context)) {
+ Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
+ context.startActivity(intent);
+ }
+
+ countingView = new TextView(context);
+ //动态请求权限
+ if (!Settings.canDrawOverlays(context)) {
+ Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
+ context.startActivity(intent);
+ }
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ //AlertDialog.Builder builder = new AlertDialog.Builder(context.getApplicationContext());
+
+ //补充
+ countingView.setText("您已摔倒"); // 假设倒计时开始时显示 5 秒
+ builder.setView(countingView);
+
+
+ builder.setTitle("跌倒警报");
+ builder.setView(countingView);
+ builder.setMessage("检测到跌倒发生,是否发出警报?");
+ builder.setIcon(R.drawable.ic_warning);
+ if (textToSpeech != null && textToSpeech.isSpeaking()) {
+ textToSpeech.stop();
+ }
+ /*
+ if (textToSpeech != null) {
+ textToSpeech.speak("检测到您已跌倒", TextToSpeech.QUEUE_ADD, null, "FallDetected");
+ }
+
+ */
+ if (textToSpeech != null) {
+ // 使用函数调用
+ speakFallDetected();
+ }
+
+ builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ timer.cancel();
+ dialog.dismiss();
+ if (isVibrate) {
+ // stopVibrate();
+ }
+ // stopAlarm();
+ Intent startIntent = new Intent(context, FallDetectionService.class);
+ context.startService(startIntent);
+ }
+ });
+ dialog = builder.create();
+ dialog.setCanceledOnTouchOutside(false);
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+ countDown();
+ dialog.show();
+ Log.d(TAG, "dialog.create()");
+ }
+
+ private void countDown() {
+ timer = new Timer();
+ TimerTask timerTask = new TimerTask() {
+ int countTime = 10;
+
+ @Override
+ public void run() {
+ if (countTime > 0) {
+ countTime--;
+ Message msgTime = handler.obtainMessage();
+ msgTime.arg1 = countTime;
+ handler.sendMessage(msgTime);
+ }
+ }
+ };
+ timer.schedule(timerTask, 50, 1000);
+ }
+
+ public Handler handler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+
+ if (msg.arg1 > 0) {
+ countingView.setText(msg.arg1 + "秒后自动报警");
+
+ /*
+ // 仅在首次倒计时启动时播放铃声
+ if (msg.arg1 == 10) {
+ startAlarm();
+ }
+
+ */
+
+ } else {
+ // 倒计时结束时停止铃声和震动
+ if (dialog != null) {
+ dialog.dismiss();
+ if (isVibrate) {
+ // stopVibrate();
+ }
+ //stopAlarm();
+ sendSMS(locationAddress, locationTime);
+ }
+ timer.cancel();
+ }
+ }
+ };
+
+ /*
+
+ private void startVibrate() {
+ if (vibrator == null) {
+ vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+ }
+ if (vibrator != null) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ VibrationEffect vibrationEffect = VibrationEffect.createWaveform(new long[]{500, 1000, 500, 1000}, 0);
+ vibrator.vibrate(vibrationEffect);
+ } else {
+ vibrator.vibrate(new long[]{500, 1000, 500, 1000}, 0);
+ }
+ Log.d(TAG, "开始震动");
+ } else {
+ Log.e(TAG, "无法获取震动服务");
+ }
+ }
+
+ private void stopVibrate() {
+ if (vibrator != null) {
+ vibrator.cancel();
+ }
+ }
+
+
+ private void startAlarm() {
+ if (mediaPlayer == null) {
+ mediaPlayer = MediaPlayer.create(context, R.raw.alarm_sound);
+ if (mediaPlayer == null) {
+ Log.e(TAG, "MediaPlayer 初始化失败,检查资源文件");
+ return;
+ }
+ mediaPlayer.setLooping(true);
+ }
+
+ if (!mediaPlayer.isPlaying()) {
+ mediaPlayer.start();
+ Log.d(TAG, "开始播放警报铃声");
+ }
+ }
+
+
+ private void stopAlarm() {
+ if (mediaPlayer != null) {
+ mediaPlayer.stop();
+ mediaPlayer.release();
+ mediaPlayer = null;
+ }
+ }
+
+
+ */
+ private void sendSMS(String address, String time) {
+ SmsManager smsManager = SmsManager.getDefault();
+ String name = sharedPreferences.getString("pre_key_name", null);
+ String phoneNum = sharedPreferences.getString("pre_key_phone", null);
+ String smsContent = time + name + "在" + address + "发生跌倒了!";
+ smsManager.sendTextMessage(phoneNum, null, smsContent, null, null);
+ Toast.makeText(context, "短信已经发出", Toast.LENGTH_SHORT).show();
+ }
+
+ private void startLocation() throws Exception {
+ Log.d(TAG, "FallLocalReceiver.startLocation()");
+ locationClient = new AMapLocationClient(context);
+ locationClientOption = new AMapLocationClientOption();
+ locationClientOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
+ locationClientOption.setOnceLocationLatest(true);
+ locationClient.setLocationListener(this);
+ locationClient.startLocation();
+ }
+
+ @Override
+ public void onLocationChanged(Location location) {
+ double latitude = location.getLatitude();
+ double longitude = location.getLongitude();
+ locationAddress = getFormattedAddress(latitude, longitude);
+ }
+
+ @Override
+ public void onStatusChanged(String s, int i, Bundle bundle) {
+ }
+
+ @Override
+ public void onProviderEnabled(String s) {
+ }
+
+ @Override
+ public void onProviderDisabled(String s) {
+ }
+
+ @Override
+ public void onLocationChanged(AMapLocation amapLocation) {
+ if (amapLocation != null) {
+ if (amapLocation.getErrorCode() == 0) {
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ Date date = new Date(amapLocation.getTime());
+ locationTime = df.format(date);
+ } else {
+ Log.e("AmapError", "location Error, ErrCode:" + amapLocation.getErrorCode() + ", errInfo:" + amapLocation.getErrorInfo());
+ }
+ }
+ }
+
+ private String getFormattedAddress(double latitude, double longitude) {
+ Geocoder geocoder = new Geocoder(context, Locale.getDefault());
+ List addresses;
+ String address = "";
+ try {
+ addresses = geocoder.getFromLocation(latitude, longitude, 1);
+ if (addresses != null && !addresses.isEmpty()) {
+ Address returnedAddress = addresses.get(0);
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i <= returnedAddress.getMaxAddressLineIndex(); i++) {
+ sb.append(returnedAddress.getAddressLine(i)).append("\n");
+ }
+ address = sb.toString();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return address;
+ }
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/FallSensorManager.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/FallSensorManager.java
new file mode 100644
index 0000000..ec61f07
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/FallSensorManager.java
@@ -0,0 +1,74 @@
+package edu.gatech.seclass.fall_detection_master;
+
+import static android.content.Context.SENSOR_SERVICE;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.util.Log;
+
+
+public class FallSensorManager {
+ private SensorManager sensorManager;
+ private Sensor accelerometer;
+ private Context context;
+ private float accX, accY, accZ;
+ private float svm;
+ public Fall fall;
+ private final String TAG = "FallDetection";
+
+ public FallSensorManager(Context context){
+ this.context = context;
+ fall = new Fall();
+ }
+ /*
+ 加载传感器
+ */
+ public void initSensor(){
+ //获取SensorManager,系统的传感器管理服务
+ sensorManager = (SensorManager) context.getSystemService(SENSOR_SERVICE);
+ //获取accelerometer加速度传感器
+ accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ Log.d(TAG, "FallSensorManager.initSensor()");
+ }
+
+ /*
+ 注册传感器
+ */
+ public void registerSensor(){
+ sensorManager.registerListener(sensorEventListener,
+ accelerometer, SensorManager.SENSOR_DELAY_GAME);
+ Log.d(TAG, "FallSensorManager.registerSensor()");
+ }
+ /*
+ 取消注册传感器
+ */
+ public void unregisterSensor(){
+ sensorManager.unregisterListener(sensorEventListener);
+ Log.d(TAG, "FallSensorManager.unregisterSensor");
+ }
+
+ public SensorEventListener sensorEventListener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ switch (event.sensor.getType()) {
+ case Sensor.TYPE_ACCELEROMETER:
+ accX = event.values[0];
+ accY = event.values[1];
+ accZ = event.values[2];
+ svm = (float) Math.sqrt(accX * accX + accY * accY + accZ * accZ);
+// Log.d(TAG,accX + " " + accY + " " + accZ );
+ Fall.svmCollector(svm);
+ Fall.setSvmFilteringData();
+ break;
+ }
+ }
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+
+ }
+ };
+
+}
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/GeofenceActivity.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/GeofenceActivity.java
new file mode 100644
index 0000000..9a3d55b
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/GeofenceActivity.java
@@ -0,0 +1,286 @@
+package edu.gatech.seclass.fall_detection_master;
+
+import android.Manifest;
+import android.content.pm.PackageManager;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+
+import com.amap.api.maps.AMap;
+import com.amap.api.maps.CameraUpdateFactory;
+import com.amap.api.maps.MapView;
+import com.amap.api.maps.MapsInitializer;
+import com.amap.api.maps.model.Circle;
+import com.amap.api.maps.model.CircleOptions;
+import com.amap.api.maps.model.LatLng;
+import com.amap.api.maps.model.Marker;
+import com.amap.api.maps.model.MarkerOptions;
+import com.amap.api.location.AMapLocationClient;
+import com.amap.api.location.AMapLocationClientOption;
+import com.amap.api.location.AMapLocationListener;
+
+public class GeofenceActivity extends AppCompatActivity {
+ private MapView mapView;
+ private AMap aMap;
+ private SeekBar seekBarRadius;
+ private TextView tvRadius;
+ private Button btnSave;
+
+ private Circle geofenceCircle;
+ private Marker centerMarker;
+ private LatLng centerPoint;
+ private int radius = 100;
+
+ private AMapLocationClient locationClient;
+ private AMapLocationClientOption locationOption;
+
+ private static final int PERMISSION_REQUEST_CODE = 1;
+ private static final String[] REQUIRED_PERMISSIONS = new String[] {
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.READ_PHONE_STATE
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // 先设置布局
+ setContentView(R.layout.activity_geofence);
+
+ // 初始化视图
+ initViews();
+
+ // 检查权限
+ if (!checkPermissions()) {
+ requestPermissions();
+ } else {
+ initializeMapAndLocation();
+ }
+ }
+
+ private boolean checkPermissions() {
+ for (String permission : REQUIRED_PERMISSIONS) {
+ if (ContextCompat.checkSelfPermission(this, permission)
+ != PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void requestPermissions() {
+ ActivityCompat.requestPermissions(
+ this,
+ REQUIRED_PERMISSIONS,
+ PERMISSION_REQUEST_CODE
+ );
+ }
+
+ private void initializeMapAndLocation() {
+ try {
+ // 设置隐私政策同意
+ AMapLocationClient.updatePrivacyShow(this, true, true);
+ AMapLocationClient.updatePrivacyAgree(this, true);
+ MapsInitializer.updatePrivacyShow(this, true, true);
+ MapsInitializer.updatePrivacyAgree(this, true);
+
+ // 初始化定位
+ locationClient = new AMapLocationClient(getApplicationContext());
+ locationOption = new AMapLocationClientOption();
+ locationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
+ locationOption.setOnceLocation(true);
+ locationClient.setLocationOption(locationOption);
+
+ // 设置定位回调
+ locationClient.setLocationListener(location -> {
+ if (location != null) {
+ LatLng currentLocation = new LatLng(location.getLatitude(), location.getLongitude());
+ if (aMap != null) {
+ aMap.moveCamera(CameraUpdateFactory.newLatLngZoom(currentLocation, 15));
+ }
+ }
+ });
+
+ // 初始化地图
+ initMap(null);
+ setupListeners();
+
+ // 开始定位
+ if (locationClient != null) {
+ locationClient.startLocation();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ Toast.makeText(this, "初始化失败: " + e.getMessage(), Toast.LENGTH_LONG).show();
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (requestCode == PERMISSION_REQUEST_CODE) {
+ boolean allGranted = true;
+ for (int result : grantResults) {
+ if (result != PackageManager.PERMISSION_GRANTED) {
+ allGranted = false;
+ break;
+ }
+ }
+
+ if (allGranted) {
+ initializeMapAndLocation();
+ } else {
+ Toast.makeText(this, "需要定位权限才能使用电子围栏功能", Toast.LENGTH_LONG).show();
+ finish();
+ }
+ }
+ }
+
+ private void initViews() {
+ mapView = findViewById(R.id.map_view);
+ seekBarRadius = findViewById(R.id.seekbar_radius);
+ tvRadius = findViewById(R.id.tv_radius);
+ btnSave = findViewById(R.id.btn_save);
+
+ seekBarRadius.setProgress(radius);
+ tvRadius.setText(radius + "米");
+ }
+
+ private void initMap(Bundle savedInstanceState) {
+ try {
+ mapView.onCreate(savedInstanceState);
+ if (aMap == null) {
+ aMap = mapView.getMap();
+
+ // 设置地图参数
+ aMap.setMyLocationEnabled(true);
+ aMap.getUiSettings().setMyLocationButtonEnabled(true);
+ aMap.getUiSettings().setZoomControlsEnabled(true);
+ aMap.getUiSettings().setScaleControlsEnabled(true);
+ }
+
+ // 设置地图点击监听
+ aMap.setOnMapClickListener(latLng -> {
+ centerPoint = latLng;
+ updateGeofence();
+ });
+ } catch (Exception e) {
+ e.printStackTrace();
+ Toast.makeText(this, "地图初始化失败: " + e.getMessage(), Toast.LENGTH_LONG).show();
+ }
+ }
+
+ private void setupListeners() {
+ seekBarRadius.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ radius = progress;
+ tvRadius.setText(progress + "米");
+ if (centerPoint != null) {
+ updateGeofence();
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {}
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {}
+ });
+
+ btnSave.setOnClickListener(v -> saveGeofence());
+ }
+
+ private void updateGeofence() {
+ // 清除旧的标记
+ if (geofenceCircle != null) {
+ geofenceCircle.remove();
+ }
+ if (centerMarker != null) {
+ centerMarker.remove();
+ }
+
+ // 添加新的围栏圆圈
+ CircleOptions circleOptions = new CircleOptions()
+ .center(centerPoint)
+ .radius(radius)
+ .strokeWidth(5)
+ .strokeColor(Color.argb(50, 1, 1, 1))
+ .fillColor(Color.argb(100, 255, 0, 0));
+ geofenceCircle = aMap.addCircle(circleOptions);
+
+ // 添加中心点标记
+ MarkerOptions markerOptions = new MarkerOptions()
+ .position(centerPoint)
+ .title("围栏中心");
+ centerMarker = aMap.addMarker(markerOptions);
+
+ // 移动相机到围栏中心
+ aMap.moveCamera(CameraUpdateFactory.newLatLngZoom(centerPoint, 15));
+ }
+
+ private void saveGeofence() {
+ if (centerPoint == null) {
+ Toast.makeText(this, "请先在地图上选择围栏中心点", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ // 保存围栏设置到SharedPreferences
+ getSharedPreferences("geofence", MODE_PRIVATE)
+ .edit()
+ .putFloat("latitude", (float) centerPoint.latitude)
+ .putFloat("longitude", (float) centerPoint.longitude)
+ .putInt("radius", radius)
+ .apply();
+
+ Toast.makeText(this, "电子围栏设置已保存", Toast.LENGTH_SHORT).show();
+ finish();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (mapView != null) {
+ mapView.onResume();
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (mapView != null) {
+ mapView.onPause();
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mapView != null) {
+ mapView.onDestroy();
+ }
+ if (locationClient != null) {
+ locationClient.onDestroy();
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ if (mapView != null) {
+ mapView.onSaveInstanceState(outState);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/HealthInfoActivity.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/HealthInfoActivity.java
new file mode 100644
index 0000000..244d963
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/HealthInfoActivity.java
@@ -0,0 +1,76 @@
+package edu.gatech.seclass.fall_detection_master;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+import androidx.appcompat.app.AppCompatActivity;
+import edu.gatech.seclass.fall_detection_master.model.HealthInfo;
+import edu.gatech.seclass.fall_detection_master.db.HealthInfoDao;
+
+public class HealthInfoActivity extends AppCompatActivity {
+ private EditText etSystolic;
+ private EditText etDiastolic;
+ private EditText etHeartRate;
+ private EditText etBloodSugar;
+ private Button btnSave;
+ private Button btnViewReport;
+ private HealthInfoDao healthInfoDao;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_health_info);
+
+ initViews();
+ initData();
+ setupListeners();
+ }
+
+ private void initViews() {
+ etSystolic = findViewById(R.id.et_systolic);
+ etDiastolic = findViewById(R.id.et_diastolic);
+ etHeartRate = findViewById(R.id.et_heart_rate);
+ etBloodSugar = findViewById(R.id.et_blood_sugar);
+ btnSave = findViewById(R.id.btn_save);
+ btnViewReport = findViewById(R.id.btn_view_report);
+ }
+
+ private void initData() {
+ healthInfoDao = new HealthInfoDao(this);
+ }
+
+ private void setupListeners() {
+ btnSave.setOnClickListener(v -> saveHealthInfo());
+ btnViewReport.setOnClickListener(v -> viewHealthReport());
+ }
+
+ private void saveHealthInfo() {
+ try {
+ HealthInfo healthInfo = new HealthInfo();
+ healthInfo.setBloodPressureHigh(Integer.parseInt(etSystolic.getText().toString()));
+ healthInfo.setBloodPressureLow(Integer.parseInt(etDiastolic.getText().toString()));
+ healthInfo.setHeartRate(Integer.parseInt(etHeartRate.getText().toString()));
+ healthInfo.setBloodSugar(Float.parseFloat(etBloodSugar.getText().toString()));
+
+ healthInfoDao.insert(healthInfo);
+ Toast.makeText(this, "健康信息保存成功", Toast.LENGTH_SHORT).show();
+ clearInputs();
+ } catch (NumberFormatException e) {
+ Toast.makeText(this, "请输入有效的数值", Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void viewHealthReport() {
+ Intent intent = new Intent(this, HealthReportActivity.class);
+ startActivity(intent);
+ }
+
+ private void clearInputs() {
+ etSystolic.setText("");
+ etDiastolic.setText("");
+ etHeartRate.setText("");
+ etBloodSugar.setText("");
+ }
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/HealthReportActivity.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/HealthReportActivity.java
new file mode 100644
index 0000000..16e1bf2
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/HealthReportActivity.java
@@ -0,0 +1,69 @@
+package edu.gatech.seclass.fall_detection_master;
+
+import android.os.Bundle;
+import android.widget.TextView;
+import androidx.appcompat.app.AppCompatActivity;
+import edu.gatech.seclass.fall_detection_master.db.HealthInfoDao;
+import edu.gatech.seclass.fall_detection_master.model.HealthInfo;
+import java.util.List;
+
+public class HealthReportActivity extends AppCompatActivity {
+ private TextView tvReport;
+ private HealthInfoDao healthInfoDao;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_health_report);
+
+ tvReport = findViewById(R.id.tv_report);
+ healthInfoDao = new HealthInfoDao(this);
+
+ generateReport();
+ }
+
+ private void generateReport() {
+ List recentRecords = healthInfoDao.getRecentRecords(7);
+
+ StringBuilder report = new StringBuilder();
+ report.append("健康状况分析报告\n\n");
+
+ if (recentRecords.isEmpty()) {
+ report.append("暂无健康记录数据");
+ } else {
+ // 计算平均值
+ double avgSystolic = 0, avgDiastolic = 0, avgHeartRate = 0, avgBloodSugar = 0;
+ for (HealthInfo info : recentRecords) {
+ avgSystolic += info.getBloodPressureHigh();
+ avgDiastolic += info.getBloodPressureLow();
+ avgHeartRate += info.getHeartRate();
+ avgBloodSugar += info.getBloodSugar();
+ }
+
+ int size = recentRecords.size();
+ avgSystolic /= size;
+ avgDiastolic /= size;
+ avgHeartRate /= size;
+ avgBloodSugar /= size;
+
+ report.append("最近").append(size).append("次记录的平均值:\n\n");
+ report.append(String.format("血压: %.0f/%.0f mmHg (高压/低压)\n", avgSystolic, avgDiastolic));
+ report.append(String.format("心率: %.0f 次/分\n", avgHeartRate));
+ report.append(String.format("血糖: %.1f mmol/L\n\n", avgBloodSugar));
+
+ // 健康建议
+ report.append("健康建议:\n");
+ if (avgSystolic > 140 || avgDiastolic > 90) {
+ report.append("· 血压偏高,建议减少盐分摄入,保持规律运动\n");
+ }
+ if (avgHeartRate > 100) {
+ report.append("· 心率偏快,建议注意休息,避免剧烈运动\n");
+ }
+ if (avgBloodSugar > 7.0) {
+ report.append("· 血糖偏高,建议控制饮食,规律作息\n");
+ }
+ }
+
+ tvReport.setText(report.toString());
+ }
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/HomeFragment.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/HomeFragment.java
new file mode 100644
index 0000000..4c8ae42
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/HomeFragment.java
@@ -0,0 +1,38 @@
+package edu.gatech.seclass.fall_detection_master;
+
+import android.app.Fragment;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.Nullable;
+
+import com.suke.widget.SwitchButton;
+
+public class HomeFragment extends Fragment {
+
+ private SwitchButton switchButton;
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_home,null);
+ switchButton = (SwitchButton) view.findViewById(R.id.switchButton);
+ switchButton.setOnCheckedChangeListener(new SwitchButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(SwitchButton view, boolean isChecked) {
+ if(view.isChecked()){
+ Intent startIntent = new Intent(getContext(), FallDetectionService.class);
+ getContext().startService(startIntent);
+ }else{
+ Intent stopIntent = new Intent(getContext(), FallDetectionService.class);
+ getContext().stopService(stopIntent);
+ }
+ }
+ });
+ return view;
+ }
+
+}
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/LoginActivity.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/LoginActivity.java
new file mode 100644
index 0000000..5dbb4da
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/LoginActivity.java
@@ -0,0 +1,68 @@
+package edu.gatech.seclass.fall_detection_master;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+import androidx.appcompat.app.AppCompatActivity;
+import edu.gatech.seclass.fall_detection_master.db.DBHelper;
+import edu.gatech.seclass.fall_detection_master.db.UserDao;
+
+public class LoginActivity extends AppCompatActivity {
+ private EditText etUsername;
+ private EditText etPassword;
+ private Button btnLogin;
+ private Button btnRegister;
+ private UserDao userDao;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_login);
+
+ // 确保数据库表存在
+ DBHelper dbHelper = new DBHelper(this);
+ dbHelper.ensureTableExists();
+
+ initViews();
+ userDao = new UserDao(this);
+ }
+
+ private void initViews() {
+ etUsername = findViewById(R.id.et_username);
+ etPassword = findViewById(R.id.et_password);
+ btnLogin = findViewById(R.id.btn_login);
+ btnRegister = findViewById(R.id.btn_register);
+
+ btnLogin.setOnClickListener(v -> login());
+ btnRegister.setOnClickListener(v -> {
+ startActivity(new Intent(this, RegisterActivity.class));
+ });
+ }
+
+ private void login() {
+ String username = etUsername.getText().toString().trim();
+ String password = etPassword.getText().toString().trim();
+
+ if (username.isEmpty() || password.isEmpty()) {
+ Toast.makeText(this, "请输入用户名和密码", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ if (userDao.login(username, password)) {
+ // 登录成功,保存登录状态
+ getSharedPreferences("user", MODE_PRIVATE)
+ .edit()
+ .putString("username", username)
+ .putBoolean("isLogin", true)
+ .apply();
+
+ // 跳转到主界面
+ startActivity(new Intent(this, MainActivity.class));
+ finish();
+ } else {
+ Toast.makeText(this, "用户名或密码错误", Toast.LENGTH_SHORT).show();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/MainActivity.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/MainActivity.java
new file mode 100644
index 0000000..1131287
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/MainActivity.java
@@ -0,0 +1,248 @@
+package edu.gatech.seclass.fall_detection_master;
+
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.core.view.GravityCompat;
+import androidx.drawerlayout.widget.DrawerLayout;
+
+import android.app.AlertDialog;
+import android.app.FragmentTransaction;
+import android.content.Intent;
+import android.os.Bundle;
+import android.speech.tts.TextToSpeech;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import com.google.android.material.navigation.NavigationView;
+
+import java.util.Locale;
+
+public class MainActivity extends AppCompatActivity implements TextToSpeech.OnInitListener{
+
+
+ private DrawerLayout mDrawerLayout;
+ private NavigationView mNavigationView;
+ private Toolbar mToolbar;
+ // private ActionBarDrawerToggle mDrawerToggle;
+ private HomeFragment homeFragment;
+ private SettingsFragment settingsFragment;
+ private TextToSpeech textToSpeech;
+ private final int HOME = 1;
+ private final int SETTINGS = 2;
+ private final int ABOUT = 3;
+
+
+
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ textToSpeech = new TextToSpeech(this, this);
+ mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+
+// mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, mToolbar, R.string.drawer_open,
+// R.string.drawer_close);
+// mDrawerToggle.syncState();
+// mDrawerLayout.addDrawerListener(mDrawerToggle);
+ mNavigationView = (NavigationView) findViewById(R.id.navigation_view);
+
+ mToolbar = (Toolbar) findViewById(R.id.toolbar);
+ //为activity窗口设置活动栏
+// setSupportActionBar(mToolbar);
+
+ final ActionBar actionBar = getSupportActionBar();
+ //设置导航图标
+ actionBar.setHomeAsUpIndicator(R.drawable.ic_menu);
+ actionBar.setDisplayHomeAsUpEnabled(true);
+
+ mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+
+ mNavigationView = (NavigationView) findViewById(R.id.navigation_view);
+ if (mNavigationView != null) {
+ setupDrawerContent(mNavigationView);
+ }
+
+// switchToHome();
+ showFragment(HOME);
+
+ // 检查是否需要显示围栏警告
+ if (getIntent().getBooleanExtra("show_geofence_alert", false)) {
+ showGeofenceAlertDialog();
+ }
+
+ }
+
+ private void setupDrawerContent(NavigationView navigationView) {
+ navigationView.setNavigationItemSelectedListener(
+ new NavigationView.OnNavigationItemSelectedListener() {
+ @Override
+ public boolean onNavigationItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.navigation_item_home:
+ showFragment(HOME);
+ mToolbar.setTitle(R.string.app_name);
+ break;
+ case R.id.navigation_item_place:
+ switchToPlace();
+ break;
+ case R.id.navigation_item_settings:
+ showFragment(SETTINGS);
+ mToolbar.setTitle(R.string.navigation_settings);
+ break;
+ case R.id.nav_map:
+ Intent mapIntent = new Intent(MainActivity.this, GeofenceActivity.class);
+ startActivity(mapIntent);
+ break;
+ case R.id.nav_health:
+ Intent healthIntent = new Intent(MainActivity.this, HealthInfoActivity.class);
+ startActivity(healthIntent);
+ break;
+ case R.id.nav_report:
+ Intent reportIntent = new Intent(MainActivity.this, HealthReportActivity.class);
+ startActivity(reportIntent);
+ break;
+ case R.id.nav_alarm:
+ Intent alarmIntent = new Intent(MainActivity.this, AlarmActivity.class);
+ startActivity(alarmIntent);
+ break;
+ }
+ item.setChecked(true);
+ mDrawerLayout.closeDrawers();
+ return true;
+ }
+ });
+ }
+
+ // private void switchToHome() {
+// getSupportFragmentManager().beginTransaction().replace(R.id.frame_content, new HomeFragment()).commit();
+// mToolbar.setTitle(R.string.app_name);
+// }
+//
+ private void switchToPlace() {
+// getSupportFragmentManager().beginTransaction().replace(R.id.frame_content, new PlaceFragment()).commit();
+// mToolbar.setTitle(R.string.navigation_place);
+ mToolbar.setTitle(R.string.app_name);
+ startActivity(new Intent(this, MapActivity.class));
+
+ }
+//
+// private void switchToSettings() {
+// getSupportFragmentManager().beginTransaction().replace(R.id.frame_content, new SettingsFragment()).commit();
+// mToolbar.setTitle(R.string.navigation_settings);
+// }
+//
+// private void switchToAbout() {
+// getSupportFragmentManager().beginTransaction().replace(R.id.frame_content, new AboutFragment()).commit();
+// mToolbar.setTitle(R.string.navigation_about);
+//
+// }
+
+ public void showFragment(int index){
+ FragmentTransaction ft = getFragmentManager().beginTransaction();
+ //想要显示一个fragment,先隐藏所有fragment,防止重叠
+ hideFragment(ft);
+
+ switch (index){
+ case HOME:
+ if(homeFragment != null){
+ ft.show(homeFragment);
+ }else{
+ homeFragment = new HomeFragment();
+ ft.add(R.id.frame_content, homeFragment);
+ }
+ break;
+ case SETTINGS:
+ if(settingsFragment != null){
+ ft.show(settingsFragment);
+ }else{
+ settingsFragment = new SettingsFragment();
+ ft.add(R.id.frame_content, settingsFragment);
+ }
+ break;
+ }
+ ft.commit();
+ }
+ /*
+ 当fragment已被实例化,就隐藏起来
+ */
+ public void hideFragment(FragmentTransaction ft){
+ if(homeFragment != null){
+ ft.hide(homeFragment);
+ }
+ if(settingsFragment != null){
+ ft.hide(settingsFragment);
+ }
+
+
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu)
+ {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item)
+ {
+ if(item.getItemId() == android.R.id.home)
+ {
+ mDrawerLayout.openDrawer(GravityCompat.START);
+ return true ;
+ }
+ if(item.getItemId() == R.id.id_menu_settings){
+ showFragment(SETTINGS);
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+
+ @Override
+ public void onInit(int status) {
+// if (status == TextToSpeech.SUCCESS) {
+// int result = textToSpeech.setLanguage(Locale.CHINESE);
+//
+// if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
+// Toast.makeText(this, "TTS语音引擎不支持或缺少中文语音数据", Toast.LENGTH_LONG).show();
+// } else {
+// String welcomeMessage = "欢迎使用老人跌倒监测系统。";
+// textToSpeech.speak(welcomeMessage, TextToSpeech.QUEUE_FLUSH, null, null);
+// Myapp.textToSpeech = textToSpeech;
+// }
+// } else {
+// Toast.makeText(this, "TTS引擎初始化失败", Toast.LENGTH_LONG).show();
+// }
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (textToSpeech != null) {
+ textToSpeech.stop();
+ textToSpeech.shutdown();
+ }
+ super.onDestroy();
+ }
+
+ private void showGeofenceAlertDialog() {
+ new AlertDialog.Builder(this)
+ .setTitle("安全提醒")
+ .setMessage("您已离开设定的安全区域,请注意安全!")
+ .setIcon(R.drawable.ic_warning)
+ .setPositiveButton("我知道了", (dialog, which) -> {
+ // 播放语音提醒
+ if (textToSpeech != null) {
+ textToSpeech.speak("请注意安全,尽快返回安全区域",
+ TextToSpeech.QUEUE_FLUSH, null, null);
+ }
+ })
+ .setCancelable(false)
+ .show();
+ }
+
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/MapActivity.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/MapActivity.java
new file mode 100644
index 0000000..4fc7d9f
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/MapActivity.java
@@ -0,0 +1,394 @@
+package edu.gatech.seclass.fall_detection_master;
+
+import android.Manifest;
+import android.content.pm.PackageManager;
+import android.location.Address;
+import android.location.Geocoder;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.speech.tts.TextToSpeech;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.ToggleButton;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.core.app.ActivityCompat;
+
+import com.amap.api.location.AMapLocation;
+import com.amap.api.location.AMapLocationClient;
+import com.amap.api.location.AMapLocationClientOption;
+import com.amap.api.location.AMapLocationListener;
+import com.amap.api.maps.AMap;
+import com.amap.api.maps.CameraUpdateFactory;
+import com.amap.api.maps.LocationSource;
+import com.amap.api.maps.MapView;
+import com.amap.api.maps.MapsInitializer;
+import com.amap.api.maps.UiSettings;
+import com.google.android.material.navigation.NavigationView;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+
+public class MapActivity extends AppCompatActivity implements LocationSource, AMapLocationListener, TextToSpeech.OnInitListener, LocationListener {
+ private MapView mMapView;
+ private AMap aMap;
+ private LocationSource.OnLocationChangedListener mListener;
+ private AMapLocationClient mLocationClient;
+ private AMapLocationClientOption mLocationOption;
+ private UiSettings mUiSettings;
+ private static final int LOCATION_PERMISSION_REQUEST_CODE = 1;
+
+ private LocationManager locationManager;
+ private TextToSpeech textToSpeech;
+ private String locationAddress;
+ private ToggleButton tb;
+ private TextView textView;
+ private NavigationView mNavigationView;
+ private Toolbar mToolbar;
+ public String nowaddress;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ textToSpeech = new TextToSpeech(this, this);
+ setContentView(R.layout.map_activity);
+
+ textView = (TextView) findViewById(R.id.address);
+
+ mNavigationView = (NavigationView) findViewById(R.id.navigation_view);
+ mToolbar = (Toolbar) findViewById(R.id.toolbar);
+ mToolbar.setTitle(R.string.navigation_place);
+
+
+ MapsInitializer.updatePrivacyShow(this,true,true);
+ MapsInitializer.updatePrivacyAgree(this,true);
+ //为activity窗口设置活动栏
+// setSupportActionBar(mToolbar);
+ final ActionBar actionBar = getSupportActionBar();
+ //设置返回图标
+ actionBar.setHomeAsUpIndicator(0);
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onBackPressed();
+ }
+ });
+ // 检查定位权限
+ if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
+ != PackageManager.PERMISSION_GRANTED) {
+ // 申请定位权限
+ ActivityCompat.requestPermissions(this,
+ new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
+ LOCATION_PERMISSION_REQUEST_CODE);
+ } else {
+ // 获取LocationManager实例
+ locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
+ // 获取TextToSpeech实例并设置初始化监听器
+ textToSpeech = new TextToSpeech(this, this);
+ // 开始监听位置变化
+ startLocationUpdates();
+ }
+
+
+ //获取地图控件引用
+ mMapView = (MapView) findViewById(R.id.map);
+ //在activity执行onCreate时执行mMapView.onCreate(savedInstanceState),创建地图
+ //实现地图生命周期管理
+ mMapView.onCreate(savedInstanceState);
+
+ //初始化AMap对象
+ aMap = mMapView.getMap();
+
+ // 设置定位监听,如果不设置此定位资源则定位按钮不可点击
+ aMap.setLocationSource(this);
+ // 设置为true表示显示定位层并可触发定位,false表示隐藏定位层并不可触发定位,默认是false
+ aMap.setMyLocationEnabled(true);
+
+ // 设置定位的类型为定位模式,有定位、跟随或地图根据面向方向旋转几种
+ aMap.setMyLocationType(AMap.LOCATION_TYPE_LOCATE);
+
+ //实例化UiSettings类
+ mUiSettings = aMap.getUiSettings();
+ //显示指南针
+ mUiSettings.setCompassEnabled(true);
+ // 显示默认的定位按钮
+ mUiSettings.setMyLocationButtonEnabled(true);
+ //显示比例尺控件
+ mUiSettings.setScaleControlsEnabled(true);
+
+
+ tb = (ToggleButton) findViewById(R.id.tb);
+ tb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (isChecked) {
+ //设置使用卫星地图
+ aMap.setMapType(AMap.MAP_TYPE_SATELLITE);
+ } else {
+ //设置使用普通地图
+ aMap.setMapType(AMap.MAP_TYPE_NORMAL);
+ }
+ }
+ });
+
+ }
+
+ private void startLocationUpdates() {
+ // 设置最小时间间隔和最小距离,这里设置为1000毫秒和1米
+ // 这样当位置发生变化时,会在接下来的1000毫秒内获取位置信息
+ if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
+ // TODO: Consider calling
+ // ActivityCompat#requestPermissions
+ // here to request the missing permissions, and then overriding
+ // public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ // int[] grantResults)
+ // to handle the case where the user grants the permission. See the documentation
+ // for ActivityCompat#requestPermissions for more details.
+ return;
+ }
+ locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
+ 10000, 10, this);
+ }
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) {
+ // 检查定位权限授权结果
+ if (grantResults.length > 0 &&
+ grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ // 获取LocationManager实例
+ locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
+ // 开始监听位置变化
+ startLocationUpdates();
+ } else {
+ // 处理没有授权的情况
+ }
+ }
+ }
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ //在activity执行onResume时执行mMapView.onResume(),重新绘制加载地图
+ //实现地图生命周期管理
+ mMapView.onResume();
+
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ //在activity执行onPause时执行mMapView.onPause(),暂停地图的绘制
+ //实现地图生命周期管理
+ mMapView.onPause();
+ }
+ public void onLocationChanged(Location location) {
+ // 当位置发生变化时,更新界面上的位置信息
+ double latitude = location.getLatitude();
+ double longitude = location.getLongitude();
+ // TODO: 在界面上显示位置信息
+ locationAddress = getFormattedAddress(latitude, longitude); // 获取格式化的位置地址
+ //textView.setText(latitude+"\n"+longitude+locationAddress);
+ speakLocationInformation(); // 开始语音播报位置信息
+ }
+
+ private String getFormattedAddress(double latitude, double longitude) {
+ Geocoder geocoder = new Geocoder(this, Locale.getDefault());
+ List addresses;
+ String address = "";
+
+ try {
+ addresses = geocoder.getFromLocation(latitude, longitude, 1);
+ if (addresses != null && addresses.size() > 0) {
+ Address returnedAddress = addresses.get(0);
+ StringBuilder sb = new StringBuilder();
+
+ for (int i = 0; i <= returnedAddress.getMaxAddressLineIndex(); i++) {
+ sb.append(returnedAddress.getAddressLine(i)).append("\n");
+ }
+
+ address = sb.toString();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ // 处理异常情况
+ }
+
+ return address;
+ }
+
+ private void speakLocationInformation() {
+ if (textToSpeech != null && textToSpeech.isSpeaking()) {
+ textToSpeech.stop();
+ }
+ if (textToSpeech != null) {
+ textToSpeech.speak("您现在位于" + locationAddress, TextToSpeech.QUEUE_FLUSH, null, null);
+ }
+ }
+
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ // 当位置提供者状态变化时,处理逻辑
+ }
+
+ @Override
+ public void onProviderEnabled(String provider) {
+ // 当位置提供者启用时,处理逻辑
+ }
+
+ @Override
+ public void onProviderDisabled(String provider) {
+ // 当位置提供者禁用时,处理逻辑
+ }
+
+ @Override
+ public void onInit(int status) {
+ if (status == TextToSpeech.SUCCESS) {
+ int result = textToSpeech.setLanguage(Locale.CHINESE);
+
+ if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
+ //Toast.makeText(this, "TTS语音引擎不支持或缺少中文语音数据", Toast.LENGTH_LONG).show();
+ } else {
+ speakLocationInformation(); // 当初始化成功后直接开始语音播报位置信息
+ }
+ } else {
+ Toast.makeText(this, "TTS引擎初始化失败", Toast.LENGTH_LONG).show();
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ // 停止位置监听
+ locationManager.removeUpdates(this);
+ // 释放TextToSpeech资源
+ if (textToSpeech != null) {
+ textToSpeech.stop();
+ textToSpeech.shutdown();
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ //在activity执行onSaveInstanceState时执行mMapView.onSaveInstanceState(outState),保存地图当前的状态
+ //实现地图生命周期管理
+ mMapView.onSaveInstanceState(outState);
+ }
+
+
+ /**
+ * 激活定位
+ */
+ @Override
+ public void activate(OnLocationChangedListener onLocationChangedListener) {
+ mListener = onLocationChangedListener;
+ if (mLocationClient == null) {
+ //初始化定位
+ try {
+ mLocationClient = new AMapLocationClient(this);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ //初始化定位参数
+ mLocationOption = new AMapLocationClientOption();
+ //设置定位回调监听
+ mLocationClient.setLocationListener(this);
+ //设置为高精度定位模式
+ mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
+ //设置定位参数
+ mLocationClient.setLocationOption(mLocationOption);
+ // 此方法为每隔固定时间会发起一次定位请求,为了减少电量消耗或网络流量消耗,
+ // 注意设置合适的定位时间的间隔(最小间隔支持为2000ms),并且在合适时间调用stopLocation()方法来取消定位请求
+ // 在定位结束后,在合适的生命周期调用onDestroy()方法
+ // 在单次定位情况下,定位无论成功与否,都无需调用stopLocation()方法移除请求,定位sdk内部会移除
+ mLocationClient.startLocation();//启动定位
+ }
+ }
+
+ /**
+ * 停止定位
+ */
+ @Override
+ public void deactivate() {
+ mListener = null;
+ if (mLocationClient != null) {
+ mLocationClient.stopLocation();
+ mLocationClient.onDestroy();
+ }
+ mLocationClient = null;
+ }
+
+ @Override
+ public void onLocationChanged(AMapLocation aMapLocation) {
+
+ if (mListener != null && aMapLocation != null) {
+ if (aMapLocation != null
+ && aMapLocation.getErrorCode() == 0) {
+ mListener.onLocationChanged(aMapLocation);// 显示系统小蓝点
+ aMap.moveCamera(CameraUpdateFactory.zoomTo(16));//将地图的缩放级别调整到17级
+
+ //定位成功回调信息,设置相关消息
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ Date date = new Date(aMapLocation.getTime());
+ String time = df.format(date);//定位时
+
+
+ StringBuffer address = new StringBuffer();
+ /*address.append(aMapLocation.getCountry()
+ + aMapLocation.getProvince()
+ + aMapLocation.getCity()
+ + aMapLocation.getDistrict()
+ + aMapLocation.getStreet()
+ + aMapLocation.getStreetNum());*/
+ address.append("纬度: "+ aMapLocation.getLatitude() + "\n");
+ address.append("经度: "+ aMapLocation.getLongitude() + "\n");
+ //如果option中设置isNeedAddress为false,则没有此结果,网络定位结果中会有地址信息,GPS定位不返回地址信息。
+ //address.append("地址: "+ aMapLocation.getAddress() + "\n");
+ //address.append("当前定位点的AOI信息: "+ aMapLocation.getAoiName() + "\n");
+ //address.append("当前室内定位的建筑物Id: "+ aMapLocation.getBuildingId() + "\n");
+ //address.append("当前室内定位的楼层: "+ aMapLocation.getFloor() + "\n");
+ Log.v("older", "address:" + time + address.toString());
+ textView.setText(time + "\n" + locationAddress+address.toString());
+ //39.11083224826389
+ // 经度: 117.35290635850694
+
+
+ } else {
+ String errText = "定位失败," + aMapLocation.getErrorCode()+ ": " + aMapLocation.getErrorInfo();
+ Log.e("AmapErr",errText);
+ }
+ }
+ }
+
+
+
+}
+
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/Myapp.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/Myapp.java
new file mode 100644
index 0000000..8419e0b
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/Myapp.java
@@ -0,0 +1,17 @@
+package edu.gatech.seclass.fall_detection_master;
+
+import android.app.Application;
+import android.speech.tts.TextToSpeech;
+
+public class Myapp extends Application {
+
+ public static TextToSpeech textToSpeech;
+
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+
+ }
+}
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/RegisterActivity.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/RegisterActivity.java
new file mode 100644
index 0000000..a6c1043
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/RegisterActivity.java
@@ -0,0 +1,67 @@
+package edu.gatech.seclass.fall_detection_master;
+
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+import androidx.appcompat.app.AppCompatActivity;
+import edu.gatech.seclass.fall_detection_master.db.UserDao;
+import edu.gatech.seclass.fall_detection_master.db.DBHelper;
+
+public class RegisterActivity extends AppCompatActivity {
+ private EditText etUsername;
+ private EditText etPassword;
+ private EditText etConfirmPassword;
+ private Button btnRegister;
+ private UserDao userDao;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_register);
+
+ // 确保数据库表存在
+ DBHelper dbHelper = new DBHelper(this);
+ dbHelper.ensureTableExists();
+
+ initViews();
+ userDao = new UserDao(this);
+ }
+
+ private void initViews() {
+ etUsername = findViewById(R.id.et_username);
+ etPassword = findViewById(R.id.et_password);
+ etConfirmPassword = findViewById(R.id.et_confirm_password);
+ btnRegister = findViewById(R.id.btn_register);
+
+ btnRegister.setOnClickListener(v -> register());
+ }
+
+ private void register() {
+ String username = etUsername.getText().toString().trim();
+ String password = etPassword.getText().toString().trim();
+ String confirmPassword = etConfirmPassword.getText().toString().trim();
+
+ if (username.isEmpty() || password.isEmpty() || confirmPassword.isEmpty()) {
+ Toast.makeText(this, "请填写所有信息", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ if (!password.equals(confirmPassword)) {
+ Toast.makeText(this, "两次输入的密码不一致", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ if (userDao.isUsernameExists(username)) {
+ Toast.makeText(this, "用户名已存在", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ if (userDao.register(username, password)) {
+ Toast.makeText(this, "注册成功", Toast.LENGTH_SHORT).show();
+ finish();
+ } else {
+ Toast.makeText(this, "注册失败", Toast.LENGTH_SHORT).show();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/SettingsFragment.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/SettingsFragment.java
new file mode 100644
index 0000000..b59e1a9
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/SettingsFragment.java
@@ -0,0 +1,74 @@
+package edu.gatech.seclass.fall_detection_master;
+
+
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceFragment;
+import android.util.Log;
+
+
+public class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener{
+ public static final String KEY_NAME = "pre_key_name";
+ public static final String KEY_SEX = "pre_key_sex";
+ public static final String KEY_AGE = "pre_key_age";
+ public static final String KEY_ALERT = "pre_key_alert";
+ public static final String KEY_VIBRATE = "pre_key_vibrate";
+ public static final String KEY_PHONE = "pre_key_phone";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.preferences);
+
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ Log.d("FallDetection", "SettingsFragment.onsharedPreferenceChanged");
+// System.out.print("key是: " + key);
+ Log.d("FallDetection", key);
+// if(key.equals(KEY_AGE)){
+// Preference agePre = findPreference(key);
+// agePre.setSummary(sharedPreferences.getString(key, "请输入年龄"));
+// }
+ switch (key){
+ case KEY_NAME:
+ Preference namePre = findPreference(key);
+ namePre.setSummary(sharedPreferences.getString(key, ""));
+ break;
+ case KEY_SEX:
+ Preference sexPre = findPreference(key);
+ sexPre.setSummary(sharedPreferences.getString(key, ""));
+ break;
+ case KEY_AGE:
+ Preference agePre = findPreference(key);
+ agePre.setSummary(sharedPreferences.getString(key, ""));
+ break;
+ case KEY_ALERT:
+ Preference alertPre = findPreference(key);
+ alertPre.setSummary(sharedPreferences.getString(key, ""));
+ break;
+ case KEY_VIBRATE:
+ break;
+ case KEY_PHONE:
+ Preference phonePre = findPreference(key);
+ phonePre.setSummary(sharedPreferences.getString(key, ""));
+ break;
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ getPreferenceManager().getSharedPreferences()
+ .registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onPause() {
+ getPreferenceManager().getSharedPreferences()
+ .unregisterOnSharedPreferenceChangeListener(this);
+ super.onPause();
+ }
+}
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/adapter/AlarmAdapter.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/adapter/AlarmAdapter.java
new file mode 100644
index 0000000..db1d52d
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/adapter/AlarmAdapter.java
@@ -0,0 +1,97 @@
+package edu.gatech.seclass.fall_detection_master.adapter;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Switch;
+import android.widget.TextView;
+import androidx.recyclerview.widget.RecyclerView;
+import edu.gatech.seclass.fall_detection_master.R;
+import edu.gatech.seclass.fall_detection_master.model.AlarmInfo;
+import java.util.List;
+
+public class AlarmAdapter extends RecyclerView.Adapter {
+ private List alarmList;
+ private OnItemClickListener listener;
+
+ public interface OnItemClickListener {
+ void onItemClick(AlarmInfo alarm);
+ void onSwitchChanged(AlarmInfo alarm, boolean isChecked);
+ void onDeleteClick(AlarmInfo alarm);
+ }
+
+ public AlarmAdapter(List alarmList) {
+ this.alarmList = alarmList;
+ }
+
+ public void setOnItemClickListener(OnItemClickListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_alarm, parent, false);
+ return new ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position) {
+ AlarmInfo alarm = alarmList.get(position);
+ holder.tvTime.setText(String.format("%02d:%02d", alarm.getHour(), alarm.getMinute()));
+ holder.switchAlarm.setChecked(alarm.isEnabled());
+
+ // 设置闹钟描述
+ if (alarm.getDescription() != null && !alarm.getDescription().isEmpty()) {
+ holder.tvDescription.setText(alarm.getDescription());
+ holder.tvDescription.setVisibility(View.VISIBLE);
+ } else {
+ holder.tvDescription.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return alarmList.size();
+ }
+
+ class ViewHolder extends RecyclerView.ViewHolder {
+ TextView tvTime;
+ TextView tvDescription;
+ Switch switchAlarm;
+
+ ViewHolder(View itemView) {
+ super(itemView);
+ tvTime = itemView.findViewById(R.id.tv_time);
+ tvDescription = itemView.findViewById(R.id.tv_description);
+ switchAlarm = itemView.findViewById(R.id.switch_alarm);
+
+ itemView.setOnClickListener(v -> {
+ if (listener != null) {
+ int position = getAdapterPosition();
+ if (position != RecyclerView.NO_POSITION) {
+ listener.onItemClick(alarmList.get(position));
+ }
+ }
+ });
+
+ switchAlarm.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ if (listener != null) {
+ int position = getAdapterPosition();
+ if (position != RecyclerView.NO_POSITION) {
+ listener.onSwitchChanged(alarmList.get(position), isChecked);
+ }
+ }
+ });
+
+ itemView.findViewById(R.id.btn_delete).setOnClickListener(v -> {
+ if (listener != null) {
+ int position = getAdapterPosition();
+ if (position != RecyclerView.NO_POSITION) {
+ listener.onDeleteClick(alarmList.get(position));
+ }
+ }
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/db/AlarmDao.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/db/AlarmDao.java
new file mode 100644
index 0000000..dade478
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/db/AlarmDao.java
@@ -0,0 +1,66 @@
+package edu.gatech.seclass.fall_detection_master.db;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import edu.gatech.seclass.fall_detection_master.model.AlarmInfo;
+import java.util.ArrayList;
+import java.util.List;
+
+public class AlarmDao {
+ private DBHelper dbHelper;
+
+ public AlarmDao(Context context) {
+ dbHelper = new DBHelper(context);
+ }
+
+ public long insert(AlarmInfo alarm) {
+ SQLiteDatabase db = dbHelper.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put("hour", alarm.getHour());
+ values.put("minute", alarm.getMinute());
+ values.put("enabled", alarm.isEnabled() ? 1 : 0);
+ values.put("description", alarm.getDescription());
+
+ return db.insert("alarm_info", null, values);
+ }
+
+ public void update(AlarmInfo alarm) {
+ SQLiteDatabase db = dbHelper.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put("hour", alarm.getHour());
+ values.put("minute", alarm.getMinute());
+ values.put("enabled", alarm.isEnabled() ? 1 : 0);
+ values.put("description", alarm.getDescription());
+
+ db.update("alarm_info", values, "id = ?",
+ new String[]{String.valueOf(alarm.getId())});
+ }
+
+ public void delete(int id) {
+ SQLiteDatabase db = dbHelper.getWritableDatabase();
+ db.delete("alarm_info", "id = ?", new String[]{String.valueOf(id)});
+ }
+
+ public List getAllAlarms() {
+ List alarmList = new ArrayList<>();
+ SQLiteDatabase db = dbHelper.getReadableDatabase();
+
+ Cursor cursor = db.query("alarm_info", null, null, null, null, null, "hour,minute");
+
+ while (cursor.moveToNext()) {
+ AlarmInfo alarm = new AlarmInfo(
+ cursor.getInt(cursor.getColumnIndex("hour")),
+ cursor.getInt(cursor.getColumnIndex("minute"))
+ );
+ alarm.setId(cursor.getInt(cursor.getColumnIndex("id")));
+ alarm.setEnabled(cursor.getInt(cursor.getColumnIndex("enabled")) == 1);
+ alarm.setDescription(cursor.getString(cursor.getColumnIndex("description")));
+ alarmList.add(alarm);
+ }
+ cursor.close();
+
+ return alarmList;
+ }
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/db/DBHelper.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/db/DBHelper.java
new file mode 100644
index 0000000..37976cb
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/db/DBHelper.java
@@ -0,0 +1,81 @@
+package edu.gatech.seclass.fall_detection_master.db;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+public class DBHelper extends SQLiteOpenHelper {
+ private static final String DATABASE_NAME = "elderly_care.db";
+ private static final int DATABASE_VERSION = 2;
+
+ public DBHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ try {
+ // 创建用户表
+ db.execSQL("CREATE TABLE IF NOT EXISTS user_info (" +
+ "id INTEGER PRIMARY KEY AUTOINCREMENT," +
+ "username TEXT UNIQUE," +
+ "password TEXT)");
+
+ // 创建健康信息表
+ db.execSQL("CREATE TABLE IF NOT EXISTS health_info (" +
+ "id INTEGER PRIMARY KEY AUTOINCREMENT," +
+ "blood_pressure_high INTEGER," +
+ "blood_pressure_low INTEGER," +
+ "heart_rate INTEGER," +
+ "blood_sugar REAL," +
+ "record_time INTEGER)");
+
+ // 创建闹钟信息表
+ db.execSQL("CREATE TABLE IF NOT EXISTS alarm_info (" +
+ "id INTEGER PRIMARY KEY AUTOINCREMENT," +
+ "hour INTEGER," +
+ "minute INTEGER," +
+ "enabled INTEGER," +
+ "description TEXT)");
+
+ Log.d("DBHelper", "数据库表创建成功");
+ } catch (Exception e) {
+ Log.e("DBHelper", "数据库表创建失败: " + e.getMessage());
+ }
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ try {
+ // 删除旧表
+ db.execSQL("DROP TABLE IF EXISTS user_info");
+ db.execSQL("DROP TABLE IF EXISTS health_info");
+ db.execSQL("DROP TABLE IF EXISTS alarm_info");
+
+ // 重新创建表
+ onCreate(db);
+
+ Log.d("DBHelper", "数据库升级成功");
+ } catch (Exception e) {
+ Log.e("DBHelper", "数据库升级失败: " + e.getMessage());
+ }
+ }
+
+ // 添加一个方法来检查表是否存在
+ public boolean isTableExists(String tableName) {
+ SQLiteDatabase db = getReadableDatabase();
+ try {
+ db.query(tableName, null, null, null, null, null, null);
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ // 添加一个方法来强制创建表
+ public void ensureTableExists() {
+ SQLiteDatabase db = getWritableDatabase();
+ onCreate(db);
+ }
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/db/HealthInfoDao.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/db/HealthInfoDao.java
new file mode 100644
index 0000000..6ae93a6
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/db/HealthInfoDao.java
@@ -0,0 +1,52 @@
+package edu.gatech.seclass.fall_detection_master.db;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import edu.gatech.seclass.fall_detection_master.model.HealthInfo;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+public class HealthInfoDao {
+ private DBHelper dbHelper;
+
+ public HealthInfoDao(Context context) {
+ dbHelper = new DBHelper(context);
+ }
+
+ public long insert(HealthInfo healthInfo) {
+ SQLiteDatabase db = dbHelper.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put("blood_pressure_high", healthInfo.getBloodPressureHigh());
+ values.put("blood_pressure_low", healthInfo.getBloodPressureLow());
+ values.put("heart_rate", healthInfo.getHeartRate());
+ values.put("blood_sugar", healthInfo.getBloodSugar());
+ values.put("record_time", healthInfo.getRecordTime().getTime());
+
+ return db.insert("health_info", null, values);
+ }
+
+ public List getRecentRecords(int limit) {
+ List list = new ArrayList<>();
+ SQLiteDatabase db = dbHelper.getReadableDatabase();
+
+ Cursor cursor = db.query("health_info", null, null, null, null, null,
+ "record_time DESC", String.valueOf(limit));
+
+ while (cursor.moveToNext()) {
+ HealthInfo info = new HealthInfo();
+ info.setId(cursor.getInt(cursor.getColumnIndex("id")));
+ info.setBloodPressureHigh(cursor.getInt(cursor.getColumnIndex("blood_pressure_high")));
+ info.setBloodPressureLow(cursor.getInt(cursor.getColumnIndex("blood_pressure_low")));
+ info.setHeartRate(cursor.getInt(cursor.getColumnIndex("heart_rate")));
+ info.setBloodSugar(cursor.getFloat(cursor.getColumnIndex("blood_sugar")));
+ info.setRecordTime(new Date(cursor.getLong(cursor.getColumnIndex("record_time"))));
+ list.add(info);
+ }
+ cursor.close();
+
+ return list;
+ }
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/db/UserDao.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/db/UserDao.java
new file mode 100644
index 0000000..2acfce0
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/db/UserDao.java
@@ -0,0 +1,51 @@
+package edu.gatech.seclass.fall_detection_master.db;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
+public class UserDao {
+ private DBHelper dbHelper;
+
+ public UserDao(Context context) {
+ dbHelper = new DBHelper(context);
+ }
+
+ public boolean register(String username, String password) {
+ SQLiteDatabase db = dbHelper.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put("username", username);
+ values.put("password", password);
+
+ try {
+ return db.insert("user_info", null, values) != -1;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean login(String username, String password) {
+ SQLiteDatabase db = dbHelper.getReadableDatabase();
+ Cursor cursor = db.query("user_info", null,
+ "username = ? AND password = ?",
+ new String[]{username, password},
+ null, null, null);
+
+ boolean result = cursor.getCount() > 0;
+ cursor.close();
+ return result;
+ }
+
+ public boolean isUsernameExists(String username) {
+ SQLiteDatabase db = dbHelper.getReadableDatabase();
+ Cursor cursor = db.query("user_info", null,
+ "username = ?",
+ new String[]{username},
+ null, null, null);
+
+ boolean result = cursor.getCount() > 0;
+ cursor.close();
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/geofence/GeofenceHelper.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/geofence/GeofenceHelper.java
new file mode 100644
index 0000000..a7aa0b7
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/geofence/GeofenceHelper.java
@@ -0,0 +1,96 @@
+package edu.gatech.seclass.fall_detection_master.geofence;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import com.amap.api.location.AMapLocation;
+import com.amap.api.location.AMapLocationClient;
+import com.amap.api.location.AMapLocationClientOption;
+import com.amap.api.location.AMapLocationListener;
+import android.location.Location;
+
+public class GeofenceHelper implements AMapLocationListener {
+ private Context context;
+ private AMapLocationClient locationClient;
+ private AMapLocationClientOption locationOption;
+ private OnGeofenceListener listener;
+ private double centerLatitude;
+ private double centerLongitude;
+ private int radius;
+ private boolean isInside = true;
+
+ public interface OnGeofenceListener {
+ void onGeofenceEnter();
+ void onGeofenceExit();
+ }
+
+ public GeofenceHelper(Context context) {
+ this.context = context;
+ loadGeofenceSettings();
+ initLocation();
+ }
+
+ private void loadGeofenceSettings() {
+ SharedPreferences sp = context.getSharedPreferences("geofence", Context.MODE_PRIVATE);
+ centerLatitude = sp.getFloat("latitude", 0);
+ centerLongitude = sp.getFloat("longitude", 0);
+ radius = sp.getInt("radius", 100);
+ }
+
+ private void initLocation() {
+ try {
+ locationClient = new AMapLocationClient(context);
+ locationOption = new AMapLocationClientOption();
+ locationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
+ locationOption.setInterval(2000);
+ locationClient.setLocationOption(locationOption);
+ locationClient.setLocationListener(this);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void startMonitoring() {
+ if (locationClient != null) {
+ locationClient.startLocation();
+ }
+ }
+
+ public void stopMonitoring() {
+ if (locationClient != null) {
+ locationClient.stopLocation();
+ }
+ }
+
+ @Override
+ public void onLocationChanged(AMapLocation aMapLocation) {
+ if (aMapLocation != null && aMapLocation.getErrorCode() == 0) {
+ // 计算当前位置与围栏中心的距离
+ float[] results = new float[1];
+ Location.distanceBetween(
+ aMapLocation.getLatitude(), aMapLocation.getLongitude(),
+ centerLatitude, centerLongitude,
+ results
+ );
+
+ boolean currentlyInside = results[0] <= radius;
+
+ // 检测是否穿越围栏边界
+ if (currentlyInside != isInside) {
+ isInside = currentlyInside;
+ if (listener != null) {
+ if (isInside) {
+ listener.onGeofenceEnter();
+ } else {
+ listener.onGeofenceExit();
+ }
+ }
+ }
+ }
+ }
+
+ public void setOnGeofenceListener(OnGeofenceListener listener) {
+ this.listener = listener;
+ }
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/model/AlarmInfo.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/model/AlarmInfo.java
new file mode 100644
index 0000000..100728e
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/model/AlarmInfo.java
@@ -0,0 +1,27 @@
+package edu.gatech.seclass.fall_detection_master.model;
+
+public class AlarmInfo {
+ private int id;
+ private int hour;
+ private int minute;
+ private boolean isEnabled;
+ private String description;
+
+ public AlarmInfo(int hour, int minute) {
+ this.hour = hour;
+ this.minute = minute;
+ this.isEnabled = true;
+ }
+
+ // Getters and setters
+ public int getId() { return id; }
+ public void setId(int id) { this.id = id; }
+ public int getHour() { return hour; }
+ public void setHour(int hour) { this.hour = hour; }
+ public int getMinute() { return minute; }
+ public void setMinute(int minute) { this.minute = minute; }
+ public boolean isEnabled() { return isEnabled; }
+ public void setEnabled(boolean enabled) { isEnabled = enabled; }
+ public String getDescription() { return description; }
+ public void setDescription(String description) { this.description = description; }
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/model/HealthInfo.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/model/HealthInfo.java
new file mode 100644
index 0000000..91debaf
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/model/HealthInfo.java
@@ -0,0 +1,30 @@
+package edu.gatech.seclass.fall_detection_master.model;
+
+import java.util.Date;
+
+public class HealthInfo {
+ private int id;
+ private int bloodPressureHigh;
+ private int bloodPressureLow;
+ private int heartRate;
+ private float bloodSugar;
+ private Date recordTime;
+
+ public HealthInfo() {
+ this.recordTime = new Date();
+ }
+
+ // Getters and setters
+ public int getId() { return id; }
+ public void setId(int id) { this.id = id; }
+ public int getBloodPressureHigh() { return bloodPressureHigh; }
+ public void setBloodPressureHigh(int bloodPressureHigh) { this.bloodPressureHigh = bloodPressureHigh; }
+ public int getBloodPressureLow() { return bloodPressureLow; }
+ public void setBloodPressureLow(int bloodPressureLow) { this.bloodPressureLow = bloodPressureLow; }
+ public int getHeartRate() { return heartRate; }
+ public void setHeartRate(int heartRate) { this.heartRate = heartRate; }
+ public float getBloodSugar() { return bloodSugar; }
+ public void setBloodSugar(float bloodSugar) { this.bloodSugar = bloodSugar; }
+ public Date getRecordTime() { return recordTime; }
+ public void setRecordTime(Date recordTime) { this.recordTime = recordTime; }
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/service/AlarmService.java b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/service/AlarmService.java
new file mode 100644
index 0000000..6c4e690
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/java/edu/gatech/seclass/fall_detection_master/service/AlarmService.java
@@ -0,0 +1,210 @@
+
+package edu.gatech.seclass.fall_detection_master.service;
+
+import android.annotation.SuppressLint;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.media.MediaPlayer;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.speech.tts.TextToSpeech;
+import androidx.core.app.NotificationCompat;
+
+import edu.gatech.seclass.fall_detection_master.Myapp;
+import edu.gatech.seclass.fall_detection_master.R;
+import edu.gatech.seclass.fall_detection_master.AlarmActivity;
+import java.util.Locale;
+
+public class AlarmService extends Service implements TextToSpeech.OnInitListener {
+ private MediaPlayer mediaPlayer;
+ // private TextToSpeech textToSpeech;
+ private boolean isPlaying = false;
+ private PowerManager.WakeLock wakeLock;
+ private static final String CHANNEL_ID = "alarm_channel";
+ private static final String CHANNEL_NAME = "闹钟提醒";
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ // 获取 WakeLock
+ PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK |
+ PowerManager.ACQUIRE_CAUSES_WAKEUP |
+ PowerManager.ON_AFTER_RELEASE, "AlarmService::WakeLock");
+
+ createNotificationChannel();
+
+ mediaPlayer = MediaPlayer.create(this, R.raw.alarm_sound);
+ mediaPlayer.setLooping(true);
+// textToSpeech = new TextToSpeech(this, this);
+ }
+
+ private void createNotificationChannel() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ NotificationChannel channel = new NotificationChannel(
+ CHANNEL_ID,
+ CHANNEL_NAME,
+ NotificationManager.IMPORTANCE_HIGH);
+ channel.setSound(null, null);
+ NotificationManager notificationManager = getSystemService(NotificationManager.class);
+ notificationManager.createNotificationChannel(channel);
+ }
+ }
+
+ @Override
+ /*
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ // 获取 WakeLock
+ wakeLock.acquire(10*60*1000L );
+ if (!isPlaying) {
+ // 显示通知
+ showAlarmNotification();
+
+ // 播放闹钟声音
+ mediaPlayer.start();
+ isPlaying = true;
+
+ // 播放语音提醒
+ speakReminder();
+ }
+ return START_STICKY;
+ }
+
+ */
+
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ // 检查是否为停止闹钟的请求
+ if (intent != null && "STOP_ALARM".equals(intent.getAction())) {
+ stopAlarm();
+ stopSelf(); // 停止服务
+ return START_NOT_STICKY;
+ }
+
+ // 获取 WakeLock
+ if (wakeLock != null && !wakeLock.isHeld()) {
+ wakeLock.acquire(10 * 60 * 1000L /*10 minutes*/);
+ }
+
+ if (!isPlaying) {
+ // 显示通知
+ showAlarmNotification();
+
+ // 播放闹钟声音
+ mediaPlayer.start();
+ isPlaying = true;
+
+ // 播放语音提醒
+ speakReminder();
+ }
+ return START_STICKY;
+ }
+
+ // 停止闹钟播放的逻辑
+ private void stopAlarm() {
+ if (mediaPlayer != null && mediaPlayer.isPlaying()) {
+ mediaPlayer.stop();
+ mediaPlayer.release();
+ mediaPlayer = null;
+ }
+
+ if (wakeLock != null && wakeLock.isHeld()) {
+ wakeLock.release();
+ }
+
+ isPlaying = false;
+ }
+
+ @Override
+ public void onDestroy() {
+ stopAlarm(); // 确保在销毁时停止闹钟
+ stopForeground(true); // 停止前台通知
+ super.onDestroy();
+ }
+
+
+ @SuppressLint("ForegroundServiceType")
+ private void showAlarmNotification() {
+ Intent stopIntent = new Intent(this, AlarmService.class);
+ stopIntent.setAction("STOP_ALARM");
+ PendingIntent stopPendingIntent = PendingIntent.getService(this, 0, stopIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+
+ Intent intent = new Intent(this, AlarmActivity.class);
+ PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+
+ Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
+ .setContentTitle("闹钟提醒")
+ .setContentText("闹钟时间到")
+ .setSmallIcon(R.drawable.ic_drawer)
+ .setAutoCancel(true)
+ .setOngoing(true)
+ .setContentIntent(pendingIntent)
+ .addAction(R.drawable.ic_about, "关闭闹钟", stopPendingIntent) // 添加停止按钮
+ .build();
+
+ startForeground(1, notification);
+ }
+
+ /*
+ @SuppressLint("ForegroundServiceType")
+ private void showAlarmNotification() {
+ Intent intent = new Intent(this, AlarmActivity.class);
+ PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+
+ Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
+ .setContentTitle("闹钟提醒")
+ .setContentText("闹钟时间到")
+ .setSmallIcon(R.drawable.ic_drawer)
+ .setAutoCancel(true)
+ .setOngoing(true)
+ .setContentIntent(pendingIntent)
+ .build();
+
+ startForeground(1, notification);
+ }
+
+ */
+
+ private void speakReminder() {
+// if (textToSpeech != null) {
+// String reminder = "闹钟时间到了,请记得按时吃药";
+// Myapp.textToSpeech.speak(reminder, TextToSpeech.QUEUE_FLUSH, null, null);
+// }
+ }
+
+ @Override
+ public void onInit(int status) {
+// if (status == TextToSpeech.SUCCESS) {
+// textToSpeech.setLanguage(Locale.CHINESE);
+// }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+/*
+ @Override
+ public void onDestroy() {
+ if (mediaPlayer != null) {
+ mediaPlayer.stop();
+ mediaPlayer.release();
+ }
+// if (textToSpeech != null) {
+// textToSpeech.stop();
+// textToSpeech.shutdown();
+// }
+ if (wakeLock.isHeld()) {
+ wakeLock.release();
+ }
+ super.onDestroy();
+ }
+
+ */
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/jniLibs/arm64-v8a/libAMapSDK_MAP_v10_0_900.so b/src/FallDetectionmaster/app/src/main/jniLibs/arm64-v8a/libAMapSDK_MAP_v10_0_900.so
new file mode 100644
index 0000000..6968c7c
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/jniLibs/arm64-v8a/libAMapSDK_MAP_v10_0_900.so differ
diff --git a/src/FallDetectionmaster/app/src/main/jniLibs/armeabi-v7a/libAMapSDK_MAP_v10_0_900.so b/src/FallDetectionmaster/app/src/main/jniLibs/armeabi-v7a/libAMapSDK_MAP_v10_0_900.so
new file mode 100644
index 0000000..198dc71
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/jniLibs/armeabi-v7a/libAMapSDK_MAP_v10_0_900.so differ
diff --git a/src/FallDetectionmaster/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/src/FallDetectionmaster/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/drawable/about_content.jpg b/src/FallDetectionmaster/app/src/main/res/drawable/about_content.jpg
new file mode 100644
index 0000000..74053e8
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/drawable/about_content.jpg differ
diff --git a/src/FallDetectionmaster/app/src/main/res/drawable/about_content1.png b/src/FallDetectionmaster/app/src/main/res/drawable/about_content1.png
new file mode 100644
index 0000000..3f91d5a
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/drawable/about_content1.png differ
diff --git a/src/FallDetectionmaster/app/src/main/res/drawable/home_content.jpg b/src/FallDetectionmaster/app/src/main/res/drawable/home_content.jpg
new file mode 100644
index 0000000..2242c6c
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/drawable/home_content.jpg differ
diff --git a/src/FallDetectionmaster/app/src/main/res/drawable/ic_about.png b/src/FallDetectionmaster/app/src/main/res/drawable/ic_about.png
new file mode 100644
index 0000000..b1f1406
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/drawable/ic_about.png differ
diff --git a/src/FallDetectionmaster/app/src/main/res/drawable/ic_add.png b/src/FallDetectionmaster/app/src/main/res/drawable/ic_add.png
new file mode 100644
index 0000000..d6b3949
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/drawable/ic_add.png differ
diff --git a/src/FallDetectionmaster/app/src/main/res/drawable/ic_app.png b/src/FallDetectionmaster/app/src/main/res/drawable/ic_app.png
new file mode 100644
index 0000000..f673203
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/drawable/ic_app.png differ
diff --git a/src/FallDetectionmaster/app/src/main/res/drawable/ic_back.png b/src/FallDetectionmaster/app/src/main/res/drawable/ic_back.png
new file mode 100644
index 0000000..baaf7d9
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/drawable/ic_back.png differ
diff --git a/src/FallDetectionmaster/app/src/main/res/drawable/ic_delete.png b/src/FallDetectionmaster/app/src/main/res/drawable/ic_delete.png
new file mode 100644
index 0000000..075b519
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/drawable/ic_delete.png differ
diff --git a/src/FallDetectionmaster/app/src/main/res/drawable/ic_drawer.png b/src/FallDetectionmaster/app/src/main/res/drawable/ic_drawer.png
new file mode 100644
index 0000000..c59f601
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/drawable/ic_drawer.png differ
diff --git a/src/FallDetectionmaster/app/src/main/res/drawable/ic_home.png b/src/FallDetectionmaster/app/src/main/res/drawable/ic_home.png
new file mode 100644
index 0000000..0a3799b
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/drawable/ic_home.png differ
diff --git a/src/FallDetectionmaster/app/src/main/res/drawable/ic_launcher_background.xml b/src/FallDetectionmaster/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/FallDetectionmaster/app/src/main/res/drawable/ic_menu.png b/src/FallDetectionmaster/app/src/main/res/drawable/ic_menu.png
new file mode 100644
index 0000000..c62db8c
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/drawable/ic_menu.png differ
diff --git a/src/FallDetectionmaster/app/src/main/res/drawable/ic_place.png b/src/FallDetectionmaster/app/src/main/res/drawable/ic_place.png
new file mode 100644
index 0000000..c7bcd08
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/drawable/ic_place.png differ
diff --git a/src/FallDetectionmaster/app/src/main/res/drawable/ic_settings.png b/src/FallDetectionmaster/app/src/main/res/drawable/ic_settings.png
new file mode 100644
index 0000000..d973325
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/drawable/ic_settings.png differ
diff --git a/src/FallDetectionmaster/app/src/main/res/drawable/ic_warning.png b/src/FallDetectionmaster/app/src/main/res/drawable/ic_warning.png
new file mode 100644
index 0000000..c872b30
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/drawable/ic_warning.png differ
diff --git a/src/FallDetectionmaster/app/src/main/res/drawable/people.png b/src/FallDetectionmaster/app/src/main/res/drawable/people.png
new file mode 100644
index 0000000..2004507
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/drawable/people.png differ
diff --git a/src/FallDetectionmaster/app/src/main/res/layout/activity_alarm.xml b/src/FallDetectionmaster/app/src/main/res/layout/activity_alarm.xml
new file mode 100644
index 0000000..a00a711
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/layout/activity_alarm.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/layout/activity_geofence.xml b/src/FallDetectionmaster/app/src/main/res/layout/activity_geofence.xml
new file mode 100644
index 0000000..2a00699
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/layout/activity_geofence.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/layout/activity_health_info.xml b/src/FallDetectionmaster/app/src/main/res/layout/activity_health_info.xml
new file mode 100644
index 0000000..8b7bf48
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/layout/activity_health_info.xml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/layout/activity_health_report.xml b/src/FallDetectionmaster/app/src/main/res/layout/activity_health_report.xml
new file mode 100644
index 0000000..0312ae9
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/layout/activity_health_report.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/layout/activity_login.xml b/src/FallDetectionmaster/app/src/main/res/layout/activity_login.xml
new file mode 100644
index 0000000..ec0837c
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/layout/activity_login.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/layout/activity_main.xml b/src/FallDetectionmaster/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..8d7b6b9
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/FallDetectionmaster/app/src/main/res/layout/activity_register.xml b/src/FallDetectionmaster/app/src/main/res/layout/activity_register.xml
new file mode 100644
index 0000000..af7461d
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/layout/activity_register.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/layout/dialog_alarm.xml b/src/FallDetectionmaster/app/src/main/res/layout/dialog_alarm.xml
new file mode 100644
index 0000000..c0d70d3
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/layout/dialog_alarm.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/layout/fragment_about.xml b/src/FallDetectionmaster/app/src/main/res/layout/fragment_about.xml
new file mode 100644
index 0000000..e07b5f7
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/layout/fragment_about.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/layout/fragment_home.xml b/src/FallDetectionmaster/app/src/main/res/layout/fragment_home.xml
new file mode 100644
index 0000000..4af9ed9
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/layout/fragment_home.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/layout/fragment_place.xml b/src/FallDetectionmaster/app/src/main/res/layout/fragment_place.xml
new file mode 100644
index 0000000..9308ba2
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/layout/fragment_place.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/layout/include_toolbar.xml b/src/FallDetectionmaster/app/src/main/res/layout/include_toolbar.xml
new file mode 100644
index 0000000..2896fd8
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/layout/include_toolbar.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/layout/item_alarm.xml b/src/FallDetectionmaster/app/src/main/res/layout/item_alarm.xml
new file mode 100644
index 0000000..7b4f42a
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/layout/item_alarm.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/layout/map_activity.xml b/src/FallDetectionmaster/app/src/main/res/layout/map_activity.xml
new file mode 100644
index 0000000..f834f77
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/layout/map_activity.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/layout/navigation_header.xml b/src/FallDetectionmaster/app/src/main/res/layout/navigation_header.xml
new file mode 100644
index 0000000..95c85db
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/layout/navigation_header.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/menu/drawer.xml b/src/FallDetectionmaster/app/src/main/res/menu/drawer.xml
new file mode 100644
index 0000000..7f3ada0
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/menu/drawer.xml
@@ -0,0 +1,39 @@
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/menu/menu_main.xml b/src/FallDetectionmaster/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..2090396
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/src/FallDetectionmaster/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/src/FallDetectionmaster/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/src/FallDetectionmaster/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..c209e78
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/src/FallDetectionmaster/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/src/FallDetectionmaster/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..b2dfe3d
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/src/FallDetectionmaster/app/src/main/res/mipmap-mdpi/ic_app.png b/src/FallDetectionmaster/app/src/main/res/mipmap-mdpi/ic_app.png
new file mode 100644
index 0000000..f673203
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/mipmap-mdpi/ic_app.png differ
diff --git a/src/FallDetectionmaster/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/src/FallDetectionmaster/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..4f0f1d6
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/src/FallDetectionmaster/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/src/FallDetectionmaster/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..62b611d
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/src/FallDetectionmaster/app/src/main/res/mipmap-mdpi/profile_image.png b/src/FallDetectionmaster/app/src/main/res/mipmap-mdpi/profile_image.png
new file mode 100644
index 0000000..680ad5a
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/mipmap-mdpi/profile_image.png differ
diff --git a/src/FallDetectionmaster/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/src/FallDetectionmaster/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..948a307
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/src/FallDetectionmaster/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/src/FallDetectionmaster/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..1b9a695
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/src/FallDetectionmaster/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/src/FallDetectionmaster/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..28d4b77
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/src/FallDetectionmaster/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/src/FallDetectionmaster/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9287f50
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/src/FallDetectionmaster/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/src/FallDetectionmaster/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..aa7d642
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/src/FallDetectionmaster/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/src/FallDetectionmaster/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9126ae3
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/src/FallDetectionmaster/app/src/main/res/raw/alarm_sound.wav b/src/FallDetectionmaster/app/src/main/res/raw/alarm_sound.wav
new file mode 100644
index 0000000..a1e296a
Binary files /dev/null and b/src/FallDetectionmaster/app/src/main/res/raw/alarm_sound.wav differ
diff --git a/src/FallDetectionmaster/app/src/main/res/values-night/themes.xml b/src/FallDetectionmaster/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..1dd341b
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/values/colors.xml b/src/FallDetectionmaster/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..718f478
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/values/colors.xml
@@ -0,0 +1,22 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+ #4C637A
+ #A2C6EA
+ #CDE6FA
+ #4C637A
+ #212121
+ #727272
+ #FFFFFF
+ #B6B6B6
+ #FFF5F5F5
+ #eeeeee
+ #C4CEB3
+ #AFB79C
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/values/strings.xml b/src/FallDetectionmaster/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..085db64
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/values/strings.xml
@@ -0,0 +1,15 @@
+
+ 老人看护系统
+ 主页
+ 地图与位置
+ 设置
+ 关于
+ android.support.design.widget.AppBarLayout$ScrollingViewBehavior
+ open
+ close
+
+
+ - 男
+ - 女
+
+
diff --git a/src/FallDetectionmaster/app/src/main/res/values/themes.xml b/src/FallDetectionmaster/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..39c94d9
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/values/themes.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/main/res/xml/preferences.xml b/src/FallDetectionmaster/app/src/main/res/xml/preferences.xml
new file mode 100644
index 0000000..4b0a933
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/main/res/xml/preferences.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FallDetectionmaster/app/src/test/java/edu/gatech/seclass/fall_detection_master/ExampleUnitTest.java b/src/FallDetectionmaster/app/src/test/java/edu/gatech/seclass/fall_detection_master/ExampleUnitTest.java
new file mode 100644
index 0000000..58bf3b3
--- /dev/null
+++ b/src/FallDetectionmaster/app/src/test/java/edu/gatech/seclass/fall_detection_master/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package edu.gatech.seclass.fall_detection_master;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/build.gradle b/src/FallDetectionmaster/build.gradle
new file mode 100644
index 0000000..f757f23
--- /dev/null
+++ b/src/FallDetectionmaster/build.gradle
@@ -0,0 +1,5 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/src/FallDetectionmaster/gradle.properties b/src/FallDetectionmaster/gradle.properties
new file mode 100644
index 0000000..9a4c7cc
--- /dev/null
+++ b/src/FallDetectionmaster/gradle.properties
@@ -0,0 +1,23 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
\ No newline at end of file
diff --git a/src/FallDetectionmaster/gradle/wrapper/gradle-wrapper.jar b/src/FallDetectionmaster/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
Binary files /dev/null and b/src/FallDetectionmaster/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/src/FallDetectionmaster/gradle/wrapper/gradle-wrapper.properties b/src/FallDetectionmaster/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..375e483
--- /dev/null
+++ b/src/FallDetectionmaster/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,7 @@
+#Sun Oct 13 14:27:54 CST 2024
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-7.2-bin.zip
+
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/src/FallDetectionmaster/gradlew b/src/FallDetectionmaster/gradlew
new file mode 100644
index 0000000..4f906e0
--- /dev/null
+++ b/src/FallDetectionmaster/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/src/FallDetectionmaster/gradlew.bat b/src/FallDetectionmaster/gradlew.bat
new file mode 100644
index 0000000..107acd3
--- /dev/null
+++ b/src/FallDetectionmaster/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/src/FallDetectionmaster/keystore b/src/FallDetectionmaster/keystore
new file mode 100644
index 0000000..917e3cd
Binary files /dev/null and b/src/FallDetectionmaster/keystore differ
diff --git a/src/FallDetectionmaster/settings.gradle b/src/FallDetectionmaster/settings.gradle
new file mode 100644
index 0000000..be1dd12
--- /dev/null
+++ b/src/FallDetectionmaster/settings.gradle
@@ -0,0 +1,28 @@
+pluginManagement {
+ repositories {
+ maven{ url 'https://maven.aliyun.com/nexus/content/groups/public/'}
+ maven {
+ url'https://maven.aliyun.com/nexus/content/groups/public/'
+ }
+ gradlePluginPortal()
+ google()
+ mavenCentral()
+ }
+ plugins {
+ id 'com.android.application' version '7.1.3'
+ id 'com.android.library' version '7.1.3'
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ maven{ url 'https://maven.aliyun.com/nexus/content/groups/public/'}
+ maven {
+ url'https://maven.aliyun.com/nexus/content/groups/public/'
+ }
+ google()
+ mavenCentral()
+ }
+}
+rootProject.name = "Fall-Detection-master"
+include ':app'
diff --git a/src/新建 文本文档.txt b/src/新建 文本文档.txt
deleted file mode 100644
index e69de29..0000000