first commit
This commit is contained in:
commit
71c0e8a538
|
@ -0,0 +1,137 @@
|
|||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <SPI.h>
|
||||
#include <MFRC522.h>
|
||||
#include <LiquidCrystal_I2C.h>
|
||||
|
||||
const char* ssid = "amriiiii.02";
|
||||
const char* password = "123sampai10";
|
||||
|
||||
const char* serverName = "http://192.168.92.176/playground_api/receive_rfid_scan.php";
|
||||
|
||||
#define SS_PIN 5
|
||||
#define RST_PIN 15
|
||||
|
||||
MFRC522 rfid(SS_PIN, RST_PIN);
|
||||
LiquidCrystal_I2C lcd(0x27, 16, 2);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
lcd.init();
|
||||
lcd.backlight();
|
||||
|
||||
SPI.begin();
|
||||
rfid.PCD_Init();
|
||||
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("Menghubungkan...");
|
||||
WiFi.begin(ssid, password);
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("WiFi Connecting..");
|
||||
}
|
||||
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("WiFi Tersambung");
|
||||
Serial.println("WiFi Tersambung!");
|
||||
Serial.print("IP Address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
delay(1000);
|
||||
lcd.clear();
|
||||
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("SISTEM PEMANTAU");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("ANAK PLAYGROUND");
|
||||
delay(2000);
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("TEMPELKAN GELANG");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("UNTUK MENDAFTAR");
|
||||
Serial.println("Sistem siap. Tempelkan gelang RFID.");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) {
|
||||
delay(50);
|
||||
return;
|
||||
}
|
||||
|
||||
String uidStr = "";
|
||||
for (byte i = 0; i < rfid.uid.size; i++) {
|
||||
if (rfid.uid.uidByte[i] < 0x10) uidStr += "0";
|
||||
uidStr += String(rfid.uid.uidByte[i], HEX);
|
||||
}
|
||||
uidStr.toUpperCase();
|
||||
|
||||
Serial.print("UID Gelang Terbaca: ");
|
||||
Serial.println(uidStr);
|
||||
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("GELANG TERBACA");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(uidStr);
|
||||
delay(1000);
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
HTTPClient http;
|
||||
http.begin(serverName);
|
||||
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||
|
||||
// Hanya kirim kode_gelang
|
||||
String httpRequestData = "kode_gelang=" + uidStr;
|
||||
|
||||
Serial.print("Mengirim data: ");
|
||||
Serial.println(httpRequestData);
|
||||
|
||||
int httpResponseCode = http.POST(httpRequestData);
|
||||
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
|
||||
if (httpResponseCode > 0) {
|
||||
String response = http.getString();
|
||||
Serial.print("HTTP Response Code: ");
|
||||
Serial.println(httpResponseCode);
|
||||
Serial.print("Server Response: ");
|
||||
Serial.println(response);
|
||||
|
||||
lcd.print("Data Dikirim OK");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("Status: ");
|
||||
lcd.print(httpResponseCode);
|
||||
} else {
|
||||
Serial.print("Error Mengirim Data. HTTP Response Code: ");
|
||||
Serial.println(httpResponseCode);
|
||||
|
||||
lcd.print("Gagal Kirim");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("Kode: ");
|
||||
lcd.print(httpResponseCode);
|
||||
}
|
||||
|
||||
http.end();
|
||||
} else {
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("WiFi Terputus!");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("Reconnecting...");
|
||||
Serial.println("WiFi Terputus, mencoba menyambungkan ulang...");
|
||||
WiFi.reconnect();
|
||||
delay(2000);
|
||||
}
|
||||
|
||||
delay(2000);
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("TEMPELKAN GELANG");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("UNTUK MENDAFTAR");
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <SPI.h>
|
||||
#include <MFRC522.h>
|
||||
#include <LiquidCrystal_I2C.h>
|
||||
|
||||
const char* ssid = "amriiiii.02";
|
||||
const char* password = "123sampai10";
|
||||
|
||||
const char* serverName = "http://192.168.92.176/playground_api/process_wahana_scan.php";
|
||||
|
||||
const char* namaWahana = "Interaktif";
|
||||
|
||||
#define SS_PIN 5
|
||||
#define RST_PIN 15
|
||||
|
||||
MFRC522 rfid(SS_PIN, RST_PIN);
|
||||
LiquidCrystal_I2C lcd(0x27, 16, 2);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
lcd.init();
|
||||
lcd.backlight();
|
||||
|
||||
SPI.begin();
|
||||
rfid.PCD_Init();
|
||||
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("Menghubungkan...");
|
||||
WiFi.begin(ssid, password);
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("WiFi Connecting..");
|
||||
}
|
||||
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("WiFi Tersambung");
|
||||
Serial.println("WiFi Tersambung!");
|
||||
Serial.print("IP Address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
delay(1000);
|
||||
lcd.clear();
|
||||
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("SISTEM PEMANTAU");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("ANAK PLAYGROUND");
|
||||
delay(2000);
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("TEMPELKAN GELANG");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(" UNTUK MASUK ");
|
||||
Serial.println("Sistem siap. Tempelkan gelang RFID.");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) {
|
||||
delay(50);
|
||||
return;
|
||||
}
|
||||
|
||||
String uidStr = "";
|
||||
for (byte i = 0; i < rfid.uid.size; i++) {
|
||||
if (rfid.uid.uidByte[i] < 0x10) uidStr += "0";
|
||||
uidStr += String(rfid.uid.uidByte[i], HEX);
|
||||
}
|
||||
uidStr.toUpperCase();
|
||||
|
||||
Serial.print("UID Gelang Terbaca: ");
|
||||
Serial.println(uidStr);
|
||||
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("GELANG TERBACA");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(uidStr);
|
||||
delay(1000);
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
HTTPClient http;
|
||||
http.begin(serverName);
|
||||
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||
|
||||
String httpRequestData = "kode_gelang=" + uidStr + "&wahana=" + String(namaWahana);
|
||||
|
||||
Serial.print("Mengirim data: ");
|
||||
Serial.println(httpRequestData);
|
||||
|
||||
int httpResponseCode = http.POST(httpRequestData);
|
||||
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
|
||||
if (httpResponseCode > 0) {
|
||||
String response = http.getString();
|
||||
Serial.print("HTTP Response Code: ");
|
||||
Serial.println(httpResponseCode);
|
||||
Serial.print("Server Response: ");
|
||||
Serial.println(response);
|
||||
|
||||
lcd.print("Data Dikirim OK");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("Status: ");
|
||||
lcd.print(httpResponseCode);
|
||||
} else {
|
||||
Serial.print("Error Mengirim Data. HTTP Response Code: ");
|
||||
Serial.println(httpResponseCode);
|
||||
|
||||
lcd.print("Gagal Kirim");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("Kode: ");
|
||||
lcd.print(httpResponseCode);
|
||||
}
|
||||
|
||||
http.end();
|
||||
} else {
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("WiFi Terputus!");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("Reconnecting...");
|
||||
Serial.println("WiFi Terputus, mencoba menyambungkan ulang...");
|
||||
WiFi.reconnect();
|
||||
delay(2000);
|
||||
}
|
||||
|
||||
delay(2000);
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("TEMPELKAN GELANG");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(" UNTUK MASUK ");
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <SPI.h>
|
||||
#include <MFRC522.h>
|
||||
#include <LiquidCrystal_I2C.h>
|
||||
|
||||
const char* ssid = "amriiiii.02";
|
||||
const char* password = "123sampai10";
|
||||
|
||||
const char* serverName = "http://192.168.92.176/playground_api/process_wahana_scan.php";
|
||||
|
||||
const char* namaWahana = "Simulasi";
|
||||
|
||||
#define SS_PIN 5
|
||||
#define RST_PIN 15
|
||||
|
||||
MFRC522 rfid(SS_PIN, RST_PIN);
|
||||
LiquidCrystal_I2C lcd(0x27, 16, 2);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
lcd.init();
|
||||
lcd.backlight();
|
||||
|
||||
SPI.begin();
|
||||
rfid.PCD_Init();
|
||||
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("Menghubungkan...");
|
||||
WiFi.begin(ssid, password);
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("WiFi Connecting..");
|
||||
}
|
||||
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("WiFi Tersambung");
|
||||
Serial.println("WiFi Tersambung!");
|
||||
Serial.print("IP Address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
delay(1000);
|
||||
lcd.clear();
|
||||
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("SISTEM PEMANTAU");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("ANAK PLAYGROUND");
|
||||
delay(2000);
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("TEMPELKAN GELANG");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(" UNTUK MASUK ");
|
||||
Serial.println("Sistem siap. Tempelkan gelang RFID.");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) {
|
||||
delay(50);
|
||||
return;
|
||||
}
|
||||
|
||||
String uidStr = "";
|
||||
for (byte i = 0; i < rfid.uid.size; i++) {
|
||||
if (rfid.uid.uidByte[i] < 0x10) uidStr += "0";
|
||||
uidStr += String(rfid.uid.uidByte[i], HEX);
|
||||
}
|
||||
uidStr.toUpperCase();
|
||||
|
||||
Serial.print("UID Gelang Terbaca: ");
|
||||
Serial.println(uidStr);
|
||||
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("GELANG TERBACA");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(uidStr);
|
||||
delay(1000);
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
HTTPClient http;
|
||||
http.begin(serverName);
|
||||
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||
|
||||
String httpRequestData = "kode_gelang=" + uidStr + "&wahana=" + String(namaWahana);
|
||||
|
||||
Serial.print("Mengirim data: ");
|
||||
Serial.println(httpRequestData);
|
||||
|
||||
int httpResponseCode = http.POST(httpRequestData);
|
||||
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
|
||||
if (httpResponseCode > 0) {
|
||||
String response = http.getString();
|
||||
Serial.print("HTTP Response Code: ");
|
||||
Serial.println(httpResponseCode);
|
||||
Serial.print("Server Response: ");
|
||||
Serial.println(response);
|
||||
|
||||
lcd.print("Data Dikirim OK");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("Status: ");
|
||||
lcd.print(httpResponseCode);
|
||||
} else {
|
||||
Serial.print("Error Mengirim Data. HTTP Response Code: ");
|
||||
Serial.println(httpResponseCode);
|
||||
|
||||
lcd.print("Gagal Kirim");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("Kode: ");
|
||||
lcd.print(httpResponseCode);
|
||||
}
|
||||
|
||||
http.end();
|
||||
} else {
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("WiFi Terputus!");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("Reconnecting...");
|
||||
Serial.println("WiFi Terputus, mencoba menyambungkan ulang...");
|
||||
WiFi.reconnect();
|
||||
delay(2000);
|
||||
}
|
||||
|
||||
delay(2000);
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("TEMPELKAN GELANG");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(" UNTUK MASUK ");
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <SPI.h>
|
||||
#include <MFRC522.h>
|
||||
#include <LiquidCrystal_I2C.h>
|
||||
|
||||
const char* ssid = "amriiiii.02";
|
||||
const char* password = "123sampai10";
|
||||
|
||||
const char* serverName = "http://192.168.92.176/playground_api/process_wahana_scan.php";
|
||||
|
||||
const char* namaWahana = "Virtual";
|
||||
|
||||
#define SS_PIN 5
|
||||
#define RST_PIN 15
|
||||
|
||||
MFRC522 rfid(SS_PIN, RST_PIN);
|
||||
LiquidCrystal_I2C lcd(0x27, 16, 2);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
lcd.init();
|
||||
lcd.backlight();
|
||||
|
||||
SPI.begin();
|
||||
rfid.PCD_Init();
|
||||
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("Menghubungkan...");
|
||||
WiFi.begin(ssid, password);
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("WiFi Connecting..");
|
||||
}
|
||||
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("WiFi Tersambung");
|
||||
Serial.println("WiFi Tersambung!");
|
||||
Serial.print("IP Address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
delay(1000);
|
||||
lcd.clear();
|
||||
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("SISTEM PEMANTAU");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("ANAK PLAYGROUND");
|
||||
delay(2000);
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("TEMPELKAN GELANG");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(" UNTUK MASUK ");
|
||||
Serial.println("Sistem siap. Tempelkan gelang RFID.");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) {
|
||||
delay(50);
|
||||
return;
|
||||
}
|
||||
|
||||
String uidStr = "";
|
||||
for (byte i = 0; i < rfid.uid.size; i++) {
|
||||
if (rfid.uid.uidByte[i] < 0x10) uidStr += "0";
|
||||
uidStr += String(rfid.uid.uidByte[i], HEX);
|
||||
}
|
||||
uidStr.toUpperCase();
|
||||
|
||||
Serial.print("UID Gelang Terbaca: ");
|
||||
Serial.println(uidStr);
|
||||
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("GELANG TERBACA");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(uidStr);
|
||||
delay(1000);
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
HTTPClient http;
|
||||
http.begin(serverName);
|
||||
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||
|
||||
String httpRequestData = "kode_gelang=" + uidStr + "&wahana=" + String(namaWahana);
|
||||
|
||||
Serial.print("Mengirim data: ");
|
||||
Serial.println(httpRequestData);
|
||||
|
||||
int httpResponseCode = http.POST(httpRequestData);
|
||||
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
|
||||
if (httpResponseCode > 0) {
|
||||
String response = http.getString();
|
||||
Serial.print("HTTP Response Code: ");
|
||||
Serial.println(httpResponseCode);
|
||||
Serial.print("Server Response: ");
|
||||
Serial.println(response);
|
||||
|
||||
lcd.print("Data Dikirim OK");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("Status: ");
|
||||
lcd.print(httpResponseCode);
|
||||
} else {
|
||||
Serial.print("Error Mengirim Data. HTTP Response Code: ");
|
||||
Serial.println(httpResponseCode);
|
||||
|
||||
lcd.print("Gagal Kirim");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("Kode: ");
|
||||
lcd.print(httpResponseCode);
|
||||
}
|
||||
|
||||
http.end();
|
||||
} else {
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("WiFi Terputus!");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("Reconnecting...");
|
||||
Serial.println("WiFi Terputus, mencoba menyambungkan ulang...");
|
||||
WiFi.reconnect();
|
||||
delay(2000);
|
||||
}
|
||||
|
||||
delay(2000);
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("TEMPELKAN GELANG");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(" UNTUK MASUK ");
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
|
@ -0,0 +1,3 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
|
@ -0,0 +1 @@
|
|||
MoniKids
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AndroidProjectSystem">
|
||||
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="21" />
|
||||
</component>
|
||||
</project>
|
|
@ -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>
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectMigrations">
|
||||
<option name="MigrateToGradleLocalJavaHome">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,9 @@
|
|||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1 @@
|
|||
/build
|
|
@ -0,0 +1,41 @@
|
|||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.tugasakhir.monikids'
|
||||
compileSdk 35
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.tugasakhir.monikids"
|
||||
minSdk 24
|
||||
targetSdk 35
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation libs.appcompat
|
||||
implementation libs.material
|
||||
implementation libs.activity
|
||||
implementation libs.constraintlayout
|
||||
testImplementation libs.junit
|
||||
androidTestImplementation libs.ext.junit
|
||||
androidTestImplementation libs.espresso.core
|
||||
implementation 'com.android.volley:volley:1.2.1'
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
|
@ -0,0 +1,26 @@
|
|||
package com.tugasakhir.monikids;
|
||||
|
||||
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 <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
assertEquals("com.tugasakhir.monikids", appContext.getPackageName());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
<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:networkSecurityConfig="@xml/network_security_config"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.MoniKids"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".view.IPSettingsActivity"
|
||||
android:exported="false">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".view.HomeActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".view.LoginActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="false" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,24 @@
|
|||
package com.tugasakhir.monikids;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.activity_main);
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
return insets;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,281 @@
|
|||
package com.tugasakhir.monikids.utils;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.tugasakhir.monikids.R;
|
||||
|
||||
public class CustomAlertDialog {
|
||||
|
||||
public enum AlertType {
|
||||
SUCCESS, ERROR, WARNING, INFO
|
||||
}
|
||||
|
||||
public interface OnAlertClickListener {
|
||||
void onPositiveClick();
|
||||
void onNegativeClick();
|
||||
}
|
||||
|
||||
private Dialog dialog;
|
||||
private Context context;
|
||||
private View dialogView;
|
||||
private TextView tvTitle, tvMessage;
|
||||
private ImageView ivIcon;
|
||||
private Button btnPositive, btnNegative;
|
||||
private ViewGroup layoutButtons;
|
||||
|
||||
public CustomAlertDialog(Context context) {
|
||||
this.context = context;
|
||||
initDialog();
|
||||
}
|
||||
|
||||
private void initDialog() {
|
||||
dialog = new Dialog(context);
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
dialog.setCancelable(false);
|
||||
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
dialogView = inflater.inflate(R.layout.dialog_custom_alert, null);
|
||||
dialog.setContentView(dialogView);
|
||||
|
||||
// Initialize views
|
||||
tvTitle = dialogView.findViewById(R.id.tvAlertTitle);
|
||||
tvMessage = dialogView.findViewById(R.id.tvAlertMessage);
|
||||
ivIcon = dialogView.findViewById(R.id.ivAlertIcon);
|
||||
btnPositive = dialogView.findViewById(R.id.btnPositive);
|
||||
btnNegative = dialogView.findViewById(R.id.btnNegative);
|
||||
layoutButtons = dialogView.findViewById(R.id.layoutButtons);
|
||||
|
||||
// Set dialog size dengan proporsi yang lebih baik
|
||||
setupDialogSize();
|
||||
}
|
||||
|
||||
private void setupDialogSize() {
|
||||
Window window = dialog.getWindow();
|
||||
if (window != null) {
|
||||
WindowManager.LayoutParams layoutParams = window.getAttributes();
|
||||
|
||||
// Dapatkan ukuran layar
|
||||
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
|
||||
int screenWidth = displayMetrics.widthPixels;
|
||||
int screenHeight = displayMetrics.heightPixels;
|
||||
|
||||
// Set lebar dialog: 85% dari lebar layar (atau maksimal 400dp)
|
||||
int maxWidthDp = 400; // maksimal 400dp
|
||||
int maxWidthPx = (int) (maxWidthDp * displayMetrics.density);
|
||||
int desiredWidth = (int) (screenWidth * 0.85f); // 85% dari lebar layar
|
||||
|
||||
layoutParams.width = Math.min(desiredWidth, maxWidthPx);
|
||||
layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
|
||||
// Center dialog di layar
|
||||
layoutParams.gravity = android.view.Gravity.CENTER;
|
||||
|
||||
window.setAttributes(layoutParams);
|
||||
}
|
||||
}
|
||||
|
||||
public CustomAlertDialog setTitle(String title) {
|
||||
tvTitle.setText(title);
|
||||
tvTitle.setVisibility(View.VISIBLE);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CustomAlertDialog setMessage(String message) {
|
||||
tvMessage.setText(message);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CustomAlertDialog setAlertType(AlertType type) {
|
||||
switch (type) {
|
||||
case SUCCESS:
|
||||
ivIcon.setImageResource(R.drawable.ic_check_circle);
|
||||
ivIcon.setColorFilter(ContextCompat.getColor(context, R.color.success_color));
|
||||
tvTitle.setTextColor(ContextCompat.getColor(context, R.color.success_color));
|
||||
break;
|
||||
case ERROR:
|
||||
ivIcon.setImageResource(R.drawable.ic_error_circle);
|
||||
ivIcon.setColorFilter(ContextCompat.getColor(context, R.color.error_color));
|
||||
tvTitle.setTextColor(ContextCompat.getColor(context, R.color.error_color));
|
||||
break;
|
||||
case WARNING:
|
||||
ivIcon.setImageResource(R.drawable.ic_warning_circle);
|
||||
ivIcon.setColorFilter(ContextCompat.getColor(context, R.color.warning_color));
|
||||
tvTitle.setTextColor(ContextCompat.getColor(context, R.color.warning_color));
|
||||
break;
|
||||
case INFO:
|
||||
ivIcon.setImageResource(R.drawable.ic_info_circle);
|
||||
ivIcon.setColorFilter(ContextCompat.getColor(context, R.color.purple_primary));
|
||||
tvTitle.setTextColor(ContextCompat.getColor(context, R.color.purple_primary));
|
||||
break;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public CustomAlertDialog setPositiveButton(String text, OnAlertClickListener listener) {
|
||||
btnPositive.setText(text);
|
||||
btnPositive.setVisibility(View.VISIBLE);
|
||||
btnPositive.setOnClickListener(v -> {
|
||||
animateButtonPress(v);
|
||||
if (listener != null) {
|
||||
listener.onPositiveClick();
|
||||
}
|
||||
dismiss();
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public CustomAlertDialog setNegativeButton(String text, OnAlertClickListener listener) {
|
||||
btnNegative.setText(text);
|
||||
btnNegative.setVisibility(View.VISIBLE);
|
||||
btnNegative.setOnClickListener(v -> {
|
||||
animateButtonPress(v);
|
||||
if (listener != null) {
|
||||
listener.onNegativeClick();
|
||||
}
|
||||
dismiss();
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public CustomAlertDialog setCancelable(boolean cancelable) {
|
||||
dialog.setCancelable(cancelable);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void show() {
|
||||
if (!dialog.isShowing()) {
|
||||
dialog.show();
|
||||
animateShow();
|
||||
}
|
||||
}
|
||||
|
||||
public void dismiss() {
|
||||
if (dialog.isShowing()) {
|
||||
animateHide(() -> dialog.dismiss());
|
||||
}
|
||||
}
|
||||
|
||||
private void animateShow() {
|
||||
// Scale and fade in animation dengan efek lebih smooth
|
||||
ObjectAnimator scaleX = ObjectAnimator.ofFloat(dialogView, "scaleX", 0.8f, 1.05f, 1f);
|
||||
ObjectAnimator scaleY = ObjectAnimator.ofFloat(dialogView, "scaleY", 0.8f, 1.05f, 1f);
|
||||
ObjectAnimator alpha = ObjectAnimator.ofFloat(dialogView, "alpha", 0f, 1f);
|
||||
|
||||
scaleX.setDuration(400);
|
||||
scaleY.setDuration(400);
|
||||
alpha.setDuration(300);
|
||||
|
||||
scaleX.setInterpolator(new AccelerateDecelerateInterpolator());
|
||||
scaleY.setInterpolator(new AccelerateDecelerateInterpolator());
|
||||
alpha.setInterpolator(new AccelerateDecelerateInterpolator());
|
||||
|
||||
scaleX.start();
|
||||
scaleY.start();
|
||||
alpha.start();
|
||||
|
||||
// Icon bounce animation yang lebih halus
|
||||
ObjectAnimator iconBounce = ObjectAnimator.ofFloat(ivIcon, "scaleX", 0f, 1.3f, 1f);
|
||||
ObjectAnimator iconBounceY = ObjectAnimator.ofFloat(ivIcon, "scaleY", 0f, 1.3f, 1f);
|
||||
iconBounce.setDuration(500);
|
||||
iconBounceY.setDuration(500);
|
||||
iconBounce.setStartDelay(200);
|
||||
iconBounceY.setStartDelay(200);
|
||||
iconBounce.setInterpolator(new AccelerateDecelerateInterpolator());
|
||||
iconBounceY.setInterpolator(new AccelerateDecelerateInterpolator());
|
||||
iconBounce.start();
|
||||
iconBounceY.start();
|
||||
}
|
||||
|
||||
private void animateHide(Runnable onComplete) {
|
||||
ObjectAnimator scaleX = ObjectAnimator.ofFloat(dialogView, "scaleX", 1f, 0.9f);
|
||||
ObjectAnimator scaleY = ObjectAnimator.ofFloat(dialogView, "scaleY", 1f, 0.9f);
|
||||
ObjectAnimator alpha = ObjectAnimator.ofFloat(dialogView, "alpha", 1f, 0f);
|
||||
|
||||
scaleX.setDuration(250);
|
||||
scaleY.setDuration(250);
|
||||
alpha.setDuration(250);
|
||||
|
||||
alpha.addListener(new android.animation.AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(android.animation.Animator animation) {
|
||||
onComplete.run();
|
||||
}
|
||||
});
|
||||
|
||||
scaleX.start();
|
||||
scaleY.start();
|
||||
alpha.start();
|
||||
}
|
||||
|
||||
private void animateButtonPress(View button) {
|
||||
ObjectAnimator scaleX = ObjectAnimator.ofFloat(button, "scaleX", 1f, 0.95f, 1f);
|
||||
ObjectAnimator scaleY = ObjectAnimator.ofFloat(button, "scaleY", 1f, 0.95f, 1f);
|
||||
scaleX.setDuration(150);
|
||||
scaleY.setDuration(150);
|
||||
scaleX.start();
|
||||
scaleY.start();
|
||||
}
|
||||
|
||||
// Builder pattern for easy creation
|
||||
public static class Builder {
|
||||
private CustomAlertDialog dialog;
|
||||
|
||||
public Builder(Context context) {
|
||||
dialog = new CustomAlertDialog(context);
|
||||
}
|
||||
|
||||
public Builder setTitle(String title) {
|
||||
dialog.setTitle(title);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMessage(String message) {
|
||||
dialog.setMessage(message);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAlertType(AlertType type) {
|
||||
dialog.setAlertType(type);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setPositiveButton(String text, OnAlertClickListener listener) {
|
||||
dialog.setPositiveButton(text, listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setNegativeButton(String text, OnAlertClickListener listener) {
|
||||
dialog.setNegativeButton(text, listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCancelable(boolean cancelable) {
|
||||
dialog.setCancelable(cancelable);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CustomAlertDialog create() {
|
||||
return dialog;
|
||||
}
|
||||
|
||||
public void show() {
|
||||
dialog.show();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
package com.tugasakhir.monikids.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
public class IPManager {
|
||||
private static final String PREF_NAME = "IP_SETTINGS";
|
||||
private static final String KEY_SERVER_IP = "SERVER_IP";
|
||||
private static final String DEFAULT_IP = "192.168.18.120";
|
||||
|
||||
private static IPManager instance;
|
||||
private SharedPreferences preferences;
|
||||
private String currentIP;
|
||||
|
||||
private IPManager(Context context) {
|
||||
preferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
|
||||
currentIP = preferences.getString(KEY_SERVER_IP, DEFAULT_IP);
|
||||
}
|
||||
|
||||
public static synchronized IPManager getInstance(Context context) {
|
||||
if (instance == null) {
|
||||
instance = new IPManager(context.getApplicationContext());
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mendapatkan IP server saat ini
|
||||
*/
|
||||
public String getCurrentIP() {
|
||||
return currentIP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mengubah IP server dan menyimpannya secara persisten
|
||||
*/
|
||||
public boolean setServerIP(String newIP) {
|
||||
if (isValidIP(newIP)) {
|
||||
currentIP = newIP;
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
editor.putString(KEY_SERVER_IP, newIP);
|
||||
return editor.commit();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mendapatkan base URL untuk API
|
||||
*/
|
||||
public String getBaseURL() {
|
||||
return "http://" + currentIP + "/playground_api/";
|
||||
}
|
||||
|
||||
/**
|
||||
* Mendapatkan home URL
|
||||
*/
|
||||
public String getHomeURL() {
|
||||
return "http://" + currentIP + "/playground_api/home.php";
|
||||
}
|
||||
|
||||
/**
|
||||
* Mendapatkan URL lengkap untuk endpoint tertentu
|
||||
*/
|
||||
public String getURL(String endpoint) {
|
||||
return getBaseURL() + endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validasi format IP address
|
||||
*/
|
||||
private boolean isValidIP(String ip) {
|
||||
if (ip == null || ip.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cek untuk localhost
|
||||
if (ip.equals("localhost") || ip.equals("127.0.0.1")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Cek untuk emulator
|
||||
if (ip.equals("10.0.2.2")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validasi IP address format
|
||||
String[] parts = ip.split("\\.");
|
||||
if (parts.length != 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
for (String part : parts) {
|
||||
int num = Integer.parseInt(part);
|
||||
if (num < 0 || num > 255) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset ke IP default
|
||||
*/
|
||||
public void resetToDefault() {
|
||||
setServerIP(DEFAULT_IP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mendapatkan daftar IP yang umum digunakan
|
||||
*/
|
||||
public static class QuickIPs {
|
||||
public static final String LOCALHOST = "127.0.0.1";
|
||||
public static final String EMULATOR = "10.0.2.2";
|
||||
public static final String DEFAULT = "192.168.18.120";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,635 @@
|
|||
package com.tugasakhir.monikids.view;
|
||||
|
||||
import android.Manifest;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.media.AudioAttributes;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.VibrationEffect;
|
||||
import android.os.Vibrator;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.android.volley.Request;
|
||||
import com.android.volley.RequestQueue;
|
||||
import com.android.volley.VolleyError;
|
||||
import com.android.volley.toolbox.JsonObjectRequest;
|
||||
import com.android.volley.toolbox.Volley;
|
||||
import com.tugasakhir.monikids.R;
|
||||
import com.tugasakhir.monikids.utils.CustomAlertDialog;
|
||||
import com.tugasakhir.monikids.utils.IPManager; // Import IPManager
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
public class HomeActivity extends AppCompatActivity {
|
||||
|
||||
private static final String TAG = "HomeActivity";
|
||||
// Hapus hardcoded HOME_URL, ini akan diset dari IPManager
|
||||
// private static final String HOME_URL = "http://192.168.18.120/playground_api/home.php";
|
||||
private String HOME_URL; // Deklarasikan sebagai non-static dan tanpa inisialisasi awal
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
private RequestQueue requestQueue;
|
||||
private IPManager ipManager; // Deklarasi instance IPManager
|
||||
|
||||
// UI Components
|
||||
private TextView tvUserName, tvBandId, tvPhoneNumber, tvEntryTime;
|
||||
private TextView tvLastAttraction, tvPlayDuration;
|
||||
private LinearLayout llTitle, llStatistik, llLogout;
|
||||
private LinearLayout timelineContainer; // Timeline container
|
||||
private CardView cardLogout;
|
||||
|
||||
// User data
|
||||
private int idAnak;
|
||||
private String nama, kodeGelang;
|
||||
private String lastKnownExitTime = null; // Untuk menyimpan waktu keluar terakhir yang diketahui
|
||||
|
||||
// Polling mechanism
|
||||
private Handler handler;
|
||||
private Runnable runnable;
|
||||
private static final long POLLING_INTERVAL_MS = 10000; // Poll setiap 10 detik
|
||||
|
||||
// Notification specific
|
||||
private static final String CHANNEL_ID = "monikids_exit_channel";
|
||||
private static final int NOTIFICATION_ID = 101; // ID unik untuk notifikasi keluar
|
||||
private Vibrator vibrator; // Untuk kontrol getaran manual
|
||||
private CustomAlertDialog activeExitDialog = null; // Referensi ke dialog yang sedang aktif
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_home);
|
||||
|
||||
initComponents();
|
||||
setupAnimations();
|
||||
getUserData();
|
||||
createNotificationChannel(); // Buat channel notifikasi
|
||||
// HOME_URL akan diinisialisasi di onResume untuk memastikan IP terbaru
|
||||
// startPollingHomeData(); // Mulai polling data akan dipanggil di onResume
|
||||
setupLogout();
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
sharedPreferences = getSharedPreferences("MoniKidsPrefs", MODE_PRIVATE);
|
||||
requestQueue = Volley.newRequestQueue(this);
|
||||
ipManager = IPManager.getInstance(this); // Inisialisasi IPManager di sini
|
||||
vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); // Inisialisasi vibrator
|
||||
|
||||
tvUserName = findViewById(R.id.tvUserName);
|
||||
tvBandId = findViewById(R.id.tvBandId);
|
||||
tvPhoneNumber = findViewById(R.id.tvPhoneNumber);
|
||||
tvEntryTime = findViewById(R.id.tvEntryTime);
|
||||
tvLastAttraction = findViewById(R.id.tvLastAttraction);
|
||||
tvPlayDuration = findViewById(R.id.tvPlayDuration);
|
||||
cardLogout = findViewById(R.id.cardLogout);
|
||||
llTitle = findViewById(R.id.llTitle);
|
||||
llStatistik = findViewById(R.id.llStatistik);
|
||||
llLogout = findViewById(R.id.llLogout);
|
||||
|
||||
// Initialize timeline container
|
||||
timelineContainer = findViewById(R.id.timelineContainer);
|
||||
}
|
||||
|
||||
private void getUserData() {
|
||||
idAnak = sharedPreferences.getInt("id_anak", 0);
|
||||
nama = sharedPreferences.getString("nama", "");
|
||||
kodeGelang = sharedPreferences.getString("kode_gelang", "");
|
||||
|
||||
tvUserName.setText(nama);
|
||||
tvBandId.setText(kodeGelang);
|
||||
|
||||
// Ambil waktu keluar terakhir yang disimpan jika ada
|
||||
lastKnownExitTime = sharedPreferences.getString("last_known_exit_time_" + idAnak, null);
|
||||
Log.d(TAG, "Initial lastKnownExitTime: " + lastKnownExitTime);
|
||||
}
|
||||
|
||||
private void createNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
CharSequence name = "Notifikasi Anak Keluar Playground";
|
||||
String description = "Notifikasi penting ketika anak keluar dari area bermain";
|
||||
int importance = NotificationManager.IMPORTANCE_HIGH; // Prioritas tinggi
|
||||
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
|
||||
channel.setDescription(description);
|
||||
|
||||
// Pengaturan getar untuk channel (pola berulang)
|
||||
long[] vibrationPattern = {0, 1000, 500, 1000, 500, 1000, 500, 1000};
|
||||
channel.enableVibration(true);
|
||||
channel.setVibrationPattern(vibrationPattern);
|
||||
|
||||
// Menggunakan USAGE_NOTIFICATION karena USAGE_NOTIFICATION_COMMUNICATION membutuhkan API 29+
|
||||
channel.setSound(null, new AudioAttributes.Builder()
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
|
||||
.build());
|
||||
|
||||
NotificationManager notificationManager = getSystemService(NotificationManager.class);
|
||||
if (notificationManager != null) {
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void startPollingHomeData() {
|
||||
handler = new Handler(Looper.getMainLooper());
|
||||
runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
loadHomeData();
|
||||
handler.postDelayed(this, POLLING_INTERVAL_MS);
|
||||
}
|
||||
};
|
||||
handler.post(runnable);
|
||||
}
|
||||
|
||||
private void stopPollingHomeData() {
|
||||
if (handler != null && runnable != null) {
|
||||
handler.removeCallbacks(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadHomeData() {
|
||||
if (idAnak == 0) {
|
||||
showError("Data user tidak valid");
|
||||
stopPollingHomeData();
|
||||
return;
|
||||
}
|
||||
|
||||
JSONObject requestData = new JSONObject();
|
||||
try {
|
||||
requestData.put("id_anak", idAnak);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Error creating request data", e);
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObjectRequest request = new JsonObjectRequest(
|
||||
Request.Method.POST,
|
||||
HOME_URL, // Menggunakan HOME_URL yang sudah diset dari IPManager
|
||||
requestData,
|
||||
this::handleHomeResponse,
|
||||
this::handleErrorResponse
|
||||
) {
|
||||
@Override
|
||||
public java.util.Map<String, String> getHeaders() {
|
||||
java.util.Map<String, String> headers = new java.util.HashMap<>();
|
||||
headers.put("Content-Type", "application/json");
|
||||
headers.put("Accept", "application/json");
|
||||
return headers;
|
||||
}
|
||||
};
|
||||
|
||||
request.setRetryPolicy(new com.android.volley.DefaultRetryPolicy(
|
||||
10000, // 10 seconds timeout
|
||||
1, // no retry
|
||||
1.0f
|
||||
));
|
||||
|
||||
requestQueue.add(request);
|
||||
}
|
||||
|
||||
private void handleHomeResponse(JSONObject response) {
|
||||
try {
|
||||
if (response.getBoolean("success")) {
|
||||
JSONObject data = response.optJSONObject("data");
|
||||
if (data != null) {
|
||||
JSONObject userData = data.optJSONObject("user_data");
|
||||
if (userData != null) {
|
||||
updateUserData(userData);
|
||||
String currentExitTime = userData.optString("exit_time", null);
|
||||
// Cek apakah currentExitTime benar-benar string "null" dari PHP
|
||||
if ("null".equals(currentExitTime)) {
|
||||
currentExitTime = null;
|
||||
}
|
||||
checkAndNotifyChildExit(currentExitTime);
|
||||
}
|
||||
|
||||
JSONArray timelineArray = data.optJSONArray("timeline");
|
||||
if (timelineArray != null) {
|
||||
populateTimeline(timelineArray);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "API Success false: " + response.optString("message"));
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Error parsing home response", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleErrorResponse(VolleyError error) {
|
||||
if (error.networkResponse != null && error.networkResponse.data != null) {
|
||||
String errorBody = new String(error.networkResponse.data);
|
||||
Log.e(TAG, "Server Error: " + errorBody);
|
||||
} else {
|
||||
Log.e(TAG, "Network Error", error);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUserData(JSONObject userData) throws JSONException {
|
||||
String phoneNumber = userData.optString("no_hp", "-");
|
||||
String entryTime = userData.optString("entry_time", "-");
|
||||
String playDuration = userData.optString("play_duration", "-");
|
||||
String lastAttraction = userData.optString("last_attraction", "-");
|
||||
|
||||
tvPhoneNumber.setText(phoneNumber);
|
||||
tvEntryTime.setText(entryTime);
|
||||
tvPlayDuration.setText(playDuration);
|
||||
tvLastAttraction.setText(lastAttraction);
|
||||
}
|
||||
|
||||
private void checkAndNotifyChildExit(String currentExitTime) {
|
||||
Log.d(TAG, "Checking child exit. Current Exit Time: " + currentExitTime + ", Last Known Exit Time: " + lastKnownExitTime);
|
||||
|
||||
// Jika currentExitTime tidak null/kosong DAN ada perubahan status
|
||||
if (currentExitTime != null && !currentExitTime.isEmpty() &&
|
||||
(lastKnownExitTime == null || !lastKnownExitTime.equals(currentExitTime))) {
|
||||
|
||||
lastKnownExitTime = currentExitTime;
|
||||
sharedPreferences.edit().putString("last_known_exit_time_" + idAnak, currentExitTime).apply();
|
||||
Log.d(TAG, "Anak terdeteksi keluar! Notifikasi akan dikirim.");
|
||||
|
||||
// Hentikan polling sementara untuk mencegah pemicuan berulang
|
||||
stopPollingHomeData();
|
||||
|
||||
sendChildExitNotification();
|
||||
showChildExitDialog();
|
||||
|
||||
} else if (currentExitTime == null || currentExitTime.isEmpty()) {
|
||||
// Jika anak masih bermain (currentExitTime null/kosong)
|
||||
// Pastikan lastKnownExitTime juga null agar deteksi berjalan lagi jika anak keluar setelah masuk kembali
|
||||
if (lastKnownExitTime != null) {
|
||||
Log.d(TAG, "Anak masuk kembali atau status reset. Resetting lastKnownExitTime.");
|
||||
lastKnownExitTime = null;
|
||||
sharedPreferences.edit().remove("last_known_exit_time_" + idAnak).apply();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendChildExitNotification() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && // Hanya berlaku untuk Android 13 (API 33) ke atas
|
||||
ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
Log.w(TAG, "Notifikasi tidak dapat dikirim: Izin POST_NOTIFICATIONS tidak diberikan.");
|
||||
return; // Hentikan jika izin tidak diberikan
|
||||
}
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_warning_circle)
|
||||
.setContentTitle("Anak Anda Keluar Playground!")
|
||||
.setContentText("Anak Anda (" + nama + ") telah keluar dari area bermain.")
|
||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setCategory(NotificationCompat.CATEGORY_ALARM)
|
||||
.setAutoCancel(false);
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
builder.setVibrate(new long[]{0, 1000, 500, 1000, 500, 1000, 500, 1000});
|
||||
}
|
||||
|
||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
|
||||
notificationManager.notify(NOTIFICATION_ID, builder.build());
|
||||
|
||||
startContinuousVibration();
|
||||
}
|
||||
|
||||
private void showChildExitDialog() {
|
||||
// Karena CustomAlertDialog Anda tidak mengekspos isShowing() secara publik,
|
||||
// kita akan menggunakan referensi activeExitDialog itu sendiri.
|
||||
// Jika activeExitDialog tidak null, asumsi dia sedang ditampilkan.
|
||||
// Anda mungkin perlu menambahkan method `isShowing()` ke CustomAlertDialog Anda
|
||||
// untuk penanganan yang lebih robust (seperti yang disarankan di respons sebelumnya).
|
||||
if (activeExitDialog != null) {
|
||||
Log.d(TAG, "Dialog keluar anak sudah ditampilkan, tidak perlu menampilkan lagi.");
|
||||
return;
|
||||
}
|
||||
|
||||
activeExitDialog = new CustomAlertDialog.Builder(this)
|
||||
.setTitle("Pemberitahuan: Anak Terdeteksi Keluar")
|
||||
.setMessage("Anak Anda, " + nama + ", telah terdeteksi keluar dari area bermain. Ini menandakan sesi bermain telah berakhir. Silakan jemput anak Anda.")
|
||||
.setAlertType(CustomAlertDialog.AlertType.WARNING)
|
||||
.setPositiveButton("OKE", new CustomAlertDialog.OnAlertClickListener() {
|
||||
@Override
|
||||
public void onPositiveClick() {
|
||||
Log.d(TAG, "Tombol OK pada dialog keluar anak ditekan.");
|
||||
stopContinuousVibration();
|
||||
clearChildExitNotification();
|
||||
activeExitDialog = null; // Reset referensi dialog
|
||||
startPollingHomeData(); // Lanjutkan polling
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNegativeClick() {
|
||||
// Tidak digunakan di sini
|
||||
}
|
||||
})
|
||||
.setCancelable(false)
|
||||
.create();
|
||||
|
||||
activeExitDialog.show();
|
||||
}
|
||||
|
||||
private void startContinuousVibration() {
|
||||
// --- Periksa izin VIBRATE sebelum memulai getaran ---
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // API 23 = Android 6.0
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED) {
|
||||
Log.w(TAG, "Vibration not allowed: VIBRATE permission denied.");
|
||||
return; // Hentikan jika izin tidak diberikan
|
||||
}
|
||||
}
|
||||
// --- Akhir pemeriksaan izin VIBRATE ---
|
||||
|
||||
if (vibrator != null && vibrator.hasVibrator()) {
|
||||
long[] pattern = {0, 1000, 500};
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
vibrator.vibrate(VibrationEffect.createWaveform(pattern, 0));
|
||||
} else {
|
||||
vibrator.vibrate(pattern, 0);
|
||||
}
|
||||
Log.d(TAG, "Continuous vibration started.");
|
||||
} else {
|
||||
Log.w(TAG, "Vibrator not available or not supported on this device.");
|
||||
}
|
||||
}
|
||||
|
||||
private void stopContinuousVibration() {
|
||||
if (vibrator != null) {
|
||||
vibrator.cancel();
|
||||
Log.d(TAG, "Continuous vibration stopped.");
|
||||
}
|
||||
}
|
||||
|
||||
private void clearChildExitNotification() {
|
||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
|
||||
notificationManager.cancel(NOTIFICATION_ID);
|
||||
Log.d(TAG, "Child exit notification cleared.");
|
||||
}
|
||||
|
||||
private void populateTimeline(JSONArray timelineArray) {
|
||||
timelineContainer.removeAllViews();
|
||||
Log.d(TAG, "Populating timeline with " + timelineArray.length() + " items");
|
||||
|
||||
try {
|
||||
for (int i = 0; i < timelineArray.length(); i++) {
|
||||
JSONObject timelineItem = timelineArray.getJSONObject(i);
|
||||
String type = timelineItem.optString("type", "placeholder");
|
||||
String wahanaName = timelineItem.optString("wahana_name", "");
|
||||
String waktuMasuk = timelineItem.optString("waktu_masuk", "");
|
||||
String waktuKeluar = timelineItem.optString("waktu_keluar", "");
|
||||
String durasi = timelineItem.optString("durasi", "");
|
||||
|
||||
Log.d(TAG, "Creating timeline item - Type: " + type + ", Wahana: " + wahanaName);
|
||||
|
||||
// Perbaiki inflate timeline_item agar tidak IllegalStateException
|
||||
// Gunakan parent (timelineContainer) dan false untuk attachToRoot
|
||||
View timelineView = createTimelineItem(type, wahanaName, waktuMasuk, waktuKeluar, durasi, i == timelineArray.length() - 1);
|
||||
if (timelineView != null) {
|
||||
timelineContainer.addView(timelineView);
|
||||
}
|
||||
}
|
||||
animateTimelineIn();
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Error parsing timeline data", e);
|
||||
}
|
||||
}
|
||||
|
||||
private View createTimelineItem(String type, String wahanaName, String waktuMasuk, String waktuKeluar, String durasi, boolean isLastItem) {
|
||||
LayoutInflater inflater = LayoutInflater.from(this);
|
||||
View timelineView = null;
|
||||
|
||||
switch (type) {
|
||||
case "active":
|
||||
timelineView = inflater.inflate(R.layout.timeline_item_active, timelineContainer, false);
|
||||
TextView tvActiveTitle = timelineView.findViewById(R.id.tvTimelineTitle);
|
||||
TextView tvActiveTime = timelineView.findViewById(R.id.tvTimelineTime);
|
||||
|
||||
if (tvActiveTitle != null) tvActiveTitle.setText(wahanaName);
|
||||
if (tvActiveTime != null) {
|
||||
String displayTime = waktuMasuk;
|
||||
if (!durasi.isEmpty()) {
|
||||
displayTime += " • " + durasi;
|
||||
}
|
||||
tvActiveTime.setText(displayTime);
|
||||
}
|
||||
break;
|
||||
|
||||
case "inactive":
|
||||
timelineView = inflater.inflate(R.layout.timeline_item_inactive, timelineContainer, false);
|
||||
TextView tvInactiveTitle = timelineView.findViewById(R.id.tvTimelineTitle);
|
||||
TextView tvInactiveTime = timelineView.findViewById(R.id.tvTimelineTime);
|
||||
TextView tvInactiveExitTime = timelineView.findViewById(R.id.tvTimelineExitTime);
|
||||
|
||||
if (tvInactiveTitle != null) tvInactiveTitle.setText(wahanaName);
|
||||
if (tvInactiveTime != null) tvInactiveTime.setText(waktuMasuk);
|
||||
if (tvInactiveExitTime != null) tvInactiveExitTime.setText(waktuKeluar);
|
||||
break;
|
||||
|
||||
case "entry":
|
||||
timelineView = inflater.inflate(R.layout.timeline_item_entry, timelineContainer, false);
|
||||
TextView tvEntryTimeView = timelineView.findViewById(R.id.tvTimelineTime); // Ubah nama variabel agar tidak konflik
|
||||
if (tvEntryTimeView != null) tvEntryTimeView.setText(waktuMasuk);
|
||||
break;
|
||||
|
||||
case "placeholder":
|
||||
timelineView = inflater.inflate(R.layout.timeline_item_placeholder, timelineContainer, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
Log.w(TAG, "Unknown timeline type: " + type);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isLastItem && timelineView != null) {
|
||||
View timelineLine = timelineView.findViewById(R.id.timelineLine);
|
||||
if (timelineLine != null) {
|
||||
timelineLine.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
return timelineView;
|
||||
}
|
||||
|
||||
private void animateTimelineIn() {
|
||||
for (int i = 0; i < timelineContainer.getChildCount(); i++) {
|
||||
View child = timelineContainer.getChildAt(i);
|
||||
child.setAlpha(0f);
|
||||
child.setTranslationX(100f);
|
||||
|
||||
ObjectAnimator fadeIn = ObjectAnimator.ofFloat(child, "alpha", 0f, 1f);
|
||||
ObjectAnimator slideIn = ObjectAnimator.ofFloat(child, "translationX", 100f, 0f);
|
||||
|
||||
fadeIn.setDuration(400);
|
||||
slideIn.setDuration(400);
|
||||
fadeIn.setStartDelay(i * 100);
|
||||
slideIn.setStartDelay(i * 100);
|
||||
fadeIn.setInterpolator(new AccelerateDecelerateInterpolator());
|
||||
slideIn.setInterpolator(new AccelerateDecelerateInterpolator());
|
||||
|
||||
fadeIn.start();
|
||||
slideIn.start();
|
||||
}
|
||||
}
|
||||
|
||||
private void setupLogout() {
|
||||
cardLogout.setOnClickListener(v -> {
|
||||
ObjectAnimator scaleX = ObjectAnimator.ofFloat(cardLogout, "scaleX", 1f, 0.95f, 1f);
|
||||
ObjectAnimator scaleY = ObjectAnimator.ofFloat(cardLogout, "scaleY", 1f, 0.95f, 1f);
|
||||
scaleX.setDuration(150);
|
||||
scaleY.setDuration(150);
|
||||
scaleX.start();
|
||||
scaleY.start();
|
||||
|
||||
performLogout();
|
||||
});
|
||||
}
|
||||
|
||||
private void performLogout() {
|
||||
new CustomAlertDialog.Builder(this)
|
||||
.setTitle("Konfirmasi Logout")
|
||||
.setMessage("Apakah Anda yakin ingin keluar dari akun ini?")
|
||||
.setAlertType(CustomAlertDialog.AlertType.INFO)
|
||||
.setPositiveButton("YA", new CustomAlertDialog.OnAlertClickListener() {
|
||||
@Override
|
||||
public void onPositiveClick() {
|
||||
showSuccessAnimation();
|
||||
new Handler().postDelayed(() -> {
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.clear();
|
||||
editor.apply();
|
||||
|
||||
stopPollingHomeData();
|
||||
|
||||
Intent intent = new Intent(HomeActivity.this, LoginActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNegativeClick() {
|
||||
// Not used
|
||||
}
|
||||
})
|
||||
.setNegativeButton("TIDAK", null)
|
||||
.setCancelable(false)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showSuccessAnimation() {
|
||||
ObjectAnimator.ofFloat(cardLogout, "alpha", 1f, 0.5f, 1f)
|
||||
.setDuration(300)
|
||||
.start();
|
||||
}
|
||||
|
||||
private void setupAnimations() {
|
||||
ObjectAnimator fadeInTitle = ObjectAnimator.ofFloat(llTitle, "alpha", 0f, 1f);
|
||||
fadeInTitle.setDuration(800);
|
||||
fadeInTitle.setInterpolator(new AccelerateDecelerateInterpolator());
|
||||
fadeInTitle.start();
|
||||
|
||||
ObjectAnimator fadeInStatistik = ObjectAnimator.ofFloat(llStatistik, "alpha", 0f, 1f);
|
||||
fadeInStatistik.setDuration(800);
|
||||
fadeInStatistik.setInterpolator(new AccelerateDecelerateInterpolator());
|
||||
fadeInStatistik.start();
|
||||
|
||||
ObjectAnimator fadeInLogout = ObjectAnimator.ofFloat(llLogout, "alpha", 0f, 1f);
|
||||
fadeInLogout.setDuration(800);
|
||||
fadeInLogout.setInterpolator(new AccelerateDecelerateInterpolator());
|
||||
fadeInLogout.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
// Karena CustomAlertDialog Anda sudah punya method show() dan dismiss() publik,
|
||||
// kita bisa menggunakannya secara langsung. Namun, tidak ada method isShowing() publik.
|
||||
// Jadi, kita berasumsi jika activeExitDialog tidak null, berarti dia sedang ditampilkan.
|
||||
// Pilihan yang lebih baik adalah menambahkan method isShowing() publik di CustomAlertDialog.
|
||||
if (activeExitDialog != null) {
|
||||
return; // Jangan lakukan apa-apa jika dialog notifikasi anak keluar sedang tampil
|
||||
}
|
||||
|
||||
new CustomAlertDialog.Builder(this)
|
||||
.setTitle("Konfirmasi Logout")
|
||||
.setMessage("Apakah Anda yakin ingin keluar dari akun ini?")
|
||||
.setAlertType(CustomAlertDialog.AlertType.INFO)
|
||||
.setPositiveButton("KELUAR", new CustomAlertDialog.OnAlertClickListener() {
|
||||
@Override
|
||||
public void onPositiveClick() {
|
||||
showSuccessAnimation();
|
||||
new Handler().postDelayed(() -> {
|
||||
stopPollingHomeData();
|
||||
Intent intent = new Intent(HomeActivity.this, LoginActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNegativeClick() {
|
||||
// Not used
|
||||
}
|
||||
})
|
||||
.setNegativeButton("BATAL", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showError(String message) {
|
||||
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
// Inisialisasi HOME_URL dari IPManager di onResume
|
||||
HOME_URL = ipManager.getURL("home.php");
|
||||
Log.d(TAG, "Using API URL: " + HOME_URL);
|
||||
|
||||
if (idAnak != 0) {
|
||||
startPollingHomeData();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
stopPollingHomeData();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
stopPollingHomeData();
|
||||
if (vibrator != null) {
|
||||
vibrator.cancel();
|
||||
}
|
||||
if (activeExitDialog != null) {
|
||||
activeExitDialog.dismiss();
|
||||
activeExitDialog = null;
|
||||
}
|
||||
// Batalkan semua request Volley untuk Activity ini saat dihancurkan
|
||||
if (requestQueue != null) {
|
||||
requestQueue.cancelAll(TAG);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
package com.tugasakhir.monikids.view;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
import com.tugasakhir.monikids.R;
|
||||
import com.tugasakhir.monikids.utils.IPManager;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class IPSettingsActivity extends AppCompatActivity {
|
||||
|
||||
private IPManager ipManager;
|
||||
private TextView tvCurrentIPDisplay;
|
||||
private TextInputEditText etNewIP;
|
||||
private TextInputLayout inputLayoutIP;
|
||||
private Button btnSaveIP, btnTestConnection;
|
||||
private Button btnLocalhost, btnEmulator, btnDefault;
|
||||
private TextView tvConnectionStatus;
|
||||
private ImageButton btnBack;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_ip_settings);
|
||||
|
||||
initViews();
|
||||
initIPManager();
|
||||
setupClickListeners();
|
||||
updateCurrentIPDisplay();
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
btnBack = findViewById(R.id.btnBack);
|
||||
tvCurrentIPDisplay = findViewById(R.id.tvCurrentIPDisplay);
|
||||
etNewIP = findViewById(R.id.etNewIP);
|
||||
inputLayoutIP = findViewById(R.id.inputLayoutIP);
|
||||
btnSaveIP = findViewById(R.id.btnSaveIP);
|
||||
btnTestConnection = findViewById(R.id.btnTestConnection);
|
||||
btnLocalhost = findViewById(R.id.btnLocalhost);
|
||||
btnEmulator = findViewById(R.id.btnEmulator);
|
||||
btnDefault = findViewById(R.id.btnDefault);
|
||||
tvConnectionStatus = findViewById(R.id.tvConnectionStatus);
|
||||
}
|
||||
|
||||
private void initIPManager() {
|
||||
ipManager = IPManager.getInstance(this);
|
||||
}
|
||||
|
||||
private void setupClickListeners() {
|
||||
btnBack.setOnClickListener(v -> finish());
|
||||
|
||||
btnSaveIP.setOnClickListener(v -> saveNewIP());
|
||||
|
||||
btnTestConnection.setOnClickListener(v -> testConnection());
|
||||
|
||||
btnLocalhost.setOnClickListener(v -> {
|
||||
etNewIP.setText(IPManager.QuickIPs.LOCALHOST);
|
||||
clearError();
|
||||
});
|
||||
|
||||
btnEmulator.setOnClickListener(v -> {
|
||||
etNewIP.setText(IPManager.QuickIPs.EMULATOR);
|
||||
clearError();
|
||||
});
|
||||
|
||||
btnDefault.setOnClickListener(v -> {
|
||||
etNewIP.setText(IPManager.QuickIPs.DEFAULT);
|
||||
clearError();
|
||||
});
|
||||
}
|
||||
|
||||
private void updateCurrentIPDisplay() {
|
||||
String currentIP = ipManager.getCurrentIP();
|
||||
tvCurrentIPDisplay.setText(currentIP);
|
||||
etNewIP.setText(currentIP);
|
||||
}
|
||||
|
||||
private void saveNewIP() {
|
||||
String newIP = etNewIP.getText().toString().trim();
|
||||
|
||||
if (newIP.isEmpty()) {
|
||||
showError("IP tidak boleh kosong");
|
||||
return;
|
||||
}
|
||||
|
||||
boolean success = ipManager.setServerIP(newIP);
|
||||
|
||||
if (success) {
|
||||
updateCurrentIPDisplay();
|
||||
clearError();
|
||||
Toast.makeText(this, "IP berhasil disimpan: " + newIP, Toast.LENGTH_SHORT).show();
|
||||
|
||||
// Broadcast change to other activities if needed
|
||||
broadcastIPChange();
|
||||
} else {
|
||||
showError("Format IP tidak valid");
|
||||
}
|
||||
}
|
||||
|
||||
private void testConnection() {
|
||||
String testIP = etNewIP.getText().toString().trim();
|
||||
|
||||
if (testIP.isEmpty()) {
|
||||
showError("Masukkan IP terlebih dahulu");
|
||||
return;
|
||||
}
|
||||
|
||||
btnTestConnection.setEnabled(false);
|
||||
btnTestConnection.setText("TESTING...");
|
||||
tvConnectionStatus.setVisibility(View.VISIBLE);
|
||||
tvConnectionStatus.setText("Menguji koneksi...");
|
||||
tvConnectionStatus.setTextColor(Color.GRAY);
|
||||
|
||||
new TestConnectionTask().execute(testIP);
|
||||
}
|
||||
|
||||
private void showError(String message) {
|
||||
inputLayoutIP.setError(message);
|
||||
}
|
||||
|
||||
private void clearError() {
|
||||
inputLayoutIP.setError(null);
|
||||
}
|
||||
|
||||
private void broadcastIPChange() {
|
||||
// Optional: Send broadcast to notify other activities about IP change
|
||||
// Intent intent = new Intent("IP_CHANGED");
|
||||
// sendBroadcast(intent);
|
||||
}
|
||||
|
||||
private class TestConnectionTask extends AsyncTask<String, Void, Boolean> {
|
||||
private String testIP;
|
||||
private String errorMessage = "";
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(String... params) {
|
||||
testIP = params[0];
|
||||
try {
|
||||
String testUrl = "http://" + testIP + "/playground_api/";
|
||||
URL url = new URL(testUrl);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setConnectTimeout(5000); // 5 seconds timeout
|
||||
connection.setReadTimeout(5000);
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
connection.disconnect();
|
||||
|
||||
return responseCode == HttpURLConnection.HTTP_OK ||
|
||||
responseCode == HttpURLConnection.HTTP_NOT_FOUND ||
|
||||
responseCode == HttpURLConnection.HTTP_FORBIDDEN;
|
||||
|
||||
} catch (IOException e) {
|
||||
errorMessage = e.getMessage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean success) {
|
||||
btnTestConnection.setEnabled(true);
|
||||
btnTestConnection.setText("UJI KONEKSI");
|
||||
|
||||
if (success) {
|
||||
tvConnectionStatus.setText("✓ Koneksi berhasil ke " + testIP);
|
||||
tvConnectionStatus.setTextColor(Color.GREEN);
|
||||
} else {
|
||||
tvConnectionStatus.setText("✗ Koneksi gagal: " + errorMessage);
|
||||
tvConnectionStatus.setTextColor(Color.RED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,627 @@
|
|||
package com.tugasakhir.monikids.view;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.InputType;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.android.volley.Request;
|
||||
import com.android.volley.RequestQueue;
|
||||
import com.android.volley.Response;
|
||||
import com.android.volley.VolleyError;
|
||||
import com.android.volley.toolbox.JsonObjectRequest;
|
||||
import com.android.volley.toolbox.Volley;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
import com.tugasakhir.monikids.R;
|
||||
import com.tugasakhir.monikids.utils.CustomAlertDialog;
|
||||
import com.tugasakhir.monikids.utils.IPManager; // Ensure this import is present
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class LoginActivity extends AppCompatActivity {
|
||||
|
||||
private Button btnLoginByName, btnLoginByCode, btnLoginByPhone, btnLogin;
|
||||
private TextInputEditText etLoginInput;
|
||||
private LinearLayout llLogin;
|
||||
private TextInputLayout inputLayout;
|
||||
private RequestQueue requestQueue;
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
private IPManager ipManager; // IPManager instance
|
||||
private TextView tvCurrentIP; // TextView to display current IP
|
||||
private ImageButton btnIPSettings; // Button for IP settings
|
||||
|
||||
// Remove hardcoded BASE_URL, it will be retrieved from IPManager
|
||||
// private static final String BASE_URL = "http://192.168.18.120/playground_api/";
|
||||
private String LOGIN_URL; // This will be set dynamically
|
||||
private static final String TAG = "LoginActivity";
|
||||
private static final String PREFS_NAME = "MoniKidsPrefs";
|
||||
|
||||
private enum LoginType {
|
||||
NAME, CODE, PHONE
|
||||
}
|
||||
|
||||
private LoginType currentLoginType = LoginType.NAME;
|
||||
private boolean isLoading = false;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_login);
|
||||
|
||||
initViews();
|
||||
initNetworkAndIPManager(); // Renamed to include IPManager init
|
||||
setupClickListeners();
|
||||
updateInputForLoginType();
|
||||
setupAnimations();
|
||||
updateCurrentIPDisplay(); // Display current IP on create
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
updateCurrentIPDisplay(); // Update IP display when returning to this activity
|
||||
// Re-initialize LOGIN_URL to ensure it reflects potential IP changes
|
||||
LOGIN_URL = ipManager.getURL("login.php");
|
||||
Log.d(TAG, "Final LOGIN_URL: " + LOGIN_URL);
|
||||
Log.d(TAG, "Current IP from IPManager: " + ipManager.getCurrentIP());
|
||||
Log.d(TAG, "Base URL: " + ipManager.getBaseURL());
|
||||
Log.d(TAG, "Using API URL: " + LOGIN_URL);
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
btnLoginByName = findViewById(R.id.btnLoginByName);
|
||||
btnLoginByCode = findViewById(R.id.btnLoginByCode);
|
||||
btnLoginByPhone = findViewById(R.id.btnLoginByPhone);
|
||||
btnLogin = findViewById(R.id.btnLogin);
|
||||
etLoginInput = findViewById(R.id.etLoginInput);
|
||||
inputLayout = findViewById(R.id.inputLayout);
|
||||
llLogin = findViewById(R.id.llLogin);
|
||||
|
||||
// Initialize IP-related views
|
||||
tvCurrentIP = findViewById(R.id.tvCurrentIP);
|
||||
btnIPSettings = findViewById(R.id.btnIPSettings);
|
||||
}
|
||||
|
||||
private void initNetworkAndIPManager() { // Renamed method
|
||||
requestQueue = Volley.newRequestQueue(this);
|
||||
sharedPreferences = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
|
||||
ipManager = IPManager.getInstance(this); // Initialize IPManager
|
||||
|
||||
// Set the LOGIN_URL dynamically using IPManager
|
||||
LOGIN_URL = ipManager.getURL("login.php");
|
||||
Log.d(TAG, "Initialized with API URL: " + LOGIN_URL);
|
||||
}
|
||||
|
||||
private void setupClickListeners() {
|
||||
btnLoginByName.setOnClickListener(v -> {
|
||||
if (currentLoginType != LoginType.NAME) {
|
||||
animateButtonSelection(v);
|
||||
selectLoginType(LoginType.NAME);
|
||||
}
|
||||
});
|
||||
|
||||
btnLoginByCode.setOnClickListener(v -> {
|
||||
if (currentLoginType != LoginType.CODE) {
|
||||
animateButtonSelection(v);
|
||||
selectLoginType(LoginType.CODE);
|
||||
}
|
||||
});
|
||||
|
||||
btnLoginByPhone.setOnClickListener(v -> {
|
||||
if (currentLoginType != LoginType.PHONE) {
|
||||
animateButtonSelection(v);
|
||||
selectLoginType(LoginType.PHONE);
|
||||
}
|
||||
});
|
||||
|
||||
btnLogin.setOnClickListener(v -> {
|
||||
if (!isLoading) {
|
||||
animateButtonPress(v);
|
||||
performLogin();
|
||||
}
|
||||
});
|
||||
|
||||
// Click listener for IP Settings button
|
||||
btnIPSettings.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(LoginActivity.this, IPSettingsActivity.class);
|
||||
startActivity(intent);
|
||||
});
|
||||
}
|
||||
|
||||
private void setupAnimations() {
|
||||
// Add entrance animations
|
||||
ObjectAnimator fadeIn = ObjectAnimator.ofFloat(llLogin, "alpha", 0f, 1f);
|
||||
fadeIn.setDuration(800);
|
||||
fadeIn.setInterpolator(new AccelerateDecelerateInterpolator());
|
||||
fadeIn.start();
|
||||
}
|
||||
|
||||
private void animateButtonSelection(View button) {
|
||||
ObjectAnimator scaleX = ObjectAnimator.ofFloat(button, "scaleX", 1f, 1.1f, 1f);
|
||||
ObjectAnimator scaleY = ObjectAnimator.ofFloat(button, "scaleY", 1f, 1.1f, 1f);
|
||||
scaleX.setDuration(200);
|
||||
scaleY.setDuration(200);
|
||||
scaleX.start();
|
||||
scaleY.start();
|
||||
}
|
||||
|
||||
private void animateButtonPress(View button) {
|
||||
ObjectAnimator scaleX = ObjectAnimator.ofFloat(button, "scaleX", 1f, 0.95f, 1f);
|
||||
ObjectAnimator scaleY = ObjectAnimator.ofFloat(button, "scaleY", 1f, 0.95f, 1f);
|
||||
scaleX.setDuration(150);
|
||||
scaleY.setDuration(150);
|
||||
scaleX.start();
|
||||
scaleY.start();
|
||||
}
|
||||
|
||||
private void selectLoginType(LoginType loginType) {
|
||||
currentLoginType = loginType;
|
||||
|
||||
// Reset all buttons to unselected state
|
||||
resetButtonStyles();
|
||||
|
||||
// Set selected button style with animation
|
||||
switch (loginType) {
|
||||
case NAME:
|
||||
btnLoginByName.setBackgroundResource(R.drawable.login_option_selected);
|
||||
btnLoginByName.setTextColor(ContextCompat.getColor(this, android.R.color.white));
|
||||
break;
|
||||
case CODE:
|
||||
btnLoginByCode.setBackgroundResource(R.drawable.login_option_selected);
|
||||
btnLoginByCode.setTextColor(ContextCompat.getColor(this, android.R.color.white));
|
||||
break;
|
||||
case PHONE:
|
||||
btnLoginByPhone.setBackgroundResource(R.drawable.login_option_selected);
|
||||
btnLoginByPhone.setTextColor(ContextCompat.getColor(this, android.R.color.white));
|
||||
break;
|
||||
}
|
||||
|
||||
updateInputForLoginType();
|
||||
}
|
||||
|
||||
private void resetButtonStyles() {
|
||||
int unselectedColor = ContextCompat.getColor(this, R.color.purple_primary);
|
||||
|
||||
btnLoginByName.setBackgroundResource(R.drawable.login_option_unselected);
|
||||
btnLoginByName.setTextColor(unselectedColor);
|
||||
|
||||
btnLoginByCode.setBackgroundResource(R.drawable.login_option_unselected);
|
||||
btnLoginByCode.setTextColor(unselectedColor);
|
||||
|
||||
btnLoginByPhone.setBackgroundResource(R.drawable.login_option_unselected);
|
||||
btnLoginByPhone.setTextColor(unselectedColor);
|
||||
}
|
||||
|
||||
private void updateInputForLoginType() {
|
||||
etLoginInput.setText(""); // Clear previous input
|
||||
inputLayout.setError(null); // Clear any previous errors
|
||||
|
||||
// Animate input field change
|
||||
ObjectAnimator fadeOut = ObjectAnimator.ofFloat(inputLayout, "alpha", 1f, 0.7f, 1f);
|
||||
fadeOut.setDuration(300);
|
||||
fadeOut.start();
|
||||
|
||||
switch (currentLoginType) {
|
||||
case NAME:
|
||||
inputLayout.setHint("Masukkan nama anak");
|
||||
inputLayout.setStartIconDrawable(R.drawable.ic_child);
|
||||
etLoginInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PERSON_NAME);
|
||||
break;
|
||||
case CODE:
|
||||
inputLayout.setHint("Masukkan kode gelang");
|
||||
inputLayout.setStartIconDrawable(R.drawable.ic_code);
|
||||
etLoginInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL);
|
||||
break;
|
||||
case PHONE:
|
||||
inputLayout.setHint("Masukkan nomor HP");
|
||||
inputLayout.setStartIconDrawable(R.drawable.ic_phone);
|
||||
etLoginInput.setInputType(InputType.TYPE_CLASS_PHONE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCurrentIPDisplay() {
|
||||
// Update the TextView with the current IP from IPManager
|
||||
if (tvCurrentIP != null && ipManager != null) {
|
||||
tvCurrentIP.setText(ipManager.getCurrentIP());
|
||||
}
|
||||
}
|
||||
|
||||
private void performLogin() {
|
||||
String input = etLoginInput.getText().toString().trim();
|
||||
|
||||
if (input.isEmpty()) {
|
||||
String errorMessage = getEmptyErrorMessage();
|
||||
|
||||
new CustomAlertDialog.Builder(this)
|
||||
.setTitle("Input Kosong")
|
||||
.setMessage(errorMessage)
|
||||
.setAlertType(CustomAlertDialog.AlertType.WARNING)
|
||||
.setPositiveButton("OK", new CustomAlertDialog.OnAlertClickListener() {
|
||||
@Override
|
||||
public void onPositiveClick() {
|
||||
etLoginInput.requestFocus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNegativeClick() {
|
||||
// Not used
|
||||
}
|
||||
})
|
||||
.show();
|
||||
|
||||
inputLayout.setError(errorMessage);
|
||||
animateErrorShake();
|
||||
return;
|
||||
}
|
||||
|
||||
inputLayout.setError(null);
|
||||
|
||||
if (!validateInput(input)) {
|
||||
animateErrorShake();
|
||||
return;
|
||||
}
|
||||
|
||||
setLoadingState(true);
|
||||
|
||||
performAPILogin(input);
|
||||
}
|
||||
|
||||
private void performAPILogin(String input) {
|
||||
try {
|
||||
JSONObject jsonBody = new JSONObject();
|
||||
jsonBody.put("login_type", currentLoginType.name());
|
||||
jsonBody.put("input_value", input);
|
||||
|
||||
JsonObjectRequest request = new JsonObjectRequest(Request.Method.POST, LOGIN_URL, jsonBody, new Response.Listener<JSONObject>() {
|
||||
@Override
|
||||
public void onResponse(JSONObject response) {
|
||||
handleLoginResponse(response, input);
|
||||
}
|
||||
}, new Response.ErrorListener() {
|
||||
@Override
|
||||
public void onErrorResponse(VolleyError error) {
|
||||
handleLoginError(error);
|
||||
}
|
||||
});
|
||||
|
||||
requestQueue.add(request);
|
||||
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Error creating JSON request", e);
|
||||
setLoadingState(false);
|
||||
showErrorAlert("Error", "Terjadi kesalahan dalam membuat request: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleLoginResponse(JSONObject response, String input) {
|
||||
setLoadingState(false);
|
||||
|
||||
try {
|
||||
boolean success = response.getBoolean("success");
|
||||
String message = response.getString("message");
|
||||
|
||||
if (success) {
|
||||
new CustomAlertDialog.Builder(this)
|
||||
.setTitle("Login Berhasil!")
|
||||
.setMessage("Selamat datang! Anda akan diarahkan ke halaman utama.")
|
||||
.setAlertType(CustomAlertDialog.AlertType.SUCCESS)
|
||||
.setPositiveButton("LANJUTKAN", new CustomAlertDialog.OnAlertClickListener() {
|
||||
@Override
|
||||
public void onPositiveClick() {
|
||||
try {
|
||||
JSONObject data = response.getJSONObject("data");
|
||||
saveLoginData(data);
|
||||
|
||||
showSuccessAnimation();
|
||||
|
||||
new Handler().postDelayed(() -> {
|
||||
navigateToHomeActivity(data);
|
||||
}, 500);
|
||||
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Error parsing data", e);
|
||||
showErrorAlert("Error", "Terjadi kesalahan dalam memproses data login.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNegativeClick() {
|
||||
// Not used
|
||||
}
|
||||
})
|
||||
.setCancelable(false)
|
||||
.show();
|
||||
|
||||
} else {
|
||||
new CustomAlertDialog.Builder(this)
|
||||
.setTitle("Login Gagal")
|
||||
.setMessage(message)
|
||||
.setAlertType(CustomAlertDialog.AlertType.ERROR)
|
||||
.setPositiveButton("COBA LAGI", new CustomAlertDialog.OnAlertClickListener() {
|
||||
@Override
|
||||
public void onPositiveClick() {
|
||||
etLoginInput.setText("");
|
||||
etLoginInput.requestFocus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNegativeClick() {
|
||||
// Not used
|
||||
}
|
||||
})
|
||||
.show();
|
||||
|
||||
inputLayout.setError(message);
|
||||
animateErrorShake();
|
||||
}
|
||||
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Error parsing login response", e);
|
||||
showErrorAlert("Error", "Terjadi kesalahan dalam memproses data. Silakan coba lagi.");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleLoginError(VolleyError error) {
|
||||
setLoadingState(false);
|
||||
|
||||
String errorMessage = "Koneksi gagal. Pastikan server berjalan dan IP server diatur dengan benar."; // Updated message
|
||||
|
||||
if (error.networkResponse != null) {
|
||||
try {
|
||||
String responseBody = new String(error.networkResponse.data, "utf-8");
|
||||
JSONObject data = new JSONObject(responseBody);
|
||||
errorMessage = data.getString("message");
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error parsing error response", e);
|
||||
}
|
||||
} else if (error.getCause() instanceof java.net.ConnectException) {
|
||||
errorMessage = "Tidak dapat terhubung ke server. Pastikan IP server sudah benar dan server sedang berjalan.";
|
||||
} else if (error.getCause() instanceof java.net.UnknownHostException) {
|
||||
errorMessage = "IP server tidak dikenal. Pastikan IP yang dimasukkan valid.";
|
||||
}
|
||||
|
||||
|
||||
Log.e(TAG, "Login API Error: " + error.toString());
|
||||
Log.e(TAG, "Error details - NetworkResponse: " + error.networkResponse);
|
||||
Log.e(TAG, "Error details - Cause: " + error.getCause());
|
||||
Log.e(TAG, "Full URL being called: " + LOGIN_URL);
|
||||
|
||||
new CustomAlertDialog.Builder(this)
|
||||
.setTitle("Koneksi Bermasalah")
|
||||
.setMessage(errorMessage)
|
||||
.setAlertType(CustomAlertDialog.AlertType.ERROR)
|
||||
.setPositiveButton("COBA LAGI", new CustomAlertDialog.OnAlertClickListener() {
|
||||
@Override
|
||||
public void onPositiveClick() {
|
||||
String input = etLoginInput.getText().toString().trim();
|
||||
if (!input.isEmpty()) {
|
||||
performLogin();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNegativeClick() {
|
||||
// Not used
|
||||
}
|
||||
})
|
||||
.setNegativeButton("BATAL", null) // Just close dialog
|
||||
.show();
|
||||
|
||||
inputLayout.setError(errorMessage);
|
||||
animateErrorShake();
|
||||
}
|
||||
|
||||
private void saveLoginData(JSONObject data) {
|
||||
try {
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putInt("id_anak", data.getInt("id_anak"));
|
||||
editor.putString("nama", data.getString("nama"));
|
||||
editor.putString("kode_gelang", data.getString("kode_gelang"));
|
||||
editor.putString("login_type", data.getString("login_type"));
|
||||
editor.putLong("login_time", System.currentTimeMillis());
|
||||
editor.apply();
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Error saving login data", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void showSuccessAnimation() {
|
||||
ObjectAnimator successScale = ObjectAnimator.ofFloat(btnLogin, "scaleX", 1f, 1.1f, 1f);
|
||||
ObjectAnimator successScaleY = ObjectAnimator.ofFloat(btnLogin, "scaleY", 1f, 1.1f, 1f);
|
||||
successScale.setDuration(300);
|
||||
successScaleY.setDuration(300);
|
||||
successScale.start();
|
||||
successScaleY.start();
|
||||
}
|
||||
|
||||
private void navigateToHomeActivity(JSONObject data) {
|
||||
try {
|
||||
Intent intent = new Intent(LoginActivity.this, HomeActivity.class);
|
||||
intent.putExtra("id_anak", data.getInt("id_anak"));
|
||||
intent.putExtra("nama", data.getString("nama"));
|
||||
intent.putExtra("kode_gelang", data.getString("kode_gelang"));
|
||||
intent.putExtra("login_type", data.getString("login_type"));
|
||||
startActivity(intent);
|
||||
finish();
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Error navigating to home activity", e);
|
||||
showErrorAlert("Error", "Terjadi kesalahan saat navigasi ke halaman utama.");
|
||||
}
|
||||
}
|
||||
|
||||
private String getEmptyErrorMessage() {
|
||||
switch (currentLoginType) {
|
||||
case NAME:
|
||||
return "Nama anak tidak boleh kosong";
|
||||
case CODE:
|
||||
return "Kode gelang tidak boleh kosong";
|
||||
case PHONE:
|
||||
return "Nomor HP tidak boleh kosong";
|
||||
default:
|
||||
return "Input tidak boleh kosong";
|
||||
}
|
||||
}
|
||||
|
||||
private void animateErrorShake() {
|
||||
ObjectAnimator shake = ObjectAnimator.ofFloat(inputLayout, "translationX", 0, 25, -25, 25, -25, 15, -15, 6, -6, 0);
|
||||
shake.setDuration(600);
|
||||
shake.start();
|
||||
}
|
||||
|
||||
private boolean validateInput(String input) {
|
||||
switch (currentLoginType) {
|
||||
case NAME:
|
||||
if (input.length() < 2) {
|
||||
showValidationAlert("Nama minimal 2 karakter");
|
||||
return false;
|
||||
}
|
||||
if (!input.matches("^[a-zA-Z\\s]+$")) {
|
||||
showValidationAlert("Nama hanya boleh berisi huruf dan spasi");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case CODE:
|
||||
if (input.length() < 4) {
|
||||
showValidationAlert("Kode gelang minimal 4 karakter");
|
||||
return false;
|
||||
}
|
||||
if (!input.matches("^[A-Za-z0-9]+$")) {
|
||||
showValidationAlert("Kode hanya boleh berisi huruf dan angka");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case PHONE:
|
||||
// Regex for common phone number formats, allows +, -, (, ), space and digits, min 10 digits
|
||||
if (!input.matches("^[0-9+\\-\\s()]+$") || input.length() < 10) {
|
||||
showValidationAlert("Format nomor HP tidak valid (minimal 10 digit)");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void showValidationAlert(String message) {
|
||||
new CustomAlertDialog.Builder(this)
|
||||
.setTitle("Input Tidak Valid")
|
||||
.setMessage(message)
|
||||
.setAlertType(CustomAlertDialog.AlertType.WARNING)
|
||||
.setPositiveButton("OK", new CustomAlertDialog.OnAlertClickListener() {
|
||||
@Override
|
||||
public void onPositiveClick() {
|
||||
etLoginInput.requestFocus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNegativeClick() {
|
||||
// Not used
|
||||
}
|
||||
})
|
||||
.show();
|
||||
|
||||
inputLayout.setError(message);
|
||||
}
|
||||
|
||||
private void setLoadingState(boolean loading) {
|
||||
isLoading = loading;
|
||||
btnLogin.setEnabled(!loading);
|
||||
|
||||
if (loading) {
|
||||
btnLogin.setText("MEMUAT...");
|
||||
ObjectAnimator pulseAnimator = ObjectAnimator.ofFloat(btnLogin, "alpha", 1f, 0.7f, 1f);
|
||||
pulseAnimator.setDuration(800);
|
||||
pulseAnimator.setRepeatCount(ValueAnimator.INFINITE);
|
||||
pulseAnimator.start();
|
||||
} else {
|
||||
btnLogin.setText("MASUK");
|
||||
btnLogin.clearAnimation();
|
||||
btnLogin.setAlpha(1f);
|
||||
}
|
||||
}
|
||||
|
||||
private void showSuccessAlert(String title, String message) {
|
||||
new CustomAlertDialog.Builder(this)
|
||||
.setTitle(title)
|
||||
.setMessage(message)
|
||||
.setAlertType(CustomAlertDialog.AlertType.SUCCESS)
|
||||
.setPositiveButton("OK", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showErrorAlert(String title, String message) {
|
||||
new CustomAlertDialog.Builder(this)
|
||||
.setTitle(title)
|
||||
.setMessage(message)
|
||||
.setAlertType(CustomAlertDialog.AlertType.ERROR)
|
||||
.setPositiveButton("OK", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showWarningAlert(String title, String message) {
|
||||
new CustomAlertDialog.Builder(this)
|
||||
.setTitle(title)
|
||||
.setMessage(message)
|
||||
.setAlertType(CustomAlertDialog.AlertType.WARNING)
|
||||
.setPositiveButton("OK", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showInfoAlert(String title, String message) {
|
||||
new CustomAlertDialog.Builder(this)
|
||||
.setTitle(title)
|
||||
.setMessage(message)
|
||||
.setAlertType(CustomAlertDialog.AlertType.INFO)
|
||||
.setPositiveButton("OK", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
new CustomAlertDialog.Builder(this)
|
||||
.setTitle("Keluar Aplikasi")
|
||||
.setMessage("Apakah Anda yakin ingin keluar dari MoniKids?")
|
||||
.setAlertType(CustomAlertDialog.AlertType.INFO)
|
||||
.setPositiveButton("KELUAR", new CustomAlertDialog.OnAlertClickListener() {
|
||||
@Override
|
||||
public void onPositiveClick() {
|
||||
LoginActivity.super.onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNegativeClick() {
|
||||
// Not used
|
||||
}
|
||||
})
|
||||
.setNegativeButton("BATAL", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (btnLogin != null) {
|
||||
btnLogin.clearAnimation();
|
||||
}
|
||||
if (requestQueue != null) {
|
||||
requestQueue.cancelAll(TAG);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/accelerate_decelerate_interpolator">
|
||||
|
||||
<scale
|
||||
android:fromXScale="1.0"
|
||||
android:toXScale="0.95"
|
||||
android:fromYScale="1.0"
|
||||
android:toYScale="0.95"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:duration="100" />
|
||||
|
||||
<scale
|
||||
android:fromXScale="0.95"
|
||||
android:toXScale="1.0"
|
||||
android:fromYScale="0.95"
|
||||
android:toYScale="1.0"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:duration="100"
|
||||
android:startOffset="100" />
|
||||
|
||||
</set>
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/bounce_interpolator">
|
||||
|
||||
<scale
|
||||
android:fromXScale="1.0"
|
||||
android:toXScale="1.1"
|
||||
android:fromYScale="1.0"
|
||||
android:toYScale="1.1"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:duration="150" />
|
||||
|
||||
<scale
|
||||
android:fromXScale="1.1"
|
||||
android:toXScale="1.0"
|
||||
android:fromYScale="1.1"
|
||||
android:toYScale="1.0"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:duration="150"
|
||||
android:startOffset="150" />
|
||||
|
||||
</set>
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/accelerate_decelerate_interpolator">
|
||||
|
||||
<alpha
|
||||
android:fromAlpha="0.0"
|
||||
android:toAlpha="1.0"
|
||||
android:duration="800" />
|
||||
|
||||
<scale
|
||||
android:fromXScale="0.9"
|
||||
android:toXScale="1.0"
|
||||
android:fromYScale="0.9"
|
||||
android:toYScale="1.0"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:duration="800" />
|
||||
|
||||
</set>
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
|
||||
android:repeatCount="infinite"
|
||||
android:repeatMode="reverse">
|
||||
|
||||
<alpha
|
||||
android:fromAlpha="1.0"
|
||||
android:toAlpha="0.6"
|
||||
android:duration="800" />
|
||||
|
||||
<scale
|
||||
android:fromXScale="1.0"
|
||||
android:toXScale="1.05"
|
||||
android:fromYScale="1.0"
|
||||
android:toYScale="1.05"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:duration="800" />
|
||||
|
||||
</set>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<translate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:fromXDelta="0"
|
||||
android:toXDelta="10"
|
||||
android:duration="1000"
|
||||
android:interpolator="@anim/shake_interpolator" />
|
|
@ -0,0 +1,3 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:cycles="7" />
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<translate
|
||||
android:fromXDelta="100%"
|
||||
android:toXDelta="0%"
|
||||
android:duration="300"
|
||||
android:interpolator="@android:anim/decelerate_interpolator" />
|
||||
|
||||
<alpha
|
||||
android:fromAlpha="0.0"
|
||||
android:toAlpha="1.0"
|
||||
android:duration="300" />
|
||||
|
||||
</set>
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<translate
|
||||
android:fromXDelta="0%"
|
||||
android:toXDelta="-100%"
|
||||
android:duration="300"
|
||||
android:interpolator="@android:anim/accelerate_interpolator" />
|
||||
|
||||
<alpha
|
||||
android:fromAlpha="1.0"
|
||||
android:toAlpha="0.0"
|
||||
android:duration="300" />
|
||||
|
||||
</set>
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/accelerate_decelerate_interpolator">
|
||||
|
||||
<translate
|
||||
android:fromYDelta="100%"
|
||||
android:toYDelta="0%"
|
||||
android:duration="600" />
|
||||
|
||||
<alpha
|
||||
android:fromAlpha="0.0"
|
||||
android:toAlpha="1.0"
|
||||
android:duration="600" />
|
||||
|
||||
</set>
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/bounce_interpolator">
|
||||
|
||||
<scale
|
||||
android:fromXScale="1.0"
|
||||
android:toXScale="1.2"
|
||||
android:fromYScale="1.0"
|
||||
android:toYScale="1.2"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:duration="300" />
|
||||
|
||||
<scale
|
||||
android:fromXScale="1.2"
|
||||
android:toXScale="1.0"
|
||||
android:fromYScale="1.2"
|
||||
android:toYScale="1.0"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:duration="400"
|
||||
android:startOffset="300" />
|
||||
|
||||
</set>
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape>
|
||||
<solid android:color="#F0F0F0" />
|
||||
<corners android:radius="12dp" />
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="#6A5ACD" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<solid android:color="@android:color/white" />
|
||||
<corners android:radius="12dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#6A5ACD" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape>
|
||||
<gradient
|
||||
android:angle="135"
|
||||
android:endColor="#5A4FCF"
|
||||
android:startColor="#7B68EE" />
|
||||
<corners android:radius="12dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<gradient
|
||||
android:angle="135"
|
||||
android:endColor="#6A5ACD"
|
||||
android:startColor="#8A7FDC" />
|
||||
<corners android:radius="12dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@android:color/white" />
|
||||
<corners android:radius="20dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#E8E8E8" />
|
||||
</shape>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#F0F0F0" />
|
||||
<corners android:radius="50dp" />
|
||||
</shape>
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape>
|
||||
<solid android:color="#F3F4F6" />
|
||||
<corners android:radius="20dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<solid android:color="@android:color/white" />
|
||||
<corners android:radius="20dp" />
|
||||
<stroke android:width="1dp" android:color="#E5E7EB" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient
|
||||
android:startColor="#667EEA"
|
||||
android:centerColor="#F093FB"
|
||||
android:endColor="#F5576C"
|
||||
android:angle="0" />
|
||||
<corners
|
||||
android:bottomLeftRadius="20dp"
|
||||
android:bottomRightRadius="20dp" />
|
||||
</shape>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient
|
||||
android:startColor="#667EEA"
|
||||
android:endColor="#764BA2"
|
||||
android:angle="45" />
|
||||
<corners android:radius="28dp" />
|
||||
</shape>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/transparent" />
|
||||
<corners android:radius="16dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/purple_light" />
|
||||
</shape>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#EEF2FF" />
|
||||
<corners android:radius="12dp" />
|
||||
<stroke android:width="1dp" android:color="#C7D2FE" />
|
||||
</shape>
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<gradient
|
||||
android:startColor="@color/purple_primary"
|
||||
android:endColor="@color/primary_purple"
|
||||
android:type="radial"
|
||||
android:gradientRadius="8dp" />
|
||||
<size
|
||||
android:width="12dp"
|
||||
android:height="12dp" />
|
||||
</shape>
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<gradient
|
||||
android:startColor="@color/success_color"
|
||||
android:endColor="#00FFFFFF"
|
||||
android:type="radial"
|
||||
android:gradientRadius="9dp" />
|
||||
<size
|
||||
android:width="18dp"
|
||||
android:height="18dp" />
|
||||
</shape>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="@android:color/white" />
|
||||
<size
|
||||
android:width="8dp"
|
||||
android:height="8dp" />
|
||||
</shape>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="@color/text_secondary" />
|
||||
<size
|
||||
android:width="10dp"
|
||||
android:height="10dp" />
|
||||
</shape>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/text_secondary" />
|
||||
<size
|
||||
android:width="16dp"
|
||||
android:height="16dp" />
|
||||
</shape>
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/text_secondary"
|
||||
android:dashWidth="3dp"
|
||||
android:dashGap="2dp" />
|
||||
<size
|
||||
android:width="8dp"
|
||||
android:height="8dp" />
|
||||
</shape>
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/text_secondary"
|
||||
android:dashWidth="4dp"
|
||||
android:dashGap="2dp" />
|
||||
<size
|
||||
android:width="16dp"
|
||||
android:height="16dp" />
|
||||
</shape>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="@color/purple_primary" />
|
||||
<size
|
||||
android:width="16dp"
|
||||
android:height="16dp" />
|
||||
</shape>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- res/drawable/gradient_background.xml -->
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient
|
||||
android:angle="135"
|
||||
android:startColor="#8B5CF6"
|
||||
android:centerColor="#A78BFA"
|
||||
android:endColor="#C4B5FD"
|
||||
android:type="linear" />
|
||||
</shape>
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient
|
||||
android:startColor="@color/background_white"
|
||||
android:centerColor="@color/purple_soft"
|
||||
android:endColor="@color/background_white"
|
||||
android:angle="135" />
|
||||
<corners android:radius="24dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/border_light" />
|
||||
</shape>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/info_light" />
|
||||
<corners android:radius="12dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/info_color" />
|
||||
</shape>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512dp"
|
||||
android:height="512dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M10.957,12.354a0.5,0.5 0,0 1,0 -0.708l4.586,-4.585a1.5,1.5 0,0 0,-2.121 -2.122L8.836,9.525a3.505,3.505 0,0 0,0 4.95l4.586,4.586a1.5,1.5 0,0 0,2.121 -2.122Z"/>
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512dp"
|
||||
android:height="512dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M15.75,9.525 L11.164,4.939A1.5,1.5 0,0 0,9.043 7.061l4.586,4.585a0.5,0.5 0,0 1,0 0.708L9.043,16.939a1.5,1.5 0,0 0,2.121 2.122l4.586,-4.586A3.505,3.505 0,0 0,15.75 9.525Z"/>
|
||||
</vector>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M6,2v6h0.01L6,8.01 10,12l-4,4 0.01,0.01H6V22h12v-5.99h-0.01L18,16.01 14,12l4,-4 -0.01,-0.01H18V2H6zM16,16.5V20H8v-3.5l4,-4 4,4zM12,11.5 8,7.5V4h8v3.5l-4,4z" />
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512dp"
|
||||
android:height="512dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M12,24c-1.65,0 -3,-1.35 -3,-3L9,3c0,-1.65 1.35,-3 3,-3s3,1.35 3,3L15,21c0,1.65 -1.35,3 -3,3ZM21,24c-1.65,0 -3,-1.35 -3,-3L18,9c0,-1.65 1.35,-3 3,-3s3,1.35 3,3v12c0,1.65 -1.35,3 -3,3ZM3,24c-1.65,0 -3,-1.35 -3,-3v-6c0,-1.65 1.35,-3 3,-3s3,1.35 3,3v6c0,1.65 -1.35,3 -3,3Z"/>
|
||||
</vector>
|
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path
|
||||
android:fillColor="#4CAF50"
|
||||
android:pathData="m12,0C5.383,0 0,5.383 0,12s5.383,12 12,12 12,-5.383 12,-12S18.617,0 12,0ZM18.2,10.512l-4.426,4.345c-0.783,0.768 -1.791,1.151 -2.8,1.151 -0.998,0 -1.996,-0.376 -2.776,-1.129l-1.899,-1.867c-0.394,-0.387 -0.399,-1.02 -0.012,-1.414 0.386,-0.395 1.021,-0.4 1.414,-0.012l1.893,1.861c0.776,0.75 2.001,0.746 2.781,-0.018l4.425,-4.344c0.393,-0.388 1.024,-0.381 1.414,0.013 0.387,0.394 0.381,1.027 -0.014,1.414Z" />
|
||||
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="18dp"
|
||||
android:height="18dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="m13.916,15c0.506,0 0.795,0.612 0.447,0.98 -0.592,0.628 -1.431,1.02 -2.362,1.02s-1.77,-0.392 -2.362,-1.02c-0.347,-0.368 -0.059,-0.98 0.447,-0.98h3.831zM20.732,14.992c-0.477,1.04 -1.196,2.064 -2.082,2.966 1.245,1.371 2.028,3.059 2.261,4.918 0.036,0.284 -0.053,0.57 -0.242,0.786 -0.19,0.215 -0.463,0.338 -0.75,0.338h-15.859c-0.287,0 -0.561,-0.123 -0.75,-0.339 -0.189,-0.215 -0.278,-0.501 -0.242,-0.786 0.234,-1.864 1.021,-3.556 2.271,-4.928 -0.881,-0.9 -1.596,-1.919 -2.071,-2.955 -1.821,-0.12 -3.268,-1.641 -3.268,-3.492 0,-1.434 0.869,-2.692 2.149,-3.226 0.825,-4.761 4.947,-8.274 9.851,-8.274s9.026,3.513 9.851,8.274c1.28,0.533 2.149,1.792 2.149,3.226 0,1.852 -1.446,3.372 -3.268,3.492zM20.5,13c0.827,0 1.5,-0.673 1.5,-1.5 0,-0.723 -0.518,-1.342 -1.23,-1.472 -0.437,-0.079 -0.77,-0.437 -0.815,-0.878 -0.308,-2.913 -2.146,-5.307 -4.665,-6.44 0.497,1.268 0.236,2.765 -0.789,3.79 -1.381,1.381 -3.619,1.381 -5,0 -1.025,-1.025 -1.285,-2.521 -0.789,-3.79 -2.52,1.133 -4.358,3.527 -4.665,6.44 -0.046,0.441 -0.379,0.799 -0.815,0.878 -0.713,0.13 -1.23,0.749 -1.23,1.472 0,0.827 0.673,1.5 1.5,1.5 0.086,0 0.17,-0.014 0.252,-0.027 0.482,-0.09 0.954,0.19 1.117,0.651 0.916,2.598 4.001,5.378 7.131,5.378s6.215,-2.78 7.131,-5.378c0.163,-0.461 0.632,-0.741 1.117,-0.651 0.082,0.014 0.166,0.027 0.252,0.027zM9.5,10c-0.828,0 -1.5,0.672 -1.5,1.5s0.672,1.5 1.5,1.5 1.5,-0.672 1.5,-1.5 -0.672,-1.5 -1.5,-1.5zM14.5,10c-0.828,0 -1.5,0.672 -1.5,1.5s0.672,1.5 1.5,1.5 1.5,-0.672 1.5,-1.5 -0.672,-1.5 -1.5,-1.5z"/>
|
||||
</vector>
|
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="120dp"
|
||||
android:height="120dp"
|
||||
android:viewportWidth="120"
|
||||
android:viewportHeight="120">
|
||||
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M60,10C32.4,10 10,32.4 10,60s22.4,50 50,50s50,-22.4 50,-50S87.6,10 60,10z" />
|
||||
|
||||
<path
|
||||
android:fillColor="#FFD6A5"
|
||||
android:pathData="M60,25C68.3,25 75,31.7 75,40s-6.7,15 -15,15s-15,-6.7 -15,-15S51.7,25 60,25z" />
|
||||
|
||||
<path
|
||||
android:fillColor="#2D3748"
|
||||
android:pathData="M55,36C56.1,36 57,36.9 57,38C57,39.1 56.1,40 55,40C53.9,40 53,39.1 53,38C53,36.9 53.9,36 55,36z" />
|
||||
<path
|
||||
android:fillColor="#2D3748"
|
||||
android:pathData="M65,36C66.1,36 67,36.9 67,38C67,39.1 66.1,40 65,40C63.9,40 63,39.1 63,38C63,36.9 63.9,36 65,36z" />
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/transparent"
|
||||
android:strokeColor="#2D3748"
|
||||
android:strokeWidth="2"
|
||||
android:strokeLineCap="round"
|
||||
android:pathData="M55,45C57,47 63,47 65,45" />
|
||||
|
||||
<path
|
||||
android:fillColor="#8B4513"
|
||||
android:pathData="M45,30C45,25 50,20 60,20s15,5 15,10c0,3 -2,6 -5,8c-1,1 -2,2 -2,3v2c0,2 -3,2 -3,0v-2c0,-1 -1,-2 -2,-3C65,36 63,33 63,30h-6c0,3 -2,6 -5,8c-1,1 -2,2 -2,3v2c0,2 -3,2 -3,0v-2c0,-1 -1,-2 -2,-3z" />
|
||||
|
||||
<path
|
||||
android:fillColor="#7C3AED"
|
||||
android:pathData="M45,55L75,55C77,55 78,57 78,59L78,85C78,87 76,89 74,89L46,89C44,89 42,87 42,85L42,59C42,57 43,55 45,55z" />
|
||||
|
||||
<path
|
||||
android:fillColor="#FFD6A5"
|
||||
android:pathData="M40,60C38,60 36,62 36,64L36,74C36,76 38,78 40,78C42,78 44,76 44,74L44,64C44,62 42,60 40,60z" />
|
||||
<path
|
||||
android:fillColor="#FFD6A5"
|
||||
android:pathData="M80,60C82,60 84,62 84,64L84,74C84,76 82,78 80,78C78,78 76,76 76,74L76,64C76,62 78,60 80,60z" />
|
||||
|
||||
<path
|
||||
android:fillColor="#FF6B9D"
|
||||
android:pathData="M55,65C55,63 56,62 58,62C59,62 60,63 60,64C60,63 61,62 62,62C64,62 65,63 65,65C65,67 60,72 60,72S55,67 55,65z" />
|
||||
|
||||
</vector>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z" />
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="18dp"
|
||||
android:height="18dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="m0.473,7.687C1.889,4.953 6.44,3 12,3c5.386,0 9.831,1.832 11.393,4.431 -0.12,1.352 -1.226,2.67 -3.05,3.686 -1.926,-1.186 -5.193,-2.117 -8.343,-2.117 -3.171,0 -6.533,0.883 -8.446,2.071 -1.818,-1.008 -2.9,-2.261 -3.081,-3.384ZM12,15c-5.404,0 -9.941,-1.667 -12,-4.115v3.615c0,3.645 5.271,6.5 12,6.5s12,-2.855 12,-6.5v-3.615c-2.059,2.448 -6.596,4.115 -12,4.115Z"/>
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="#FF000000" android:pathData="M22.5,21h-1.5L21,6.5c0,-2.481 -2.019,-4.5 -4.5,-4.5h-1.258c-0.243,-0.363 -0.541,-0.694 -0.889,-0.979C13.307,0.163 11.946,-0.179 10.617,0.089l-3.598,0.72c-2.329,0.466 -4.02,2.527 -4.02,4.902v15.289L1.5,21c-0.829,0 -1.5,0.672 -1.5,1.5s0.671,1.5 1.5,1.5L22.5,24c0.829,0 1.5,-0.672 1.5,-1.5s-0.671,-1.5 -1.5,-1.5ZM18,6.5v14.5h-2L16,5h0.5c0.827,0 1.5,0.673 1.5,1.5ZM6,5.711c0,-0.95 0.676,-1.774 1.608,-1.961l3.598,-0.72c0.444,-0.087 0.897,0.025 1.246,0.312 0.349,0.285 0.549,0.708 0.549,1.159L13.001,21L6,21L6,5.711ZM12,12.5c0,0.828 -0.672,1.5 -1.5,1.5s-1.5,-0.672 -1.5,-1.5 0.672,-1.5 1.5,-1.5 1.5,0.672 1.5,1.5Z"/>
|
||||
|
||||
</vector>
|
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
|
||||
<path
|
||||
android:fillColor="#F44336"
|
||||
android:pathData="M256,0C114.61,0 0,114.61 0,256s114.61,256 256,256s256,-114.61 256,-256C511.85,114.68 397.32,0.15 256,0zM341.33,311.19c8.67,7.98 9.23,21.48 1.25,30.14c-7.98,8.67 -21.48,9.23 -30.14,1.25c-0.43,-0.4 -0.85,-0.82 -1.25,-1.25L256,286.17l-55.17,55.17c-8.48,8.19 -21.98,7.95 -30.17,-0.52c-7.98,-8.27 -7.98,-21.37 0,-29.64L225.84,256l-55.17,-55.17c-8.19,-8.48 -7.95,-21.98 0.52,-30.17c8.27,-7.98 21.37,-7.98 29.64,0L256,225.84l55.19,-55.17c7.98,-8.67 21.48,-9.23 30.14,-1.25c8.67,7.98 9.23,21.48 1.25,30.14c-0.4,0.43 -0.82,0.85 -1.25,1.25L286.17,256L341.33,311.19z" />
|
||||
|
||||
</vector>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M20,4H4C2.89,4 2.01,4.89 2.01,6L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V6C22,4.89 21.11,4 20,4zM20,18H4V6h16V18zM6,10h2v2H6V10zM10,10h8v2h-8V10zM6,14h2v2H6V14zM10,14h8v2h-8V14z" />
|
||||
</vector>
|
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path
|
||||
android:fillColor="#2196F3"
|
||||
android:pathData="M12,24A12,12 0,1 0,0 12,12.013 12.013,0 0,0 12,24ZM12,5a1.5,1.5 0,1 1,-1.5 1.5A1.5,1.5 0,0 1,12 5ZM11,10h1a2,2 0,0 1,2 2v6a1,1 0,0 1,-2 0L12,12L11,12a1,1 0,0 1,0 -2Z" />
|
||||
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="m19.776,3.208c-2.076,-2.068 -4.839,-3.208 -7.776,-3.208s-5.701,1.139 -7.777,3.208c-2.078,2.069 -3.223,4.821 -3.223,7.747s1.145,5.678 3.236,7.761l4.151,3.818c0.971,0.945 2.254,1.466 3.612,1.466s2.641,-0.521 3.602,-1.457l4.175,-3.842c2.078,-2.069 3.223,-4.821 3.223,-7.747s-1.145,-5.678 -3.223,-7.747ZM19.085,17.979l-4.171,3.838c-1.566,1.526 -4.253,1.536 -5.839,-0.01l-4.147,-3.814c-1.888,-1.88 -2.928,-4.379 -2.928,-7.038s1.04,-5.158 2.928,-7.038c1.889,-1.881 4.4,-2.917 7.072,-2.917s5.183,1.036 7.071,2.917c1.888,1.88 2.928,4.379 2.928,7.038s-1.04,5.158 -2.914,7.024ZM10,7.5v7c0,0.276 -0.224,0.5 -0.5,0.5s-0.5,-0.224 -0.5,-0.5v-7c0,-0.276 0.224,-0.5 0.5,-0.5s0.5,0.224 0.5,0.5ZM14.5,7h-1.5c-0.552,0 -1,0.449 -1,1v6.5c0,0.276 0.224,0.5 0.5,0.5s0.5,-0.224 0.5,-0.5v-2.5h1.5c1.379,0 2.5,-1.122 2.5,-2.5s-1.121,-2.5 -2.5,-2.5ZM14.5,11h-1.5v-3h1.5c0.827,0 1.5,0.673 1.5,1.5s-0.673,1.5 -1.5,1.5Z"/>
|
||||
</vector>
|
|
@ -0,0 +1,170 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
|
@ -0,0 +1,30 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zM12,11.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/>
|
||||
</vector>
|
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512dp"
|
||||
android:height="512dp"
|
||||
android:viewportWidth="512.29"
|
||||
android:viewportHeight="512.29">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M256.06,0L256.06,0c17.67,0 32,14.33 32,32v106.67c0,17.67 -14.33,32 -32,32l0,0c-17.67,0 -32,-14.33 -32,-32V32C224.06,14.33 238.39,0 256.06,0z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M330.73,105.39L330.73,105.39c-0.16,10.74 5.26,20.8 14.31,26.58c80.43,49.14 105.8,154.18 56.65,234.62S247.51,472.38 167.08,423.24S61.28,269.05 110.43,188.62c14.04,-22.98 33.32,-42.31 56.27,-56.42c9.21,-5.78 14.77,-15.92 14.7,-26.8l0,0c0.05,-17.67 -14.24,-32.04 -31.91,-32.09c-6.07,-0.02 -12.02,1.69 -17.16,4.93C22.23,146.63 -11.58,291.32 56.8,401.41s213.07,143.91 323.16,75.52s143.91,-213.07 75.52,-323.16c-19.03,-30.65 -44.88,-56.49 -75.52,-75.52c-15,-9.46 -34.82,-4.97 -44.28,10.02C332.45,93.4 330.73,99.33 330.73,105.39z"/>
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="m21,14c1.654,0 3,-1.346 3,-3s-1.346,-3 -3,-3c-1.595,0 -2.891,1.255 -2.983,2.828l-3.28,0.647c-0.278,-0.861 -0.775,-1.616 -1.438,-2.203l2.475,-3.541c0.375,0.169 0.788,0.268 1.226,0.268 1.654,0 3,-1.346 3,-3s-1.346,-3 -3,-3 -3,1.346 -3,3c0,0.857 0.366,1.626 0.944,2.173l-2.457,3.515c-0.735,-0.426 -1.577,-0.688 -2.487,-0.688s-1.751,0.262 -2.487,0.688l-2.457,-3.515c0.578,-0.547 0.944,-1.316 0.944,-2.173 0,-1.654 -1.346,-3 -3,-3S0,1.346 0,3s1.346,3 3,3c0.437,0 0.85,-0.099 1.226,-0.268l2.475,3.541c-1.034,0.917 -1.701,2.24 -1.701,3.727 0,1.308 0.516,2.491 1.341,3.383l-1.802,2.056c-0.452,-0.272 -0.975,-0.439 -1.539,-0.439 -1.654,0 -3,1.346 -3,3s1.346,3 3,3 3,-1.346 3,-3c0,-0.728 -0.271,-1.387 -0.704,-1.908l1.792,-2.045c0.822,0.593 1.823,0.953 2.911,0.953 1.284,0 2.445,-0.501 3.332,-1.299l3.114,2.747c-0.277,0.454 -0.446,0.982 -0.446,1.553 0,1.654 1.346,3 3,3s3,-1.346 3,-3 -1.346,-3 -3,-3c-0.723,0 -1.377,0.267 -1.896,0.694l-3.098,-2.733c0.616,-0.831 0.994,-1.85 0.994,-2.962 0,-0.187 -0.035,-0.364 -0.055,-0.546l3.185,-0.628c0.361,1.251 1.504,2.175 2.87,2.175ZM17,1c1.103,0 2,0.897 2,2s-0.897,2 -2,2 -2,-0.897 -2,-2 0.897,-2 2,-2ZM1,3c0,-1.103 0.897,-2 2,-2s2,0.897 2,2 -0.897,2 -2,2 -2,-0.897 -2,-2ZM3,23c-1.103,0 -2,-0.897 -2,-2s0.897,-2 2,-2 2,0.897 2,2 -0.897,2 -2,2ZM10,17c-2.206,0 -4,-1.794 -4,-4s1.794,-4 4,-4 4,1.794 4,4 -1.794,4 -4,4ZM19,19c1.103,0 2,0.897 2,2s-0.897,2 -2,2 -2,-0.897 -2,-2 0.897,-2 2,-2ZM21,9c1.103,0 2,0.897 2,2s-0.897,2 -2,2 -2,-0.897 -2,-2 0.897,-2 2,-2Z"/>
|
||||
</vector>
|
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="18dp"
|
||||
android:height="18dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M15.045,0c-0.027,-0.001 -6.063,-0.001 -6.09,0 -2.736,0.024 -4.955,2.258 -4.955,4.999v14c0,2.757 2.243,5 5,5h6c2.757,0 5,-2.243 5,-5L20,5C20,2.258 17.781,0.025 15.045,0ZM18,18.999c0,1.654 -1.346,3 -3,3h-6c-1.654,0 -3,-1.346 -3,-3L6,5c0,-1.453 1.038,-2.667 2.411,-2.942l0.694,1.389c0.169,0.339 0.516,0.553 0.895,0.553h4c0.379,0 0.725,-0.214 0.895,-0.553l0.694,-1.389c1.373,0.274 2.411,1.489 2.411,2.942v14ZM13,19.999h-2c-0.552,0 -1,-0.448 -1,-1h0c0,-0.552 0.448,-1 1,-1h2c0.552,0 1,0.448 1,1h0c0,0.552 -0.448,1 -1,1Z" />
|
||||
|
||||
</vector>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M6.62,10.79c1.44,2.83 3.76,5.14 6.59,6.59l2.2,-2.2c0.27,-0.27 0.67,-0.36 1.02,-0.24 1.12,0.37 2.33,0.57 3.57,0.57 0.55,0 1,0.45 1,1V20c0,0.55 -0.45,1 -1,1 -9.39,0 -17,-7.61 -17,-17 0,-0.55 0.45,-1 1,-1h3.5c0.55,0 1,0.45 1,1 0,1.25 0.2,2.45 0.57,3.57 0.11,0.35 0.03,0.74 -0.25,1.02l-2.2,2.2z" />
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="#FF000000" android:pathData="M24,11a3,3 0,0 0,-2.841 -2.984,9.918 9.918,0 0,0 -5.175,-5.175A3,3 0,0 0,13 0L11,0A3,3 0,0 0,8.016 2.841,9.921 9.921,0 0,0 2.841,8.016a2.985,2.985 0,0 0,-0.659 5.856,9.964 9.964,0 0,0 5.332,7.069l-0.48,1.8a1,1 0,0 0,0.708 1.224A1.011,1.011 0,0 0,8 24a1,1 0,0 0,0.966 -0.742L9.4,21.652a9.928,9.928 0,0 0,5.211 0l0.428,1.606a1,1 0,0 0,1.932 -0.516l-0.48,-1.8a9.974,9.974 0,0 0,5.333 -7.07A2.994,2.994 0,0 0,24 11ZM12,20a8.021,8.021 0,0 1,-2.09 -0.28l1.7,-6.364a0.407,0.407 0,0 1,0.787 0l1.7,6.364A8.034,8.034 0,0 1,12 20ZM15.955,18.951 L14.325,12.841A2.38,2.38 0,0 0,12 11.054h0a2.38,2.38 0,0 0,-2.326 1.787l-1.629,6.11A7.962,7.962 0,0 1,4.26 14L5,14a3,3 0,0 0,0.081 -5.992A8.1,8.1 0,0 1,8.588 4.767,2.989 2.989,0 0,0 11,6h2a2.988,2.988 0,0 0,2.412 -1.233,8.094 8.094,0 0,1 3.506,3.241A3,3 0,0 0,19 14h0.74A7.968,7.968 0,0 1,15.955 18.951Z"/>
|
||||
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="m12.263,22.634c0.12,0.25 0.015,0.549 -0.234,0.668 -0.431,0.207 -0.676,0.304 -0.96,0.429 -0.156,0.069 -0.531,0.268 -0.761,0.268s-0.512,-0.196 -0.65,-0.266c-2.191,-1.116 -9.375,-5.2 -9.375,-11.825l0.002,-5.357c0,-1.948 1.242,-3.667 3.091,-4.281l6.774,-2.245c0.102,-0.033 0.213,-0.033 0.315,0l6.774,2.245c1.849,0.614 3.091,2.333 3.091,4.281v2.917c0,0.277 -0.225,0.501 -0.502,0.501s-0.501,-0.224 -0.501,-0.501v-2.917c0,-1.514 -0.965,-2.853 -2.403,-3.33l-6.616,-2.193 -6.616,2.193c-1.438,0.477 -2.404,1.815 -2.404,3.33l-0.002,5.357c0,6.05 6.751,9.895 8.82,10.929l0.224,0.113 0.232,-0.094c0.272,-0.11 0.624,-0.26 1.032,-0.456 0.251,-0.12 0.549,-0.015 0.669,0.234zM20.33,17.987c0,1.105 -0.899,2.004 -2.004,2.004s-2.004,-0.899 -2.004,-2.004 0.899,-2.004 2.004,-2.004 2.004,0.899 2.004,2.004zM19.327,17.987c0,-0.553 -0.449,-1.002 -1.002,-1.002s-1.002,0.449 -1.002,1.002 0.449,1.002 1.002,1.002 1.002,-0.449 1.002,-1.002zM23.668,19.894c0.101,0.39 0.044,0.794 -0.16,1.14 -0.203,0.345 -0.528,0.592 -0.917,0.693 -0.39,0.103 -0.794,0.045 -1.141,-0.16l-0.223,-0.131c-0.408,0.341 -0.883,0.608 -1.399,0.788v0.272c0,0.829 -0.674,1.503 -1.503,1.503s-1.503,-0.674 -1.503,-1.503v-0.272c-0.516,-0.18 -0.99,-0.447 -1.399,-0.788l-0.223,0.131c-0.69,0.426 -1.663,0.172 -2.059,-0.534 -0.424,-0.687 -0.17,-1.662 0.534,-2.057l0.234,-0.138c-0.064,-0.299 -0.094,-0.576 -0.094,-0.851s0.03,-0.552 0.095,-0.852l-0.235,-0.138c-0.705,-0.394 -0.958,-1.37 -0.533,-2.057 0.395,-0.706 1.368,-0.958 2.058,-0.533l0.223,0.131c0.408,-0.341 0.883,-0.608 1.399,-0.788v-0.272c0,-0.829 0.674,-1.503 1.503,-1.503s1.503,0.674 1.503,1.503v0.272c0.516,0.18 0.99,0.447 1.399,0.788l0.223,-0.131c0.689,-0.425 1.663,-0.172 2.059,0.534 0.423,0.687 0.171,1.663 -0.535,2.057l-0.234,0.137c0.065,0.3 0.095,0.577 0.095,0.852s-0.03,0.552 -0.094,0.851l0.233,0.137c0.346,0.205 0.593,0.53 0.694,0.919zM21.909,16.461 L22.466,16.133c0.236,-0.132 0.32,-0.456 0.179,-0.685 -0.131,-0.237 -0.457,-0.32 -0.686,-0.179l-0.541,0.319c-0.198,0.116 -0.449,0.085 -0.609,-0.077 -0.432,-0.433 -0.987,-0.745 -1.607,-0.905 -0.221,-0.058 -0.376,-0.257 -0.376,-0.485v-0.643c0,-0.276 -0.225,-0.501 -0.501,-0.501s-0.501,0.225 -0.501,0.501v0.643c0,0.228 -0.155,0.428 -0.376,0.485 -0.62,0.161 -1.175,0.473 -1.607,0.905 -0.161,0.163 -0.411,0.195 -0.609,0.077l-0.541,-0.319c-0.228,-0.143 -0.555,-0.057 -0.685,0.178 -0.142,0.229 -0.058,0.554 0.177,0.685l0.558,0.329c0.197,0.115 0.29,0.349 0.227,0.569 -0.103,0.362 -0.151,0.666 -0.151,0.957s0.048,0.595 0.151,0.957c0.063,0.219 -0.031,0.453 -0.228,0.57l-0.557,0.327c-0.235,0.13 -0.319,0.457 -0.178,0.685 0.13,0.236 0.46,0.322 0.686,0.179l0.541,-0.319c0.195,-0.116 0.447,-0.085 0.609,0.077 0.432,0.433 0.987,0.745 1.607,0.905 0.221,0.058 0.376,0.257 0.376,0.485v0.643c0,0.276 0.225,0.501 0.501,0.501s0.501,-0.225 0.501,-0.501v-0.643c0,-0.228 0.155,-0.428 0.376,-0.485 0.62,-0.161 1.175,-0.473 1.607,-0.905 0.161,-0.161 0.413,-0.193 0.609,-0.077l0.541,0.319c0.227,0.14 0.555,0.058 0.685,-0.178 0.142,-0.23 0.058,-0.555 -0.178,-0.687l-0.556,-0.326c-0.197,-0.116 -0.291,-0.35 -0.228,-0.57 0.103,-0.362 0.151,-0.666 0.151,-0.957s-0.048,-0.595 -0.151,-0.957c-0.063,-0.219 0.03,-0.453 0.227,-0.569zM9.306,12.976c0,0.553 0.449,1.002 1.002,1.002s1.002,-0.449 1.002,-1.002 -0.449,-1.002 -1.002,-1.002 -1.002,0.449 -1.002,1.002zM12.312,9.969h-4.009c-1.105,0 -2.004,0.899 -2.004,2.004v2.004c0,1.105 0.899,2.004 2.004,2.004h2.505c0.277,0 0.501,0.224 0.501,0.501s-0.224,0.501 -0.501,0.501h-2.505c-1.658,0 -3.007,-1.349 -3.007,-3.007v-2.004c0,-1.305 0.84,-2.407 2.004,-2.822v-1.187c0,-1.658 1.349,-3.007 3.007,-3.007s3.007,1.349 3.007,3.007v1.187c1.164,0.415 2.004,1.517 2.004,2.822 0,0.277 -0.224,0.501 -0.501,0.501s-0.501,-0.224 -0.501,-0.501c0,-1.105 -0.899,-2.004 -2.004,-2.004zM12.312,8.967v-1.002c0,-1.105 -0.899,-2.004 -2.004,-2.004s-2.004,0.899 -2.004,2.004v1.002h4.009z"/>
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="m6.343,13.343c-0.155,0.155 -0.303,0.315 -0.443,0.48 -0.099,0.116 -0.24,0.176 -0.381,0.176 -0.115,0 -0.229,-0.039 -0.324,-0.119 -0.21,-0.179 -0.236,-0.495 -0.058,-0.705 0.157,-0.185 0.324,-0.365 0.499,-0.54 2.631,-2.632 6.677,-3.373 10.07,-1.84 0.251,0.113 0.363,0.41 0.25,0.661 -0.114,0.252 -0.409,0.363 -0.662,0.25 -3.014,-1.362 -6.611,-0.704 -8.951,1.636ZM20.709,6.784c0.227,0.16 0.537,0.109 0.698,-0.116 0.16,-0.225 0.108,-0.537 -0.116,-0.697C14.908,1.409 6.242,2.128 0.687,7.686c-0.192,0.192 -0.378,0.387 -0.558,0.586 -0.185,0.205 -0.169,0.521 0.036,0.707 0.096,0.086 0.216,0.129 0.335,0.129 0.136,0 0.272,-0.056 0.371,-0.165 0.168,-0.187 0.342,-0.37 0.522,-0.549 5.208,-5.21 13.332,-5.887 19.315,-1.609ZM23.854,7.853l-10.137,10.137c0.176,0.297 0.283,0.64 0.283,1.01 0,1.103 -0.897,2 -2,2s-2,-0.897 -2,-2 0.897,-2 2,-2c0.37,0 0.712,0.108 1.01,0.283l10.137,-10.137c0.195,-0.195 0.512,-0.195 0.707,0s0.195,0.512 0,0.707ZM13,18.999c0,-0.551 -0.449,-1 -1,-1s-1,0.449 -1,1 0.449,1 1,1 1,-0.449 1,-1Z"/>
|
||||
</vector>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M15,1H9v2h6V1zM11,14h2V8h-2v6zM19.03,7.39l1.42,-1.42c-0.43,-0.51 -0.9,-0.99 -1.41,-1.41l-1.42,1.42C16.07,4.74 14.12,4 12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9 9,-4.03 9,-9c0,-2.12 -0.74,-4.07 -1.97,-5.61zM12,20c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF9800"
|
||||
android:pathData="M23.08,15.33L15,2.57c-0.68,-0.98 -1.81,-1.57 -3,-1.57s-2.32,0.58 -3.03,1.6L0.93,15.31c-1.02,1.46 -1.21,3.21 -0.5,4.56 0.7,1.35 2.17,2.12 4.01,2.12h15.12c1.85,0 3.31,-0.77 4.01,-2.12 0.7,-1.35 0.51,-3.09 -0.49,-4.54ZM11,7c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v6c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1L11,7ZM12,19c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5Z"/>
|
||||
</vector>
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_focused="true">
|
||||
<shape>
|
||||
<solid android:color="@color/background_white" />
|
||||
<corners android:radius="16dp" />
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="@color/border_focus" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<solid android:color="@color/background_white" />
|
||||
<corners android:radius="16dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/border_light" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#F3F4F6" />
|
||||
<corners android:radius="12dp" />
|
||||
<stroke android:width="1dp" android:color="#E5E7EB" />
|
||||
</shape>
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape>
|
||||
<gradient
|
||||
android:startColor="@color/pressed_state"
|
||||
android:endColor="@color/button_gradient_end"
|
||||
android:angle="45" />
|
||||
<corners android:radius="16dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item android:state_enabled="false">
|
||||
<shape>
|
||||
<solid android:color="@color/disabled_color" />
|
||||
<corners android:radius="16dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<gradient
|
||||
android:startColor="@color/button_gradient_start"
|
||||
android:centerColor="@color/button_gradient_middle"
|
||||
android:endColor="@color/button_gradient_end"
|
||||
android:angle="45" />
|
||||
<corners android:radius="16dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient
|
||||
android:startColor="@color/button_gradient_start"
|
||||
android:centerColor="@color/button_gradient_middle"
|
||||
android:endColor="@color/button_gradient_end"
|
||||
android:angle="45" />
|
||||
<corners android:radius="12dp" />
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="@color/purple_primary" />
|
||||
</shape>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/transparent" />
|
||||
<corners android:radius="12dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/purple_light" />
|
||||
</shape>
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<gradient
|
||||
android:startColor="@color/purple_light"
|
||||
android:endColor="@color/purple_extra_light"
|
||||
android:angle="45" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/background_white" />
|
||||
</shape>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient
|
||||
android:angle="135"
|
||||
android:startColor="#FFFFFF"
|
||||
android:centerColor="#FFEBEE"
|
||||
android:endColor="#FFFFFF"
|
||||
android:type="linear" />
|
||||
<corners android:radius="24dp" />
|
||||
</shape>
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape>
|
||||
<solid android:color="#E0E7FF" />
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<solid android:color="@android:color/transparent" />
|
||||
<corners android:radius="8dp" />
|
||||
<stroke android:width="1dp" android:color="#C7D2FE" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/ripple_color">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="12dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<gradient
|
||||
android:startColor="@color/purple_primary"
|
||||
android:endColor="@color/primary_purple"
|
||||
android:angle="90" />
|
||||
<corners android:radius="2dp" />
|
||||
</shape>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/text_secondary" />
|
||||
<corners android:radius="1dp" />
|
||||
</shape>
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape>
|
||||
<solid android:color="#E0E7FF" />
|
||||
<corners android:radius="20dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<solid android:color="#F8FAFC" />
|
||||
<corners android:radius="20dp" />
|
||||
<stroke android:width="1dp" android:color="#E2E8F0" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="@color/success_color" />
|
||||
<size
|
||||
android:width="6dp"
|
||||
android:height="6dp" />
|
||||
</shape>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="@android:color/white" />
|
||||
<size
|
||||
android:width="6dp"
|
||||
android:height="6dp" />
|
||||
</shape>
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape>
|
||||
<solid android:color="#F3F4F6" />
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<solid android:color="@android:color/white" />
|
||||
<corners android:radius="8dp" />
|
||||
<stroke android:width="1dp" android:color="#D1D5DB" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#F9FAFB" />
|
||||
<corners android:radius="12dp" />
|
||||
<stroke android:width="1dp" android:color="#E5E7EB" />
|
||||
</shape>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue