add flutter

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

0
.gitignore vendored Normal file → Executable file
View File

47
BUILD_DEBUG_APK.md Normal file
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
FCM_V1_PROCESS.md Normal file
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 apps `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
JDK_REQUIREMENTS.md Normal file
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**).

0
LICENSE Normal file → Executable file
View File

78
ORDERING_WORKFLOW.md Normal file
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 apps `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 users lat/long. **No region** filter. Distance cap is `fitur.jarak_minimum`.
Drivers `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. 12 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 fiturs `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.

0
OnTime_Driver_live/.gitignore vendored Normal file → Executable file
View File

0
OnTime_Driver_live/app/.gitignore vendored Normal file → Executable file
View File

16
OnTime_Driver_live/app/build.gradle Normal file → Executable file
View File

@@ -8,12 +8,12 @@ android {
// Use default debug keystore on this machine
}
}
compileSdk 33
compileSdk 34
defaultConfig {
applicationId "id.ontime.driver"
minSdkVersion 21
targetSdkVersion 33
minSdkVersion 23
targetSdkVersion 34
versionCode 10
versionName '1.1.0'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -55,11 +55,11 @@ dependencies {
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-auth:20.1.0'
implementation 'com.google.firebase:firebase-database:20.0.3'
implementation 'com.google.firebase:firebase-storage:20.0.0'
implementation 'com.google.firebase:firebase-core:20.1.0'
implementation 'com.google.firebase:firebase-messaging:20.2.0'
implementation 'com.google.firebase:firebase-auth:19.3.1'
implementation platform('com.google.firebase:firebase-bom:33.1.0')
implementation 'com.google.firebase:firebase-database'
implementation 'com.google.firebase:firebase-storage'
implementation 'com.google.firebase:firebase-messaging'
implementation 'com.google.firebase:firebase-auth'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'com.google.android.material:material:1.5.0'

0
OnTime_Driver_live/app/butterknife-proguard-rules.pro Normal file → Executable file
View File

0
OnTime_Driver_live/app/guava-proguard-rules.pro Normal file → Executable file
View File

0
OnTime_Driver_live/app/okhttp3-proguard-rules.pro Normal file → Executable file
View File

0
OnTime_Driver_live/app/proguard-rules.pro vendored Normal file → Executable file
View File

0
OnTime_Driver_live/app/release/output-metadata.json Normal file → Executable file
View File

0
OnTime_Driver_live/app/retrofit-proguard-rules.pro Normal file → Executable file
View File

0
OnTime_Driver_live/app/saripaar-proguard-rules.pro Normal file → Executable file
View File

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
OnTime_Driver_live/app/src/main/AndroidManifest.xml Normal file → Executable file
View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="id.ontime.driver"
android:installLocation="auto">
<uses-permission android:name="android.permission.CALL_PHONE" />

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -1359,7 +1359,9 @@ public class ChatActivity extends AppCompatActivity {
}
private void sendMessageToDriver(final String regIDTujuan, final Chat chat) {
if (regIDTujuan == null || regIDTujuan.trim().isEmpty()) {
return;
}
final FCMMessage message = new FCMMessage();
message.setTo(regIDTujuan);
message.setData(chat);

View File

@@ -31,8 +31,8 @@ import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;
import com.google.firebase.auth.PhoneAuthCredential;
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.R;
import id.ontime.driver.constants.Constants;
@@ -478,7 +478,7 @@ public class LoginActivity extends AppCompatActivity {
private void onSignInClick() {
progressshow();
LoginRequestJson request = new LoginRequestJson();
final LoginRequestJson request = new LoginRequestJson();
String emailText = email != null ? email.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.setPassword(password.getText().toString());
try {
FirebaseInstanceId token = FirebaseInstanceId.getInstance();
request.setRegId(token.getToken());
} catch (Exception e) {
request.setRegId(null);
// Enforce FCM token: login ONLY proceeds when we have a non-empty Firebase token.
// 1) Try to use token that was already stored in Realm (BaseApp / MessagingService.onNewToken).
io.realm.Realm realm = BaseApp.getInstance(this).getRealmInstance();
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();
DriverService service = ServiceGenerator.createService(DriverService.class, basicUser, request.getPassword());
service.login(request).enqueue(new Callback<LoginResponseJson>() {

View File

@@ -372,6 +372,7 @@ public class MainActivity extends AppCompatActivity {
public void onLocationChanged(Location location) {
if (location != null) {
User loginUser = BaseApp.getInstance(this).getLoginUser();
if (loginUser == null) return;
DriverService service = ServiceGenerator.createService(DriverService.class, loginUser.getEmail(), loginUser.getPassword());
UpdateLocationRequestJson param = new UpdateLocationRequestJson();
@@ -379,6 +380,12 @@ public class MainActivity extends AppCompatActivity {
param.setLatitude(String.valueOf(location.getLatitude()));
param.setLongitude(String.valueOf(location.getLongitude()));
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>() {
@Override

View File

@@ -54,7 +54,7 @@ import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.auth.PhoneAuthCredential;
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.constants.Constants;
import id.ontime.driver.json.JobResponseJson;
@@ -164,7 +164,12 @@ public class RegisterActivity extends AppCompatActivity {
rlnotif2 = findViewById(R.id.rlnotif2);
textnotif2 = findViewById(R.id.textnotif2);
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);
numTwo = findViewById(R.id.numtwo);
numThree = findViewById(R.id.numthree);

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.LocationServices;
import com.google.firebase.FirebaseApp;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.messaging.FirebaseMessaging;
import android.util.Log;
import id.ontime.driver.json.UpdateLocationRequestJson;
import id.ontime.driver.utils.MyLocationService;
import id.ontime.driver.utils.api.ServiceGenerator;
@@ -71,22 +71,29 @@ public class BaseApp extends Application {
// realmInstance = Realm.getInstance(config);
realmInstance = Realm.getDefaultInstance();
// Best-effort Firebase initialization and token registration for driver.
// If Firebase is not configured, ignore failures so the app can still run.
// FCM v1: async token fetch and topic subscribe (reference: test app).
try {
FirebaseApp app = FirebaseApp.initializeApp(this);
if (app != null) {
String fcmToken = FirebaseInstanceId.getInstance().getToken();
if (fcmToken != null) {
FirebaseToken token = new FirebaseToken(fcmToken);
FirebaseMessaging.getInstance().subscribeToTopic("ouride");
FirebaseMessaging.getInstance().subscribeToTopic("driver");
realmInstance.beginTransaction();
realmInstance.delete(FirebaseToken.class);
realmInstance.copyToRealm(token);
realmInstance.commitTransaction();
}
FirebaseMessaging.getInstance().getToken()
.addOnCompleteListener(task -> {
if (task.isSuccessful() && task.getResult() != null && !task.getResult().isEmpty()) {
String fcmToken = task.getResult();
Log.d("FCM_TOKEN", "BaseApp init token=" + fcmToken);
FirebaseToken token = new FirebaseToken(fcmToken);
FirebaseMessaging.getInstance().subscribeToTopic("ouride");
FirebaseMessaging.getInstance().subscribeToTopic("driver");
try {
realmInstance.beginTransaction();
realmInstance.delete(FirebaseToken.class);
realmInstance.copyToRealm(token);
realmInstance.commitTransaction();
} catch (Exception e) {
if (realmInstance.isInTransaction()) realmInstance.cancelTransaction();
e.printStackTrace();
}
}
});
}
} catch (IllegalStateException e) {
e.printStackTrace();
@@ -160,6 +167,7 @@ public class BaseApp extends Application {
public void onLocationChanged(Location location) {
if (location != null) {
User loginUser = getLoginUser();
if (loginUser == null) return;
DriverService service = ServiceGenerator.createService(DriverService.class, loginUser.getEmail(), loginUser.getPassword());
UpdateLocationRequestJson param = new UpdateLocationRequestJson();
@@ -167,6 +175,11 @@ public class BaseApp extends Application {
param.setLatitude(String.valueOf(location.getLatitude()));
param.setLongitude(String.valueOf(location.getLongitude()));
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>() {
@Override

View File

@@ -6,6 +6,7 @@ import java.util.Locale;
public class Constants {
/** Test backend API base URL (api/, notification/send_generic, etc.). */
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 CONNECTION = BASE_URL + "api/";

Some files were not shown because too many files have changed in this diff Show More