diff --git a/app/build.gradle b/app/build.gradle
index b227441..4bd256b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -45,6 +45,7 @@ dependencies {
implementation 'com.google.firebase:firebase-storage:20.0.1'
implementation 'com.google.android.gms:play-services-maps:18.1.0'
implementation 'com.google.firebase:firebase-database:20.0.4'
+ implementation 'com.google.firebase:firebase-messaging:23.0.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a91a979..f0df2d0 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,12 +3,14 @@
xmlns:tools="http://schemas.android.com/tools">
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png
new file mode 100644
index 0000000..3e42340
Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ
diff --git a/app/src/main/java/com/capstone/foodify/shipper/API/FirebaseMessagingAPI.java b/app/src/main/java/com/capstone/foodify/shipper/API/FirebaseMessagingAPI.java
new file mode 100644
index 0000000..5c05892
--- /dev/null
+++ b/app/src/main/java/com/capstone/foodify/shipper/API/FirebaseMessagingAPI.java
@@ -0,0 +1,28 @@
+package com.capstone.foodify.shipper.API;
+
+import com.capstone.foodify.shipper.Model.FirebaseMessaging.FirebaseMessaging;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import retrofit2.Call;
+import retrofit2.Retrofit;
+import retrofit2.converter.gson.GsonConverterFactory;
+import retrofit2.http.Body;
+import retrofit2.http.Headers;
+import retrofit2.http.POST;
+
+public interface FirebaseMessagingAPI {
+ Gson gson = new GsonBuilder().setDateFormat("HH:mm:ss dd-MM-yyyy").setLenient().create();
+
+ FirebaseMessagingAPI apiService = new Retrofit.Builder()
+ .baseUrl("https://fcm.googleapis.com/").addConverterFactory(GsonConverterFactory.create(gson))
+ .build()
+ .create(FirebaseMessagingAPI.class);
+
+ @Headers({
+ "Content-Type: application/json",
+ "Authorization: Bearer AAAAMcAdgF0:APA91bE9OPI9SBgvFV_8KijtnWEQ5nx4PyOhrY61u8BRv5xKnPzhiqCqcLvz4WVYSgVNLHWjUiOJBaxhIiwhwB6YsAPXDn1aNfbKx-q8FvypzW7lmiKC8vOxFpYUAh8YaItk4Vf-eZ_F"
+ })
+ @POST("fcm/send")
+ Call sendNotification(@Body FirebaseMessaging content);
+}
diff --git a/app/src/main/java/com/capstone/foodify/shipper/API/FoodApi.java b/app/src/main/java/com/capstone/foodify/shipper/API/FoodApi.java
index f142d45..85415ab 100644
--- a/app/src/main/java/com/capstone/foodify/shipper/API/FoodApi.java
+++ b/app/src/main/java/com/capstone/foodify/shipper/API/FoodApi.java
@@ -1,5 +1,6 @@
package com.capstone.foodify.shipper.API;
+import com.capstone.foodify.shipper.Common;
import com.capstone.foodify.shipper.Model.Shipper;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@@ -15,7 +16,7 @@ public interface FoodApi {
Gson gson = new GsonBuilder().setDateFormat("HH:mm:ss dd-MM-yyyy").setLenient().create();
FoodApi apiService = new Retrofit.Builder()
- .baseUrl("http://192.168.1.183:8080/api/").addConverterFactory(GsonConverterFactory.create(gson))
+ .baseUrl(Common.BASE_URL).addConverterFactory(GsonConverterFactory.create(gson))
.build()
.create(FoodApi.class);
diff --git a/app/src/main/java/com/capstone/foodify/shipper/API/FoodApiToken.java b/app/src/main/java/com/capstone/foodify/shipper/API/FoodApiToken.java
index 1563318..0a32d76 100644
--- a/app/src/main/java/com/capstone/foodify/shipper/API/FoodApiToken.java
+++ b/app/src/main/java/com/capstone/foodify/shipper/API/FoodApiToken.java
@@ -39,7 +39,7 @@ public interface FoodApiToken {
FoodApiToken apiService = new Retrofit.Builder()
.client(client)
- .baseUrl("http://192.168.1.183:8080/api/").addConverterFactory(GsonConverterFactory.create(gson))
+ .baseUrl(Common.BASE_URL).addConverterFactory(GsonConverterFactory.create(gson))
.build()
.create(FoodApiToken.class);
@@ -56,4 +56,6 @@ public interface FoodApiToken {
@PUT("users/{userId}/orders/{orderId}/status")
Call changeStatusOrder(@Path("userId") int userId, @Path("orderId") int orderId, @Query("status") String status);
+ @PUT("users/{userId}/update/fcm")
+ Call updateFCMToken(@Path("userId") int userId, @Body String token);
}
diff --git a/app/src/main/java/com/capstone/foodify/shipper/API/TokenFCMFirebaseAPI.java b/app/src/main/java/com/capstone/foodify/shipper/API/TokenFCMFirebaseAPI.java
new file mode 100644
index 0000000..b740f2b
--- /dev/null
+++ b/app/src/main/java/com/capstone/foodify/shipper/API/TokenFCMFirebaseAPI.java
@@ -0,0 +1,20 @@
+package com.capstone.foodify.shipper.API;
+
+import com.capstone.foodify.shipper.Common;
+
+import retrofit2.Call;
+import retrofit2.Retrofit;
+import retrofit2.converter.scalars.ScalarsConverterFactory;
+import retrofit2.http.GET;
+import retrofit2.http.Path;
+
+public interface TokenFCMFirebaseAPI {
+
+ TokenFCMFirebaseAPI apiService = new Retrofit.Builder()
+ .baseUrl(Common.BASE_URL).addConverterFactory(ScalarsConverterFactory.create())
+ .build()
+ .create(TokenFCMFirebaseAPI.class);
+
+ @GET("users/{userId}/fcm")
+ Call getTokenFCM(@Path("userId") int userId);
+}
diff --git a/app/src/main/java/com/capstone/foodify/shipper/Activity/AccountAndProfileActivity.java b/app/src/main/java/com/capstone/foodify/shipper/Activity/AccountAndProfileActivity.java
index 324000a..9143db5 100644
--- a/app/src/main/java/com/capstone/foodify/shipper/Activity/AccountAndProfileActivity.java
+++ b/app/src/main/java/com/capstone/foodify/shipper/Activity/AccountAndProfileActivity.java
@@ -14,15 +14,12 @@ import android.content.ContentResolver;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.os.CountDownTimer;
import android.view.View;
import android.webkit.MimeTypeMap;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
import android.widget.Toast;
import com.capstone.foodify.shipper.API.FoodApiToken;
@@ -42,10 +39,6 @@ import com.google.firebase.storage.StorageReference;
import com.google.firebase.storage.UploadTask;
import com.makeramen.roundedimageview.RoundedImageView;
import com.squareup.picasso.Picasso;
-import com.thecode.aestheticdialogs.AestheticDialog;
-import com.thecode.aestheticdialogs.DialogAnimation;
-import com.thecode.aestheticdialogs.DialogStyle;
-import com.thecode.aestheticdialogs.DialogType;
import java.text.SimpleDateFormat;
import java.util.Calendar;
@@ -225,7 +218,7 @@ public class AccountAndProfileActivity extends AppCompatActivity {
@Override
public void onFailure(Call call, Throwable t) {
- Common.showErrorServerNotification(AccountAndProfileActivity.this, "Không thể cập nhật thông tin, vui lòng thử lại sau!");
+ Common.showErrorDialog(AccountAndProfileActivity.this, "Không thể cập nhật thông tin, vui lòng thử lại sau!");
progressLayout.setVisibility(View.GONE);
}
});
diff --git a/app/src/main/java/com/capstone/foodify/shipper/Activity/MainActivity.java b/app/src/main/java/com/capstone/foodify/shipper/Activity/MainActivity.java
index 5cfea16..66327e6 100644
--- a/app/src/main/java/com/capstone/foodify/shipper/Activity/MainActivity.java
+++ b/app/src/main/java/com/capstone/foodify/shipper/Activity/MainActivity.java
@@ -2,12 +2,19 @@ package com.capstone.foodify.shipper.Activity;
import android.Manifest;
import android.annotation.SuppressLint;
+import android.content.ComponentName;
+import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.ColorStateList;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
+import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
@@ -15,18 +22,34 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.AppCompatCheckBox;
import androidx.core.app.ActivityCompat;
+import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
import androidx.viewpager2.widget.ViewPager2;
+import com.capstone.foodify.shipper.API.FoodApiToken;
import com.capstone.foodify.shipper.BuildConfig;
+import com.capstone.foodify.shipper.Common;
+import com.capstone.foodify.shipper.Model.CustomResponse;
import com.capstone.foodify.shipper.R;
import com.capstone.foodify.shipper.ViewPagerAdapter;
+import com.google.android.gms.tasks.OnCompleteListener;
+import com.google.android.gms.tasks.Task;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.navigation.NavigationBarView;
+import com.google.firebase.messaging.FirebaseMessaging;
+
+import java.util.Arrays;
+import java.util.List;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
public class MainActivity extends AppCompatActivity {
+ private static final String TAG = "MainActivity";
ViewPager2 viewPager2;
ViewPagerAdapter viewPagerAdapter;
BottomNavigationView bottomNavigationView;
@@ -46,6 +69,9 @@ public class MainActivity extends AppCompatActivity {
bottomNavigation();
checkLocationPermission();
checkBackgroundLocationPermission();
+ checkNotificationPermission();
+ startPowerSaverIntent(this);
+ getTokenFCM();
}
private void bottomNavigation() {
viewPagerAdapter = new ViewPagerAdapter(this);
@@ -85,7 +111,71 @@ public class MainActivity extends AppCompatActivity {
});
}
- //Location
+ private void getTokenFCM(){
+ FirebaseMessaging.getInstance().getToken()
+ .addOnCompleteListener(new OnCompleteListener() {
+ @Override
+ public void onComplete(@NonNull Task task) {
+ if(!task.isSuccessful()){
+ Log.d(TAG, "Failed to registration token!");
+
+ return;
+ }
+
+ String token = task.getResult();
+
+ Common.FCM_TOKEN_SHIPPER = token;
+ FoodApiToken.apiService.updateFCMToken(Common.CURRENT_USER.getId(), token).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ if(response.code() != 200)
+ Toast.makeText(MainActivity.this, "Không thể cập nhật FCM Token. Mã lỗi: " + response.code(), Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ Toast.makeText(MainActivity.this, "Đã có lỗi khi kết nối đến hệ thống!", Toast.LENGTH_SHORT).show();
+ }
+ });
+
+ Log.d(TAG, "Token: " + token);
+ }
+ });
+ }
+
+ //Check notification
+ private void checkNotificationPermission(){
+ if(!NotificationManagerCompat.from(this).areNotificationsEnabled()){
+ showDialogNotificationPermission();
+ }
+ }
+
+ private void showDialogNotificationPermission() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+
+ builder.setMessage("Vui lòng bật quyền thông báo trên thiết bị của bạn để có thể cập nhật đơn một cách " +
+ "nhanh nhất!")
+ .setCancelable(false)
+ .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ openSettings();
+ dialog.cancel();
+ }
+ })
+ .setNegativeButton("Thoát", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ }
+ });
+
+ AlertDialog alertDialog = builder.create();
+ alertDialog.setTitle("Thông báo!");
+ alertDialog.show();
+ }
+
+ //Check location permission
private void checkLocationPermission(){
if(ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED){
@@ -124,33 +214,7 @@ public class MainActivity extends AppCompatActivity {
}
private void requestForPermission() {
- ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE);
- }
- private void showDialogPermission() {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
-
- builder.setMessage("Ứng dụng này cần quyền truy cập vị trí để hoạt động. Xin vui lòng cấp quyền cho ứng dụng!")
- .setCancelable(false)
- .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- openSettings();
- finishAffinity();
- System.exit(0);
- }
- })
- .setNegativeButton("Thoát", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.cancel();
- finishAffinity();
- System.exit(0);
- }
- });
-
- AlertDialog alertDialog = builder.create();
- alertDialog.setTitle("Thông báo!");
- alertDialog.show();
+ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE);
}
private void openSettings() {
@@ -167,4 +231,62 @@ public class MainActivity extends AppCompatActivity {
}
+ //Check auto start permission
+ public static List POWER_MANAGER_INTENTS = Arrays.asList(
+ new Intent().setComponent(new ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity")),
+ new Intent().setComponent(new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.AutobootManageActivity")),
+ new Intent().setComponent(new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity")),
+ new Intent().setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity")),
+ new Intent().setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.startupapp.StartupAppListActivity")),
+ new Intent().setComponent(new ComponentName("com.oppo.safe", "com.oppo.safe.permission.startup.StartupAppListActivity")),
+ new Intent().setComponent(new ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.AddWhiteListActivity")),
+ new Intent().setComponent(new ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.BgStartUpManager")),
+ new Intent().setComponent(new ComponentName("com.vivo.permissionmanager", "com.vivo.permissionmanager.activity.BgStartUpManagerActivity")),
+ new Intent().setComponent(new ComponentName("com.asus.mobilemanager", "com.asus.mobilemanager.entry.FunctionActivity")).setData(android.net.Uri.parse("mobilemanager://function/entry/AutoStart"))
+ );
+
+
+ public void startPowerSaverIntent(Context context) {
+ SharedPreferences settings = context.getSharedPreferences("ProtectedApps", Context.MODE_PRIVATE);
+ boolean skipMessage = settings.getBoolean("skipProtectedAppCheck", false);
+ if (!skipMessage) {
+ final SharedPreferences.Editor editor = settings.edit();
+ boolean foundCorrectIntent = false;
+ for (Intent intent : POWER_MANAGER_INTENTS) {
+ if (isCallable(context, intent)) {
+ foundCorrectIntent = true;
+ final AppCompatCheckBox dontShowAgain = new AppCompatCheckBox(context);
+ dontShowAgain.setText("Không hiện hộp thoại này nữa!");
+ dontShowAgain.setButtonTintList(ColorStateList.valueOf(getResources().getColor(R.color.primaryColor, null)));
+ dontShowAgain.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ editor.putBoolean("skipProtectedAppCheck", isChecked);
+ editor.apply();
+ });
+
+ new AlertDialog.Builder(context)
+ .setTitle("Phát hiện máy " + Build.MANUFACTURER + "!")
+ .setMessage(String.format("Vì một số dòng máy Trung Quốc tự động tắt chế độ chạy nền của app %s, nên cần bạn " +
+ "cho phép ứng dụng luôn tự khởi chạy ở cài đặt ứng dụng để có thể không bỏ lỡ bất kỳ thông báo nào!%n", context.getString(R.string.app_name)))
+ .setView(dontShowAgain)
+ .setPositiveButton("Đi đến cài đặt", (dialog, which) -> context.startActivity(intent))
+ .setNegativeButton("Đóng", null)
+ .show();
+ break;
+ }
+ }
+ if (!foundCorrectIntent) {
+ editor.putBoolean("skipProtectedAppCheck", true);
+ editor.apply();
+ }
+ }
+ }
+
+
+ private static boolean isCallable(Context context, Intent intent) {
+ List list = context.getPackageManager().queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ return list.size() > 0;
+ }
+
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/capstone/foodify/shipper/Activity/OrderDetailActivity.java b/app/src/main/java/com/capstone/foodify/shipper/Activity/OrderDetailActivity.java
index 54cd3f1..3ac5c63 100644
--- a/app/src/main/java/com/capstone/foodify/shipper/Activity/OrderDetailActivity.java
+++ b/app/src/main/java/com/capstone/foodify/shipper/Activity/OrderDetailActivity.java
@@ -2,27 +2,20 @@ package com.capstone.foodify.shipper.Activity;
import static com.capstone.foodify.shipper.Common.ACTION_START_LOCATION_SERVICE;
import static com.capstone.foodify.shipper.Common.ACTION_STOP_LOCATION_SERVICE;
-import static com.capstone.foodify.shipper.Common.FASTEST_UPDATE_IN_MILLISECONDS;
-import static com.capstone.foodify.shipper.Common.LOCATION_REQUEST_CODE;
import static com.capstone.foodify.shipper.Common.MAX_WAIT_TIME_IN_MILLISECONDS;
import static com.capstone.foodify.shipper.Common.REQUEST_CHECK_SETTINGS;
-import static com.capstone.foodify.shipper.Common.UPDATE_INTERVAL_IN_MILLISECONDS;
-import android.Manifest;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.PendingIntent;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
-import android.content.pm.PackageManager;
import android.location.Location;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
-import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.widget.Button;
@@ -32,18 +25,15 @@ import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
-import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;
-import androidx.core.app.ActivityCompat;
-import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.capstone.foodify.shipper.API.FoodApiToken;
import com.capstone.foodify.shipper.API.GoogleMapApi;
+import com.capstone.foodify.shipper.API.TokenFCMFirebaseAPI;
import com.capstone.foodify.shipper.Adapter.OrderDetailAdapter;
-import com.capstone.foodify.shipper.BuildConfig;
import com.capstone.foodify.shipper.Common;
import com.capstone.foodify.shipper.GoogleMap.GeofenceHelper;
import com.capstone.foodify.shipper.LocationService;
@@ -75,8 +65,6 @@ import com.thecode.aestheticdialogs.DialogType;
import com.thecode.aestheticdialogs.OnDialogClickListener;
import java.text.DecimalFormat;
-import java.util.Timer;
-import java.util.TimerTask;
import retrofit2.Call;
import retrofit2.Callback;
@@ -97,7 +85,7 @@ public class OrderDetailActivity extends AppCompatActivity {
LinearLayout layoutConfirmOrder;
private GeofencingClient geofencingClient;
private GeofenceHelper geofenceHelper;
- private float GEOFENCE_RADIUS = 150;
+ private float GEOFENCE_RADIUS = 200;
private String GEOFENCE_ID = "SOME_GEOFENCE_ID";
//Location
private FusedLocationProviderClient mFusedLocationClient;
@@ -117,7 +105,7 @@ public class OrderDetailActivity extends AppCompatActivity {
order = (Order) getIntent().getSerializableExtra("order");
}
-
+ //Set data order when shipper return back the app from Google Map
if (order == null && Common.CURRENT_ORDER != null) {
order = Common.CURRENT_ORDER;
}
@@ -131,6 +119,8 @@ public class OrderDetailActivity extends AppCompatActivity {
initComponent();
initData();
+ getFCMTokenUser();
+
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, RecyclerView.VERTICAL, false);
rcv_list_order.setLayoutManager(linearLayoutManager);
@@ -162,6 +152,8 @@ public class OrderDetailActivity extends AppCompatActivity {
Uri.parse("google.navigation:q=" + order.getLat() + "," + order.getLng() + "&mode=l"));
intent.setPackage("com.google.android.apps.maps");
startActivity(intent);
+ } else{
+ Common.showErrorDialog(OrderDetailActivity.this, "Không thể lấy được thông tin đơn, vui lòng thử lại sau!");
}
}
});
@@ -216,6 +208,25 @@ public class OrderDetailActivity extends AppCompatActivity {
}
}
+ private void getFCMTokenUser(){
+ TokenFCMFirebaseAPI.apiService.getTokenFCM(order.getUser().getId()).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ if(response.code() == 200){
+ String tempToken = response.body();
+ assert tempToken != null;
+ Common.FCM_TOKEN_USER = tempToken.replace("\"", "");
+ } else {
+ Toast.makeText(OrderDetailActivity.this, "Error code: " + response.code(), Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ Toast.makeText(OrderDetailActivity.this, Common.ERROR_CONNECT_SERVER, Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
private void updateStatus(String status) {
Common.CURRENT_LOCATION = null;
changeOrderStatus(status);
@@ -514,4 +525,12 @@ public class OrderDetailActivity extends AppCompatActivity {
changeLayoutButton(Common.CURRENT_LOCATION.getLatitude(), Common.CURRENT_LOCATION.getLongitude());
}
}
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ stopLocationService();
+ Common.CURRENT_ORDER = null;
+ Common.FCM_TOKEN_USER = null;
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/capstone/foodify/shipper/Activity/SignInActivity.java b/app/src/main/java/com/capstone/foodify/shipper/Activity/SignInActivity.java
index b298d73..4bae66d 100644
--- a/app/src/main/java/com/capstone/foodify/shipper/Activity/SignInActivity.java
+++ b/app/src/main/java/com/capstone/foodify/shipper/Activity/SignInActivity.java
@@ -5,7 +5,6 @@ import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
-import android.widget.Button;
import android.widget.Toast;
import androidx.annotation.NonNull;
@@ -207,7 +206,7 @@ public class SignInActivity extends AppCompatActivity {
@Override
public void onFailure(Call call, Throwable t) {
System.out.println("ERROR: " + t);
- Common.showErrorServerNotification(SignInActivity.this, "Không thể đăng nhập tài khoản! Vui lòng thử lại sau!");
+ Common.showErrorDialog(SignInActivity.this, "Không thể đăng nhập tài khoản! Vui lòng thử lại sau!");
}
});
} else {
diff --git a/app/src/main/java/com/capstone/foodify/shipper/Common.java b/app/src/main/java/com/capstone/foodify/shipper/Common.java
index 486cb3e..1d41672 100644
--- a/app/src/main/java/com/capstone/foodify/shipper/Common.java
+++ b/app/src/main/java/com/capstone/foodify/shipper/Common.java
@@ -25,7 +25,10 @@ public class Common {
public static Shipper CURRENT_SHIPPER = null;
public static Order CURRENT_ORDER = null;
public static String TOKEN = null;
+ public static String FCM_TOKEN_SHIPPER = null;
+ public static String FCM_TOKEN_USER = null;
public static Location CURRENT_LOCATION = null;
+ public static final String BASE_URL = "http://192.168.1.183:8080/api/";
public static final String MAP_API = "AIzaSyAY14Ic32UP26Hg6GILznOfbBihiY5BUxw";
public static final String FORMAT_DATE="dd-MM-yyyy";
public static final String VALID_EMAIL_ADDRESS_REGEX = "^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$";
@@ -51,7 +54,7 @@ public class Common {
return Typeface.createFromAsset(assetManager, "font/opensans.ttf");
}
- public static void showErrorServerNotification(Activity activity, String message){
+ public static void showErrorDialog(Activity activity, String message){
new AestheticDialog.Builder(activity, DialogStyle.RAINBOW, DialogType.ERROR)
.setTitle("LỖI!")
.setMessage(message)
diff --git a/app/src/main/java/com/capstone/foodify/shipper/Fragment/Order/ShippingOrder.java b/app/src/main/java/com/capstone/foodify/shipper/Fragment/Order/ShippingOrder.java
index 2a3c5ef..7e9bd64 100644
--- a/app/src/main/java/com/capstone/foodify/shipper/Fragment/Order/ShippingOrder.java
+++ b/app/src/main/java/com/capstone/foodify/shipper/Fragment/Order/ShippingOrder.java
@@ -86,6 +86,7 @@ public class ShippingOrder extends Fragment {
public void onResponse(Call call, Response response) {
if(response.code() == 200){
+ assert response.body() != null;
listOrders.addAll(response.body().getOrders());
orderAdapter.setData(listOrders);
diff --git a/app/src/main/java/com/capstone/foodify/shipper/GoogleMap/GeofenceBroadcastReceiver.java b/app/src/main/java/com/capstone/foodify/shipper/GoogleMap/GeofenceBroadcastReceiver.java
index bcaf9a6..9ea5d76 100644
--- a/app/src/main/java/com/capstone/foodify/shipper/GoogleMap/GeofenceBroadcastReceiver.java
+++ b/app/src/main/java/com/capstone/foodify/shipper/GoogleMap/GeofenceBroadcastReceiver.java
@@ -1,28 +1,27 @@
package com.capstone.foodify.shipper.GoogleMap;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.location.Location;
-import android.net.Uri;
import android.util.Log;
-import android.widget.Toast;
+import com.capstone.foodify.shipper.API.FirebaseMessagingAPI;
import com.capstone.foodify.shipper.Activity.OrderDetailActivity;
import com.capstone.foodify.shipper.Common;
-import com.capstone.foodify.shipper.Model.Order;
+import com.capstone.foodify.shipper.Model.FirebaseMessaging.FirebaseMessaging;
+import com.capstone.foodify.shipper.Model.FirebaseMessaging.Notification;
import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.GeofencingEvent;
-import com.google.android.gms.location.LocationResult;
import java.util.List;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
public class GeofenceBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "GeofenceBroadcastReceive";
- public static final String ACTION_PROCESS_UPDATE = "com.capstone.foodify.shipper.GoogleMap.UPDATE_LOCATION";
@Override
public void onReceive(Context context, Intent intent) {
@@ -43,9 +42,31 @@ public class GeofenceBroadcastReceiver extends BroadcastReceiver {
if(transitionType == Geofence.GEOFENCE_TRANSITION_ENTER){
notificationHelper.sendHighPriorityNotification("Đã tới khu vực giao!", "Nhấp vào đây để quay về app!", OrderDetailActivity.class);
+
+ if(Common.FCM_TOKEN_USER != null)
+ sendNotification();
}
+ }
+ private void sendNotification(){
+ Notification notification = new Notification("Thông báo!", "Đơn hàng đang gần đến bạn!");
+ FirebaseMessaging firebaseMessaging = new FirebaseMessaging();
+ firebaseMessaging.setTo(Common.FCM_TOKEN_USER);
+ firebaseMessaging.setNotification(notification);
+ FirebaseMessagingAPI.apiService.sendNotification(firebaseMessaging).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ if(response.code() != 200){
+ Log.d(TAG, "Error to send notification. Code: " + response.code());
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ Log.e(TAG, "Error to connect FCM Messaging!");
+ }
+ });
}
}
diff --git a/app/src/main/java/com/capstone/foodify/shipper/Model/FirebaseMessaging/FirebaseMessaging.java b/app/src/main/java/com/capstone/foodify/shipper/Model/FirebaseMessaging/FirebaseMessaging.java
new file mode 100644
index 0000000..35f5c85
--- /dev/null
+++ b/app/src/main/java/com/capstone/foodify/shipper/Model/FirebaseMessaging/FirebaseMessaging.java
@@ -0,0 +1,22 @@
+package com.capstone.foodify.shipper.Model.FirebaseMessaging;
+
+public class FirebaseMessaging {
+ private String to;
+ private Notification notification;
+
+ public String getTo() {
+ return to;
+ }
+
+ public void setTo(String to) {
+ this.to = to;
+ }
+
+ public Notification getNotification() {
+ return notification;
+ }
+
+ public void setNotification(Notification notification) {
+ this.notification = notification;
+ }
+}
diff --git a/app/src/main/java/com/capstone/foodify/shipper/Model/FirebaseMessaging/Notification.java b/app/src/main/java/com/capstone/foodify/shipper/Model/FirebaseMessaging/Notification.java
new file mode 100644
index 0000000..47e92c3
--- /dev/null
+++ b/app/src/main/java/com/capstone/foodify/shipper/Model/FirebaseMessaging/Notification.java
@@ -0,0 +1,47 @@
+package com.capstone.foodify.shipper.Model.FirebaseMessaging;
+
+public class Notification {
+ private String title;
+ private String body;
+ private String sound;
+ private String android_channel_id;
+
+ public Notification(String title, String body) {
+ this.title = title;
+ this.body = body;
+ this.sound = "notificationsound.mp3";
+ this.android_channel_id = "foodify-notification";
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getBody() {
+ return body;
+ }
+
+ public void setBody(String body) {
+ this.body = body;
+ }
+
+ public String getSound() {
+ return sound;
+ }
+
+ public void setSound(String sound) {
+ this.sound = sound;
+ }
+
+ public String getAndroid_channel_id() {
+ return android_channel_id;
+ }
+
+ public void setAndroid_channel_id(String android_channel_id) {
+ this.android_channel_id = android_channel_id;
+ }
+}
diff --git a/app/src/main/java/com/capstone/foodify/shipper/NotificationService.java b/app/src/main/java/com/capstone/foodify/shipper/NotificationService.java
new file mode 100644
index 0000000..65f0684
--- /dev/null
+++ b/app/src/main/java/com/capstone/foodify/shipper/NotificationService.java
@@ -0,0 +1,115 @@
+package com.capstone.foodify.shipper;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioAttributes;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.util.Log;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.core.app.NotificationCompat;
+
+
+import com.capstone.foodify.shipper.API.FoodApiToken;
+import com.capstone.foodify.shipper.Activity.MainActivity;
+import com.capstone.foodify.shipper.Model.CustomResponse;
+import com.google.firebase.messaging.FirebaseMessagingService;
+import com.google.firebase.messaging.RemoteMessage;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class NotificationService extends FirebaseMessagingService {
+
+ private static final String TAG = "NotificationService";
+ public static final String channelId = "foodify-notification";
+
+ @Override
+ public void onMessageReceived(RemoteMessage remoteMessage) {
+ // TODO(developer): Handle FCM messages here.
+ // Not getting messages here? See why this may be: https://goo.gl/39bRNJ
+ Log.d(TAG, "From: " + remoteMessage.getFrom());
+
+ // Check if message contains a notification payload.
+ if (remoteMessage.getNotification() != null) {
+ Log.d(TAG, "Message notification Body: " + remoteMessage.getNotification().getBody());
+ }
+
+ // Also if you intend on generating your own notifications as a result of a received FCM
+ // message, here is where that should be initiated. See sendNotification method below.
+
+ sendNotification(remoteMessage.getNotification().getTitle() , remoteMessage.getNotification().getBody());
+ }
+
+ private void sendNotification(String title, String messageBody) {
+ Intent intent = new Intent(this, MainActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
+ PendingIntent.FLAG_IMMUTABLE);
+
+ Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
+ NotificationCompat.Builder notificationBuilder =
+ new NotificationCompat.Builder(this, channelId)
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setContentTitle(title)
+ .setContentText(messageBody)
+ .setAutoCancel(true)
+ .setSound(defaultSoundUri)
+ .setContentIntent(pendingIntent);
+
+ NotificationManager notificationManager =
+ (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+
+ createNotificationChannel(notificationManager);
+
+ notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
+ }
+
+ private void createNotificationChannel(NotificationManager notificationManager) {
+ Uri sound = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + this.getPackageName() + "/" + R.raw.notificationsound); //Here is FILE_NAME is the name of file that you want to play
+ // Create the NotificationChannel, but only on API 26+ because
+ // the NotificationChannel class is new and not in the support library
+ CharSequence name = "Notification";
+ String description = "testing";
+ int importance = NotificationManager.IMPORTANCE_HIGH;
+ AudioAttributes audioAttributes = new AudioAttributes.Builder()
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .setUsage(AudioAttributes.USAGE_ALARM)
+ .build();
+ NotificationChannel channel = new NotificationChannel(channelId, name, importance);
+ channel.setDescription(description);
+ channel.enableLights(true); channel.enableVibration(true);
+ channel.setSound(sound, audioAttributes);
+ notificationManager.createNotificationChannel(channel);
+ }
+
+
+ @Override
+ public void onNewToken(@NonNull String token) {
+ super.onNewToken(token);
+
+ if(Common.CURRENT_USER != null){
+ FoodApiToken.apiService.updateFCMToken(Common.CURRENT_USER.getId(), token).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ if(response.code() != 200)
+ Toast.makeText(NotificationService.this, "Không thể cập nhật FCM Token. Mã lỗi: " + response.code(), Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ Toast.makeText(NotificationService.this, "Đã có lỗi khi kết nối đến hệ thống!", Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+
+ Log.d(TAG, "Renew token: " + token);
+ }
+}
diff --git a/app/src/main/res/drawable/logo.png b/app/src/main/res/drawable/logo.png
index bd41f95..ea85045 100644
Binary files a/app/src/main/res/drawable/logo.png and b/app/src/main/res/drawable/logo.png differ
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
index 6f3b755..036d09b 100644
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -1,6 +1,5 @@
-
-
-
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
index 6f3b755..036d09b 100644
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -1,6 +1,5 @@
-
-
-
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..6094744
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp
deleted file mode 100644
index c209e78..0000000
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..8a802c2
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..2ea1ed9
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
deleted file mode 100644
index b2dfe3d..0000000
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..bf9a1b2
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp
deleted file mode 100644
index 4f0f1d6..0000000
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..3e22726
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..06dddc3
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
deleted file mode 100644
index 62b611d..0000000
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..5fd91af
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
deleted file mode 100644
index 948a307..0000000
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..07d4457
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..e574ed4
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
deleted file mode 100644
index 1b9a695..0000000
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..879239f
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
deleted file mode 100644
index 28d4b77..0000000
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..3fca3d6
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..155ac8f
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
deleted file mode 100644
index 9287f50..0000000
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..45c4cea
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
deleted file mode 100644
index aa7d642..0000000
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..059c9d5
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..978ac9d
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
deleted file mode 100644
index 9126ae3..0000000
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/app/src/main/res/raw/notificationsound.mp3 b/app/src/main/res/raw/notificationsound.mp3
new file mode 100644
index 0000000..1f2bacb
Binary files /dev/null and b/app/src/main/res/raw/notificationsound.mp3 differ
diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml
new file mode 100644
index 0000000..27a641b
--- /dev/null
+++ b/app/src/main/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+
+
+ #00C0FE
+
\ No newline at end of file
diff --git a/app/src/main/res/values/splash.xml b/app/src/main/res/values/splash.xml
index 858ae98..24f4fa3 100644
--- a/app/src/main/res/values/splash.xml
+++ b/app/src/main/res/values/splash.xml
@@ -2,7 +2,7 @@
\ No newline at end of file