diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..603b1407 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +*.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 diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 00000000..b3405b3b --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +My Application \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..681f41ae --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,116 @@ + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+
+
\ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 00000000..169fd0dd --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..f5c6d9eb --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 00000000..7f68460d --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..94a25f7f --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 8485fa33..73ea5c50 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,22 @@ # disaster-management Lynk Hackathon 2019 - Disaster Management + +Helfy App +an android app that creates a mesh network to stimulate a decentralized network communication which is build upon wifi, bluetooth, network, sms and the internet. this network enables the users to perform complex message transfer and the message analysis on the every device connected to the network to help them in times of emergency. + +Project Specification: +1. the app is entirely written in kotlin. +2. uses bridgify and hype mesh sdk to connect with near by devices +3. custom location class component to indentify users location and even without interent or any network connection +4. uses twilio to send sms and what's app message to the emergency contact +5. uses android architecture components like, livedata coroutines, viewmodel, navigation +4. uses room library to collect and store the nearby devices id. + +app features: +1. send emergency sms to others without network or internet. +2. request for food or medicien support through the network. +3. enable volunteers to join the network easily and help the victims +4. send out a alert message to nearby devices, to help the person in danger +5. send network call request to volunteer server to inform about the situation and help needed. +6. all the payload consists of location of the user +7. allow anyone to work as a volunteer on the fly without any registration diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 00000000..9f68cb8e --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,91 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' +apply plugin: 'kotlin-kapt' + +android { + compileSdkVersion 29 + buildToolsVersion "29.0.2" + defaultConfig { + applicationId "com.example.myapplication" + minSdkVersion 21 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_1_8 + } + + packagingOptions { + exclude 'META-INF/DEPENDENCIES' + exclude 'META-INF/LICENSE' + exclude 'META-INF/LICENSE.txt' + exclude 'META-INF/license.txt' + exclude 'META-INF/NOTICE' + exclude 'META-INF/NOTICE.txt' + exclude 'META-INF/notice.txt' + exclude 'META-INF/ASL2.0' + } +} + +dependencies { + def lifecycle_version = "2.1.0" + def nav_version = "2.1.0" + def google_play_service = '17.0.0' + + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'androidx.appcompat:appcompat:1.0.2' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' + implementation "androidx.core:core-ktx:+" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'com.bridgefy:android-sdk:1.1.24' + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" + // For Kotlin use lifecycle-viewmodel-ktx + // Kotlin + implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" + implementation "androidx.navigation:navigation-ui-ktx:$nav_version" + implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0' + + //twilio for sms + implementation group: "com.twilio.sdk", name: "twilio", version: "7.42.0" + + implementation "com.google.android.gms:play-services-location:${google_play_service}" + implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.1.0' + + def room_version = "2.2.0" + + implementation "androidx.room:room-runtime:$room_version" + kapt "androidx.room:room-compiler:$room_version" + // For Kotlin use kapt instead of annotationProcessor + + // optional - Kotlin Extensions and Coroutines support for Room + implementation "androidx.room:room-ktx:$room_version" + + // Animations + implementation 'com.airbnb.android:lottie:3.0.7' + implementation 'com.wang.avi:library:2.1.3' + implementation 'com.skyfishjy.ripplebackground:library:1.0.1' + + +} +repositories { + mavenCentral() + maven { + url "http://maven.bridgefy.com/artifactory/libs-release-local" + artifactUrls = ["http://jcenter.bintray.com/"] + } +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 00000000..7064aba6 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,32 @@ +# 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 +-keep class com.bridgefy.sdk.** { *; } +-dontwarn com.bridgefy.sdk.** +-dontwarn org.w3c.dom.bootstrap.DOMImplementationRegistry +-keepattributes *Annotation*,EnclosingMethod,Signature +-keepnames class com.fasterxml.jackson.** { *; } + -dontwarn com.fasterxml.jackson.databind.** + -keep class org.codehaus.** { *; } + -keepclassmembers public final enum org.codehaus.jackson.annotate.JsonAutoDetect$Visibility { + public static final org.codehaus.jackson.annotate.JsonAutoDetect$Visibility *; } +-keep class org.msgpack.core.**{ *; } +-dontwarn org.msgpack.core.** \ No newline at end of file diff --git a/app/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.java new file mode 100644 index 00000000..af713468 --- /dev/null +++ b/app/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.java @@ -0,0 +1,27 @@ +package com.example.myapplication; + +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("com.example.myapplication", appContext.getPackageName()); + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..6713262e --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/bridgefy-license.txt b/app/src/main/assets/bridgefy-license.txt new file mode 100644 index 00000000..c52a67ad --- /dev/null +++ b/app/src/main/assets/bridgefy-license.txt @@ -0,0 +1 @@ +gqRkYXRh3ABLMjAyMC0wMi0yMVQxOTo1Nzo1NC43OTFafDIwMjAtMDQtMDNUMTk6NTg6MDAuMzQyWnxjb20uZXhhbXBsZS5teWFwcGxpY2F0aW9uqXNpZ25hdHVyZdwBAAYOzN8YEcz4fUTMuMyib8yTzMVXzN3MxszsZcyyzODMoczKJi0AzO3MucyZzMFGMEbMrszeT8y0DxnMusyEzK/Mv8y+zKjM7syrzNrM+czxRghyzPIDzLHMgMyAzOFzzLrM92rMyxvM7GUreg3MnyhmSsyxzMbMqU1hLhnMj03MnW/M6EPMssybzJgTfV/M025OQzoVzNQ+zIsWB3fM3CfMng8hzMQSzNh5zPMgc8yNzOjMy3x7AxTM18y4zMPMoFZ4zPTMzgFOzPkjzPVYzLzMtibM3syuRczFYszJCszdzMfMwFxWzJzMrcy4zOYJzMYPzKvM1inMohjMtcz+zLEYzJjM6XhFBsyuzKXMrwTMsjVazLQRang/QE/MsMzPzPvMzSfMyFnM+cyVD0jMhsytR8ybP8y2GszazKnM+MzzzLHM0Rt9JBBrC8y9zJ0yW8zyZszlzJBfzJxKSzICEm4fMX1uYDQ4zKnMwnHM5w5yzI86WAnM+m5dzPLMp8yi \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/BridegfyVictim/BridgefyActivity.kt b/app/src/main/java/com/example/myapplication/BridegfyVictim/BridgefyActivity.kt new file mode 100644 index 00000000..36378c8f --- /dev/null +++ b/app/src/main/java/com/example/myapplication/BridegfyVictim/BridgefyActivity.kt @@ -0,0 +1,99 @@ +package com.example.myapplication.BridegfyVictim + +import android.Manifest +import android.content.pm.PackageManager +import android.os.Bundle +import android.os.PersistableBundle +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import androidx.core.app.ComponentActivity.ExtraData +import androidx.core.content.ContextCompat.getSystemService +import android.icu.lang.UCharacter.GraphemeClusterBreak.T +import androidx.navigation.findNavController +import com.example.myapplication.BridegfyVictim.Dao.AppDatabaseInstance +import com.example.myapplication.R +import kotlinx.coroutines.runBlocking + +class BridgefyActivity : AppCompatActivity() { + + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_bridgefy) + // Here, thisActivity is the current activity + if (ContextCompat.checkSelfPermission(this, + Manifest.permission.ACCESS_COARSE_LOCATION) + != PackageManager.PERMISSION_GRANTED) { + + // Permission is not granted + // Should we show an explanation? + if (ActivityCompat.shouldShowRequestPermissionRationale(this, + Manifest.permission.READ_CONTACTS)) { + // Show an explanation to the user *asynchronously* -- don't block + // this thread waiting for the user's response! After the user + // sees the explanation, try again to request the permission. + } else { + // No explanation needed, we can request the permission. + ActivityCompat.requestPermissions(this, + arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION), + 100) + + // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an + // app-defined int constant. The callback method gets the + // result of the request. + } + } else { + // Permission has already been granted + } + checkForSmsPermission() + val userDao = AppDatabaseInstance.getDb(this).userDao() + runBlocking { + val currentUser = userDao.getCurrentUser() + if (currentUser.isNull()) { //no user instance found + findNavController(R.id.nav_host_fragment).navigate(R.id.loginFragment) + } else { + findNavController(R.id.nav_host_fragment).navigate(R.id.mainFragment) + } + } + } + + private fun checkForSmsPermission() { + if (ActivityCompat.checkSelfPermission(this, + Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED) { + // Permission not yet granted. Use requestPermissions(). + // MY_PERMISSIONS_REQUEST_SEND_SMS is an + // app-defined int constant. The callback method gets the + // result of the request. + ActivityCompat.requestPermissions(this, + arrayOf(Manifest.permission.SEND_SMS), + 101) + } else { + // Permission already granted. Enable the SMS button. + } + } + + override fun onRequestPermissionsResult(requestCode: Int, + permissions: Array, grantResults: IntArray) { + when (requestCode) { + 100 -> { + // If request is cancelled, the result arrays are empty. + if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { + // permission was granted, yay! Do the + // contacts-related task you need to do. + } else { + // permission denied, boo! Disable the + // functionality that depends on this permission. + } + return + } + + // Add other 'when' lines to check for other + // permissions this app might request. + else -> { + // Ignore all other requests. + } + } + } +} diff --git a/app/src/main/java/com/example/myapplication/BridegfyVictim/CommonViewModel.kt b/app/src/main/java/com/example/myapplication/BridegfyVictim/CommonViewModel.kt new file mode 100644 index 00000000..9f32938a --- /dev/null +++ b/app/src/main/java/com/example/myapplication/BridegfyVictim/CommonViewModel.kt @@ -0,0 +1,18 @@ +package com.example.myapplication.BridegfyVictim + +import androidx.lifecycle.ViewModel +import com.bridgefy.sdk.client.Bridgefy +import com.bridgefy.sdk.client.BridgefyClient +import com.example.myapplication.BridegfyVictim.model.DisasterResources + +class CommonViewModel: ViewModel() { + + companion object { + fun broadcastMessage(disasterResources: DisasterResources) { + val json = NetworkUtil.gson.toJson(disasterResources) + val data = HashMap() + data["data"] = json + Bridgefy.sendBroadcastMessage(data) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/BridegfyVictim/Dao/AppDatabase.kt b/app/src/main/java/com/example/myapplication/BridegfyVictim/Dao/AppDatabase.kt new file mode 100644 index 00000000..6a72d70a --- /dev/null +++ b/app/src/main/java/com/example/myapplication/BridegfyVictim/Dao/AppDatabase.kt @@ -0,0 +1,30 @@ +package com.example.myapplication.BridegfyVictim.Dao + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.room.TypeConverters +import com.example.myapplication.BridegfyVictim.Dao.food.FoodServiceDao +import com.example.myapplication.BridegfyVictim.Dao.food.FoodServiceEntity +import com.example.myapplication.BridegfyVictim.Dao.user.UserDao +import com.example.myapplication.BridegfyVictim.Dao.user.UserDetail + +object AppDatabaseInstance { + fun getDb(context: Context) : AppDatabase { + return Room.databaseBuilder( + context, + AppDatabase::class.java, "database-name" + ) + .allowMainThreadQueries() + .fallbackToDestructiveMigration() + .build() + } +} + +@Database(entities = [FoodServiceEntity::class, UserDetail::class], version = 2) +@TypeConverters(UserDetail.EnumConverters::class) +abstract class AppDatabase : RoomDatabase() { + abstract fun foodServiceDao(): FoodServiceDao + abstract fun userDao(): UserDao +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/BridegfyVictim/Dao/food/FoodServiceDao.kt b/app/src/main/java/com/example/myapplication/BridegfyVictim/Dao/food/FoodServiceDao.kt new file mode 100644 index 00000000..327fb488 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/BridegfyVictim/Dao/food/FoodServiceDao.kt @@ -0,0 +1,15 @@ +package com.example.myapplication.BridegfyVictim.Dao.food + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query + +@Dao +interface FoodServiceDao { + + @Query("SELECT * FROM FoodServiceEntity") + fun getSavedFoodServices(): List + + @Insert + fun insertFoodService(foodServiceEntity: FoodServiceEntity) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/BridegfyVictim/Dao/food/FoodServiceEntity.kt b/app/src/main/java/com/example/myapplication/BridegfyVictim/Dao/food/FoodServiceEntity.kt new file mode 100644 index 00000000..f2242a99 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/BridegfyVictim/Dao/food/FoodServiceEntity.kt @@ -0,0 +1,13 @@ +package com.example.myapplication.BridegfyVictim.Dao.food + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity +data class FoodServiceEntity( + @PrimaryKey(autoGenerate = true) val _id: Int = 0, + val longitude: Double, + val latitude: Double, + val message: String +) { +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/BridegfyVictim/Dao/user/UserDao.kt b/app/src/main/java/com/example/myapplication/BridegfyVictim/Dao/user/UserDao.kt new file mode 100644 index 00000000..e05beb57 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/BridegfyVictim/Dao/user/UserDao.kt @@ -0,0 +1,14 @@ +package com.example.myapplication.BridegfyVictim.Dao.user + +import androidx.room.* + +@Dao +interface UserDao { + + @Query("SELECT * FROM UserDetail WHERE _userId=0") + suspend fun getCurrentUser(): UserDetail? + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun updateUser(userDetail: UserDetail) + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/BridegfyVictim/Dao/user/UserDetail.kt b/app/src/main/java/com/example/myapplication/BridegfyVictim/Dao/user/UserDetail.kt new file mode 100644 index 00000000..73010a81 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/BridegfyVictim/Dao/user/UserDetail.kt @@ -0,0 +1,45 @@ +package com.example.myapplication.BridegfyVictim.Dao.user + +import androidx.room.Embedded +import androidx.room.Entity +import androidx.room.PrimaryKey +import androidx.room.TypeConverter + +@Entity +data class UserDetail ( + @PrimaryKey(autoGenerate = false) val _userId: Int = 0, + val userName: String, + val phoneNumber: Long, + val gender: Gender = Gender.NONE, + @Embedded val emergencyContact: EmergencyContact + ) { + enum class Gender { + MALE, FEMALE, NONE + } + + data class EmergencyContact( + val contact1: Long, + val contact2: Long + ) + + class EnumConverters() { + @TypeConverter + fun fromGenderToInt(gender: Gender): Int { + return when (gender) { + Gender.MALE -> 1 + Gender.FEMALE -> 2 + Gender.NONE -> 3 + } + } + + @TypeConverter + fun fromIntToGender(int: Int): Gender{ + return when (int) { + 1 -> Gender.MALE + 2 ->Gender.FEMALE + else -> Gender.NONE + } + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/BridegfyVictim/Repo/SmsRepo.kt b/app/src/main/java/com/example/myapplication/BridegfyVictim/Repo/SmsRepo.kt new file mode 100644 index 00000000..cd69ce62 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/BridegfyVictim/Repo/SmsRepo.kt @@ -0,0 +1,28 @@ +package com.example.myapplication.BridegfyVictim.Repo + +import com.twilio.Twilio + +import com.twilio.rest.api.v2010.account.Message +import com.twilio.type.PhoneNumber + +class SmsRepo { + +} + +object SmsSender { + // Find your Account Sid and Auth Token at twilio.com/console + val ACCOUNT_SID = "AC455f51a42329ffcb2e4f6b438b9082df" + val AUTH_TOKEN = "00cea12e845806de3578cd5390c7015d" + + fun sendSms() { + Twilio.init(ACCOUNT_SID, AUTH_TOKEN) + + val message = Message + .creator(PhoneNumber("+917010065028"), // to + PhoneNumber("+12568040107"), // from + "Where's Wallace?") + .create() + + println(message.status) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/BridegfyVictim/SettingsFragment.kt b/app/src/main/java/com/example/myapplication/BridegfyVictim/SettingsFragment.kt new file mode 100644 index 00000000..bca84a44 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/BridegfyVictim/SettingsFragment.kt @@ -0,0 +1,97 @@ +package com.example.myapplication.BridegfyVictim + +import android.content.Context +import android.content.SharedPreferences +import android.net.Uri +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import com.example.myapplication.BridegfyVictim.model.DisasterResources +import com.example.myapplication.BridegfyVictim.model.NativeLocationFragment +import com.example.myapplication.BridegfyVictim.model.OneOf + +import com.example.myapplication.R +import kotlinx.android.synthetic.main.activity_food.* +import kotlinx.android.synthetic.main.fragment_settings.* + +class SettingsFragment : NativeLocationFragment() { + + lateinit var sharedPreferences: SharedPreferences + lateinit var viewModel: CommonViewModel + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + sharedPreferences = requireContext().getSharedPreferences("Settings", Context.MODE_PRIVATE) + viewModel = activity?.run { + ViewModelProviders.of(this)[CommonViewModel::class.java] + } ?: throw Exception("Invalid Activity") + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_settings, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + switch1.isChecked = sharedPreferences.getBoolean(FOOD_AVAILABLE, false) + switch2.isChecked = sharedPreferences.getBoolean(MEDICINE_AVAILABLE, false) + + switch3.isChecked = sharedPreferences.getBoolean(ALERT, false) + + switch1.setOnClickListener { + sharedPreferences.edit() + .putBoolean(FOOD_AVAILABLE, switch1.isChecked) + .apply() + if (switch1.isChecked) { + detectLocation() + } + } + + switch2.setOnClickListener { + sharedPreferences.edit() + .putBoolean(FOOD_AVAILABLE, switch2.isChecked) + .apply() + } + + switch3.setOnClickListener { + sharedPreferences.edit() + .putBoolean(ALERT, switch3.isChecked) + .apply() + } + } + + private fun detectLocation() { + updateLocation() + locationLiveData.observe(this@SettingsFragment, Observer { location -> + when (location) { + is OneOf.Success -> { + val sendRequest = DisasterResources.FoodService( + latitude = location.a.latitude, + longitude = location.a.longitude, + numOfPeople = 3, + deviceId = getDeviceId(requireContext()) + ) + CommonViewModel.broadcastMessage(sendRequest) + locationLiveData.removeObservers(this) + } + is OneOf.Failure -> { + Toast.makeText(requireContext(), "failed : ${location.b}", Toast.LENGTH_SHORT) + .show() + } + } + }) + } + + companion object { + const val FOOD_AVAILABLE = "food_available" + const val MEDICINE_AVAILABLE = "medicine_available" + const val ALERT = "alert" + } +} diff --git a/app/src/main/java/com/example/myapplication/BridegfyVictim/Util.kt b/app/src/main/java/com/example/myapplication/BridegfyVictim/Util.kt new file mode 100644 index 00000000..81fcfedd --- /dev/null +++ b/app/src/main/java/com/example/myapplication/BridegfyVictim/Util.kt @@ -0,0 +1,69 @@ +package com.example.myapplication.BridegfyVictim + +import android.content.Context +import android.content.Context.CONNECTIVITY_SERVICE +import android.net.ConnectivityManager +import android.provider.Settings +import android.telephony.TelephonyManager +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import com.example.myapplication.BridegfyVictim.model.DisasterResources +import com.example.myapplication.R +import com.google.gson.Gson + +fun isNetworkAvailable(context: Context): Boolean { + val tel = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager + return !(tel.networkOperator != null && tel.networkOperator == "") +} + +fun isInternetAvailable(context: Context): Boolean { + val cm = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager + val netInfo = cm.activeNetworkInfo + return netInfo != null && netInfo.isAvailable +} + +fun showNotification(context: Context, disasterResources: DisasterResources) { + + val builder = NotificationCompat.Builder(context, "100") + .setSmallIcon(R.drawable.ic_launcher_foreground) + .setContentTitle( + getTitle(disasterResources) + ) + .setContentText(getContent(disasterResources)) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + + with(NotificationManagerCompat.from(context)) { + // notificationId is a unique int for each notification that you must define + notify(1, builder.build()) + } +} + +fun getContent(disasterResources: DisasterResources): String { + return when (disasterResources) { + is DisasterResources.SendEmergencyMessage -> "Send sms" + is DisasterResources.FoodRequest -> "Required ${disasterResources.requiredFood} at ${disasterResources.area}" + is DisasterResources.Medication -> " Medication Required ${disasterResources.requiredMedication} at ${disasterResources.area}" + is DisasterResources.FoodService -> "" + } +} + +fun getTitle(disasterResources: DisasterResources): String { + return when (disasterResources) { + is DisasterResources.SendEmergencyMessage -> "Send emergency contact" + is DisasterResources.FoodRequest -> "Food Request" + is DisasterResources.Medication -> " Medication Request" + is DisasterResources.FoodService -> "" + } +} + + +fun getDeviceId(context: Context): String { + return Settings.Secure.getString(context.contentResolver, + Settings.Secure.ANDROID_ID) +} + +object NetworkUtil { + val gson = Gson() +} + +fun Any?.isNull(): Boolean = this == null \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/BridegfyVictim/model/DisasterResources.kt b/app/src/main/java/com/example/myapplication/BridegfyVictim/model/DisasterResources.kt new file mode 100644 index 00000000..41000f76 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/BridegfyVictim/model/DisasterResources.kt @@ -0,0 +1,49 @@ +package com.example.myapplication.BridegfyVictim.model + +import com.bridgefy.sdk.client.Bridgefy + +sealed class DisasterResources( + val userDeviceId: String +) { + + data class SendEmergencyMessage( + val emergencyContact: Long, + val message: String, + val deviceId: String, + private val tag: String = EMERGENCY_CONTACT_TAG + ): DisasterResources(deviceId) + + data class FoodRequest( + val latitude: Double?, + val longitude: Double?, + val area: String, + val numOfPeople: Int, + val requiredFood: String, + val deviceId: String, + private val tag: String = FOOD_REQUEST + ) : DisasterResources(deviceId) + + data class Medication( + val latitude: Double?, + val longitude: Double?, + val area: String, + val requiredMedication: String, + val deviceId: String, + private val tag: String = MEDICATION + ) : DisasterResources(deviceId) + + data class FoodService( + val latitude: Double, + val longitude: Double, + val numOfPeople: Int, + val deviceId: String, + private val tag: String = FOOD_SERVICE + ) : DisasterResources(deviceId) + + companion object { + const val EMERGENCY_CONTACT_TAG = "emergency_tag" + const val FOOD_REQUEST = "food_request" + const val MEDICATION = "MedicationFragment" + const val FOOD_SERVICE = "food_service" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/BridegfyVictim/model/Failure.kt b/app/src/main/java/com/example/myapplication/BridegfyVictim/model/Failure.kt new file mode 100644 index 00000000..e127ea49 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/BridegfyVictim/model/Failure.kt @@ -0,0 +1,56 @@ +package com.example.myapplication.BridegfyVictim.model + +sealed class Failure { + + /** + * class : UnknownFailure + * this type of failure is used when the cause of error is unknown + * and this failure also represents the generic failure + * */ + object UnKnownFailure : Failure() + + /** + * The request was valid, but the server is refusing action. + * The user might not have the necessary permissions for a resource, + * or may need an account of some sort. This code is also typically used + * if the request provided authentication via the WWW-Authenticate header field, but the server did not accept that authentication. + * */ + object ForbiddenFailure : Failure() + + /** + The server timed out waiting for the request. According to HTTP specifications: "The client did not produce a request + within the time that the server was prepared to wait. The client MAY repeat the request without modifications at any later time. + */ + object RequestTimeoutFailure : Failure() + + /** + The requested resource could not be found but may be available in the future. Subsequent requests by the client are permissible. + */ + object NotFoundFailure : Failure() + + /** + * This failure represents a generic network failure. for eg: this failure can be used in + * case of no internet connection + */ + + object NetworkFailure : Failure() + + object DatabaseFailure : Failure() + + /** + * use this failure if it is not possible to fetch location. if both fetch from network and database is unsuccessful + * */ + + + sealed class LocationFailure : Failure() { + + object LocationPermissionDeniedFailure : LocationFailure() + + object LocationNotEnabledFailure : LocationFailure() + + object NoSavedLocationFailure : LocationFailure() + + object UnknownLocationFailure : LocationFailure() + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/BridegfyVictim/model/NativeLocationFragment.kt b/app/src/main/java/com/example/myapplication/BridegfyVictim/model/NativeLocationFragment.kt new file mode 100644 index 00000000..fd845d3a --- /dev/null +++ b/app/src/main/java/com/example/myapplication/BridegfyVictim/model/NativeLocationFragment.kt @@ -0,0 +1,245 @@ +package com.example.myapplication.BridegfyVictim.model + +import android.Manifest +import android.app.Activity +import android.content.Intent +import android.content.IntentSender +import android.content.pm.PackageManager +import android.location.Location +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import androidx.lifecycle.MutableLiveData +import com.example.myapplication.R +import com.google.android.gms.common.api.ResolvableApiException +import com.google.android.gms.location.* + +/** + * This Fragment represents the base Location class, any fragment which needs access to the + * native location feature will extend from this Fragment + * */ +open class NativeLocationFragment : Fragment() { + + private lateinit var fusedLocationProviderClient: FusedLocationProviderClient + + /** + * [locationLiveData] will expose the current location obtained from the device, + * [OneOf.Success] represents the location has been successfully obtained + * [OneOf.Failure] is used if the location cannot be obtained due to lack of permission + * or required setting is not available/enabled in the device + * */ + val locationLiveData: MutableLiveData> = + MutableLiveData() + + private val locationRequest = LocationRequest.create().apply { + interval = 10 + priority = LocationRequest.PRIORITY_HIGH_ACCURACY + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + fusedLocationProviderClient = + LocationServices.getFusedLocationProviderClient(requireContext()) + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_native_location, container, false) + } + + /** + *call this method to update the location value in [locationLiveData].The execution is as follows, + * [isLocationPermissionGranted] -> [checkRequiredLocationSettings] (if success) -> [fetchLastKnownLocation] + * (if failure) -> open settings screen to enable location + */ + fun updateLocation() { + + if (isLocationPermissionGranted()) { + checkRequiredLocationSettings( + successListener = { + fetchLastKnownLocation() + + }, + failureListener = { exception -> + if (exception is ResolvableApiException) { + // Location settings are not satisfied + try { + // Show the dialog by calling startIntentSenderForResult(), + // and check the result in onActivityResult(). + startIntentSenderForResult( + exception.resolution.intentSender, + REQUEST_CHECK_SETTINGS, + null, + 0, + 0, + 0, + null + ) + } catch (sendEx: IntentSender.SendIntentException) { + //Ignore + } + } + } + ) + } else { + requestLocationPermission() + } + } + + /** + * check if the location permission is granted or not + */ + private fun isLocationPermissionGranted(): Boolean { + return ContextCompat.checkSelfPermission( + requireContext(), Manifest.permission.ACCESS_COARSE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + } + + /** + * request the runtime permission for location, for devices above android 6 + */ + private fun requestLocationPermission() { + requestPermissions( + arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), + LOCATION_PERMISSION_CHECK + ) + } + + /** + * check if the current devices settings match the required settings needed to obtain the location + * [successListener] is a lambda with a param [LocationSettingsResponse] will be executed if the current settings + * match the required settings + * [failureListener] will be invoked if the current settings and the required settings doesn't match + */ + private fun checkRequiredLocationSettings( + successListener: (LocationSettingsResponse) -> Unit, + failureListener: (Exception) -> Unit + ) { + val locationSettingRequest = LocationSettingsRequest.Builder() + .addLocationRequest(locationRequest) + .build() + + val settingsClient = LocationServices.getSettingsClient(requireContext()) + + settingsClient.checkLocationSettings(locationSettingRequest) + .addOnSuccessListener(successListener) + .addOnFailureListener(failureListener) + } + + /** + * this method is used to post the last know location using [fusedLocationProviderClient.lastLocation] + * returns [OneOf.Success] if the location value is not null and [OneOf.Failure] if the operation fails + * if the [Location] value returned is null, a new locationUpdateRequest is subscribed by calling [requestNewLocationUpdates] + */ + private fun fetchLastKnownLocation() { + fusedLocationProviderClient.lastLocation + .addOnSuccessListener { location: Location? -> + if (location != null) { + locationLiveData.postValue( + OneOf.Success( + location, + OneOf.Success.DataState.FRESH_DATA + ) + ) + } else { + requestNewLocationUpdates() + } + } + .addOnFailureListener { + locationLiveData.postValue( + OneOf.Failure( + Failure.LocationFailure.UnknownLocationFailure + ) + ) + } + } + + /** + * call this method if the [fusedLocationProviderClient.lastLocation] returns null. + * this method will setup a new location update callback with single update for location. + */ + private fun requestNewLocationUpdates() { + fusedLocationProviderClient.requestLocationUpdates( + locationRequest.apply { + numUpdates = 1 + }, + object : LocationCallback() { + override fun onLocationResult(locations: LocationResult?) { + super.onLocationResult(locations) + locations?.let { + locationLiveData.postValue( + OneOf.Success( + it.lastLocation, + OneOf.Success.DataState.FRESH_DATA + ) + ) + } ?: run { + // this block is executed if the location is null + locationLiveData.postValue( + OneOf.Failure( + Failure.LocationFailure.UnknownLocationFailure + ) + ) + } + } + }, + null + ) + + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + + when (requestCode) { + REQUEST_CHECK_SETTINGS -> { + if (resultCode == Activity.RESULT_OK) { + //the users has changed the current location settings to meet the requirements, retry getting location + updateLocation() + } else { + //the user failed to update the location settings to meet the requirement + locationLiveData.postValue( + OneOf.Failure( + Failure.LocationFailure.LocationNotEnabledFailure + ) + ) + } + } + } + } + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + + when (requestCode) { + LOCATION_PERMISSION_CHECK -> { + if (grantResults.isNotEmpty() and (grantResults[0] == PackageManager.PERMISSION_GRANTED)) { + //the user has granted the location permission, hence retry to obtain the current location + updateLocation() + } else { + //the user has denied the location permission + locationLiveData.postValue( + OneOf.Failure( + Failure.LocationFailure.LocationPermissionDeniedFailure + ) + ) + } + } + } + } + + companion object { + private const val REQUEST_CHECK_SETTINGS = 100 + private const val LOCATION_PERMISSION_CHECK = 101 + } +} + diff --git a/app/src/main/java/com/example/myapplication/BridegfyVictim/model/OneOf.kt b/app/src/main/java/com/example/myapplication/BridegfyVictim/model/OneOf.kt new file mode 100644 index 00000000..b90f51ae --- /dev/null +++ b/app/src/main/java/com/example/myapplication/BridegfyVictim/model/OneOf.kt @@ -0,0 +1,44 @@ +package com.example.myapplication.BridegfyVictim.model + +/** + * Represents a value of one of two possible types (a disjoint union). + * Instances of [OneOf] are either an instance of [Success] or [Failure]. + * + * @see Success + * @see Failure + */ +sealed class OneOf { + /** * Represents the success side of [OneOf] class which by convention is a "Failure". + * @param S represents the data type to be returned by the successful response + * @param dataState represents the type of data loaded into the success response, + * [DataState.CACHED_DATA] represents cached data and + * [DataState.FRESH_DATA] represents a newly loaded data + * */ + data class Success( + val a: S, + val dataState: DataState + ) : OneOf() { + enum class DataState { + FRESH_DATA, CACHED_DATA + } + } + + /** * Represents the failure side of [OneOf] class which by convention is a "Failure". */ + data class Failure(val b: F) : OneOf() + + val isFailure get() = this is Failure + val isSuccess get() = this is Success + + fun success(a: S, dataState: Success.DataState) = Success(a, dataState) + fun failure(b: F) = Failure(b) + + suspend fun oneOf(fnS: suspend (S) -> Any, fnF: suspend (F) -> Any): Any = + when (this) { + is Success -> fnS(a) + is Failure -> fnF(b) + } +} + +fun ((A) -> B).c(f: (B) -> C): (A) -> C = { + f(this(it)) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/EmergencyFragment.kt b/app/src/main/java/com/example/myapplication/EmergencyFragment.kt new file mode 100644 index 00000000..ccc35c19 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/EmergencyFragment.kt @@ -0,0 +1,99 @@ +package com.example.myapplication + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.Fragment +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import androidx.navigation.fragment.findNavController +import com.example.myapplication.BridegfyVictim.CommonViewModel +import com.example.myapplication.BridegfyVictim.CommonViewModel.Companion.broadcastMessage +import com.example.myapplication.BridegfyVictim.Dao.AppDatabaseInstance +import com.example.myapplication.BridegfyVictim.getDeviceId +import com.example.myapplication.BridegfyVictim.model.DisasterResources +import com.example.myapplication.BridegfyVictim.model.NativeLocationFragment +import com.example.myapplication.BridegfyVictim.model.OneOf +import kotlinx.android.synthetic.main.activity_emergencyactivity.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +class EmergencyFragment : NativeLocationFragment() { + + lateinit var viewModel: CommonViewModel + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + viewModel = activity?.run { + ViewModelProviders.of(this)[CommonViewModel::class.java] + } ?: throw Exception("Invalid Activity") + return inflater.inflate(R.layout.activity_emergencyactivity, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + detectLocation() + default_button.setOnClickListener { + message_button.isChecked = false + + } + + message_button.setOnClickListener { + default_button.isChecked = false + } + + back.setOnClickListener { + findNavController().popBackStack() + } + + broadcast.setOnClickListener { + CoroutineScope(Dispatchers.IO).launch { + val userDao = AppDatabaseInstance.getDb(requireContext()).userDao() + val message = if (default_button.isActivated) { + default_sos.text.toString() + } else { + message.editableText.toString().trim() + } + + val contact1 = DisasterResources.SendEmergencyMessage( + emergencyContact = userDao.getCurrentUser()?.emergencyContact?.contact1 ?: 8788043980, + message = message, + deviceId = getDeviceId(requireContext()) + ) + val contact2 = DisasterResources.SendEmergencyMessage( + emergencyContact = userDao.getCurrentUser()?.emergencyContact?.contact2 ?: 7010065028, + message = message, + deviceId = getDeviceId(requireContext()) + ) + broadcastMessage(contact1) + broadcastMessage(contact2) + + withContext(Dispatchers.Main){ + Toast.makeText(requireContext(), "Message sent to nearby" + + " ${(this@EmergencyFragment.activity?.application as MyApplication).availableDevices.size}", + Toast.LENGTH_SHORT).show() + this@EmergencyFragment.findNavController().popBackStack() + } + } + } + } + + private fun detectLocation() { + updateLocation() + locationLiveData.observe(this, Observer { location -> + when (location) { + is OneOf.Success -> { + default_sos.text = "SOS, I am stuck and need help. I am here at ${location.a.latitude}, ${ location.a.longitude}" + locationLiveData.removeObservers(this) + } + is OneOf.Failure -> { + Toast.makeText(requireContext(), "failed : ${location.b}", Toast.LENGTH_SHORT) + .show() + } + } + }) + } +} diff --git a/app/src/main/java/com/example/myapplication/FoodFragment.kt b/app/src/main/java/com/example/myapplication/FoodFragment.kt new file mode 100644 index 00000000..3efa6926 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/FoodFragment.kt @@ -0,0 +1,65 @@ +package com.example.myapplication + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.Fragment +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelProviders +import com.example.myapplication.BridegfyVictim.CommonViewModel +import com.example.myapplication.BridegfyVictim.CommonViewModel.Companion.broadcastMessage +import com.example.myapplication.BridegfyVictim.getDeviceId +import com.example.myapplication.BridegfyVictim.model.DisasterResources +import com.example.myapplication.BridegfyVictim.model.NativeLocationFragment +import com.example.myapplication.BridegfyVictim.model.OneOf +import kotlinx.android.synthetic.main.activity_food.* + +class FoodFragment : NativeLocationFragment() { + + private lateinit var viewModel: CommonViewModel + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + viewModel = activity?.run { + ViewModelProviders.of(this)[CommonViewModel::class.java] + } ?: throw Exception("Invalid Activity") + return inflater.inflate(R.layout.activity_food, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + send_food_request.setOnClickListener { + detectLocation() + } + } + + private fun detectLocation() { + updateLocation() + locationLiveData.observe(this@FoodFragment, Observer { location -> + when (location) { + is OneOf.Success -> { + val area = area_text_box.editableText.toString().trim() + val numberOfPeople = no_of_people_text_box.editableText.toString().trim() + val message = message_box.editableText.toString().trim() + val sendRequest = DisasterResources.FoodRequest( + latitude = location.a.latitude, + longitude = location.a.longitude, + area = area, + numOfPeople = numberOfPeople.toInt(), + requiredFood = message, + deviceId = getDeviceId(requireContext()) + ) + broadcastMessage(sendRequest) + locationLiveData.removeObservers(this) + } + is OneOf.Failure -> { + Toast.makeText(requireContext(), "failed : ${location.b}", Toast.LENGTH_SHORT) + .show() + } + } + }) + } + + +} diff --git a/app/src/main/java/com/example/myapplication/MainFragment.kt b/app/src/main/java/com/example/myapplication/MainFragment.kt new file mode 100644 index 00000000..31091f08 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/MainFragment.kt @@ -0,0 +1,38 @@ +package com.example.myapplication + +import android.content.Intent +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.Fragment +import androidx.navigation.fragment.findNavController +import kotlinx.android.synthetic.main.activity_main.* + +class MainFragment : Fragment() { + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.activity_main, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + ripple.startRippleAnimation() + ripple_img.setOnClickListener { + findNavController().navigate(R.id.action_mainFragment_to_emergencyFragment) + } + +// food_button.setOnClickListener { +// findNavController().navigate(R.id.action_mainFragment_to_foodFragment) +// } +// +// medication_button.setOnClickListener { +// findNavController().navigate(R.id.action_mainFragment_to_medicationFragment) +// } +// +// settings_icon.setOnClickListener { +// findNavController().navigate(R.id.action_mainFragment_to_settingsFragment) +// } + } +} diff --git a/app/src/main/java/com/example/myapplication/MedicationFragment.kt b/app/src/main/java/com/example/myapplication/MedicationFragment.kt new file mode 100644 index 00000000..0401583d --- /dev/null +++ b/app/src/main/java/com/example/myapplication/MedicationFragment.kt @@ -0,0 +1,43 @@ +package com.example.myapplication + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProviders +import com.example.myapplication.BridegfyVictim.CommonViewModel +import com.example.myapplication.BridegfyVictim.CommonViewModel.Companion.broadcastMessage +import com.example.myapplication.BridegfyVictim.getDeviceId +import com.example.myapplication.BridegfyVictim.model.DisasterResources +import kotlinx.android.synthetic.main.activity_medication.* + +class MedicationFragment : Fragment() { + + private lateinit var viewModel: CommonViewModel + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + viewModel = activity?.run { + ViewModelProviders.of(this)[CommonViewModel::class.java] + } ?: throw Exception("Invalid Activity") + return inflater.inflate(R.layout.activity_medication, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + send_medication_button.setOnClickListener { + val area = area_text_box.editableText.toString().trim() + val noOfPeople = no_of_people_text_box.editableText.toString().trim() + val message = message_box.editableText.toString().trim() + + val medicationRequest = DisasterResources.Medication( + latitude = null, + longitude = null, + area = area, + requiredMedication = message, + deviceId = getDeviceId(requireContext()) + ) + + broadcastMessage(medicationRequest) + } + } +} diff --git a/app/src/main/java/com/example/myapplication/MyApplication.kt b/app/src/main/java/com/example/myapplication/MyApplication.kt new file mode 100644 index 00000000..0a1c17ce --- /dev/null +++ b/app/src/main/java/com/example/myapplication/MyApplication.kt @@ -0,0 +1,239 @@ +package com.example.myapplication + +import android.app.Application +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import android.os.Build +import android.util.Log +import android.widget.Toast +import com.bridgefy.sdk.client.* +import com.example.myapplication.BridegfyVictim.model.DisasterResources +import com.google.gson.JsonParser +import com.bridgefy.sdk.client.Bridgefy +import com.bridgefy.sdk.framework.exceptions.MessageException +import java.util.* +import android.telephony.SmsManager +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import com.example.myapplication.BridegfyVictim.* +import com.example.myapplication.BridegfyVictim.Dao.food.FoodServiceDao +import com.example.myapplication.BridegfyVictim.Dao.AppDatabaseInstance +import com.example.myapplication.BridegfyVictim.Dao.food.FoodServiceEntity + +class MyApplication : Application() { + + val availableDevices: MutableList = mutableListOf() + lateinit var foodServiceDao: FoodServiceDao + + val messageListener = object : MessageListener() { + override fun onMessageSent(messageId: String?) { + super.onMessageSent(messageId) + Log.v("bridgefy", messageId) + } + + override fun onBroadcastMessageReceived(message: Message?) { + super.onBroadcastMessageReceived(message) + Log.v("Broadcast message", message?.toString()) + handleMessage(message?.content?.get("data").toString()) + } + + override fun onMessageDataProgress(message: UUID?, progress: Long, fullSize: Long) { + super.onMessageDataProgress(message, progress, fullSize) + Log.v("progress", progress.toString()) + } + + override fun onMessageFailed(message: Message?, e: MessageException?) { + super.onMessageFailed(message, e) + Log.v("failed", e.toString()) + } + } + + val stateListener = object : StateListener() { + + override fun onStarted() { + super.onStarted() + Log.v("bridgefy", "started") + Toast.makeText(this@MyApplication, "started", Toast.LENGTH_SHORT).show() + } + + override fun onStartError(message: String?, errorCode: Int) { + super.onStartError(message, errorCode) + Log.v("bridgefy", "started_error") + } + override fun onDeviceConnected(device: Device?, session: Session?) { + super.onDeviceConnected(device, session) + Toast.makeText(applicationContext, "Device connected", Toast.LENGTH_SHORT).show() + if (applicationContext.getSharedPreferences("Settings", Context.MODE_PRIVATE).getBoolean(SettingsFragment.ALERT, false)) { + val builder = NotificationCompat.Builder(applicationContext, "100") + .setSmallIcon(R.drawable.ic_launcher_foreground) + .setContentTitle( + "Alert" + ) + .setContentText("someone nearby needs help") + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + + with(NotificationManagerCompat.from(applicationContext)) { + // notificationId is a unique int for each notification that you must define + notify(1, builder.build()) + } + } + + availableDevices.add(device) + } + + override fun onDeviceLost(device: Device?) { + super.onDeviceLost(device) + availableDevices.removeAll { + it?.userId == device?.userId + } + } + } + + override fun onCreate() { + super.onCreate() + + createNotificationChannel() + foodServiceDao = AppDatabaseInstance.getDb(applicationContext).foodServiceDao() + Bridgefy.initialize(applicationContext, "2b56623e-15a4-43ca-9050-eb6d8fd3edf7", object : RegistrationListener() { + + override fun onRegistrationSuccessful(bridgefyClient: BridgefyClient) { + // BridgefyActivity is ready to start + val builder = Config.Builder() + builder.apply { + setEnergyProfile(BFEnergyProfile.HIGH_PERFORMANCE) + setEncryption(false) + } + + Bridgefy.start(messageListener, stateListener, builder.build()) + } + + override fun onRegistrationFailed(errorCode: Int, message: String) { + // Something went wrong: handle error code, maybe print the message + Log.e("error", message) + } + } + ) + } + + private fun handleMessage(json: String) { + val messageTag = JsonParser().parse(json).asJsonObject.getAsJsonPrimitive("tag").asString + when (messageTag) { + DisasterResources.MEDICATION -> { + val medicationResources = NetworkUtil.gson.fromJson(json, DisasterResources.Medication::class.java) + handleMedicationRequest(medicationResources) + } + + DisasterResources.FOOD_REQUEST -> { + val foodResources = NetworkUtil.gson.fromJson(json, DisasterResources.FoodRequest::class.java) + handleFoodRequest(foodResources) + } + + DisasterResources.EMERGENCY_CONTACT_TAG -> { + val sendMessageResources = NetworkUtil.gson.fromJson(json, DisasterResources.SendEmergencyMessage::class.java) + handleEmergencyMessages(sendMessageResources) + } + + DisasterResources.FOOD_SERVICE -> { + val foodService = NetworkUtil.gson.fromJson(json, DisasterResources.FoodService::class.java) + handleFoodService(foodService) + } + } + } + + private fun handleFoodService(foodService: DisasterResources.FoodService) { + Toast.makeText(applicationContext, "$foodService", Toast.LENGTH_SHORT).show() + foodServiceDao.insertFoodService( + FoodServiceEntity( + longitude = foodService.longitude, + latitude = foodService.latitude, + message = foodService.numOfPeople.toString() + ) + ) + } + + private fun handleFoodRequest(foodResources: DisasterResources.FoodRequest) { + Toast.makeText(this, foodResources.toString(), Toast.LENGTH_LONG).show() + if (doesProvideFood()) { + showNotification(applicationContext, foodResources) + } else { + if (isNetworkAvailable(applicationContext)) { + //Send Api Request + } else { + if (hasConnectedDevices() and (foodResources.userDeviceId != getDeviceId(applicationContext))) { + CommonViewModel.broadcastMessage(foodResources) + } else { + enqueueTask(foodResources) + } + } + } + } + + private fun handleMedicationRequest(medication: DisasterResources.Medication) { + Toast.makeText(this, medication.toString(), Toast.LENGTH_LONG).show() + if (doesProvideMedication()) { + showNotification(applicationContext, medication) + } else { + if (isNetworkAvailable(applicationContext) and false) { + //Send Api Request + } else { + if (hasConnectedDevices() and (medication.userDeviceId != getDeviceId(applicationContext))) { + CommonViewModel.broadcastMessage(medication) + } else { + enqueueTask(medication) + } + } + } + } + + private fun enqueueTask(disasterResoource: DisasterResources) { + // try to send it later + } + + + private fun handleEmergencyMessages(emergencyRequest: DisasterResources.SendEmergencyMessage) { + Toast.makeText(this, emergencyRequest.toString(), Toast.LENGTH_LONG).show() + smsSendMessage(emergencyRequest) + } + + private fun smsSendMessage(emergencyRequest: DisasterResources.SendEmergencyMessage) { + try { + val smsManager = SmsManager.getDefault() + smsManager.sendTextMessage(emergencyRequest.emergencyContact.toString() , null, emergencyRequest.message, null, null) + Toast.makeText(applicationContext, "Message Sent", + Toast.LENGTH_LONG).show() + } catch (ex: Exception) { + Toast.makeText(applicationContext, ex.message.toString(), + Toast.LENGTH_LONG).show() + ex.printStackTrace() + } + } + + private fun doesProvideFood(): Boolean { + return getSharedPreferences("Settings", Context.MODE_PRIVATE) + .getBoolean(SettingsFragment.FOOD_AVAILABLE, false) + } + + private fun doesProvideMedication(): Boolean { + return getSharedPreferences("Settings", Context.MODE_PRIVATE) + .getBoolean(SettingsFragment.MEDICINE_AVAILABLE, false) + } + + private fun createNotificationChannel() { + // Create the NotificationChannel, but only on API 26+ because + // the NotificationChannel class is new and not in the support library + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val name = getString(R.string.channel_name) + val importance = NotificationManager.IMPORTANCE_DEFAULT + val channel = NotificationChannel("100", name, importance) + // Register the channel with the system + val notificationManager: NotificationManager = + getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(channel) + } + } + + private fun hasConnectedDevices(): Boolean { + return availableDevices.size > 0 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/UtilitiesFragment.kt b/app/src/main/java/com/example/myapplication/UtilitiesFragment.kt new file mode 100644 index 00000000..be99c9f3 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/UtilitiesFragment.kt @@ -0,0 +1,19 @@ +package com.example.myapplication + +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment + +import android.content.Context +import android.os.Bundle +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup + +class UtilitiesFragment : Fragment() { + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.activity_utilities, container) + + } +} diff --git a/app/src/main/java/com/example/myapplication/VolunteeringFragment.kt b/app/src/main/java/com/example/myapplication/VolunteeringFragment.kt new file mode 100644 index 00000000..de81fe14 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/VolunteeringFragment.kt @@ -0,0 +1,16 @@ +package com.example.myapplication + +import androidx.appcompat.app.AppCompatActivity + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment + +class VolunteeringFragment : Fragment() { + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.activity_volunteering, container) + } +} diff --git a/app/src/main/java/com/example/myapplication/ui/login/LoginFragment.kt b/app/src/main/java/com/example/myapplication/ui/login/LoginFragment.kt new file mode 100644 index 00000000..491aa43c --- /dev/null +++ b/app/src/main/java/com/example/myapplication/ui/login/LoginFragment.kt @@ -0,0 +1,56 @@ +package com.example.myapplication.ui.login + +import androidx.lifecycle.ViewModelProviders +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.navigation.fragment.findNavController +import com.example.myapplication.BridegfyVictim.Dao.AppDatabaseInstance +import com.example.myapplication.BridegfyVictim.Dao.user.UserDetail + +import com.example.myapplication.R +import kotlinx.android.synthetic.main.login_fragment.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +class LoginFragment : Fragment() { + + companion object { + fun newInstance() = LoginFragment() + } + + private lateinit var viewModel: LoginViewModel + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.login_fragment, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + viewModel = ViewModelProviders.of(this.requireActivity()).get(LoginViewModel::class.java) + register.setOnClickListener { + val name = name.editableText.toString() + val phoneNumber = number.editableText.toString().toLong() + val gender = UserDetail.Gender.NONE + + val user = UserDetail( + userName = name, + phoneNumber = phoneNumber, + gender = gender, + emergencyContact = UserDetail.EmergencyContact( + contact1 = 0, + contact2 = 0 + ) + ) + viewModel.userDetail = user + findNavController().navigate(R.id.action_loginFragment_to_registerFragment) + + } + } + +} diff --git a/app/src/main/java/com/example/myapplication/ui/login/LoginViewModel.kt b/app/src/main/java/com/example/myapplication/ui/login/LoginViewModel.kt new file mode 100644 index 00000000..5a654bca --- /dev/null +++ b/app/src/main/java/com/example/myapplication/ui/login/LoginViewModel.kt @@ -0,0 +1,11 @@ +package com.example.myapplication.ui.login + +import androidx.lifecycle.ViewModel +import com.example.myapplication.BridegfyVictim.Dao.user.UserDetail + +class LoginViewModel : ViewModel() { + + var userDetail: UserDetail = UserDetail( + 0, "", 0, emergencyContact = UserDetail.EmergencyContact(0, 0) + ) +} diff --git a/app/src/main/java/com/example/myapplication/ui/login/NativeLocationFragment.kt b/app/src/main/java/com/example/myapplication/ui/login/NativeLocationFragment.kt new file mode 100644 index 00000000..9241fa05 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/ui/login/NativeLocationFragment.kt @@ -0,0 +1,236 @@ +package com.example.myapplication.ui.login + + +import android.Manifest +import android.app.Activity +import android.content.Intent +import android.content.IntentSender +import android.content.pm.PackageManager +import android.location.Location +import android.os.Bundle +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import androidx.lifecycle.MutableLiveData +import com.example.myapplication.BridegfyVictim.model.Failure +import com.example.myapplication.BridegfyVictim.model.OneOf +import com.google.android.gms.common.api.ResolvableApiException +import com.google.android.gms.location.* + +/** + * This Fragment represents the base Location class, any fragment which needs access to the + * native location feature will extend from this Fragment + * */ +abstract class NativeLocationFragment : Fragment() { + + private lateinit var fusedLocationProviderClient: FusedLocationProviderClient + + /** + * [locationLiveData] will expose the current location obtained from the device, + * [OneOf.Success] represents the location has been successfully obtained + * [OneOf.Failure] is used if the location cannot be obtained due to lack of permission + * or required setting is not available/enabled in the device + * */ + val locationLiveData: MutableLiveData> = + MutableLiveData() + + private val locationRequest = LocationRequest.create().apply { + interval = 10 + priority = LocationRequest.PRIORITY_HIGH_ACCURACY + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + fusedLocationProviderClient = + LocationServices.getFusedLocationProviderClient(requireContext()) + } + + /** + *call this method to update the location value in [locationLiveData].The execution is as follows, + * [isLocationPermissionGranted] -> [checkRequiredLocationSettings] (if success) -> [fetchLastKnownLocation] + * (if failure) -> open settings screen to enable location + */ + fun updateLocation() { + + if (isLocationPermissionGranted()) { + checkRequiredLocationSettings( + successListener = { + fetchLastKnownLocation() + + }, + failureListener = { exception -> + if (exception is ResolvableApiException) { + // Location settings are not satisfied + try { + // Show the dialog by calling startIntentSenderForResult(), + // and check the result in onActivityResult(). + startIntentSenderForResult( + exception.resolution.intentSender, + REQUEST_CHECK_SETTINGS, + null, + 0, + 0, + 0, + null + ) + } catch (sendEx: IntentSender.SendIntentException) { + //Ignore + } + } + } + ) + } else { + requestLocationPermission() + } + } + + /** + * check if the location permission is granted or not + */ + private fun isLocationPermissionGranted(): Boolean { + return ContextCompat.checkSelfPermission( + requireContext(), Manifest.permission.ACCESS_COARSE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + } + + /** + * request the runtime permission for location, for devices above android 6 + */ + private fun requestLocationPermission() { + requestPermissions( + arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), + LOCATION_PERMISSION_CHECK + ) + } + + /** + * check if the current devices settings match the required settings needed to obtain the location + * [successListener] is a lambda with a param [LocationSettingsResponse] will be executed if the current settings + * match the required settings + * [failureListener] will be invoked if the current settings and the required settings doesn't match + */ + private fun checkRequiredLocationSettings( + successListener: (LocationSettingsResponse) -> Unit, + failureListener: (Exception) -> Unit + ) { + val locationSettingRequest = LocationSettingsRequest.Builder() + .addLocationRequest(locationRequest) + .build() + + val settingsClient = LocationServices.getSettingsClient(requireContext()) + + settingsClient.checkLocationSettings(locationSettingRequest) + .addOnSuccessListener(successListener) + .addOnFailureListener(failureListener) + } + + /** + * this method is used to post the last know location using [fusedLocationProviderClient.lastLocation] + * returns [OneOf.Success] if the location value is not null and [OneOf.Failure] if the operation fails + * if the [Location] value returned is null, a new locationUpdateRequest is subscribed by calling [requestNewLocationUpdates] + */ + private fun fetchLastKnownLocation() { + fusedLocationProviderClient.lastLocation + .addOnSuccessListener { location: Location? -> + if (location != null) { + locationLiveData.postValue( + OneOf.Success( + location, + OneOf.Success.DataState.FRESH_DATA + ) + ) + } else { + requestNewLocationUpdates() + } + } + .addOnFailureListener { + locationLiveData.postValue( + OneOf.Failure( + Failure.LocationFailure.UnknownLocationFailure + ) + ) + } + } + + /** + * call this method if the [fusedLocationProviderClient.lastLocation] returns null. + * this method will setup a new location update callback with single update for location. + */ + private fun requestNewLocationUpdates() { + fusedLocationProviderClient.requestLocationUpdates( + locationRequest.apply { + numUpdates = 1 + }, + object : LocationCallback() { + override fun onLocationResult(locations: LocationResult?) { + super.onLocationResult(locations) + locations?.let { + locationLiveData.postValue( + OneOf.Success( + it.lastLocation, + OneOf.Success.DataState.FRESH_DATA + ) + ) + } ?: run { + // this block is executed if the location is null + locationLiveData.postValue( + OneOf.Failure( + Failure.LocationFailure.UnknownLocationFailure + ) + ) + } + } + }, + null + ) + + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + + when (requestCode) { + REQUEST_CHECK_SETTINGS -> { + if (resultCode == Activity.RESULT_OK) { + //the users has changed the current location settings to meet the requirements, retry getting location + updateLocation() + } else { + //the user failed to update the location settings to meet the requirement + locationLiveData.postValue( + OneOf.Failure( + Failure.LocationFailure.LocationNotEnabledFailure + ) + ) + } + } + } + } + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + + when (requestCode) { + LOCATION_PERMISSION_CHECK -> { + if (grantResults.isNotEmpty() and (grantResults[0] == PackageManager.PERMISSION_GRANTED)) { + //the user has granted the location permission, hence retry to obtain the current location + updateLocation() + } else { + //the user has denied the location permission + locationLiveData.postValue( + OneOf.Failure( + Failure.LocationFailure.LocationPermissionDeniedFailure + ) + ) + } + } + } + } + + companion object { + private const val REQUEST_CHECK_SETTINGS = 100 + private const val LOCATION_PERMISSION_CHECK = 101 + } +} + diff --git a/app/src/main/java/com/example/myapplication/ui/login/register/RegisterFragment.kt b/app/src/main/java/com/example/myapplication/ui/login/register/RegisterFragment.kt new file mode 100644 index 00000000..73b3a720 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/ui/login/register/RegisterFragment.kt @@ -0,0 +1,60 @@ +package com.example.myapplication.ui.login.register + +import android.content.Context +import android.net.Uri +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.lifecycle.ViewModelProviders +import androidx.navigation.fragment.findNavController +import com.example.myapplication.BridegfyVictim.Dao.AppDatabaseInstance +import com.example.myapplication.BridegfyVictim.Dao.user.UserDetail + +import com.example.myapplication.R +import com.example.myapplication.ui.login.LoginViewModel +import kotlinx.android.synthetic.main.fragment_register.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +class RegisterFragment : Fragment() { + + private lateinit var viewModel: LoginViewModel + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_register, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + viewModel = ViewModelProviders.of(this.requireActivity()).get(LoginViewModel::class.java) + + save.setOnClickListener { + val emergencyContact1 = number_one.editableText.toString().toLong() + val emergencyContact2 = number_two.editableText.toString().toLong() + + CoroutineScope(Dispatchers.IO).launch { + val userDao = AppDatabaseInstance.getDb(requireContext()).userDao() + userDao.updateUser( + viewModel.userDetail.copy( + emergencyContact = UserDetail.EmergencyContact( + emergencyContact1, emergencyContact2 + ) + ) + ) + withContext(Dispatchers.Main) { + findNavController().navigate(R.id.action_registerFragment_to_mainFragment) + } + } + } + } +} diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..1f6bb290 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/border.xml b/app/src/main/res/drawable/border.xml new file mode 100644 index 00000000..68c64e53 --- /dev/null +++ b/app/src/main/res/drawable/border.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/button_round_mehroon.xml b/app/src/main/res/drawable/button_round_mehroon.xml new file mode 100644 index 00000000..d18a39e8 --- /dev/null +++ b/app/src/main/res/drawable/button_round_mehroon.xml @@ -0,0 +1,16 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/gradient.xml b/app/src/main/res/drawable/gradient.xml new file mode 100644 index 00000000..ba7e2ba3 --- /dev/null +++ b/app/src/main/res/drawable/gradient.xml @@ -0,0 +1,8 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..0d025f9b --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_settings_black_24dp.xml b/app/src/main/res/drawable/ic_settings_black_24dp.xml new file mode 100644 index 00000000..68ce9103 --- /dev/null +++ b/app/src/main/res/drawable/ic_settings_black_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/icon_alert.xml b/app/src/main/res/drawable/icon_alert.xml new file mode 100644 index 00000000..d1e45a88 --- /dev/null +++ b/app/src/main/res/drawable/icon_alert.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/icon_back.xml b/app/src/main/res/drawable/icon_back.xml new file mode 100644 index 00000000..fe59cb4c --- /dev/null +++ b/app/src/main/res/drawable/icon_back.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/icon_phone.xml b/app/src/main/res/drawable/icon_phone.xml new file mode 100644 index 00000000..ee487ebe --- /dev/null +++ b/app/src/main/res/drawable/icon_phone.xml @@ -0,0 +1,30 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/icon_round_add.xml b/app/src/main/res/drawable/icon_round_add.xml new file mode 100644 index 00000000..e73aeb5d --- /dev/null +++ b/app/src/main/res/drawable/icon_round_add.xml @@ -0,0 +1,18 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/icon_user.xml b/app/src/main/res/drawable/icon_user.xml new file mode 100644 index 00000000..9e938543 --- /dev/null +++ b/app/src/main/res/drawable/icon_user.xml @@ -0,0 +1,18 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/rectangle.xml b/app/src/main/res/drawable/rectangle.xml new file mode 100644 index 00000000..8a86d6af --- /dev/null +++ b/app/src/main/res/drawable/rectangle.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/round_border_white.xml b/app/src/main/res/drawable/round_border_white.xml new file mode 100644 index 00000000..0bcbd623 --- /dev/null +++ b/app/src/main/res/drawable/round_border_white.xml @@ -0,0 +1,18 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/round_mehroon.xml b/app/src/main/res/drawable/round_mehroon.xml new file mode 100644 index 00000000..03a0c010 --- /dev/null +++ b/app/src/main/res/drawable/round_mehroon.xml @@ -0,0 +1,18 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/round_white.xml b/app/src/main/res/drawable/round_white.xml new file mode 100644 index 00000000..27ff1945 --- /dev/null +++ b/app/src/main/res/drawable/round_white.xml @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/font/montserrat.ttf b/app/src/main/res/font/montserrat.ttf new file mode 100644 index 00000000..626355a2 Binary files /dev/null and b/app/src/main/res/font/montserrat.ttf differ diff --git a/app/src/main/res/layout/activity_bridgefy.xml b/app/src/main/res/layout/activity_bridgefy.xml new file mode 100644 index 00000000..857cc6a4 --- /dev/null +++ b/app/src/main/res/layout/activity_bridgefy.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_emergencyactivity.xml b/app/src/main/res/layout/activity_emergencyactivity.xml new file mode 100644 index 00000000..6a057762 --- /dev/null +++ b/app/src/main/res/layout/activity_emergencyactivity.xml @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_food.xml b/app/src/main/res/layout/activity_food.xml new file mode 100644 index 00000000..e2544de1 --- /dev/null +++ b/app/src/main/res/layout/activity_food.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + +