Add notification shipper
@@ -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'
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
@@ -55,6 +57,14 @@
|
||||
<activity
|
||||
android:name=".Activity.MainActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<service
|
||||
android:name=".NotificationService"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
After Width: | Height: | Size: 400 KiB |
@@ -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<FirebaseMessaging> sendNotification(@Body FirebaseMessaging content);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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<CustomResponse> changeStatusOrder(@Path("userId") int userId, @Path("orderId") int orderId, @Query("status") String status);
|
||||
|
||||
@PUT("users/{userId}/update/fcm")
|
||||
Call<CustomResponse> updateFCMToken(@Path("userId") int userId, @Body String token);
|
||||
}
|
||||
|
||||
@@ -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<String> getTokenFCM(@Path("userId") int userId);
|
||||
}
|
||||
@@ -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<User> 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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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<String>() {
|
||||
@Override
|
||||
public void onComplete(@NonNull Task<String> 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<CustomResponse>() {
|
||||
@Override
|
||||
public void onResponse(Call<CustomResponse> call, Response<CustomResponse> 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<CustomResponse> 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<Intent> 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<ResolveInfo> list = context.getPackageManager().queryIntentActivities(intent,
|
||||
PackageManager.MATCH_DEFAULT_ONLY);
|
||||
return list.size() > 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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<String>() {
|
||||
@Override
|
||||
public void onResponse(Call<String> call, Response<String> 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<String> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<User> 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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -86,6 +86,7 @@ public class ShippingOrder extends Fragment {
|
||||
public void onResponse(Call<Orders> call, Response<Orders> response) {
|
||||
if(response.code() == 200){
|
||||
|
||||
assert response.body() != null;
|
||||
listOrders.addAll(response.body().getOrders());
|
||||
|
||||
orderAdapter.setData(listOrders);
|
||||
|
||||
@@ -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<FirebaseMessaging>() {
|
||||
@Override
|
||||
public void onResponse(Call<FirebaseMessaging> call, Response<FirebaseMessaging> response) {
|
||||
if(response.code() != 200){
|
||||
Log.d(TAG, "Error to send notification. Code: " + response.code());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<FirebaseMessaging> call, Throwable t) {
|
||||
Log.e(TAG, "Error to connect FCM Messaging!");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<CustomResponse>() {
|
||||
@Override
|
||||
public void onResponse(Call<CustomResponse> call, Response<CustomResponse> 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<CustomResponse> 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);
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 471 KiB |
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 982 B |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 156 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#00C0FE</color>
|
||||
</resources>
|
||||
@@ -2,7 +2,7 @@
|
||||
<resources>
|
||||
<style name="Theme.FoodifyShipper.SplashScreen" parent="Theme.SplashScreen">
|
||||
<item name="windowSplashScreenBackground">#FFFFFF</item>
|
||||
<item name="windowSplashScreenAnimatedIcon">@drawable/icon_shipper_with_background</item>
|
||||
<item name="windowSplashScreenAnimatedIcon">@drawable/logo</item>
|
||||
<item name="postSplashScreenTheme">@style/Theme.FoodifyShipper</item>
|
||||
</style>
|
||||
</resources>
|
||||