๐Ÿ“š Mobile Computing ยท Teknik Informatika

Smart Attendance
Mobile System

Tutorial lengkap membangun sistem absensi mahasiswa berbasis GPS dan Kamera menggunakan Kodular, PHP Native, dan MySQL

โฌ‡ Download File
๐Ÿ“
ABSENSI GPS
Lat: -6.2088 | Lng: 106.8456
๐Ÿ“ธ
SELFIE CAPTURED
โœ“ HADIR
8
Pertemuan
120'
Per Sesi
7
Teknologi
9
Fitur Utama
๐ŸŽฏ Deskripsi

Apa yang Akan Kamu Pelajari?

๐ŸŽ“

Capaian Pembelajaran

  • Memahami konsep Mobile Computing
  • Membuat aplikasi Android dengan Kodular
  • Mengintegrasikan GPS dan kamera
  • Membuat REST API dengan PHP Native
  • Menghubungkan mobile app ke MySQL
  • Implementasi sistem absensi berbasis lokasi
  • Mengembangkan project client-server utuh
๐Ÿš€

Fitur Project Akhir

  • Login User dengan session
  • Ambil Lokasi GPS real-time
  • Ambil Foto Selfie via kamera
  • Validasi Radius Lokasi (โ‰ค100m)
  • Kirim Data ke Server via HTTP
  • Penyimpanan Database MySQL
  • Riwayat Absensi mahasiswa
  • Status Hadir / Terlambat otomatis
  • Dashboard Admin sederhana

Arsitektur Sistem

๐Ÿ“ฑ
Android App
Kodular
โ†’
๐ŸŒ
HTTP Request
JSON POST
โ†’
โš™๏ธ
PHP API
Backend
โ†’
๐Ÿ—„๏ธ
MySQL DB
XAMPP
โ†’
โœ…
Response
JSON
๐Ÿ› ๏ธ Stack

Teknologi yang Digunakan

๐Ÿงฉ
Kodular
Frontend Mobile Android
๐Ÿ˜
PHP Native
Backend REST API
๐Ÿ—„๏ธ
MySQL
Database Storage
๐Ÿ–ฅ๏ธ
XAMPP
Local Server
๐Ÿ“‹
JSON
Format Data Exchange
๐Ÿ“
GPS Android
Lokasi Absensi
๐Ÿ“ธ
Camera Android
Foto Selfie
๐Ÿ“… RPS

Rencana Pembelajaran 8 Pertemuan

1

Pengantar Mobile Computing & Arsitektur Sistem

โฑ 120 Menit
  • Definisi & karakteristik Mobile Computingklik โ€บ
  • Mobile client-server architectureklik โ€บ
  • Konsep REST APIklik โ€บ
  • Pengenalan Kodular (Interface, Component, Block)klik โ€บ
  • Overview project akhirklik โ€บ
๐Ÿ”ฌ Praktikum
  • Instalasi XAMPP & Kodular Companion
  • Buat project baru di Kodular
  • Buat tampilan splash screen
  • Buat halaman login sederhana
2

Desain UI/UX dan Navigasi Aplikasi

โฑ 120 Menit
  • Layout Android & Responsive Designklik โ€บ
  • User Experience principlesklik โ€บ
  • Komponen: Button, Label, TextBox, Imageklik โ€บ
  • Arrangement & Navigation Drawerklik โ€บ
  • Intent Screen antar halamanklik โ€บ
๐Ÿ”ฌ Praktikum
  • Buat halaman Login lengkap
  • Buat Dashboard utama
  • Buat Menu Navigasi
  • Buat Form Absensi
3

Database MySQL & Backend PHP Native

โฑ 120 Menit
  • Konsep Database, Table, Primary Keyklik โ€บ
  • Relasi tabel users & absensiklik โ€บ
  • Struktur PHP API (GET & POST)klik โ€บ
  • Format JSON Responseklik โ€บ
  • Koneksi PHP ke MySQLklik โ€บ
๐Ÿ”ฌ Praktikum
  • Buat database absensi_db
  • Buat tabel users & absensi
  • Buat file koneksi PHP
  • Buat API login & simpan absensi
4

Integrasi HTTP Request & JSON di Kodular

โฑ 120 Menit
  • HTTP Communication & Web APIklik โ€บ
  • Request POST dari Kodularklik โ€บ
  • JSON Parsing di Kodularklik โ€บ
  • Web Component & TinyDBklik โ€บ
  • Menyimpan session loginklik โ€บ
๐Ÿ”ฌ Praktikum
  • Implementasi login via API PHP
  • Parsing JSON response
  • Simpan session ke TinyDB
  • Tampilkan data user di dashboard
5

Implementasi GPS & Validasi Lokasi

โฑ 120 Menit
  • Location Based Service (LBS)klik โ€บ
  • GPS Android: Latitude & Longitudeklik โ€บ
  • Konsep Geolocation & radiusklik โ€บ
  • Perhitungan jarak koordinatklik โ€บ
  • Validasi area absensi โ‰ค 100mklik โ€บ
๐Ÿ”ฌ Praktikum
  • Ambil lokasi GPS real-time
  • Tampilkan koordinat di layar
  • Kirim lokasi ke server PHP
  • Validasi radius kampus
6

Implementasi Kamera & Upload Foto

โฑ 120 Menit
  • Camera Permission di Androidklik โ€บ
  • Capture Image via Kodularklik โ€บ
  • Konversi gambar ke Base64klik โ€บ
  • PHP Upload Image handlerklik โ€บ
  • Simpan path gambar di MySQLklik โ€บ
๐Ÿ”ฌ Praktikum
  • Ambil foto selfie via kamera
  • Konversi ke Base64 string
  • Upload ke server PHP
  • Simpan path di database
7

Integrasi Sistem Absensi Lengkap

โฑ 120 Menit
  • Integrasi: Login + GPS + Kamera + Uploadklik โ€บ
  • Error Handling (GPS gagal, internet putus)klik โ€บ
  • Validasi kehadiran otomatisklik โ€บ
  • Status Hadir / Terlambat (batas 08:00)klik โ€บ
  • Riwayat absensi mahasiswaklik โ€บ
๐Ÿ”ฌ Praktikum
  • Buat sistem absensi final
  • Implementasi semua validasi
  • Tampilkan riwayat absensi
  • Testing end-to-end
8

Presentasi & Evaluasi Project Akhir

โฑ 120 Menit
  • Presentasi UI aplikasi Androidklik โ€บ
  • Demo fitur sistem lengkapklik โ€บ
  • Penjelasan cara kerja backendklik โ€บ
  • Review struktur databaseklik โ€บ
๐ŸŽฏ Demonstrasi
  • Demo Login user
  • Demo Absensi GPS
  • Demo Foto selfie
  • Verifikasi data tersimpan di DB
๐Ÿ—„๏ธ Database

Struktur Database MySQL

๐Ÿ‘ค Tabel: users
id PKINT AUTO_INCREMENT
namaVARCHAR(100)
nimVARCHAR(20) UNIQUE
passwordVARCHAR(255)
created_atTIMESTAMP
๐Ÿ“‹ Tabel: absensi
id PKINT AUTO_INCREMENT
user_id FKINT
latitudeDOUBLE
longitudeDOUBLE
fotoTEXT
waktuDATETIME
statusVARCHAR(20)
๐Ÿ’ป Source Code

Kode Program Lengkap

๐Ÿ“„ database/absensi_db.sql
-- ============================================
-- Smart Attendance System - Database Setup
-- ============================================

CREATE DATABASE IF NOT EXISTS absensi_db
  CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

USE absensi_db;

-- Tabel Users (Mahasiswa)
CREATE TABLE users (
  id         INT AUTO_INCREMENT PRIMARY KEY,
  nama       VARCHAR(100) NOT NULL,
  nim        VARCHAR(20)  NOT NULL UNIQUE,
  password   VARCHAR(255) NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Tabel Absensi
CREATE TABLE absensi (
  id        INT AUTO_INCREMENT PRIMARY KEY,
  user_id   INT NOT NULL,
  latitude  DOUBLE NOT NULL,
  longitude DOUBLE NOT NULL,
  foto      TEXT,
  waktu     DATETIME DEFAULT CURRENT_TIMESTAMP,
  status    VARCHAR(20) DEFAULT 'hadir',
  FOREIGN KEY (user_id) REFERENCES users(id)
);

-- Data sample mahasiswa (password: 123456)
INSERT INTO users (nama, nim, password) VALUES
('Ahmad Fauzi',   '2021001', MD5('123456')),
('Siti Rahayu',   '2021002', MD5('123456')),
('Budi Santoso',  '2021003', MD5('123456'));
๐Ÿ“„ api/koneksi.php
<?php
// Konfigurasi koneksi database
define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', '');
define('DB_NAME', 'absensi_db');

// Header CORS untuk akses dari mobile app
header('Access-Control-Allow-Origin: *');
header('Content-Type: application/json');
header('Access-Control-Allow-Methods: POST, GET');

function getConnection() {
    $conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
    if ($conn->connect_error) {
        http_response_code(500);
        echo json_encode([
            'status'  => 'error',
            'message' => 'Koneksi database gagal'
        ]);
        exit();
    }
    $conn->set_charset('utf8mb4');
    return $conn;
}

function sendResponse($status, $message, $data = null) {
    $response = ['status' => $status, 'message' => $message];
    if ($data !== null) $response['data'] = $data;
    echo json_encode($response);
    exit();
}
?>
๐Ÿ“„ api/login.php
<?php
require_once 'koneksi.php';

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    sendResponse('error', 'Method tidak diizinkan');
}

// Ambil data dari POST atau JSON body
$input = json_decode(file_get_contents('php://input'), true);
$nim      = trim($input['nim']      ?? $_POST['nim']      ?? '');
$password = trim($input['password'] ?? $_POST['password'] ?? '');

if (empty($nim) || empty($password)) {
    sendResponse('error', 'NIM dan password wajib diisi');
}

$conn     = getConnection();
$passMd5  = md5($password);
$stmt     = $conn->prepare(
    "SELECT id, nama, nim FROM users WHERE nim = ? AND password = ?"
);
$stmt->bind_param('ss', $nim, $passMd5);
$stmt->execute();
$result = $stmt->get_result();

if ($result->num_rows === 0) {
    sendResponse('error', 'NIM atau password salah');
}

$user = $result->fetch_assoc();
$conn->close();

sendResponse('success', 'Login berhasil', [
    'user_id' => $user['id'],
    'nama'    => $user['nama'],
    'nim'     => $user['nim']
]);
?>
๐Ÿ“„ api/absensi.php
<?php
require_once 'koneksi.php';

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    sendResponse('error', 'Method tidak diizinkan');
}

$input     = json_decode(file_get_contents('php://input'), true);
$userId    = (int)($input['user_id']   ?? $_POST['user_id']   ?? 0);
$latitude  = (float)($input['latitude']  ?? $_POST['latitude']  ?? 0);
$longitude = (float)($input['longitude'] ?? $_POST['longitude'] ?? 0);
$foto      = $input['foto'] ?? $_POST['foto'] ?? '';

if (!$userId || !$latitude || !$longitude) {
    sendResponse('error', 'Data tidak lengkap');
}

// Koordinat kampus (sesuaikan dengan lokasi kampus)
$kampusLat = -6.2088;
$kampusLng = 106.8456;
$maxRadius = 100; // meter

// Hitung jarak menggunakan Haversine formula
function hitungJarak($lat1, $lng1, $lat2, $lng2) {
    $R    = 6371000; // radius bumi dalam meter
    $dLat = deg2rad($lat2 - $lat1);
    $dLng = deg2rad($lng2 - $lng1);
    $a    = sin($dLat/2) * sin($dLat/2) +
             cos(deg2rad($lat1)) * cos(deg2rad($lat2)) *
             sin($dLng/2) * sin($dLng/2);
    return $R * 2 * atan2(sqrt($a), sqrt(1 - $a));
}

$jarak = hitungJarak($latitude, $longitude, $kampusLat, $kampusLng);
if ($jarak > $maxRadius) {
    sendResponse('error', 'Lokasi di luar radius kampus (' . round($jarak) . 'm)');
}

// Tentukan status hadir/terlambat
$jam    = (int)date('H');
$menit  = (int)date('i');
$status = ($jam < 8 || ($jam == 8 && $menit == 0)) ? 'hadir' : 'terlambat';

// Simpan foto Base64 jika ada
$fotoPath = '';
if (!empty($foto)) {
    $fotoData = base64_decode(preg_replace('#^data:image/\w+;base64,#i', '', $foto));
    $fotoName = 'foto_' . $userId . '_' . time() . '.jpg';
    $fotoDir  = __DIR__ . '/../uploads/';
    if (!is_dir($fotoDir)) mkdir($fotoDir, 0755, true);
    file_put_contents($fotoDir . $fotoName, $fotoData);
    $fotoPath = 'uploads/' . $fotoName;
}

$conn = getConnection();
$stmt = $conn->prepare(
    "INSERT INTO absensi (user_id,latitude,longitude,foto,status) VALUES (?,?,?,?,?)"
);
$stmt->bind_param('iddss', $userId, $latitude, $longitude, $fotoPath, $status);
$stmt->execute();
$conn->close();

sendResponse('success', 'Absensi berhasil', [
    'status' => $status,
    'jarak'  => round($jarak) . 'm',
    'waktu'  => date('Y-m-d H:i:s')
]);
?>
๐Ÿ“„ api/riwayat.php
<?php
require_once 'koneksi.php';

$userId = (int)($_GET['user_id'] ?? 0);
if (!$userId) {
    sendResponse('error', 'user_id diperlukan');
}

$conn = getConnection();
$stmt = $conn->prepare("
    SELECT a.id, a.latitude, a.longitude, a.foto,
           a.waktu, a.status, u.nama, u.nim
    FROM absensi a
    JOIN users u ON a.user_id = u.id
    WHERE a.user_id = ?
    ORDER BY a.waktu DESC
    LIMIT 30
");
$stmt->bind_param('i', $userId);
$stmt->execute();
$result = $stmt->get_result();

$data = [];
while ($row = $result->fetch_assoc()) {
    $data[] = $row;
}
$conn->close();

sendResponse('success', 'Data riwayat', $data);
?>
๐Ÿ“„ api/upload_foto.php
<?php
require_once 'koneksi.php';

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    sendResponse('error', 'Method tidak diizinkan');
}

$input  = json_decode(file_get_contents('php://input'), true);
$base64 = $input['foto'] ?? '';

if (empty($base64)) {
    sendResponse('error', 'Data foto kosong');
}

// Bersihkan header base64 jika ada
$imageData = preg_replace('#^data:image/\w+;base64,#i', '', $base64);
$decoded   = base64_decode($imageData);

if ($decoded === false) {
    sendResponse('error', 'Format base64 tidak valid');
}

$uploadDir = __DIR__ . '/../uploads/';
if (!is_dir($uploadDir)) {
    mkdir($uploadDir, 0755, true);
}

$filename = 'selfie_' . uniqid() . '.jpg';
$filepath = $uploadDir . $filename;

if (file_put_contents($filepath, $decoded) === false) {
    sendResponse('error', 'Gagal menyimpan foto');
}

sendResponse('success', 'Foto berhasil diupload', [
    'path' => 'uploads/' . $filename,
    'url'  => 'http://localhost/absensi/uploads/' . $filename
]);
?>
๐Ÿ“„ admin/index.php
<?php
require_once '../api/koneksi.php';
// Nonaktifkan JSON header untuk halaman HTML
header_remove('Content-Type');
header('Content-Type: text/html; charset=utf-8');

$conn    = getConnection();
$result  = $conn->query("
    SELECT a.*, u.nama, u.nim
    FROM absensi a JOIN users u ON a.user_id = u.id
    ORDER BY a.waktu DESC LIMIT 50
");
$absensi = $result->fetch_all(MYSQLI_ASSOC);

$total     = $conn->query("SELECT COUNT(*) as c FROM absensi")->fetch_assoc()['c'];
$hadir     = $conn->query("SELECT COUNT(*) as c FROM absensi WHERE status='hadir'")->fetch_assoc()['c'];
$terlambat = $conn->query("SELECT COUNT(*) as c FROM absensi WHERE status='terlambat'")->fetch_assoc()['c'];
$conn->close();
?>
<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="UTF-8">
  <title>Admin Dashboard Absensi</title>
  <style>
    body { font-family: sans-serif; background: #0a1628; color: #fff; margin: 0; padding: 20px; }
    h1   { color: #f97316; }
    .stats { display: flex; gap: 20px; margin: 20px 0; }
    .stat  { background: #1a3a6b; padding: 20px; border-radius: 12px; text-align: center; flex: 1; }
    .stat .num { font-size: 2rem; font-weight: 900; color: #f97316; }
    table  { width: 100%; border-collapse: collapse; background: #1a3a6b; border-radius: 12px; overflow: hidden; }
    th     { background: #2563eb; padding: 12px; text-align: left; font-size: 0.85rem; }
    td     { padding: 10px 12px; border-bottom: 1px solid rgba(255,255,255,0.05); font-size: 0.82rem; }
    .hadir     { color: #22c55e; font-weight: 700; }
    .terlambat { color: #f97316; font-weight: 700; }
  </style>
</head>
<body>
  <h1>๐Ÿ“Š Dashboard Admin Absensi</h1>
  <div class="stats">
    <div class="stat"><div class="num"><?= $total ?></div>Total Absensi</div>
    <div class="stat"><div class="num" style="color:#22c55e"><?= $hadir ?></div>Hadir</div>
    <div class="stat"><div class="num"><?= $terlambat ?></div>Terlambat</div>
  </div>
  <table>
    <tr><th>Nama</th><th>NIM</th><th>Waktu</th><th>Lokasi</th><th>Status</th></tr>
    <?php foreach ($absensi as $row): ?>
    <tr>
      <td><?= htmlspecialchars($row['nama']) ?></td>
      <td><?= htmlspecialchars($row['nim']) ?></td>
      <td><?= $row['waktu'] ?></td>
      <td><?= round($row['latitude'],4) ?>, <?= round($row['longitude'],4) ?></td>
      <td class="<?= $row['status'] ?>"><?= strtoupper($row['status']) ?></td>
    </tr>
    <?php endforeach; ?>
  </table>
</body></html>
๐Ÿ“Š Evaluasi

Kriteria Penilaian

15%
UI/UX Aplikasi
10%
Fungsi Login
20%
Integrasi GPS
20%
Integrasi Kamera
15%
Backend PHP
10%
Database MySQL
10%
Presentasi

Progress Bobot Penilaian

Integrasi GPS20%
Integrasi Kamera20%
UI/UX Aplikasi15%
Backend PHP15%
Fungsi Login10%
Database MySQL10%
Presentasi10%
โš™๏ธ Setup

Panduan Instalasi & Setup

๐Ÿ–ฅ๏ธ Setup Backend (XAMPP)

1

Install XAMPP

Download dari apachefriends.org, install dan jalankan Apache + MySQL dari XAMPP Control Panel.

2

Buat Folder Project

Buat folder absensi di dalam C:/xampp/htdocs/

3

Import Database

Buka phpMyAdmin di localhost/phpmyadmin, buat database absensi_db, lalu import file SQL.

4

Copy File PHP

Salin semua file PHP ke folder htdocs/absensi/api/ dan htdocs/absensi/admin/

5

Test API

Buka browser, akses localhost/absensi/api/login.php untuk verifikasi.

๐Ÿ“ฑ Setup Kodular

1

Buka Kodular

Akses create.kodular.io di browser, login atau buat akun baru.

2

Buat Project Baru

Klik "New Project", beri nama SmartAttendance, pilih template kosong.

3

Install Kodular Companion

Install app Kodular Companion di HP Android untuk live testing langsung dari browser.

4

Tambah Komponen

Tambahkan: LocationSensor, Camera, Web (HTTP), TinyDB, dan komponen UI yang diperlukan.

5

Set URL API

Ganti localhost dengan IP komputer kamu (cek via ipconfig), contoh: 192.168.1.x

๐Ÿ’ก Tips Penting: Pastikan HP Android dan komputer terhubung ke WiFi yang sama saat testing. Gunakan IP lokal (bukan localhost) di URL API Kodular.
๐Ÿ“š Referensi

Tools & Dokumentasi

๐Ÿงฉ
Kodular
create.kodular.io
๐Ÿ“–
Kodular Docs
docs.kodular.io
๐Ÿ–ฅ๏ธ
XAMPP
apachefriends.org
๐Ÿ˜
PHP Manual
php.net/docs.php
๐Ÿ—„๏ธ
MySQL Docs
dev.mysql.com/doc
๐Ÿ’ป
VS Code
code.visualstudio.com
โ†‘