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 @@ + + + + + + + + + + + + + +