Compare commits
14 Commits
master
...
kelompok-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0416151945 | ||
|
|
c1b2fc181b | ||
|
|
56ec2b4263 | ||
|
|
b0531c0412 | ||
|
|
961ebfd757 | ||
|
|
9280ce0a89 | ||
|
|
67ae1e6643 | ||
|
|
04315b8e58 | ||
|
|
b3c44df833 | ||
|
|
f0d6074b55 | ||
|
|
97d37a43ad | ||
|
|
ba36f0447b | ||
|
|
9d34345d24 | ||
|
|
2e35f8bcd6 |
123
.idea/codeStyles/Project.xml
generated
Normal file
123
.idea/codeStyles/Project.xml
generated
Normal file
@ -0,0 +1,123 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<JetCodeStyleSettings>
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
<option name="FORCE_REARRANGE_MODE" value="1" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
<arrangement>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:android</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:id</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>style</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
126
.idea/copilot.data.migration.agent.xml
generated
Normal file
126
.idea/copilot.data.migration.agent.xml
generated
Normal file
@ -0,0 +1,126 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AgentMigrationStateService">
|
||||
<option name="migrationStatus" value="IN_PROGRESS" />
|
||||
<option name="pendingSessionIds">
|
||||
<option value="05f02dcc-4a62-4a3b-a6bb-d4764cbd1bab" />
|
||||
<option value="1aa6c162-aaff-428f-bfae-3b53f8ce3f76" />
|
||||
<option value="1e2fee64-523c-4038-ad08-03d8f8196cb1" />
|
||||
<option value="46f36349-a2e4-46bf-b85e-8ee46252660a" />
|
||||
<option value="4e8befa7-c7cb-4874-9ac5-4b69e81d19b8" />
|
||||
<option value="63e1cd83-39a1-4de9-83e4-a7d978cb9048" />
|
||||
<option value="82cd9415-28a8-49b5-841d-6178af5419de" />
|
||||
<option value="9e78818c-2d23-4dc2-8dd8-c82d3ade6f9d" />
|
||||
<option value="a1f1bc1b-b1d4-4655-9677-4653b213cc14" />
|
||||
<option value="ad5bcc95-675a-45d4-8896-b1645cb788b9" />
|
||||
<option value="b944cd09-1573-42de-840e-06d91ce8729c" />
|
||||
<option value="bf5061b4-a1e9-4600-a273-ea8e51b2ce89" />
|
||||
<option value="d23e68d4-6558-4c12-a57e-d937ea639547" />
|
||||
<option value="dd38c49b-b13d-4206-8c24-bcf78317eec5" />
|
||||
<option value="e6a32dc0-5413-49cf-ad92-9fb87982ed75" />
|
||||
</option>
|
||||
<option name="pendingTurns">
|
||||
<map>
|
||||
<entry key="1aa6c162-aaff-428f-bfae-3b53f8ce3f76">
|
||||
<value>
|
||||
<set>
|
||||
<option value="cbcc2ea0-3963-436d-be88-f495313b8d20" />
|
||||
<option value="c00b235a-a5a8-4158-baf4-7a3ca74345b0" />
|
||||
<option value="16815d5b-8699-467d-9929-397e12de1084" />
|
||||
<option value="f6f34f7d-39e2-4634-a01d-82dae28320aa" />
|
||||
<option value="43c8a7ee-b78d-4cf3-824e-8d0c98692b00" />
|
||||
<option value="eab6d8f0-3fdf-46bc-ab06-c8eb1243fe00" />
|
||||
<option value="207107b2-1903-41f3-a6d3-30ee3505d45b" />
|
||||
<option value="73b11a61-7794-4de6-bbdf-5f37c67fd8f0" />
|
||||
</set>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="63e1cd83-39a1-4de9-83e4-a7d978cb9048">
|
||||
<value>
|
||||
<set>
|
||||
<option value="17ec376a-38fd-48ac-a4e1-52e9f7df3100" />
|
||||
<option value="3ac3800e-363e-4148-86b3-0d4f3b0a8b62" />
|
||||
<option value="44c8c4f4-3bbe-4fb8-88e4-58bdb184dad0" />
|
||||
<option value="484561a1-1c08-44cd-bcd3-a53895fc3851" />
|
||||
<option value="5460190d-c8ff-4194-9316-a6298c7cb54b" />
|
||||
<option value="a0c23b11-ba1e-4275-a3e4-38b3c8fdbe00" />
|
||||
<option value="a20448f7-57cd-4a40-89b1-460451412588" />
|
||||
<option value="c640b33a-4bc6-4ee0-98ed-6a71e7270331" />
|
||||
<option value="f099f72f-0866-4d16-85e4-8323be39e45f" />
|
||||
</set>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="9e78818c-2d23-4dc2-8dd8-c82d3ade6f9d">
|
||||
<value>
|
||||
<set>
|
||||
<option value="3a14eb41-53f1-4190-a7f8-24c23cb882e8" />
|
||||
<option value="4a2475a2-98ab-4ae0-b689-cbc2da702b83" />
|
||||
<option value="72c7c9c6-ecb5-444b-86de-559ea9d893e5" />
|
||||
<option value="a58fb4e2-20bd-480e-9eff-a5b9dc57c1b4" />
|
||||
<option value="f43c2e1c-5a6c-4deb-9fbf-5e7694c69f6d" />
|
||||
<option value="faff10ea-f875-4de1-adc0-6095c7c3e718" />
|
||||
</set>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="bf5061b4-a1e9-4600-a273-ea8e51b2ce89">
|
||||
<value>
|
||||
<set>
|
||||
<option value="056c3bd2-6151-4564-b839-d7d6f53d4342" />
|
||||
<option value="0c019b23-843c-4710-8469-2dd1211d89a3" />
|
||||
<option value="0e6a533b-db2a-4977-8228-bfeaa867eef9" />
|
||||
<option value="1bb82ec0-7888-48c9-b016-116821338f0e" />
|
||||
<option value="26bdd760-c8b3-419a-9b6b-a194651aed3d" />
|
||||
<option value="2a8432cf-e7cb-4250-9c7c-00664ec22a28" />
|
||||
<option value="2b41e5bf-4d90-46d8-8fc8-7b41c3f788e1" />
|
||||
<option value="34b3c4dc-eb50-43b4-98d0-2b9771161619" />
|
||||
<option value="38d23208-212e-48d5-84ea-03d6cf590d51" />
|
||||
<option value="39a0d9c4-a68f-4050-bb14-eda17dc695db" />
|
||||
<option value="44cb5a83-abdc-41a2-95fa-283ec339fd2d" />
|
||||
<option value="44d2412a-3471-4972-8b90-5cad4c5a00e3" />
|
||||
<option value="461c1e05-de5d-4220-af67-2f29e576b9ea" />
|
||||
<option value="4f53b359-f3df-449a-ab3e-d112d5df446a" />
|
||||
<option value="51c4a9e4-545f-4c67-8fee-80b3de88d2be" />
|
||||
<option value="520750f7-1625-4419-beb3-c4ecf7d9dc2b" />
|
||||
<option value="52e099a9-0cd7-4716-8310-8a43635ca894" />
|
||||
<option value="6971fc05-6110-4b33-ac58-69628d336a48" />
|
||||
<option value="6dffb37e-ecb0-4c89-8cbe-e38ed347c397" />
|
||||
<option value="7216273c-1271-4ea2-88d8-177e139316f1" />
|
||||
<option value="7237b251-2008-431b-b6b3-005dea48c3bf" />
|
||||
<option value="72c65d6c-d369-4e6e-ad89-52749cab36a6" />
|
||||
<option value="76858669-1b07-4bac-bc00-8448c673d06d" />
|
||||
<option value="80ef4e51-2808-48f4-9166-bf883578c6f0" />
|
||||
<option value="8589889d-a532-45c6-9ffe-0671c2d32583" />
|
||||
<option value="8861f660-2aea-480d-a773-4e1f58fe9ac0" />
|
||||
<option value="8fab86d7-b6d2-4ce8-9174-172bd49c7906" />
|
||||
<option value="94d90579-6a6d-44a9-8b93-de694c0c38ef" />
|
||||
<option value="9523ff40-4e2b-4c96-b52e-69093f946698" />
|
||||
<option value="9ebc3bf3-3f18-425d-a4fb-c87a6a121c9a" />
|
||||
<option value="aaf8bf1e-ce4a-48ab-a65e-6da5790fc566" />
|
||||
<option value="ac68227b-a347-492a-8990-f2313a4c4838" />
|
||||
<option value="b32841ec-424b-46e4-af25-b31a5ca4ae51" />
|
||||
<option value="ba4f0602-d62b-4a9c-b86f-ee0dce40374d" />
|
||||
<option value="c759a8ff-cf83-4618-90d4-31101dea7c77" />
|
||||
<option value="e2907e39-fda1-4832-99ff-fb7407d64db3" />
|
||||
<option value="ea6afe5a-c296-461b-a4a7-49209b3d7937" />
|
||||
<option value="ec11cbcb-475d-4b28-8eb3-f71715723d6c" />
|
||||
<option value="ee1de16e-76b5-4384-8ed5-b2e3f4e3c2cb" />
|
||||
<option value="f607f271-4885-4c4e-91da-e2caf2abd67a" />
|
||||
<option value="fe272d92-3acf-42e8-bd88-66dc00800c3f" />
|
||||
</set>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
<pendingWorkingSetItems>
|
||||
<entry key="1aa6c162-aaff-428f-bfae-3b53f8ce3f76">
|
||||
<set>
|
||||
<option value="file://$PROJECT_DIR$/app/src/main/java/id/ac/ubharajaya/panicbutton/MainScreen.kt" />
|
||||
<option value="file://$PROJECT_DIR$/app/src/main/java/id/ac/ubharajaya/panicbutton/EvacuationMaps.kt" />
|
||||
</set>
|
||||
</entry>
|
||||
<entry key="63e1cd83-39a1-4de9-83e4-a7d978cb9048" />
|
||||
<entry key="9e78818c-2d23-4dc2-8dd8-c82d3ade6f9d" />
|
||||
<entry key="bf5061b4-a1e9-4600-a273-ea8e51b2ce89" />
|
||||
</pendingWorkingSetItems>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/copilot.data.migration.ask.xml
generated
Normal file
6
.idea/copilot.data.migration.ask.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AskMigrationStateService">
|
||||
<option name="migrationStatus" value="COMPLETED" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/copilot.data.migration.ask2agent.xml
generated
Normal file
6
.idea/copilot.data.migration.ask2agent.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Ask2AgentMigrationStateService">
|
||||
<option name="migrationStatus" value="COMPLETED" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/copilot.data.migration.edit.xml
generated
Normal file
6
.idea/copilot.data.migration.edit.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="EditMigrationStateService">
|
||||
<option name="migrationStatus" value="COMPLETED" />
|
||||
</component>
|
||||
</project>
|
||||
10
.idea/deploymentTargetSelector.xml
generated
Normal file
10
.idea/deploymentTargetSelector.xml
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetSelector">
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
</project>
|
||||
2
.idea/vcs.xml
generated
2
.idea/vcs.xml
generated
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@ -50,6 +50,8 @@ dependencies {
|
||||
implementation(libs.androidx.compose.ui.graphics)
|
||||
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||
implementation(libs.androidx.compose.material3)
|
||||
// Foundation (for verticalScroll, rememberScrollState, etc.)
|
||||
implementation("androidx.compose.foundation:foundation:1.5.0")
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
@ -59,6 +61,15 @@ dependencies {
|
||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
||||
//praktikum 1
|
||||
implementation("com.squareup.okhttp3:okhttp:4.11.0")
|
||||
implementation("androidx.compose.material3:material3:1.1.1")
|
||||
implementation("androidx.compose.material3:material3:1.2.0")
|
||||
implementation("androidx.compose.material:material-icons-extended:<versi-compose>")
|
||||
|
||||
// Navigation for Compose
|
||||
implementation("androidx.navigation:navigation-compose:2.6.0")
|
||||
|
||||
// AndroidX SplashScreen (required for installSplashScreen API)
|
||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||
|
||||
// Play Services Location for GPS (FusedLocationProviderClient)
|
||||
implementation("com.google.android.gms:play-services-location:21.0.1")
|
||||
}
|
||||
@ -2,26 +2,39 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:icon="@drawable/logo"
|
||||
android:roundIcon="@drawable/logo"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.PanicButton">
|
||||
android:theme="@style/Theme.PanicButton"
|
||||
android:label="@string/app_name">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.PanicButton">
|
||||
android:theme="@style/Theme.App.Starting">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Evacuation map activities -->
|
||||
<activity
|
||||
android:name=".EvacuationMapsActivity"
|
||||
android:exported="false"
|
||||
android:label="Peta Evakuasi" />
|
||||
|
||||
<activity
|
||||
android:name=".EvacuationMapDetailActivity"
|
||||
android:exported="false"
|
||||
android:label="Detail Peta" />
|
||||
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
</manifest>
|
||||
@ -0,0 +1,34 @@
|
||||
package id.ac.ubharajaya.panicbutton
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
class EvacuationMapDetailActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val which = intent.getStringExtra("which") ?: "selatan"
|
||||
val resId = if (which == "selatan") R.drawable.lantai_1_selatan else R.drawable.lantai_1_utara
|
||||
setContent {
|
||||
MaterialTheme {
|
||||
EvacuationMapDetailContent(resId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun EvacuationMapDetailContent(drawableResId: Int) {
|
||||
Column(modifier = Modifier.fillMaxSize().padding(12.dp)) {
|
||||
Text(text = "Detail Peta", modifier = Modifier.padding(bottom = 12.dp))
|
||||
Image(painter = painterResource(id = drawableResId), contentDescription = "Map Detail", modifier = Modifier.fillMaxWidth().heightIn(max = 800.dp))
|
||||
}
|
||||
}
|
||||
170
app/src/main/java/id/ac/ubharajaya/panicbutton/EvacuationMaps.kt
Normal file
170
app/src/main/java/id/ac/ubharajaya/panicbutton/EvacuationMaps.kt
Normal file
@ -0,0 +1,170 @@
|
||||
package id.ac.ubharajaya.panicbutton
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavController
|
||||
|
||||
// --- Constants for better readability ---
|
||||
val CardElevation = 8.dp
|
||||
val CornerRadius = 16.dp
|
||||
val PaddingDefault = 16.dp
|
||||
val ImageSize = 100.dp
|
||||
|
||||
// ====================================================================
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun EvacuationMapsScreen(navController: NavController, onBack: () -> Unit) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Peta Jalur Evakuasi") },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onBack) {
|
||||
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Kembali")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
.padding(horizontal = PaddingDefault)
|
||||
) {
|
||||
// Deskripsi Singkat
|
||||
Text(
|
||||
text = "Pilih area peta jalur evakuasi yang ingin Anda lihat.",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(bottom = PaddingDefault)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
// Card 1 - Selatan
|
||||
EvacuationMapCard(
|
||||
title = "Jalur Evakuasi Selatan",
|
||||
description = "Lihat detail jalur evakuasi lantai selatan.",
|
||||
drawableId = R.drawable.lantai_1_selatan,
|
||||
onClick = { navController.navigate("evac_map/selatan") }
|
||||
)
|
||||
|
||||
// Card 2 - Utara
|
||||
EvacuationMapCard(
|
||||
title = "Jalur Evakuasi Utara",
|
||||
description = "Lihat detail jalur evakuasi lantai utara.",
|
||||
drawableId = R.drawable.lantai_1_utara,
|
||||
onClick = { navController.navigate("evac_map/utara") }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun EvacuationMapCard(
|
||||
title: String,
|
||||
description: String,
|
||||
drawableId: Int,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 12.dp)
|
||||
.clickable(onClick = onClick),
|
||||
shape = RoundedCornerShape(CornerRadius),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh
|
||||
),
|
||||
elevation = CardDefaults.cardElevation(defaultElevation = CardElevation)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.padding(PaddingDefault)
|
||||
) {
|
||||
// Gambar dengan shape dan padding
|
||||
Image(
|
||||
painter = painterResource(id = drawableId),
|
||||
contentDescription = title,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier
|
||||
.size(ImageSize)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(MaterialTheme.colorScheme.primaryContainer)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(PaddingDefault))
|
||||
|
||||
// Kolom Teks
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = title,
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
text = description,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun EvacuationMapDetailScreen(drawableResId: Int, onBack: () -> Unit) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Detail Peta") },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onBack) {
|
||||
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Kembali")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
) {
|
||||
// Gambar Detail Peta
|
||||
Image(
|
||||
painter = painterResource(id = drawableResId),
|
||||
contentDescription = "Map Detail",
|
||||
contentScale = ContentScale.Fit, // Gunakan Fit untuk memastikan peta terlihat keseluruhan
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(PaddingDefault)
|
||||
.clip(RoundedCornerShape(CornerRadius))
|
||||
.background(Color.LightGray) // Warna latar belakang untuk gambar
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
package id.ac.ubharajaya.panicbutton
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
class EvacuationMapsActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
MaterialTheme {
|
||||
EvacuationMapsContent(onSelect = { which ->
|
||||
val intent = Intent(this, EvacuationMapDetailActivity::class.java)
|
||||
intent.putExtra("which", which)
|
||||
startActivity(intent)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun EvacuationMapsContent(onSelect: (String) -> Unit) {
|
||||
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
|
||||
Text(text = "Peta Jalur Evakuasi", style = MaterialTheme.typography.titleLarge, modifier = Modifier.padding(bottom = 12.dp))
|
||||
|
||||
// Selatan
|
||||
Card(modifier = Modifier.fillMaxWidth().padding(bottom = 12.dp).clickable { onSelect("selatan") }, shape = RoundedCornerShape(12.dp), elevation = CardDefaults.cardElevation(defaultElevation = 6.dp)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(12.dp)) {
|
||||
Image(painter = painterResource(id = R.drawable.lantai_1_selatan), contentDescription = "Selatan", modifier = Modifier.size(120.dp))
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Column {
|
||||
Text(text = "Jalur Evakuasi Selatan", fontSize = 18.sp)
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
Text(text = "Lihat detail jalur evakuasi lantai selatan.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Utara
|
||||
Card(modifier = Modifier.fillMaxWidth().padding(bottom = 12.dp).clickable { onSelect("utara") }, shape = RoundedCornerShape(12.dp), elevation = CardDefaults.cardElevation(defaultElevation = 6.dp)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(12.dp)) {
|
||||
Image(painter = painterResource(id = R.drawable.lantai_1_utara), contentDescription = "Utara", modifier = Modifier.size(120.dp))
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Column {
|
||||
Text(text = "Jalur Evakuasi Utara", fontSize = 18.sp)
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
Text(text = "Lihat detail jalur evakuasi lantai utara.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,52 +1,69 @@
|
||||
package id.ac.ubharajaya.panicbutton
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import id.ac.ubharajaya.panicbutton.ui.theme.PanicButtonTheme
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import com.google.android.gms.location.LocationServices
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
private lateinit var viewModel: MainViewModel
|
||||
|
||||
private val requestPermissionLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
|
||||
if (isGranted) {
|
||||
if (::viewModel.isInitialized) viewModel.dialogMessage = "Izin lokasi diberikan. Tekan kembali untuk menyertakan koordinat."
|
||||
} else {
|
||||
if (::viewModel.isInitialized) viewModel.dialogMessage = "Izin lokasi ditolak. Laporan akan dikirim tanpa koordinat."
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
|
||||
|
||||
val fusedClient = LocationServices.getFusedLocationProviderClient(this)
|
||||
|
||||
val openEvacMaps = {
|
||||
startActivity(Intent(this, EvacuationMapsActivity::class.java))
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
fun handleSendAlert() {
|
||||
// Check permission
|
||||
val fineGranted = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|
||||
val coarseGranted = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
if (!fineGranted && !coarseGranted) {
|
||||
requestPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
viewModel.dialogMessage = "Izin lokasi dibutuhkan untuk menyertakan koordinat. Silakan tekan lagi setelah mengizinkan lokasi."
|
||||
return
|
||||
}
|
||||
|
||||
fusedClient.lastLocation
|
||||
.addOnSuccessListener { loc ->
|
||||
if (loc != null) {
|
||||
viewModel.sendAlert(loc.latitude, loc.longitude)
|
||||
} else {
|
||||
viewModel.sendAlert()
|
||||
}
|
||||
}
|
||||
.addOnFailureListener {
|
||||
viewModel.sendAlert()
|
||||
}
|
||||
}
|
||||
|
||||
setContent {
|
||||
PanicButtonTheme {
|
||||
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
||||
Greeting(
|
||||
name = "Android",
|
||||
modifier = Modifier.padding(innerPadding)
|
||||
)
|
||||
}
|
||||
MaterialTheme {
|
||||
MainScreen(viewModel = viewModel, onOpenEvacMaps = openEvacMaps, onSendAlert = ::handleSendAlert)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Greeting(name: String, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
text = "Hello $name!",
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun GreetingPreview() {
|
||||
PanicButtonTheme {
|
||||
Greeting("Android")
|
||||
}
|
||||
}
|
||||
484
app/src/main/java/id/ac/ubharajaya/panicbutton/MainScreen.kt
Normal file
484
app/src/main/java/id/ac/ubharajaya/panicbutton/MainScreen.kt
Normal file
@ -0,0 +1,484 @@
|
||||
package id.ac.ubharajaya.panicbutton
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.Warning
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MainScreen(viewModel: MainViewModel, onOpenEvacMaps: () -> Unit, onSendAlert: () -> Unit) {
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
topBar = {
|
||||
// Put the emergency box in the topBar so Scaffold will position it below the status bar automatically
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 20.dp, vertical = 8.dp),
|
||||
contentAlignment = Alignment.TopCenter
|
||||
) {
|
||||
Text(
|
||||
text = "JANGAN PANIK, SEGERA EVAKUASI DIRI ANDA KE TITIK KUMPUL!!",
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
style = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Bold),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
MaterialTheme.colorScheme.error.copy(alpha = 0.2f),
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
)
|
||||
.padding(12.dp),
|
||||
maxLines = 2
|
||||
)
|
||||
}
|
||||
}
|
||||
) { padding -> // Inset padding yang disediakan oleh Scaffold
|
||||
|
||||
// Use the Scaffold padding for content so it's laid out under the topBar correctly
|
||||
val contentPaddingModifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(padding)
|
||||
|
||||
Column(
|
||||
modifier = contentPaddingModifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(horizontal = 20.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
// Main card and other content
|
||||
EmergencyConditionCard(viewModel)
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
Button(
|
||||
onClick = onOpenEvacMaps,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(0.6f)
|
||||
.height(48.dp),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
) {
|
||||
Text(text = "Lihat Peta Evakuasi")
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
|
||||
// Move PanicButton into scrollable content so it scrolls with the page
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 24.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
// keep same size and shadow as before
|
||||
PanicButton(onClick = { onSendAlert() }, buttonSize = 130.dp, shadowSize = 160.dp)
|
||||
}
|
||||
|
||||
// create some extra space below button for comfortable scrolling
|
||||
Spacer(modifier = Modifier.height(40.dp))
|
||||
}
|
||||
|
||||
// Dialog notifikasi
|
||||
viewModel.dialogMessage
|
||||
?.takeIf { it.isNotBlank() }
|
||||
?.let { msg ->
|
||||
AlertDialog(
|
||||
onDismissRequest = { viewModel.clearDialog() },
|
||||
confirmButton = {
|
||||
TextButton(onClick = { viewModel.clearDialog() }) { Text("OK") }
|
||||
},
|
||||
title = { Text("🚨 Notifikasi", style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold)) },
|
||||
text = { Text(msg) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// EmergencyConditionCard
|
||||
// =======================================================
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun EmergencyConditionCard(viewModel: MainViewModel) {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
|
||||
Card(
|
||||
shape = RoundedCornerShape(24.dp),
|
||||
elevation = CardDefaults.cardElevation(defaultElevation = 8.dp),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surface
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.shadow(
|
||||
elevation = 4.dp,
|
||||
shape = RoundedCornerShape(24.dp),
|
||||
clip = true
|
||||
)
|
||||
) {
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(24.dp)
|
||||
) {
|
||||
|
||||
// Header dengan gradient background
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
brush = Brush.linearGradient(
|
||||
colors = listOf(
|
||||
MaterialTheme.colorScheme.primary.copy(alpha = 0.1f),
|
||||
MaterialTheme.colorScheme.secondary.copy(alpha = 0.05f)
|
||||
)
|
||||
),
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
)
|
||||
.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
// Icon dengan background
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.15f),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = "🚨",
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
}
|
||||
|
||||
Column {
|
||||
Text(
|
||||
text = "Jenis Kondisi Darurat",
|
||||
style = MaterialTheme.typography.titleLarge.copy(
|
||||
fontWeight = FontWeight.Bold
|
||||
),
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
Text(
|
||||
text = "Pilih satu atau lebih kondisi",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(top = 2.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
|
||||
// Custom Styled Dropdown
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f),
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = if (expanded)
|
||||
MaterialTheme.colorScheme.primary.copy(alpha = 0.5f)
|
||||
else
|
||||
MaterialTheme.colorScheme.outline.copy(alpha = 0.3f),
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
)
|
||||
.clickable { expanded = !expanded }
|
||||
.padding(horizontal = 18.dp, vertical = 16.dp)
|
||||
) {
|
||||
|
||||
Column {
|
||||
// Selected items preview
|
||||
val selectedItems = viewModel.options.filter { viewModel.isChecked(it.label) }
|
||||
|
||||
if (selectedItems.isEmpty()) {
|
||||
Text(
|
||||
"Pilih kondisi darurat...",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
} else {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Text(
|
||||
"${selectedItems.size} kondisi terpilih",
|
||||
style = MaterialTheme.typography.bodySmall.copy(
|
||||
fontWeight = FontWeight.Medium
|
||||
),
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
|
||||
// Show first 2 selected items as chips
|
||||
selectedItems.take(2).forEach { opt ->
|
||||
SelectedChip(option = opt, viewModel = viewModel)
|
||||
}
|
||||
|
||||
if (selectedItems.size > 2) {
|
||||
Text(
|
||||
"+${selectedItems.size - 2} lainnya",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dropdown Menu
|
||||
DropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false },
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.surface,
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.width(320.dp)
|
||||
.padding(vertical = 8.dp)
|
||||
.heightIn(max = 280.dp)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
viewModel.options.forEach { opt ->
|
||||
EmergencyOptionItem(
|
||||
option = opt,
|
||||
isChecked = viewModel.isChecked(opt.label),
|
||||
onCheckedChange = { checked ->
|
||||
viewModel.setChecked(opt.label, checked)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dropdown arrow
|
||||
Icon(
|
||||
imageVector = if (expanded)
|
||||
Icons.Filled.KeyboardArrowUp
|
||||
else
|
||||
Icons.Filled.KeyboardArrowDown,
|
||||
contentDescription = if (expanded) "Tutup" else "Buka",
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.align(Alignment.CenterEnd)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(20.dp))
|
||||
|
||||
// Extra note input dengan styling yang lebih baik
|
||||
Column {
|
||||
Text(
|
||||
"Catatan Tambahan",
|
||||
style = MaterialTheme.typography.bodyMedium.copy(
|
||||
fontWeight = FontWeight.Medium
|
||||
),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
modifier = Modifier.padding(bottom = 8.dp, start = 4.dp)
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = viewModel.otherNote,
|
||||
onValueChange = { viewModel.otherNote = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
"Tambahkan catatan jika diperlukan...",
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = 56.dp, max = 120.dp),
|
||||
shape = RoundedCornerShape(14.dp),
|
||||
colors = OutlinedTextFieldDefaults.colors(
|
||||
focusedBorderColor = MaterialTheme.colorScheme.primary,
|
||||
unfocusedBorderColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.4f),
|
||||
cursorColor = MaterialTheme.colorScheme.primary,
|
||||
focusedTextColor = MaterialTheme.colorScheme.onSurface,
|
||||
unfocusedTextColor = MaterialTheme.colorScheme.onSurface
|
||||
),
|
||||
textStyle = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
|
||||
// Validation jika pilih 'Lainnya' tapi kosong
|
||||
if (viewModel.isChecked("Lainnya") && viewModel.otherNote.isBlank()) {
|
||||
Row(
|
||||
modifier = Modifier.padding(top = 6.dp, start = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Warning,
|
||||
contentDescription = "Warning",
|
||||
tint = MaterialTheme.colorScheme.error,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
Spacer(Modifier.width(4.dp))
|
||||
Text(
|
||||
"Catatan wajib diisi jika memilih 'Lainnya'",
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// EmergencyOptionItem
|
||||
// =======================================================
|
||||
|
||||
@Composable
|
||||
fun EmergencyOptionItem(
|
||||
option: EmergencyOption,
|
||||
isChecked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { onCheckedChange(!isChecked) }
|
||||
.padding(horizontal = 12.dp, vertical = 8.dp)
|
||||
) {
|
||||
// Icon dengan background
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f),
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = option.icon,
|
||||
style = MaterialTheme.typography.bodyLarge
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(Modifier.width(12.dp))
|
||||
|
||||
// Label
|
||||
Text(
|
||||
text = option.label,
|
||||
style = MaterialTheme.typography.bodyMedium.copy(
|
||||
fontWeight = if (isChecked) FontWeight.Medium else FontWeight.Normal
|
||||
),
|
||||
color = if (isChecked)
|
||||
MaterialTheme.colorScheme.primary
|
||||
else
|
||||
MaterialTheme.colorScheme.onSurface,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
Spacer(Modifier.width(8.dp))
|
||||
|
||||
// Custom Checkbox
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.background(
|
||||
color = if (isChecked)
|
||||
MaterialTheme.colorScheme.primary
|
||||
else
|
||||
MaterialTheme.colorScheme.surface,
|
||||
shape = RoundedCornerShape(4.dp)
|
||||
)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = if (isChecked)
|
||||
MaterialTheme.colorScheme.primary
|
||||
else
|
||||
MaterialTheme.colorScheme.outline.copy(alpha = 0.6f),
|
||||
shape = RoundedCornerShape(4.dp)
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (isChecked) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = "Selected",
|
||||
tint = MaterialTheme.colorScheme.onPrimary,
|
||||
modifier = Modifier.size(14.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// SelectedChip
|
||||
// =======================================================
|
||||
|
||||
@Composable
|
||||
fun SelectedChip(option: EmergencyOption, viewModel: MainViewModel) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.wrapContentWidth()
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f),
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
)
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = "${option.icon} ${option.label}",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
maxLines = 1
|
||||
)
|
||||
|
||||
Spacer(Modifier.width(4.dp))
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = "Hapus",
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier
|
||||
.size(14.dp)
|
||||
.clickable { viewModel.setChecked(option.label, false) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
package id.ac.ubharajaya.panicbutton
|
||||
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
data class EmergencyOption(
|
||||
val label: String,
|
||||
val icon: String
|
||||
)
|
||||
|
||||
class MainViewModel : ViewModel() {
|
||||
val options = listOf(
|
||||
EmergencyOption("Kebakaran", "🔥"),
|
||||
EmergencyOption("Banjir", "🌊"),
|
||||
EmergencyOption("Gempa Bumi", "🌍"),
|
||||
EmergencyOption("Huru Hara/Demonstrasi", "💥"),
|
||||
EmergencyOption("Lainnya", "✏️")
|
||||
)
|
||||
|
||||
// observable map so Compose recomposes on changes
|
||||
private val _checkedState = mutableStateMapOf<String, Boolean>().apply {
|
||||
options.forEach { put(it.label, false) }
|
||||
}
|
||||
|
||||
var otherNote by mutableStateOf("")
|
||||
var dialogMessage by mutableStateOf<String?>(null)
|
||||
|
||||
fun isChecked(label: String): Boolean = _checkedState[label] == true
|
||||
|
||||
fun setChecked(label: String, isChecked: Boolean) {
|
||||
_checkedState[label] = isChecked
|
||||
if (label == "Lainnya" && !isChecked) {
|
||||
otherNote = ""
|
||||
}
|
||||
}
|
||||
|
||||
fun sendAlert(latitude: Double? = null, longitude: Double? = null) {
|
||||
val selectedOptions = options.filter { isChecked(it.label) }.map { it.label }
|
||||
|
||||
if (selectedOptions.isEmpty()) {
|
||||
dialogMessage = "Pilih setidaknya satu kondisi darurat."
|
||||
return
|
||||
}
|
||||
|
||||
if (isChecked("Lainnya") && otherNote.isBlank()) {
|
||||
dialogMessage = "Catatan wajib diisi jika Anda memilih 'Lainnya'."
|
||||
return
|
||||
}
|
||||
|
||||
val bodyBuilder = StringBuilder()
|
||||
bodyBuilder.append("Kondisi: ${selectedOptions.joinToString(", ")}")
|
||||
if (otherNote.isNotBlank()) {
|
||||
bodyBuilder.append("\nCatatan: ${otherNote.trim()}")
|
||||
}
|
||||
|
||||
if (latitude != null && longitude != null) {
|
||||
bodyBuilder.append("\nLokasi: https://maps.google.com/?q=$latitude,$longitude")
|
||||
}
|
||||
|
||||
val payload = bodyBuilder.toString()
|
||||
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val resultMessage = NotificationSender.sendNotification(payload)
|
||||
dialogMessage = resultMessage
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
dialogMessage = "Gagal mengirim laporan. Silakan periksa koneksi Anda dan coba lagi."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun clearDialog() {
|
||||
dialogMessage = null
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
package id.ac.ubharajaya.panicbutton
|
||||
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import java.io.IOException
|
||||
|
||||
object NotificationSender {
|
||||
private val client = OkHttpClient.Builder()
|
||||
.build()
|
||||
private const val server = "https://ntfy.ubharajaya.ac.id"
|
||||
private const val topic = "panic-button"
|
||||
private const val TAG = "NotificationSender"
|
||||
|
||||
private fun sanitizeHeaderValue(value: String): String {
|
||||
val sb = StringBuilder()
|
||||
for (ch in value) {
|
||||
val code = ch.code
|
||||
if (code in 32..126) sb.append(ch)
|
||||
}
|
||||
val out = sb.toString()
|
||||
return if (out.isBlank()) "Alert" else out
|
||||
}
|
||||
|
||||
suspend fun sendNotification(message: String): String = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val url = "$server/$topic"
|
||||
Log.d(TAG, "Preparing notification to $url")
|
||||
val titleSafe = sanitizeHeaderValue("🚨 Alert 🚨")
|
||||
val cleanMessage = message.trim()
|
||||
|
||||
val body = cleanMessage.toRequestBody("text/plain".toMediaType())
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.addHeader("Title", titleSafe)
|
||||
.addHeader("Priority", "urgent")
|
||||
.addHeader("Tags", "warning")
|
||||
.post(body)
|
||||
.build()
|
||||
|
||||
// Debug logging
|
||||
Log.d(TAG, "Body to send: $cleanMessage")
|
||||
|
||||
client.newCall(request).execute().use { resp ->
|
||||
val respCode = resp.code
|
||||
val respMessage = resp.message
|
||||
val respBody = try {
|
||||
resp.body?.string()
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
Log.d(TAG, "Response: code=$respCode message=$respMessage body=$respBody")
|
||||
|
||||
return@withContext if (resp.isSuccessful) {
|
||||
"Notifikasi berhasil dikirim!"
|
||||
} else {
|
||||
"Gagal mengirim notifikasi: $respCode $respMessage ${respBody ?: ""}".trim()
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "IO error", e)
|
||||
return@withContext "Gagal mengirim notifikasi: ${e.localizedMessage}"
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unexpected error", e)
|
||||
return@withContext "Gagal mengirim notifikasi: ${e.localizedMessage}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
package id.ac.ubharajaya.panicbutton
|
||||
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.collectIsPressedAsState
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.scale
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.unit.Dp
|
||||
|
||||
@Composable
|
||||
fun PanicButton(onClick: () -> Unit, buttonSize: Dp = 170.dp, shadowSize: Dp = 210.dp) {
|
||||
val scaleFactor = buttonSize.value / 170f
|
||||
val panicColor = Color(0xFFB71C1C)
|
||||
val darkShade = Color(0xFF7F0F0F)
|
||||
val lightAccent = Color(0xFFFF8A80)
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val isPressedState = interactionSource.collectIsPressedAsState()
|
||||
val isPressed = isPressedState.value
|
||||
val scaleAnim = animateFloatAsState(targetValue = if (isPressed) 0.96f else 1f, animationSpec = tween(120)).value
|
||||
val elevationAnim = animateDpAsState(targetValue = if (isPressed) 6.dp else 18.dp, animationSpec = tween(120)).value
|
||||
val gradient = Brush.verticalGradient(listOf(lightAccent, panicColor, darkShade))
|
||||
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.size(buttonSize)
|
||||
.scale(scaleAnim)
|
||||
.shadow(elevation = elevationAnim * scaleFactor, shape = CircleShape)
|
||||
.background(brush = gradient, shape = CircleShape)
|
||||
.clickable(indication = null, interactionSource = interactionSource) { onClick() }
|
||||
) {
|
||||
// highlight bubble (scaled)
|
||||
val highlightSize = 70.dp * scaleFactor
|
||||
val highlightOffsetX = (-24).dp * scaleFactor
|
||||
val highlightOffsetY = (-28).dp * scaleFactor
|
||||
|
||||
Box(modifier = Modifier.size(highlightSize).offset(x = highlightOffsetX, y = highlightOffsetY).background(color = Color.White.copy(alpha = 0.16f), shape = CircleShape))
|
||||
val fontSize = (72f * scaleFactor).sp
|
||||
Text("!", color = Color.White, fontSize = fontSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
package id.ac.ubharajaya.panicbutton
|
||||
|
||||
data class ReportOption(val label: String, val icon: String)
|
||||
@ -2,10 +2,11 @@ package id.ac.ubharajaya.panicbutton.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
val Blue_primary = Color(0xFF007BFF)
|
||||
val Blue_secondary = Color(0xFF00A2E8)
|
||||
val Dark_blue = Color(0xFF0056b3)
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val Pink40 = Color(0xFF7D5260)
|
||||
val Grey_light = Color(0xFFF5F5F5)
|
||||
val Grey_dark = Color(0xFF333333)
|
||||
|
||||
val Red_cancel = Color(0xFFDC3545)
|
||||
|
||||
@ -1,53 +1,53 @@
|
||||
package id.ac.ubharajaya.panicbutton.ui.theme
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.WindowCompat
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Purple80,
|
||||
secondary = PurpleGrey80,
|
||||
tertiary = Pink80
|
||||
primary = Blue_secondary,
|
||||
secondary = Blue_primary,
|
||||
background = Grey_dark,
|
||||
surface = Color(0xFF1E1E1E),
|
||||
onPrimary = Grey_dark,
|
||||
onSecondary = Grey_dark,
|
||||
onBackground = Grey_light,
|
||||
onSurface = Grey_light,
|
||||
error = Red_cancel
|
||||
)
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Purple40,
|
||||
secondary = PurpleGrey40,
|
||||
tertiary = Pink40
|
||||
|
||||
/* Other default colors to override
|
||||
background = Color(0xFFFFFBFE),
|
||||
surface = Color(0xFFFFFBFE),
|
||||
primary = Blue_primary,
|
||||
secondary = Blue_secondary,
|
||||
tertiary = Dark_blue,
|
||||
background = Grey_light,
|
||||
surface = Color.White,
|
||||
onPrimary = Color.White,
|
||||
onSecondary = Color.White,
|
||||
onTertiary = Color.White,
|
||||
onBackground = Color(0xFF1C1B1F),
|
||||
onSurface = Color(0xFF1C1B1F),
|
||||
*/
|
||||
onBackground = Grey_dark,
|
||||
onSurface = Grey_dark,
|
||||
error = Red_cancel
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun PanicButtonTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = true,
|
||||
dynamicColor: Boolean = false, // Disable dynamic color
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme
|
||||
val view = LocalView.current
|
||||
if (!view.isInEditMode) {
|
||||
val window = (view.context as Activity).window
|
||||
window.statusBarColor = colorScheme.primary.toArgb()
|
||||
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
|
||||
BIN
app/src/main/res/drawable/lantai_1_selatan.jpg
Normal file
BIN
app/src/main/res/drawable/lantai_1_selatan.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 524 KiB |
BIN
app/src/main/res/drawable/lantai_1_utara.jpg
Normal file
BIN
app/src/main/res/drawable/lantai_1_utara.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 580 KiB |
21
app/src/main/res/drawable/logo.xml
Normal file
21
app/src/main/res/drawable/logo.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="800dp"
|
||||
android:height="800dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<path
|
||||
android:pathData="M507.49,426.07L282.86,53.54c-5.68,-9.41 -15.87,-15.17 -26.86,-15.17c-10.99,0 -21.19,5.76 -26.86,15.17L4.51,426.07c-5.84,9.69 -6.01,21.77 -0.45,31.63c5.56,9.85 16,15.94 27.32,15.94h449.26c11.31,0 21.75,-6.09 27.32,-15.94C513.51,447.84 513.34,435.76 507.49,426.07z"
|
||||
android:fillColor="#FF9900"/>
|
||||
<path
|
||||
android:pathData="M256,38.37c-10.99,0 -21.19,5.76 -26.86,15.17L4.51,426.07c-5.84,9.69 -6.01,21.77 -0.45,31.63c5.56,9.85 16,15.94 27.32,15.94h224.63L256,38.37L256,38.37z"
|
||||
android:fillColor="#FFDC35"/>
|
||||
<path
|
||||
android:pathData="M445.33,432.79H67.11c-3.59,0 -6.91,-1.91 -8.72,-5.01c-1.81,-3.1 -1.83,-6.93 -0.05,-10.06L247.23,85.03c1.79,-3.15 5.14,-5.11 8.77,-5.11c0,0 0,0 0,0c3.63,0 6.97,1.95 8.77,5.1l189.32,332.69c1.78,3.12 1.76,6.95 -0.05,10.06S448.92,432.79 445.33,432.79zM84.44,412.62h343.54L256.01,110.42L84.44,412.62z"
|
||||
android:fillColor="#F20013"/>
|
||||
<path
|
||||
android:pathData="M256.33,412.62H84.44l171.58,-302.19l-0.01,-30.5h-0c-3.63,0 -6.98,1.95 -8.77,5.11L58.34,417.72c-1.77,3.12 -1.75,6.95 0.05,10.06c1.81,3.1 5.13,5.01 8.72,5.01h189.22v-20.17H256.33z"
|
||||
android:fillColor="#FF4B00"/>
|
||||
<path
|
||||
android:pathData="M279.36,376.88c0,12.34 -10.54,23.18 -22.88,23.18c-13.25,0 -23.18,-10.84 -23.18,-23.18c0,-12.64 9.94,-23.18 23.18,-23.18C268.83,353.7 279.36,364.24 279.36,376.88zM273.64,319.68c0,9.33 -10.24,13.25 -17.46,13.25c-9.63,0 -17.76,-3.91 -17.76,-13.25c0,-35.83 -4.21,-87.31 -4.21,-123.13c0,-11.74 9.63,-18.36 21.98,-18.36c11.74,0 21.68,6.62 21.68,18.36C277.86,232.37 273.64,283.86 273.64,319.68z"
|
||||
android:fillColor="#533F29"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/splash_icon_padded.xml
Normal file
9
app/src/main/res/drawable/splash_icon_padded.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:drawable="@drawable/ubhara"
|
||||
android:top="50dp"
|
||||
android:bottom="50dp"
|
||||
android:left="50dp"
|
||||
android:right="50dp"/>
|
||||
</layer-list>
|
||||
BIN
app/src/main/res/drawable/ubhara.png
Normal file
BIN
app/src/main/res/drawable/ubhara.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 398 KiB |
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<background android:drawable="@android:color/transparent" />
|
||||
<foreground android:drawable="@drawable/logo" />
|
||||
<monochrome android:drawable="@drawable/logo" />
|
||||
</adaptive-icon>
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<background android:drawable="@android:color/transparent" />
|
||||
<foreground android:drawable="@drawable/logo" />
|
||||
<monochrome android:drawable="@drawable/logo" />
|
||||
</adaptive-icon>
|
||||
10
app/src/main/res/values/styles.xml
Normal file
10
app/src/main/res/values/styles.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="Theme.App.Starting" parent="Theme.SplashScreen.IconBackground">
|
||||
|
||||
<item name="postSplashScreenTheme">@style/Theme.PanicButton</item>
|
||||
<item name="windowSplashScreenAnimatedIcon">@drawable/splash_icon_padded</item>
|
||||
|
||||
<item name="windowSplashScreenAnimationDuration">500</item>
|
||||
</style>
|
||||
</resources>
|
||||
Loading…
x
Reference in New Issue
Block a user