add flutter

This commit is contained in:
Ariska committed 2026-03-11 15:29:37 +07:00
1 parent c253e1a370
commit 619d758027
9490 files changed
+135801 -1353

No files matched your search

Regular → Executable
View File
File mode changed.
+47
View File
@@ -0,0 +1,47 @@
# Build debug APKs (User, Driver, Merchant)
## One command (recommended)
From the repo root (where this file is):
```bash
./build-debug-apks.sh
```
**Requires:** JDK 17+ and Android SDK (API 33) installed. If you use Android Studio, it provides both.
---
## Output locations
After a successful run you get:
| App | Debug APK path |
|---------|----------------|
| **User** | `OnTime_User_live/app/build/outputs/apk/debug/app-debug.apk` |
| **Driver** | `OnTime_Driver_live/app/build/outputs/apk/debug/app-debug.apk` |
| **Merchant**| `OnTime_Merchant_live/app/build/outputs/apk/debug/app-debug.apk` |
---
## Build each app separately
```bash
# User
cd OnTime_User_live && ./gradlew assembleDebug && cd ..
# Driver
cd OnTime_Driver_live && ./gradlew assembleDebug && cd ..
# Merchant
cd OnTime_Merchant_live && ./gradlew assembleDebug && cd ..
```
---
## Signing
- **User** and **Merchant** use `app/ontimekeystore.jks` (same keystore). User has it; the script copies it to Merchant if missing.
- **Driver** uses the default Android debug keystore (no custom jks).
If User or Merchant build fails with “keystore not found”, ensure `OnTime_User_live/app/ontimekeystore.jks` exists, then run the script again.
+47
View File
@@ -0,0 +1,47 @@
# Firebase FCM v1 Process (Login, Send, Receive)
Reference: **test** app (`test/app`). All three live apps and the backend use the same Firebase project **ngojol-trial** and FCM HTTP v1 for login, sending, and receiving messages.
## 1. Firebase project and config
- **Project:** `ngojol-trial` (project_number: 158861273889)
- **Backend:** Uses `ngojol-trial-firebase-adminsdk-lc81n-00c9e935db.json` and `fcm_v1_helper.php` to send via FCM HTTP v1 API.
- **Android apps:** Each app uses `google-services.json` that includes the **id.ontime.*** package entries for this project:
- **User:** `id.ontime.customer`
- **Driver:** `id.ontime.driver`
- **Merchant:** `id.ontime.merchant`
Use the same multi-app `google-services.json` as in **test/app/google-services.json** (or ensure your Firebase Console has these three Android apps under ngojol-trial).
## 2. Login and FCM token (FCM v1)
- **Token retrieval:** All apps use `FirebaseMessaging.getInstance().getToken()` (async) instead of deprecated `FirebaseInstanceId.getInstance().getToken()`.
- **BaseApp (User, Driver, Merchant):** On startup, get token with `getToken().addOnCompleteListener(...)`, then subscribe to topics (`pelanggan` / `driver`+`ouride` / `mitra`+`ouride`) and store the token in Realm.
- **LoginActivity:** On sign-in, get token with `getToken().addOnCompleteListener(...)` and send it to the backend as `reg_id` in the login request so the server can target this device for FCM.
- **RegisterActivity (User):** Registration request sends the FCM token via `getToken().addOnCompleteListener(...)` then `request.setToken(...)` and `service.register(request)`.
- **onNewToken:** In each app’s `MessagingService`, `onNewToken()` saves the new token in Realm and posts it on EventBus so the UI can use the latest token; the next login will send the updated token to the backend.
## 3. Sending messages (backend → devices)
- Backend uses **FCM HTTP v1** only:
- `application/helpers/fcm_v1_helper.php`: `fcm_v1_get_access_token()`, `fcm_v1_send($target, $data, $is_topic, $options)`.
- `application/models/Notification_model.php`: `send_generic_to_token()` and `send_generic_to_topic()` call `fcm_v1_send`.
- Config in `application/config/config.php`: `FCM_PROJECT_ID` = `ngojol-trial`, `FCM_CREDENTIALS_PATH` = path to the service account JSON.
- All notification flows (order, chat, top-up, etc.) go through this v1 path.
## 4. Receiving messages (devices)
- Each app has a `MessagingService` extending `FirebaseMessagingService`:
- **User:** `id.ontime.customer.utils.api.service.MessagingService`
- **Driver:** `id.ontime.driver.utils.api.service.MessagingService`
- **Merchant:** `id.ontime.merchant.utils.api.service.MessagingService`
- `onMessageReceived(RemoteMessage)` handles data payloads (e.g. `type` 1=order, 2=chat, 3/4=other), shows notifications and updates UI (including broadcasts for order status).
- No legacy FCM or `FirebaseInstanceId` usage; token handling is FCM v1 only.
## 5. Checklist
- [x] BaseApp: FCM token via `FirebaseMessaging.getToken()` async + topic subscribe.
- [x] LoginActivity: Send `reg_id` to backend using token from `getToken().addOnCompleteListener(...)`.
- [x] RegisterActivity (User): Send token with register request via async `getToken()`.
- [x] MessagingService: `onNewToken()` implemented; token stored in Realm and posted on EventBus.
- [x] Backend: Sends all FCM via `fcm_v1_helper` (FCM HTTP v1).
- [x] All apps and backend use the same Firebase project (**ngojol-trial**) and matching `google-services.json` / service account.
+112
View File
@@ -0,0 +1,112 @@
# JDK version for OnTime (User, Driver, Merchant)
## Required: **JDK 17** or **JDK 21**
The project uses:
- **Gradle 8.9** (gradle-wrapper)
- **Android Gradle Plugin 8.5.0** (User app; Driver/Merchant use similar)
- **App source/target:** Java 11 (Driver, Merchant); User follows Gradle default
Android Gradle Plugin 8.x needs **JDK 17** or newer. Use **JDK 17** or **JDK 21**.
---
## Install JDK
### macOS (Homebrew)
```bash
# JDK 17 (LTS)
brew install openjdk@17
# Or JDK 21
brew install openjdk@21
```
Then link and set `JAVA_HOME` (add to `~/.zshrc` or `~/.bash_profile`):
```bash
# For JDK 17
export JAVA_HOME="$(/usr/libexec/java_home -v 17 2>/dev/null || echo /opt/homebrew/opt/openjdk@17)"
export PATH="$JAVA_HOME/bin:$PATH"
```
### macOS (manual)
1. Download **JDK 17** or **21** from [Adoptium](https://adoptium.net/) or [Oracle](https://www.oracle.com/java/technologies/downloads/).
2. Install the `.pkg`.
3. Set `JAVA_HOME`, e.g. for Adoptium 17 on Apple Silicon:
```bash
export JAVA_HOME=/Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home
```
### Windows
1. Download **JDK 17** or **21** from [Adoptium](https://adoptium.net/).
2. Run the installer and note the install path (e.g. `C:\Program Files\Eclipse Adoptium\jdk-17`).
3. Set environment variables:
- `JAVA_HOME` = that path
- Add `%JAVA_HOME%\bin` to `Path`.
### Linux (apt)
```bash
# JDK 17
sudo apt update
sudo apt install openjdk-17-jdk
# Or JDK 21
sudo apt install openjdk-21-jdk
```
Set `JAVA_HOME` if needed (e.g. in `~/.bashrc`):
```bash
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export PATH="$JAVA_HOME/bin:$PATH"
```
---
## Check version
```bash
java -version
javac -version
```
You should see version **17** or **21**.
---
## Android SDK (required for building)
Gradle needs the Android SDK path. Either:
1. **Set `ANDROID_HOME`** (recommended):
```bash
export ANDROID_HOME=$HOME/Library/Android/sdk # macOS default
export PATH=$PATH:$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools
```
2. **Or use `local.properties`** in each app folder (already created if you opened the project):
- `OnTime_User_live/local.properties`
- `OnTime_Driver_live/local.properties`
- `OnTime_Merchant_live/local.properties`
Each should contain:
```properties
sdk.dir=/Users/YOUR_USERNAME/Library/Android/sdk
```
Replace `YOUR_USERNAME` with your macOS username, or use the path where Android Studio installed the SDK (e.g. Windows: `C:\\Users\\You\\AppData\\Local\\Android\\Sdk`).
If the SDK is in a different location, edit `sdk.dir` in each `local.properties` or set `ANDROID_HOME` to that path.
---
Then build the apps:
```bash
./build-debug-apks.sh
```
Or open **OnTime_User_live**, **OnTime_Driver_live**, or **OnTime_Merchant_live** in Android Studio (it will use the JDK configured in **File → Settings → Build → Build Tools → Gradle**).
Regular → Executable
View File
File mode changed.
+78
View File
@@ -0,0 +1,78 @@
# OnTime Ordering Workflow & Firebase Alignment
## Order flow (User → Driver → Tujuan)
1. **User: Set jemput (pickup)** – In RideCarActivity, user sets pickup via map/Places (`pickUpText`, `pickUpLatLang`).
2. **User: Set tujuan (destination)** – User sets destination (`destinationText`, `destinationLatLang`). Route and price are calculated.
3. **User: Set payment** – Cash or wallet (`checkedpaywallet`, `cashpayment` / `walletpayment`).
4. **Search driver** – `GetNearRideCarRequestJson` / `GetNearRideCarResponseJson` loads nearby drivers; `driverAvailable` is used for FCM.
5. **User: Order** – `sendRequestTransaksi()` calls backend, then `fcmBroadcast()` sends FCM to each driver token via **backend** `notification/send_generic`.
6. **Driver: Bid or auto bid** – Driver receives FCM in `MessagingService` (type=1), opens `NewOrderActivity`. Manual: tap "order"; Auto: `getaccept(true)` after 2s if setting is ON.
7. **Driver: Accept** – `getaccept()` calls backend `accept`, then sends FCM to **user token** (`regid`) with `OrderFCM` (response="2", type=1, id_driver, id_transaksi).
8. **User: Driver accepted** – User app `MessagingService` gets type=1, response="2", posts `DriverResponse` via EventBus; `RideCarActivity.onMessageEvent` starts `ProgressActivity` (response "2").
9. **Progress (until tujuan)** – User stays in `ProgressActivity`; driver in `OrderFragment` can tap "finish" (start) then "finish" (arrive). Each action: driver calls backend (`startrequest` / `finishrequest`), then FCM to user with response "3" (start) or "4" (finish/tujuan).
10. **User: Start / Finish** – `MessagingService` parses type=1 and response "3"/"4", posts `DriverResponse`; `ProgressActivity` receives via EventBus or broadcast, `orderHandler()` updates status (e.g. "driver start", "finish/tiba di tujuan").
## Test backend API
**To test backend, use endpoint: `apitest.semestaterpadu.my.id`**
All apps and the backend panel use the same test API base URL:
| Component | Config location | Base URL | API base |
|-------------|-----------------|----------|----------|
| User app | `OnTime_User_live` → `Constants.java` | `https://apitest.semestaterpadu.my.id/` | `.../api/` |
| Driver app | `OnTime_Driver_live` → `Constants.java` | `https://apitest.semestaterpadu.my.id/` | `.../api/` |
| Backend | `backendpanel/application/config/config.php` → `$config['base_url']` | `https://apitest.semestaterpadu.my.id/` | same |
Example test endpoints:
- Base: `https://apitest.semestaterpadu.my.id/`
- API (login, transaksi, etc.): `https://apitest.semestaterpadu.my.id/api/`
- FCM send: `POST https://apitest.semestaterpadu.my.id/api/notification/send_generic`
---
## Firebase: same project for User, Backend, Driver
- **Backend** – Single FCM project: uses `fcm_v1_helper` (HTTP v1) with service account (e.g. `FCM_CREDENTIALS_PATH` / `FCM_PROJECT_ID`). All sends go through **backend** `api/notification/send_generic` (POST).
- **User app** – Receives FCM via `FirebaseMessagingService`; **sends** by calling same backend `Constants.CONNECTION + "notification/send_generic"` with `target` = driver token and `data` = `DriverRequest` (order offer).
- **Driver app** – Receives FCM via `FirebaseMessagingService`; **sends** by calling same backend `Constants.CONNECTION + "notification/send_generic"` with `target` = user token and `data` = `OrderFCM` (accept/start/finish).
Both apps use the same `BASE_URL` (e.g. `https://apitest.semestaterpadu.my.id/`) and same endpoint, so they share the same Firebase project and backend.
## FCM type and payload alignment
| Type | Meaning | Sender | Receiver | Key payload |
|------|-----------|----------|----------|-------------|
| 1 | Order | User→Driver: `DriverRequest` (offer); Driver→User: `OrderFCM` (accept/start/finish/cancel) | Both | id_transaksi, id_driver, response ("2"/"3"/"4"/"5"), plus offer fields for driver |
| 2 | Chat | User/Driver | User/Driver | chat fields |
| 3 | Other | Backend | User/Driver | title, message |
| 4 | Other2 | Backend | User/Driver | title, message |
Backend `send_generic_to_token` / `send_generic_to_topic` flatten `data` and stringify values so FCM receives a flat string map.
## Driver & merchant matching: purely GPS (no region)
- **Driver nearby** (`list_ride` / `list_car`): Uses only **real GPS** – driver position from `config_driver` (updated by driver app’s `update_location`) and user/customer position from the request. **No region/wilayah** is used. Radius is `fitur.jarak_minimum` (km), with a minimum of 10 km.
- **Merchant nearby**: Uses only **real GPS** – `merchant.latitude_merchant`, `merchant.longitude_merchant` and user’s lat/long. **No region** filter. Distance cap is `fitur.jarak_minimum`.
Driver’s `wilayah` (partner_region) is only used in the admin panel for display; it is **not** used when finding nearby drivers or merchants for the user.
---
## "Tidak ada pengemudi di sekitar Anda" – checklist
If the user always sees "Maaf, tidak ada pengemudi di sekitar Anda" even when a driver is close:
1. **Driver must be "online"** – In the driver app the driver must be in "online" / status 1 mode. The backend only returns drivers where `config_driver.status = '1'`.
2. **Driver location** – The driver app sends location via `driver/update_location`. Ensure the driver has opened the app and location permission is granted so `config_driver` has recent latitude/longitude.
3. **Backend radius** – The backend uses `fitur.jarak_minimum` (km). A minimum radius of **10 km** is now applied so that small values in the admin panel (e.g. 1–2 km) still show drivers within 10 km. Admin can set **Services → jarak_minimum** to a larger value (e.g. 15 or 20 km) if needed.
4. **User app** – If the first fetch was slow or location was not ready, the user can tap **Order** anyway: the app will **refetch** nearby drivers once (using pickup or last location) and then either place the order or show the message.
## Fixes applied
- **Backend:** Implemented `send_generic_to_token` and `send_generic_to_topic` in `Notification_model` using `fcm_v1_send`; controller loads `fcm_v1_helper` so generic FCM from both apps works.
- **User MessagingService:** Null-safe handling for `getData()` and `type`; broadcast bundle now includes `code`, `response`, `id_transaksi`, `id_driver` so ProgressActivity can update when driver sends start/finish.
- **ProgressActivity:** BroadcastReceiver only applies updates when `id_transaksi` matches; updates local `response` and calls `orderHandler(code)` so status and flow (e.g. sampai tujuan) stay correct.
- **Driver MessagingService:** Null-safe handling for `getData()`, `type`, and `harga` to avoid NPE/crash on malformed FCM.
- **"No driver near you":** Backend `get_driver_ride` now uses a minimum radius of 10 km (`GREATEST(COALESCE(f.jarak_minimum, 10), 10)`) so drivers within 10 km are found even if the fitur’s `jarak_minimum` is set very small. User app: when the user taps Order and the driver list is empty, the app refetches nearby drivers once (using pickup or last location) and then proceeds or shows the message.
Regular → Executable
View File
File mode changed.
View File
File mode changed.
+8 -8
View File
@@ -8,12 +8,12 @@ android {
// Use default debug keystore on this machine // Use default debug keystore on this machine
} }
} }
compileSdk 33 compileSdk 34
defaultConfig { defaultConfig {
applicationId "id.ontime.driver" applicationId "id.ontime.driver"
minSdkVersion 21 minSdkVersion 23
targetSdkVersion 33 targetSdkVersion 34
versionCode 10 versionCode 10
versionName '1.1.0' versionName '1.1.0'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -55,11 +55,11 @@ dependencies {
implementation 'com.google.android.libraries.places:places:2.5.0' implementation 'com.google.android.libraries.places:places:2.5.0'
implementation 'com.google.android.gms:play-services-maps:18.0.2' implementation 'com.google.android.gms:play-services-maps:18.0.2'
implementation 'com.google.android.gms:play-services-auth:20.1.0' implementation 'com.google.android.gms:play-services-auth:20.1.0'
implementation 'com.google.firebase:firebase-database:20.0.3' implementation platform('com.google.firebase:firebase-bom:33.1.0')
implementation 'com.google.firebase:firebase-storage:20.0.0' implementation 'com.google.firebase:firebase-database'
implementation 'com.google.firebase:firebase-core:20.1.0' implementation 'com.google.firebase:firebase-storage'
implementation 'com.google.firebase:firebase-messaging:20.2.0' implementation 'com.google.firebase:firebase-messaging'
implementation 'com.google.firebase:firebase-auth:19.3.1' implementation 'com.google.firebase:firebase-auth'
implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'com.google.android.material:material:1.5.0' implementation 'com.google.android.material:material:1.5.0'
View File
File mode changed.
View File
File mode changed.
View File
File mode changed.
Vendored Regular → Executable
View File
File mode changed.
View File
File mode changed.
View File
File mode changed.
View File
File mode changed.
+26
View File
@@ -0,0 +1,26 @@
Alias name: ontimekeystore
Creation date: Oct 26, 2022
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Ontime Apps
Issuer: CN=Ontime Apps
Serial number: 39fdbb23
Valid from: Wed Oct 26 11:27:28 WIB 2022 until: Thu Oct 13 11:27:28 WIB 2072
Certificate fingerprints:
SHA1: DC:13:DA:DC:9F:5F:A8:AD:3A:6C:F7:1C:5C:94:55:BB:AD:2E:F6:10
SHA256: 73:17:40:D3:E1:63:4A:2E:AB:C1:BA:0A:65:BD:A4:77:A2:FB:CC:F5:46:C1:87:3D:AE:6D:87:4B:94:5C:2B:66
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
Extensions:
#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 04 8E EC F7 50 72 86 16 C2 AF 1C 2B A9 B4 63 47 ....Pr.....+..cG
0010: 71 55 24 44 qU$D
]
]
-1
View File
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="id.ontime.driver"
android:installLocation="auto"> android:installLocation="auto">
<uses-permission android:name="android.permission.CALL_PHONE" /> <uses-permission android:name="android.permission.CALL_PHONE" />
View File
File mode changed.
View File
File mode changed.
View File
File mode changed.
View File
File mode changed.
View File
File mode changed.
View File
File mode changed.
View File
File mode changed.
View File
File mode changed.
View File
File mode changed.
View File
File mode changed.
View File
File mode changed.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
@@ -1359,7 +1359,9 @@ public class ChatActivity extends AppCompatActivity {
} }
private void sendMessageToDriver(final String regIDTujuan, final Chat chat) { private void sendMessageToDriver(final String regIDTujuan, final Chat chat) {
if (regIDTujuan == null || regIDTujuan.trim().isEmpty()) {
return;
}
final FCMMessage message = new FCMMessage(); final FCMMessage message = new FCMMessage();
message.setTo(regIDTujuan); message.setTo(regIDTujuan);
message.setData(chat); message.setData(chat);
File mode changed.
File mode changed.
File mode changed.
File mode changed.
@@ -31,8 +31,8 @@ import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException; import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;
import com.google.firebase.auth.PhoneAuthCredential; import com.google.firebase.auth.PhoneAuthCredential;
import com.google.firebase.auth.PhoneAuthProvider; import com.google.firebase.auth.PhoneAuthProvider;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.iid.FirebaseInstanceId;
import id.ontime.driver.constants.BaseApp; import id.ontime.driver.constants.BaseApp;
import id.ontime.driver.R; import id.ontime.driver.R;
import id.ontime.driver.constants.Constants; import id.ontime.driver.constants.Constants;
@@ -478,7 +478,7 @@ public class LoginActivity extends AppCompatActivity {
private void onSignInClick() { private void onSignInClick() {
progressshow(); progressshow();
LoginRequestJson request = new LoginRequestJson(); final LoginRequestJson request = new LoginRequestJson();
String emailText = email != null ? email.getText().toString().trim() : ""; String emailText = email != null ? email.getText().toString().trim() : "";
String phoneDigits = phoneText.getText().toString().trim(); String phoneDigits = phoneText.getText().toString().trim();
@@ -490,13 +490,47 @@ public class LoginActivity extends AppCompatActivity {
request.setNotelepon(countryCode.getText().toString().replace("+", "") + phoneDigits); request.setNotelepon(countryCode.getText().toString().replace("+", "") + phoneDigits);
} }
request.setPassword(password.getText().toString()); request.setPassword(password.getText().toString());
try {
FirebaseInstanceId token = FirebaseInstanceId.getInstance(); // Enforce FCM token: login ONLY proceeds when we have a non-empty Firebase token.
request.setRegId(token.getToken()); // 1) Try to use token that was already stored in Realm (BaseApp / MessagingService.onNewToken).
} catch (Exception e) { io.realm.Realm realm = BaseApp.getInstance(this).getRealmInstance();
request.setRegId(null); FirebaseToken storedToken = realm.where(FirebaseToken.class).findFirst();
if (storedToken != null && storedToken.getTokenId() != null && !storedToken.getTokenId().isEmpty()) {
request.setRegId(storedToken.getTokenId());
doLoginRequest(request, emailText);
return;
} }
// 2) If not yet stored, request a fresh token from FirebaseMessaging.
try {
FirebaseMessaging.getInstance().getToken()
.addOnCompleteListener(task -> {
if (task.isSuccessful() && task.getResult() != null && !task.getResult().isEmpty()) {
String fcmToken = task.getResult();
request.setRegId(fcmToken);
// Persist so future logins can use the cached value.
realm.executeTransaction(r -> {
FirebaseToken t = new FirebaseToken(fcmToken);
r.delete(FirebaseToken.class);
r.copyToRealm(t);
});
doLoginRequest(request, emailText);
} else {
// Token is required: stop login and show message.
progresshide();
notif("Firebase token not ready, please check your network and try again.");
}
});
} catch (IllegalStateException e) {
// Firebase not initialized: block login instead of proceeding without token.
progresshide();
notif("Firebase is not initialized, cannot login. Please restart the app.");
}
}
private void doLoginRequest(LoginRequestJson request, String emailText) {
String basicUser = !TextUtils.isEmpty(emailText) ? emailText : request.getNotelepon(); String basicUser = !TextUtils.isEmpty(emailText) ? emailText : request.getNotelepon();
DriverService service = ServiceGenerator.createService(DriverService.class, basicUser, request.getPassword()); DriverService service = ServiceGenerator.createService(DriverService.class, basicUser, request.getPassword());
service.login(request).enqueue(new Callback<LoginResponseJson>() { service.login(request).enqueue(new Callback<LoginResponseJson>() {
File mode changed.
@@ -372,6 +372,7 @@ public class MainActivity extends AppCompatActivity {
public void onLocationChanged(Location location) { public void onLocationChanged(Location location) {
if (location != null) { if (location != null) {
User loginUser = BaseApp.getInstance(this).getLoginUser(); User loginUser = BaseApp.getInstance(this).getLoginUser();
if (loginUser == null) return;
DriverService service = ServiceGenerator.createService(DriverService.class, loginUser.getEmail(), loginUser.getPassword()); DriverService service = ServiceGenerator.createService(DriverService.class, loginUser.getEmail(), loginUser.getPassword());
UpdateLocationRequestJson param = new UpdateLocationRequestJson(); UpdateLocationRequestJson param = new UpdateLocationRequestJson();
@@ -379,6 +380,12 @@ public class MainActivity extends AppCompatActivity {
param.setLatitude(String.valueOf(location.getLatitude())); param.setLatitude(String.valueOf(location.getLatitude()));
param.setLongitude(String.valueOf(location.getLongitude())); param.setLongitude(String.valueOf(location.getLongitude()));
param.setBearing(String.valueOf(location.getBearing())); param.setBearing(String.valueOf(location.getBearing()));
// Include FCM token so backend always has latest reg_id for push notifications
io.realm.Realm realm = BaseApp.getInstance(this).getRealmInstance();
id.ontime.driver.models.FirebaseToken ft = realm.where(id.ontime.driver.models.FirebaseToken.class).findFirst();
if (ft != null && ft.getTokenId() != null && !ft.getTokenId().isEmpty()) {
param.setRegId(ft.getTokenId());
}
service.updatelocation(param).enqueue(new Callback<ResponseJson>() { service.updatelocation(param).enqueue(new Callback<ResponseJson>() {
@Override @Override
File mode changed.
File mode changed.
File mode changed.
@@ -54,7 +54,7 @@ import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;
import com.google.firebase.auth.FirebaseUser; import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.auth.PhoneAuthCredential; import com.google.firebase.auth.PhoneAuthCredential;
import com.google.firebase.auth.PhoneAuthProvider; import com.google.firebase.auth.PhoneAuthProvider;
import com.google.firebase.iid.FirebaseInstanceId; import com.google.firebase.messaging.FirebaseMessaging;
import id.ontime.driver.R; import id.ontime.driver.R;
import id.ontime.driver.constants.Constants; import id.ontime.driver.constants.Constants;
import id.ontime.driver.json.JobResponseJson; import id.ontime.driver.json.JobResponseJson;
@@ -164,7 +164,12 @@ public class RegisterActivity extends AppCompatActivity {
rlnotif2 = findViewById(R.id.rlnotif2); rlnotif2 = findViewById(R.id.rlnotif2);
textnotif2 = findViewById(R.id.textnotif2); textnotif2 = findViewById(R.id.textnotif2);
confirmButton = findViewById(R.id.buttonconfirm); confirmButton = findViewById(R.id.buttonconfirm);
token = FirebaseInstanceId.getInstance().getToken(); // FCM v1: async token via FirebaseMessaging.getToken()
FirebaseMessaging.getInstance().getToken().addOnCompleteListener(task -> {
if (task.isSuccessful() && task.getResult() != null && !task.getResult().isEmpty()) {
token = task.getResult();
}
});
numOne = findViewById(R.id.numone); numOne = findViewById(R.id.numone);
numTwo = findViewById(R.id.numtwo); numTwo = findViewById(R.id.numtwo);
numThree = findViewById(R.id.numthree); numThree = findViewById(R.id.numthree);
File mode changed.
File mode changed.
File mode changed.
File mode changed.
+27 -14
View File
@@ -16,8 +16,8 @@ import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationRequest; import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices; import com.google.android.gms.location.LocationServices;
import com.google.firebase.FirebaseApp; import com.google.firebase.FirebaseApp;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.messaging.FirebaseMessaging; import com.google.firebase.messaging.FirebaseMessaging;
import android.util.Log;
import id.ontime.driver.json.UpdateLocationRequestJson; import id.ontime.driver.json.UpdateLocationRequestJson;
import id.ontime.driver.utils.MyLocationService; import id.ontime.driver.utils.MyLocationService;
import id.ontime.driver.utils.api.ServiceGenerator; import id.ontime.driver.utils.api.ServiceGenerator;
@@ -71,22 +71,29 @@ public class BaseApp extends Application {
// realmInstance = Realm.getInstance(config); // realmInstance = Realm.getInstance(config);
realmInstance = Realm.getDefaultInstance(); realmInstance = Realm.getDefaultInstance();
// Best-effort Firebase initialization and token registration for driver. // FCM v1: async token fetch and topic subscribe (reference: test app).
// If Firebase is not configured, ignore failures so the app can still run.
try { try {
FirebaseApp app = FirebaseApp.initializeApp(this); FirebaseApp app = FirebaseApp.initializeApp(this);
if (app != null) { if (app != null) {
String fcmToken = FirebaseInstanceId.getInstance().getToken(); FirebaseMessaging.getInstance().getToken()
if (fcmToken != null) { .addOnCompleteListener(task -> {
FirebaseToken token = new FirebaseToken(fcmToken); if (task.isSuccessful() && task.getResult() != null && !task.getResult().isEmpty()) {
FirebaseMessaging.getInstance().subscribeToTopic("ouride"); String fcmToken = task.getResult();
FirebaseMessaging.getInstance().subscribeToTopic("driver"); Log.d("FCM_TOKEN", "BaseApp init token=" + fcmToken);
FirebaseToken token = new FirebaseToken(fcmToken);
realmInstance.beginTransaction(); FirebaseMessaging.getInstance().subscribeToTopic("ouride");
realmInstance.delete(FirebaseToken.class); FirebaseMessaging.getInstance().subscribeToTopic("driver");
realmInstance.copyToRealm(token); try {
realmInstance.commitTransaction(); realmInstance.beginTransaction();
} realmInstance.delete(FirebaseToken.class);
realmInstance.copyToRealm(token);
realmInstance.commitTransaction();
} catch (Exception e) {
if (realmInstance.isInTransaction()) realmInstance.cancelTransaction();
e.printStackTrace();
}
}
});
} }
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
e.printStackTrace(); e.printStackTrace();
@@ -160,6 +167,7 @@ public class BaseApp extends Application {
public void onLocationChanged(Location location) { public void onLocationChanged(Location location) {
if (location != null) { if (location != null) {
User loginUser = getLoginUser(); User loginUser = getLoginUser();
if (loginUser == null) return;
DriverService service = ServiceGenerator.createService(DriverService.class, loginUser.getEmail(), loginUser.getPassword()); DriverService service = ServiceGenerator.createService(DriverService.class, loginUser.getEmail(), loginUser.getPassword());
UpdateLocationRequestJson param = new UpdateLocationRequestJson(); UpdateLocationRequestJson param = new UpdateLocationRequestJson();
@@ -167,6 +175,11 @@ public class BaseApp extends Application {
param.setLatitude(String.valueOf(location.getLatitude())); param.setLatitude(String.valueOf(location.getLatitude()));
param.setLongitude(String.valueOf(location.getLongitude())); param.setLongitude(String.valueOf(location.getLongitude()));
param.setBearing(String.valueOf(location.getBearing())); param.setBearing(String.valueOf(location.getBearing()));
// Include FCM token so backend always has latest reg_id for push notifications
FirebaseToken ft = realmInstance.where(FirebaseToken.class).findFirst();
if (ft != null && ft.getTokenId() != null && !ft.getTokenId().isEmpty()) {
param.setRegId(ft.getTokenId());
}
service.updatelocation(param).enqueue(new Callback<ResponseJson>() { service.updatelocation(param).enqueue(new Callback<ResponseJson>() {
@Override @Override
@@ -6,6 +6,7 @@ import java.util.Locale;
public class Constants { public class Constants {
/** Test backend API base URL (api/, notification/send_generic, etc.). */
private static final String BASE_URL = "https://apitest.semestaterpadu.my.id/"; private static final String BASE_URL = "https://apitest.semestaterpadu.my.id/";
public static final String URL = "https://panic.on-time.id/"; //panicButton public static final String URL = "https://panic.on-time.id/"; //panicButton
public static final String CONNECTION = BASE_URL + "api/"; public static final String CONNECTION = BASE_URL + "api/";
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
Loaded 100 of 9490 files, more files were not shown because too many files have changed in this diff. Show more