Show notification, tracking realtime shipper

This commit is contained in:
Nezumi
2023-04-10 16:13:26 +07:00
parent 933174a8c1
commit 99b40d242f
41 changed files with 346 additions and 21 deletions
+1 -1
View File
@@ -12,6 +12,6 @@
</deviceKey>
</Target>
</runningDeviceTargetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2023-04-08T07:13:47.827233500Z" />
<timeTargetWasSelectedWithDropDown value="2023-04-10T09:08:51.884599900Z" />
</component>
</project>
+3 -1
View File
@@ -16,7 +16,7 @@ android {
defaultConfig {
applicationId "com.capstone.foodify"
minSdk 26
targetSdk 32
targetSdk 31
versionCode 1
versionName "1.0"
@@ -58,6 +58,8 @@ dependencies {
implementation 'com.google.android.gms:play-services-location:21.0.1'
implementation files('libs/zpdk-release-v3.1.aar')
implementation 'com.google.firebase:firebase-database-ktx:20.1.0'
implementation 'com.google.firebase:firebase-messaging'
implementation 'com.google.firebase:firebase-analytics'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
+66
View File
@@ -13,6 +13,72 @@
"package_name": "com.capstone.foodify"
}
},
"oauth_client": [
{
"client_id": "213676556381-bu8t1g75ma9l38e360ikreg2rchlciv1.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.capstone.foodify",
"certificate_hash": "e0920906e0bbcc37124d669e6f5095a2f7e08ebf"
}
},
{
"client_id": "213676556381-jmh1dva918r6juc331d1jclgjeinqor1.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyBBboME-vz27_Rgg98ftivvSvMVoH9lV1A"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "213676556381-jmh1dva918r6juc331d1jclgjeinqor1.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:213676556381:android:cebb0c6035fa9e05e88db4",
"android_client_info": {
"package_name": "com.capstone.foodify.shipper"
}
},
"oauth_client": [
{
"client_id": "213676556381-jmh1dva918r6juc331d1jclgjeinqor1.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyBBboME-vz27_Rgg98ftivvSvMVoH9lV1A"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "213676556381-jmh1dva918r6juc331d1jclgjeinqor1.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:213676556381:android:d951cc73dfe7438ee88db4",
"android_client_info": {
"package_name": "com.capstone.foodify.testapplication"
}
},
"oauth_client": [
{
"client_id": "213676556381-jmh1dva918r6juc331d1jclgjeinqor1.apps.googleusercontent.com",
+13
View File
@@ -16,6 +16,7 @@
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:allowBackup="true"
@@ -44,6 +45,10 @@
android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyAY14Ic32UP26Hg6GILznOfbBihiY5BUxw" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="foodify-notification"/>
<activity
android:name=".Activity.TrackingOrderActivity"
android:exported="false"
@@ -116,6 +121,14 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".NotificationService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>
Binary file not shown.

After

Width:  |  Height:  |  Size: 457 KiB

@@ -94,9 +94,9 @@ public interface FoodApiToken {
@GET("users/{userId}/orders/status/{status}")
Call<Orders> getOrderUser(@Path("userId") int userId, @Path("status") String status, @Query("pageNo") int pageNo, @Query("pageSize") int pageSize, @Query("sortBy") String sortBy, @Query("sortDir") String sortDir);
@DELETE("users/{userId}/orders/{orderId}")
Call<CustomResponse> deleteOrder(@Path("userId") int userId, @Path("orderId") int orderId);
@PUT("users/{userId}/orders/{orderId}/status")
Call<CustomResponse> updateOrderStatus(@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);
}
@@ -2,12 +2,18 @@ package com.capstone.foodify.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.IntentSender;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.ColorStateList;
import android.location.Location;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -19,8 +25,10 @@ import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.appcompat.widget.AppCompatCheckBox;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
import androidx.core.splashscreen.SplashScreen;
@@ -32,6 +40,7 @@ import com.capstone.foodify.API.FoodApiToken;
import com.capstone.foodify.BuildConfig;
import com.capstone.foodify.Common;
import com.capstone.foodify.Fragment.HomeFragment;
import com.capstone.foodify.Model.Response.CustomResponse;
import com.capstone.foodify.Model.User;
import com.capstone.foodify.R;
import com.capstone.foodify.ViewPagerAdapter;
@@ -57,9 +66,13 @@ import com.google.android.material.navigation.NavigationBarView;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.auth.GetTokenResult;
import com.google.firebase.messaging.FirebaseMessaging;
import org.objectweb.asm.Handle;
import java.util.Arrays;
import java.util.List;
import io.paperdb.Paper;
import retrofit2.Call;
import retrofit2.Callback;
@@ -79,7 +92,7 @@ public class MainActivity extends AppCompatActivity {
private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 1000;
private static final long MAX_WAIT_TIME_IN_MILLISECONDS = 1000;
private static final int LOCATION_REQUEST_CODE = 100;
private static final String TAG = MainActivity.class.getSimpleName();
private static final String TAG = "MainActivity";
private FusedLocationProviderClient mFusedLocationClient;
private SettingsClient mSettingsClient;
@@ -117,6 +130,8 @@ public class MainActivity extends AppCompatActivity {
Common.CURRENT_USER = userData;
HomeFragment.setNameUser();
getTokenFCM();
progressLayout.setVisibility(View.GONE);
}
}
@@ -150,6 +165,12 @@ public class MainActivity extends AppCompatActivity {
bottomNavigation();
startPowerSaverIntent(this);
checkNotificationPermission();
if(Common.CURRENT_USER != null)
getTokenFCM();
if (Common.CURRENT_LOCATION == null) {
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
@@ -189,9 +210,40 @@ public class MainActivity extends AppCompatActivity {
}
}
}, 3000);
}, 5000);
}
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();
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);
}
});
}
private void initComponent() {
bottomNavigationView = findViewById(R.id.bottom_nav);
viewPager2 = findViewById(R.id.viewPager);
@@ -256,6 +308,13 @@ public class MainActivity extends AppCompatActivity {
});
}
//myringtone permission
private void checkNotificationPermission(){
if(!NotificationManagerCompat.from(this).areNotificationsEnabled()){
showDialogPermission("Hãy bật quyền thông báo trên thiết bị của bạn để chúng thôi có thể cung cấp thông tin cho bạn một cách nhanh nhất!");
}
}
//Location
private void checkLocationPermission(){
@@ -283,15 +342,15 @@ public class MainActivity extends AppCompatActivity {
if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
Toast.makeText(this, "Đã cấp quyền thành công!", Toast.LENGTH_SHORT).show();
} else {
showDialogPermission();
showDialogPermission("Hãy cho ứng dụng truy cập vào vị trí của bạn để trải nghiệm tốt hơn!");
}
}
}
private void showDialogPermission() {
private void showDialogPermission(String message) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Hãy cho ứng dụng truy cập vào vị trí của bạn để trải nghiệm tốt hơn!")
builder.setMessage(message)
.setCancelable(false)
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
@Override
@@ -382,6 +441,66 @@ public class MainActivity extends AppCompatActivity {
Common.CURRENT_LOCATION = mCurrentLocation;
}
}
if(Common.CURRENT_USER != null)
getTokenFCM();
}
//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;
}
}
@@ -3,6 +3,7 @@ package com.capstone.foodify.Activity;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
@@ -146,7 +147,7 @@ public class TrackingOrderActivity extends FragmentActivity implements OnMapRead
shippingMarker = mMap.addMarker(new MarkerOptions()
.position(latLng)
.title("Người giao")
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN)));
.icon(BitmapDescriptorFactory.fromResource(R.drawable.shipper)));
} else {
shippingMarker.setPosition(latLng);
}
@@ -247,6 +248,13 @@ public class TrackingOrderActivity extends FragmentActivity implements OnMapRead
}
}
@Override
protected void onResume() {
super.onResume();
orderDatabase.addValueEventListener(this);
}
@Override
protected void onStop() {
orderDatabase.removeEventListener(this);
@@ -0,0 +1,114 @@
package com.capstone.foodify;
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.API.FoodApiToken;
import com.capstone.foodify.Activity.MainActivity;
import com.capstone.foodify.Model.Response.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);
}
}
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z"/>
</vector>
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

@@ -1,5 +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" />
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
@@ -1,5 +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" />
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
@@ -1,6 +0,0 @@
<?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" />
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#EB8085</color>
</resources>