From ddc8e98c19afa6ae5c707912f2de7195b8cf43e8 Mon Sep 17 00:00:00 2001 From: Alex <78383757+Lextical@users.noreply.github.com> Date: Sat, 4 Apr 2026 18:15:05 -0600 Subject: [PATCH] integrated hilt so we dont have to manually pass context and inject retrofit in andriod --- android/app/build.gradle.kts | 4 + .../petstoremobile/PetStoreApplication.java | 3 +- .../activities/HomeActivity.java | 6 +- .../activities/MainActivity.java | 24 ++- .../petstoremobile/adapters/PetAdapter.java | 28 ++- .../adapters/ProductAdapter.java | 28 ++- .../api/auth/AuthInterceptor.java | 6 +- .../petstoremobile/api/auth/TokenManager.java | 22 +-- .../petstoremobile/di/NetworkModule.java | 176 ++++++++++++++++++ .../fragments/ChatFragment.java | 31 +-- .../fragments/ListFragment.java | 10 +- .../fragments/ProfileFragment.java | 23 ++- .../listfragments/AdoptionFragment.java | 9 +- .../listfragments/AppointmentFragment.java | 16 +- .../listfragments/InventoryFragment.java | 16 +- .../fragments/listfragments/PetFragment.java | 14 +- .../listfragments/ProductFragment.java | 11 +- .../ProductSupplierFragment.java | 11 +- .../listfragments/PurchaseOrderFragment.java | 11 +- .../fragments/listfragments/SaleFragment.java | 8 + .../listfragments/ServiceFragment.java | 8 +- .../listfragments/SupplierFragment.java | 8 +- .../AdoptionDetailFragment.java | 21 ++- .../AppointmentDetailFragment.java | 29 ++- .../InventoryDetailFragment.java | 12 +- .../detailfragments/PetDetailFragment.java | 12 +- .../ProductDetailFragment.java | 26 ++- .../ProductSupplierDetailFragment.java | 21 ++- .../PurchaseOrderDetailFragment.java | 3 + .../detailfragments/RefundDetailFragment.java | 8 + .../ServiceDetailFragment.java | 12 +- .../SupplierDetailFragment.java | 12 +- .../PetProfileFragment.java | 13 +- .../services/ChatNotificationService.java | 27 +-- .../websocket/StompChatManager.java | 23 +-- android/build.gradle.kts | 1 + android/gradle/libs.versions.toml | 5 +- 37 files changed, 504 insertions(+), 194 deletions(-) create mode 100644 android/app/src/main/java/com/example/petstoremobile/di/NetworkModule.java diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index a6d27404..ef712a40 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -2,6 +2,7 @@ import java.util.Properties plugins { alias(libs.plugins.android.application) + alias(libs.plugins.hilt) } val localProperties = Properties().apply { @@ -60,6 +61,9 @@ dependencies { implementation(libs.activity) implementation(libs.constraintlayout) + implementation(libs.hilt.android) + annotationProcessor(libs.hilt.compiler) + implementation("com.squareup.retrofit2:retrofit:2.9.0") implementation("com.squareup.retrofit2:converter-gson:2.9.0") implementation("com.squareup.okhttp3:logging-interceptor:4.12.0") diff --git a/android/app/src/main/java/com/example/petstoremobile/PetStoreApplication.java b/android/app/src/main/java/com/example/petstoremobile/PetStoreApplication.java index b041d54d..75def31b 100644 --- a/android/app/src/main/java/com/example/petstoremobile/PetStoreApplication.java +++ b/android/app/src/main/java/com/example/petstoremobile/PetStoreApplication.java @@ -1,8 +1,9 @@ package com.example.petstoremobile; import android.app.Application; -import com.example.petstoremobile.api.auth.TokenManager; +import dagger.hilt.android.HiltAndroidApp; +@HiltAndroidApp public class PetStoreApplication extends Application { @Override public void onCreate() { diff --git a/android/app/src/main/java/com/example/petstoremobile/activities/HomeActivity.java b/android/app/src/main/java/com/example/petstoremobile/activities/HomeActivity.java index aea1f427..513c3683 100644 --- a/android/app/src/main/java/com/example/petstoremobile/activities/HomeActivity.java +++ b/android/app/src/main/java/com/example/petstoremobile/activities/HomeActivity.java @@ -10,7 +10,6 @@ import android.util.Log; import androidx.activity.EdgeToEdge; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; -import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; import androidx.core.graphics.Insets; @@ -25,6 +24,9 @@ import com.example.petstoremobile.fragments.ProfileFragment; import com.example.petstoremobile.services.ChatNotificationService; import com.google.android.material.bottomnavigation.BottomNavigationView; +import dagger.hilt.android.AndroidEntryPoint; + +@AndroidEntryPoint public class HomeActivity extends AppCompatActivity { private BottomNavigationView bottomNav; @@ -125,4 +127,4 @@ public class HomeActivity extends AppCompatActivity { .replace(R.id.fragment_container, fragment) .commit(); } -} +} \ No newline at end of file diff --git a/android/app/src/main/java/com/example/petstoremobile/activities/MainActivity.java b/android/app/src/main/java/com/example/petstoremobile/activities/MainActivity.java index 7615b27e..b4b00db4 100644 --- a/android/app/src/main/java/com/example/petstoremobile/activities/MainActivity.java +++ b/android/app/src/main/java/com/example/petstoremobile/activities/MainActivity.java @@ -18,15 +18,19 @@ import androidx.core.view.WindowInsetsCompat; import com.example.petstoremobile.R; import com.example.petstoremobile.api.auth.AuthApi; import com.example.petstoremobile.api.auth.TokenManager; -import com.example.petstoremobile.api.RetrofitClient; import com.example.petstoremobile.dtos.AuthDTO; import com.example.petstoremobile.dtos.UserDTO; +import javax.inject.Inject; +import javax.inject.Named; + +import dagger.hilt.android.AndroidEntryPoint; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; //The login screen activity +@AndroidEntryPoint public class MainActivity extends AppCompatActivity { private EditText etUser; @@ -34,6 +38,10 @@ public class MainActivity extends AppCompatActivity { private Button btnLogin; private TextView tvLoginStatus; + @Inject AuthApi authApi; + @Inject TokenManager tokenManager; + @Inject @Named("baseUrl") String baseUrl; + @Override protected void onCreate(Bundle savedInstanceState) { @@ -41,7 +49,6 @@ public class MainActivity extends AppCompatActivity { super.onCreate(savedInstanceState); // Check if user is already logged in - TokenManager tokenManager = TokenManager.getInstance(this); if (tokenManager.isLoggedIn()) { if ("CUSTOMER".equalsIgnoreCase(tokenManager.getRole())) { // If a customer somehow remained logged in, clear them out @@ -92,8 +99,6 @@ public class MainActivity extends AppCompatActivity { return; } - AuthApi authApi = RetrofitClient.getAuthApi(this); - //Call login from api and get response authApi.login(new AuthDTO.LoginRequest(username,password)).enqueue(new Callback() { @Override @@ -109,21 +114,20 @@ public class MainActivity extends AppCompatActivity { } //save login data in shared preferences - TokenManager.getInstance(MainActivity.this).saveLoginData( + tokenManager.saveLoginData( response.body().getToken(), response.body().getUsername(), role ); //fetch user id from api then login to home activity - RetrofitClient.getAuthApi(MainActivity.this).getMe() + authApi.getMe() .enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { if (response.isSuccessful() && response.body() != null) { - TokenManager.getInstance(MainActivity.this) - .saveUserId(response.body().getId()); + tokenManager.saveUserId(response.body().getId()); } Toast.makeText(MainActivity.this, "Login successful", Toast.LENGTH_SHORT).show(); @@ -169,7 +173,7 @@ public class MainActivity extends AppCompatActivity { if (t instanceof java.net.ConnectException || t instanceof java.net.SocketTimeoutException || t instanceof java.net.UnknownHostException) { - errorMessage = "Cannot connect to server at " + RetrofitClient.BASE_URL + + errorMessage = "Cannot connect to server at " + baseUrl + ". Please check if the backend is running."; } else if (t instanceof java.io.IOException) { errorMessage = "Network error. Please check your connection."; @@ -183,4 +187,4 @@ public class MainActivity extends AppCompatActivity { }); }); } -} +} \ No newline at end of file diff --git a/android/app/src/main/java/com/example/petstoremobile/adapters/PetAdapter.java b/android/app/src/main/java/com/example/petstoremobile/adapters/PetAdapter.java index 166b0d1d..d5008aff 100644 --- a/android/app/src/main/java/com/example/petstoremobile/adapters/PetAdapter.java +++ b/android/app/src/main/java/com/example/petstoremobile/adapters/PetAdapter.java @@ -13,7 +13,6 @@ import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.example.petstoremobile.R; import com.example.petstoremobile.api.PetApi; -import com.example.petstoremobile.api.RetrofitClient; import com.example.petstoremobile.dtos.PetDTO; import java.util.List; @@ -21,6 +20,7 @@ public class PetAdapter extends RecyclerView.Adapter { private List petList; private OnPetClickListener petClickListener; + private String baseUrl; // Interface for pet click on recycler view public interface OnPetClickListener { @@ -33,6 +33,10 @@ public class PetAdapter extends RecyclerView.Adapter { this.petClickListener = petClickListener; } + public void setBaseUrl(String baseUrl) { + this.baseUrl = baseUrl; + } + // Get the controls of each row in recycler view public static class PetViewHolder extends RecyclerView.ViewHolder { TextView tvPetName, tvPetSpeciesBreed, tvPetAge, tvPetPrice, tvPetStatus; @@ -83,15 +87,19 @@ public class PetAdapter extends RecyclerView.Adapter { } // Load pet image using Glide with circle crop - String imageUrl = RetrofitClient.BASE_URL + String.format(PetApi.PET_IMAGE_PATH, pet.getPetId()); - Glide.with(holder.itemView.getContext()) - .load(imageUrl) - .circleCrop() - .diskCacheStrategy(DiskCacheStrategy.NONE) - .skipMemoryCache(true) - .placeholder(R.drawable.placeholder) - .error(R.drawable.placeholder) - .into(holder.ivPetProfile); + if (baseUrl != null) { + String imageUrl = baseUrl + String.format(PetApi.PET_IMAGE_PATH, pet.getPetId()); + Glide.with(holder.itemView.getContext()) + .load(imageUrl) + .circleCrop() + .diskCacheStrategy(DiskCacheStrategy.NONE) + .skipMemoryCache(true) + .placeholder(R.drawable.placeholder) + .error(R.drawable.placeholder) + .into(holder.ivPetProfile); + } else { + holder.ivPetProfile.setImageResource(R.drawable.placeholder); + } //when a row is clicked, open the detail view holder.itemView.setOnClickListener(v -> petClickListener.onPetClick(position)); diff --git a/android/app/src/main/java/com/example/petstoremobile/adapters/ProductAdapter.java b/android/app/src/main/java/com/example/petstoremobile/adapters/ProductAdapter.java index a44ec993..5bcc4d3a 100644 --- a/android/app/src/main/java/com/example/petstoremobile/adapters/ProductAdapter.java +++ b/android/app/src/main/java/com/example/petstoremobile/adapters/ProductAdapter.java @@ -10,7 +10,6 @@ import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.example.petstoremobile.R; import com.example.petstoremobile.api.ProductApi; -import com.example.petstoremobile.api.RetrofitClient; import com.example.petstoremobile.dtos.ProductDTO; import java.util.List; @@ -18,6 +17,7 @@ public class ProductAdapter extends RecyclerView.Adapter productList; private OnProductClickListener listener; + private String baseUrl; public interface OnProductClickListener { void onProductClick(int position); @@ -28,6 +28,10 @@ public class ProductAdapter extends RecyclerView.Adapter listener.onProductClick(position)); } diff --git a/android/app/src/main/java/com/example/petstoremobile/api/auth/AuthInterceptor.java b/android/app/src/main/java/com/example/petstoremobile/api/auth/AuthInterceptor.java index dd17fffd..02bbe3c0 100644 --- a/android/app/src/main/java/com/example/petstoremobile/api/auth/AuthInterceptor.java +++ b/android/app/src/main/java/com/example/petstoremobile/api/auth/AuthInterceptor.java @@ -1,7 +1,5 @@ package com.example.petstoremobile.api.auth; -import android.content.Context; - import androidx.annotation.NonNull; import java.io.IOException; @@ -15,8 +13,8 @@ public class AuthInterceptor implements Interceptor { private final TokenManager tokenManager; - public AuthInterceptor(Context context) { - this.tokenManager = TokenManager.getInstance(context); + public AuthInterceptor(TokenManager tokenManager) { + this.tokenManager = tokenManager; } @NonNull diff --git a/android/app/src/main/java/com/example/petstoremobile/api/auth/TokenManager.java b/android/app/src/main/java/com/example/petstoremobile/api/auth/TokenManager.java index b0f90508..aa9ab363 100644 --- a/android/app/src/main/java/com/example/petstoremobile/api/auth/TokenManager.java +++ b/android/app/src/main/java/com/example/petstoremobile/api/auth/TokenManager.java @@ -3,7 +3,12 @@ package com.example.petstoremobile.api.auth; import android.content.Context; import android.content.SharedPreferences; -//Store login token in shared preferences +import javax.inject.Inject; +import javax.inject.Singleton; + +import dagger.hilt.android.qualifiers.ApplicationContext; + +@Singleton public class TokenManager { private static final String TOKEN_KEY = "token"; private static final String USERNAME_KEY = "username"; @@ -11,20 +16,13 @@ public class TokenManager { private static final String PREFS_NAME = "auth_prefs"; private static final String USER_ID_KEY = "user_id"; - private static TokenManager instance; private SharedPreferences prefs; - private TokenManager(Context context) { + @Inject + public TokenManager(@ApplicationContext Context context) { prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); } - public static TokenManager getInstance(Context context) { - if (instance == null) { - instance = new TokenManager(context); - } - return instance; - } - //save login data after login public void saveLoginData(String token, String username, String role) { prefs.edit() @@ -65,6 +63,4 @@ public class TokenManager { public void clearLoginData() { prefs.edit().clear().apply(); } - - -} +} \ No newline at end of file diff --git a/android/app/src/main/java/com/example/petstoremobile/di/NetworkModule.java b/android/app/src/main/java/com/example/petstoremobile/di/NetworkModule.java new file mode 100644 index 00000000..0311840d --- /dev/null +++ b/android/app/src/main/java/com/example/petstoremobile/di/NetworkModule.java @@ -0,0 +1,176 @@ +package com.example.petstoremobile.di; + +import android.content.Context; +import android.os.Build; + +import com.example.petstoremobile.BuildConfig; +import com.example.petstoremobile.api.*; +import com.example.petstoremobile.api.auth.AuthApi; +import com.example.petstoremobile.api.auth.AuthInterceptor; +import com.example.petstoremobile.api.auth.TokenManager; + +import java.util.concurrent.TimeUnit; + +import javax.inject.Named; +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; +import dagger.hilt.InstallIn; +import dagger.hilt.android.qualifiers.ApplicationContext; +import dagger.hilt.components.SingletonComponent; +import okhttp3.OkHttpClient; +import okhttp3.logging.HttpLoggingInterceptor; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; + +//Module to provide dependencies injection for the api +@Module +@InstallIn(SingletonComponent.class) +public class NetworkModule { + + @Provides + @Singleton + @Named("baseUrl") + public static String provideBaseUrl() { + return isEmulator() ? BuildConfig.EMULATOR_BACKEND_URL : BuildConfig.DEVICE_BACKEND_URL; + } + + // Check if the device is an emulator + private static boolean isEmulator() { + return Build.FINGERPRINT.startsWith("generic") + || Build.FINGERPRINT.startsWith("unknown") + || Build.MODEL.contains("google_sdk") + || Build.MODEL.contains("Emulator") + || Build.MODEL.contains("Android SDK built for x86") + || Build.MANUFACTURER.contains("Genymotion") + || Build.HARDWARE.contains("goldfish") + || Build.HARDWARE.contains("ranchu") + || Build.PRODUCT.contains("sdk") + || Build.PRODUCT.contains("sdk_gphone") + || (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")); + } + + @Provides + @Singleton + public static OkHttpClient provideOkHttpClient(TokenManager tokenManager) { + HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); + + return new OkHttpClient.Builder() + .addInterceptor(interceptor) + .addInterceptor(new AuthInterceptor(tokenManager)) + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .writeTimeout(30, TimeUnit.SECONDS) + .build(); + } + + //build the retrofit instance + @Provides + @Singleton + public static Retrofit provideRetrofit(@Named("baseUrl") String baseUrl, OkHttpClient client) { + return new Retrofit.Builder() + .baseUrl(baseUrl) + .addConverterFactory(GsonConverterFactory.create()) + .client(client) + .build(); + } + + //associate the api with the retrofit instance + @Provides + @Singleton + public static PetApi providePetApi(Retrofit retrofit) { + return retrofit.create(PetApi.class); + } + + @Provides + @Singleton + public static ServiceApi provideServiceApi(Retrofit retrofit) { + return retrofit.create(ServiceApi.class); + } + + @Provides + @Singleton + public static SupplierApi provideSupplierApi(Retrofit retrofit) { + return retrofit.create(SupplierApi.class); + } + + @Provides + @Singleton + public static AdoptionApi provideAdoptionApi(Retrofit retrofit) { + return retrofit.create(AdoptionApi.class); + } + + @Provides + @Singleton + public static AppointmentApi provideAppointmentApi(Retrofit retrofit) { + return retrofit.create(AppointmentApi.class); + } + + @Provides + @Singleton + public static ProductApi provideProductApi(Retrofit retrofit) { + return retrofit.create(ProductApi.class); + } + + @Provides + @Singleton + public static SaleApi provideSaleApi(Retrofit retrofit) { + return retrofit.create(SaleApi.class); + } + + @Provides + @Singleton + public static PurchaseOrderApi providePurchaseOrderApi(Retrofit retrofit) { + return retrofit.create(PurchaseOrderApi.class); + } + + @Provides + @Singleton + public static ProductSupplierApi provideProductSupplierApi(Retrofit retrofit) { + return retrofit.create(ProductSupplierApi.class); + } + + @Provides + @Singleton + public static InventoryApi provideInventoryApi(Retrofit retrofit) { + return retrofit.create(InventoryApi.class); + } + + @Provides + @Singleton + public static AuthApi provideAuthApi(Retrofit retrofit) { + return retrofit.create(AuthApi.class); + } + + @Provides + @Singleton + public static ChatApi provideChatApi(Retrofit retrofit) { + return retrofit.create(ChatApi.class); + } + + @Provides + @Singleton + public static CustomerApi provideCustomerApi(Retrofit retrofit) { + return retrofit.create(CustomerApi.class); + } + + @Provides + @Singleton + public static MessageApi provideMessageApi(Retrofit retrofit) { + return retrofit.create(MessageApi.class); + } + + @Provides + @Singleton + public static StoreApi provideStoreApi(Retrofit retrofit) { + return retrofit.create(StoreApi.class); + } + + @Provides + @Singleton + public static CategoryApi provideCategoryApi(Retrofit retrofit) { + return retrofit.create(CategoryApi.class); + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/ChatFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/ChatFragment.java index 47ec7f3f..a30f1f03 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/ChatFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/ChatFragment.java @@ -26,7 +26,6 @@ import com.example.petstoremobile.api.auth.TokenManager; import com.example.petstoremobile.api.ChatApi; import com.example.petstoremobile.api.CustomerApi; import com.example.petstoremobile.api.MessageApi; -import com.example.petstoremobile.api.RetrofitClient; import com.example.petstoremobile.dtos.ConversationDTO; import com.example.petstoremobile.dtos.CustomerDTO; import com.example.petstoremobile.dtos.MessageDTO; @@ -36,10 +35,17 @@ import com.example.petstoremobile.models.Chat; import com.example.petstoremobile.models.Message; import com.example.petstoremobile.services.ChatNotificationService; import com.example.petstoremobile.websocket.StompChatManager; + import java.util.*; import java.util.stream.Collectors; + +import javax.inject.Inject; +import javax.inject.Named; + +import dagger.hilt.android.AndroidEntryPoint; import retrofit2.*; +@AndroidEntryPoint public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickListener, StompChatManager.MessageListener, StompChatManager.ConversationListener, StompChatManager.ConnectionListener { @@ -70,9 +76,11 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis private Uri pendingAttachmentUri; // APIs - private ChatApi chatApi; - private CustomerApi customerApi; - private MessageApi messageApi; + @Inject ChatApi chatApi; + @Inject CustomerApi customerApi; + @Inject MessageApi messageApi; + @Inject TokenManager tokenManager; + @Inject @Named("baseUrl") String baseUrl; // chat private Long currentUserId; @@ -103,10 +111,6 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis View view = inflater.inflate(R.layout.fragment_chat, container, false); - chatApi = RetrofitClient.getChatApi(requireContext()); - customerApi = RetrofitClient.getCustomerApi(requireContext()); - messageApi = RetrofitClient.getMessageApi(requireContext()); - drawerLayout = view.findViewById(R.id.chatDrawerLayout); rvChatList = view.findViewById(R.id.rvChatList); rvMessages = view.findViewById(R.id.rvMessages); @@ -169,16 +173,15 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis //Helper function to load token and user id then connect to websocket private void loadInitialData() { - TokenManager tm = TokenManager.getInstance(requireContext()); - String token = tm.getToken(); - currentUserId = tm.getUserId(); - String role = tm.getRole(); + String token = tokenManager.getToken(); + currentUserId = tokenManager.getUserId(); + String role = tokenManager.getRole(); messageAdapter.setCurrentUserId(currentUserId); // if token exist then connect to websocket if (token != null) { - stompChatManager = new StompChatManager(token, role); + stompChatManager = new StompChatManager(token, role, baseUrl); stompChatManager.setMessageListener(this); stompChatManager.setConversationListener(this); stompChatManager.setConnectionListener(this); @@ -561,4 +564,4 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis ChatNotificationService.activeConversationIdInUi = null; if (stompChatManager != null) stompChatManager.disconnect(); } -} +} \ No newline at end of file diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/ListFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/ListFragment.java index b63b42b1..97e5100c 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/ListFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/ListFragment.java @@ -26,7 +26,12 @@ import com.example.petstoremobile.fragments.listfragments.ProductSupplierFragmen import com.example.petstoremobile.fragments.listfragments.PurchaseOrderFragment; import com.example.petstoremobile.fragments.listfragments.SaleFragment; +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; + //The Fragment for the displaying the list of entities to be viewed +@AndroidEntryPoint public class ListFragment extends Fragment { private DrawerLayout drawerLayout; @@ -37,6 +42,7 @@ public class ListFragment extends Fragment { private LinearLayout drawerAdoptions, drawerAppointments, drawerInventory, drawerProducts, drawerProductSupplier, drawerPurchaseOrderView, drawerSale; + @Inject TokenManager tokenManager; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -58,7 +64,7 @@ public class ListFragment extends Fragment { // Check user role and restrict access for STAFF - String role = TokenManager.getInstance(requireContext()).getRole(); + String role = tokenManager.getRole(); if ("STAFF".equalsIgnoreCase(role)) { drawerSuppliers.setVisibility(View.GONE); drawerInventory.setVisibility(View.GONE); @@ -178,4 +184,4 @@ public class ListFragment extends Fragment { .addToBackStack(null) .commit(); } -} +} \ No newline at end of file diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/ProfileFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/ProfileFragment.java index b29c038d..f470ab31 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/ProfileFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/ProfileFragment.java @@ -32,7 +32,6 @@ import com.bumptech.glide.load.model.GlideUrl; import com.bumptech.glide.load.model.LazyHeaders; import com.example.petstoremobile.R; import com.example.petstoremobile.activities.MainActivity; -import com.example.petstoremobile.api.RetrofitClient; import com.example.petstoremobile.api.auth.AuthApi; import com.example.petstoremobile.api.auth.TokenManager; import com.example.petstoremobile.dtos.ErrorResponse; @@ -49,6 +48,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.inject.Inject; +import javax.inject.Named; + +import dagger.hilt.android.AndroidEntryPoint; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.RequestBody; @@ -56,6 +59,7 @@ import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; +@AndroidEntryPoint public class ProfileFragment extends Fragment { //initialize the view/controls @@ -65,6 +69,10 @@ public class ProfileFragment extends Fragment { private UserDTO currentUser; private boolean hasImage = false; + @Inject AuthApi authApi; + @Inject TokenManager tokenManager; + @Inject @Named("baseUrl") String baseUrl; + //Initialize the launchers for camera and gallery private ActivityResultLauncher galleryLauncher; private ActivityResultLauncher cameraLauncher; @@ -251,7 +259,7 @@ public class ProfileFragment extends Fragment { Intent serviceIntent = new Intent(requireContext(), ChatNotificationService.class); requireContext().stopService(serviceIntent); - TokenManager.getInstance(requireContext()).clearLoginData(); // clear the token for next login + tokenManager.clearLoginData(); // clear the token for next login //get the intent to the main activity and clear the back stack so the back button won't allow the user to go back to the previous screen Intent intent = new Intent(getActivity(), MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); @@ -275,8 +283,6 @@ public class ProfileFragment extends Fragment { //Helper function to call the backend to get profile data and load it to the view private void loadProfileData() { - AuthApi authApi = RetrofitClient.getAuthApi(requireContext()); - authApi.getMe().enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { @@ -291,8 +297,8 @@ public class ProfileFragment extends Fragment { tvProfileRole.setText(currentUser.getRole()); // get the avatar endpoint to load profile image and the token for authorization - String avatarUrl = RetrofitClient.BASE_URL + AuthApi.AVATAR_FILE_PATH; - String token = TokenManager.getInstance(requireContext()).getToken(); + String avatarUrl = baseUrl + AuthApi.AVATAR_FILE_PATH; + String token = tokenManager.getToken(); if (token != null) { // Create GlideUrl with token to fetch the image @@ -354,7 +360,6 @@ public class ProfileFragment extends Fragment { MultipartBody.Part body = MultipartBody.Part.createFormData("avatar", file.getName(), requestFile); //Call the backend to upload the avatar - AuthApi authApi = RetrofitClient.getAuthApi(requireContext()); authApi.uploadAvatar(body).enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { @@ -380,7 +385,6 @@ public class ProfileFragment extends Fragment { } private void deleteAvatar() { - AuthApi authApi = RetrofitClient.getAuthApi(requireContext()); authApi.deleteAvatar().enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { @@ -423,7 +427,6 @@ public class ProfileFragment extends Fragment { //Helper function to update a profile field in the backend private void updateProfileField(String fieldName, String value) { - AuthApi authApi = RetrofitClient.getAuthApi(requireContext()); Map updates = new HashMap<>(); updates.put(fieldName, value); @@ -456,4 +459,4 @@ public class ProfileFragment extends Fragment { } }); } -} +} \ No newline at end of file diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AdoptionFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AdoptionFragment.java index c655f78b..06757a32 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AdoptionFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AdoptionFragment.java @@ -14,7 +14,6 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.AdoptionAdapter; import com.example.petstoremobile.api.AdoptionApi; -import com.example.petstoremobile.api.RetrofitClient; import com.example.petstoremobile.dtos.AdoptionDTO; import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.fragments.ListFragment; @@ -28,14 +27,19 @@ import com.prolificinteractive.materialcalendarview.OnDateSelectedListener; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; + +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; import retrofit2.*; +@AndroidEntryPoint public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdoptionClickListener { private List adoptionList = new ArrayList<>(); private List filteredList = new ArrayList<>(); private AdoptionAdapter adapter; - private AdoptionApi api; + @Inject AdoptionApi api; private SwipeRefreshLayout swipeRefresh; private EditText etSearch; private ImageButton hamburger; @@ -50,7 +54,6 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_adoption, container, false); - api = RetrofitClient.getAdoptionApi(requireContext()); hamburger = view.findViewById(R.id.btnHamburgerAdoption); calendarView = view.findViewById(R.id.calendarViewAdoption); btnToggleCalendarMode = view.findViewById(R.id.btnToggleCalendarModeAdoption); diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AppointmentFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AppointmentFragment.java index 49da714c..83115a3b 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AppointmentFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AppointmentFragment.java @@ -23,7 +23,6 @@ import com.example.petstoremobile.adapters.AppointmentAdapter; import com.example.petstoremobile.api.AppointmentApi; import com.example.petstoremobile.api.PetApi; import com.example.petstoremobile.api.ServiceApi; -import com.example.petstoremobile.api.RetrofitClient; import com.example.petstoremobile.dtos.AppointmentDTO; import com.example.petstoremobile.dtos.ServiceDTO; import com.example.petstoremobile.dtos.PageResponse; @@ -46,10 +45,14 @@ import java.util.HashSet; import java.util.List; import java.util.Locale; +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; +@AndroidEntryPoint public class AppointmentFragment extends Fragment implements AppointmentAdapter.OnAppointmentClickListener { private List appointmentList = new ArrayList<>(); @@ -58,7 +61,10 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter. private List serviceList = new ArrayList<>(); private AppointmentAdapter adapter; - private AppointmentApi api; + @Inject AppointmentApi api; + @Inject PetApi petApi; + @Inject ServiceApi serviceApi; + private SwipeRefreshLayout swipeRefreshLayout; private EditText etSearch; private ImageButton hamburger; @@ -73,7 +79,6 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter. Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_appointment, container, false); - api = RetrofitClient.getAppointmentApi(requireContext()); hamburger = view.findViewById(R.id.btnHamburger); calendarView = view.findViewById(R.id.calendarView); btnToggleCalendarMode = view.findViewById(R.id.btnToggleCalendarMode); @@ -267,7 +272,6 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter. // Load Pets private void loadPets() { - PetApi petApi = RetrofitClient.getPetApi(requireContext()); petApi.getAllPets(0,100).enqueue(new Callback>() { @Override @@ -290,8 +294,6 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter. // Load Services private void loadServices() { - ServiceApi serviceApi = RetrofitClient.getServiceApi(requireContext()); - serviceApi.getAllServices(0,100).enqueue(new Callback>() { @Override public void onResponse(Call> call, Response> response) { @@ -331,4 +333,4 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter. recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); recyclerView.setAdapter(adapter); } -} +} \ No newline at end of file diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/InventoryFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/InventoryFragment.java index 37e533ee..ce93ca54 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/InventoryFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/InventoryFragment.java @@ -10,7 +10,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.ImageButton; @@ -28,7 +27,6 @@ import com.example.petstoremobile.adapters.BlackTextArrayAdapter; import com.example.petstoremobile.adapters.InventoryAdapter; import com.example.petstoremobile.api.CategoryApi; import com.example.petstoremobile.api.InventoryApi; -import com.example.petstoremobile.api.RetrofitClient; import com.example.petstoremobile.dtos.BulkDeleteRequest; import com.example.petstoremobile.dtos.CategoryDTO; import com.example.petstoremobile.dtos.InventoryDTO; @@ -40,10 +38,14 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; +@AndroidEntryPoint public class InventoryFragment extends Fragment implements InventoryAdapter.OnInventoryClickListener { private static final String TAG = "InventoryFragment"; @@ -52,8 +54,8 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn private final List inventoryList = new ArrayList<>(); private final List categoryList = new ArrayList<>(); private InventoryAdapter adapter; - private InventoryApi inventoryApi; - private CategoryApi categoryApi; + @Inject InventoryApi inventoryApi; + @Inject CategoryApi categoryApi; private SwipeRefreshLayout swipeRefreshLayout; private EditText etSearch; @@ -83,9 +85,6 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_inventory, container, false); - inventoryApi = RetrofitClient.getInventoryApi(requireContext()); - categoryApi = RetrofitClient.getCategoryApi(requireContext()); - hamburger = view.findViewById(R.id.btnHamburger); btnBulkDelete = view.findViewById(R.id.btnBulkDelete); tvSelectionCount = view.findViewById(R.id.tvSelectionCount); @@ -175,8 +174,7 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn private void setupSearch(View view) { etSearch = view.findViewById(R.id.etSearchInventory); etSearch.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int i, int i1, int i2) { + @Override public void beforeTextChanged(CharSequence s, int i, int i1, int i2) { } @Override diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/PetFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/PetFragment.java index 4c8effcf..693e261f 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/PetFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/PetFragment.java @@ -14,7 +14,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.ArrayAdapter; import android.widget.EditText; import android.widget.ImageButton; import android.widget.Spinner; @@ -24,7 +23,6 @@ import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.BlackTextArrayAdapter; import com.example.petstoremobile.adapters.PetAdapter; import com.example.petstoremobile.api.PetApi; -import com.example.petstoremobile.api.RetrofitClient; import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.dtos.PetDTO; import com.example.petstoremobile.fragments.ListFragment; @@ -35,16 +33,22 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; +import javax.inject.Named; + +import dagger.hilt.android.AndroidEntryPoint; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; +@AndroidEntryPoint public class PetFragment extends Fragment implements PetAdapter.OnPetClickListener { private List petList = new ArrayList<>(); private List filteredList = new ArrayList<>(); private ImageButton hamburger; private PetAdapter adapter; - private PetApi api; + @Inject PetApi api; + @Inject @Named("baseUrl") String baseUrl; private SwipeRefreshLayout swipeRefreshLayout; private EditText etSearch; private Spinner spinnerStatus; @@ -55,9 +59,6 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_pet, container, false); - //get retrofit - api = RetrofitClient.getPetApi(requireContext()); - hamburger = view.findViewById(R.id.btnHamburger); setupRecyclerView(view); @@ -231,6 +232,7 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen private void setupRecyclerView(View view) { RecyclerView recyclerView = view.findViewById(R.id.recyclerViewPets); adapter = new PetAdapter(filteredList, this); + adapter.setBaseUrl(baseUrl); recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); recyclerView.setAdapter(adapter); } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ProductFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ProductFragment.java index e8b29611..ce15a7e8 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ProductFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ProductFragment.java @@ -11,15 +11,20 @@ import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.ProductAdapter; -import com.example.petstoremobile.api.RetrofitClient; +import com.example.petstoremobile.api.ProductApi; import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.dtos.ProductDTO; import com.example.petstoremobile.fragments.ListFragment; import com.example.petstoremobile.fragments.listfragments.detailfragments.ProductDetailFragment; import com.google.android.material.floatingactionbutton.FloatingActionButton; import java.util.*; + +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; import retrofit2.*; +@AndroidEntryPoint public class ProductFragment extends Fragment implements ProductAdapter.OnProductClickListener { private List productList = new ArrayList<>(); @@ -28,6 +33,8 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc private SwipeRefreshLayout swipeRefresh; private EditText etSearch; + @Inject ProductApi api; + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -93,7 +100,7 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc private void loadProducts() { if (swipeRefresh != null) swipeRefresh.setRefreshing(true); - RetrofitClient.getProductApi(requireContext()).getAllProducts(null, 0, 100) + api.getAllProducts(null, 0, 100) .enqueue(new Callback>() { public void onResponse(Call> c, Response> r) { diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ProductSupplierFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ProductSupplierFragment.java index 4ff88f16..0f76041e 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ProductSupplierFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ProductSupplierFragment.java @@ -11,15 +11,20 @@ import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.ProductSupplierAdapter; -import com.example.petstoremobile.api.RetrofitClient; +import com.example.petstoremobile.api.ProductSupplierApi; import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.dtos.ProductSupplierDTO; import com.example.petstoremobile.fragments.ListFragment; import com.example.petstoremobile.fragments.listfragments.detailfragments.ProductSupplierDetailFragment; import com.google.android.material.floatingactionbutton.FloatingActionButton; import java.util.*; + +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; import retrofit2.*; +@AndroidEntryPoint public class ProductSupplierFragment extends Fragment implements ProductSupplierAdapter.OnProductSupplierClickListener { @@ -29,6 +34,8 @@ public class ProductSupplierFragment extends Fragment private SwipeRefreshLayout swipeRefresh; private EditText etSearch; + @Inject ProductSupplierApi api; + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -92,7 +99,7 @@ public class ProductSupplierFragment extends Fragment private void loadData() { if (swipeRefresh != null) swipeRefresh.setRefreshing(true); - RetrofitClient.getProductSupplierApi(requireContext()).getAllProductSuppliers(0, 100) + api.getAllProductSuppliers(0, 100) .enqueue(new Callback>() { public void onResponse(Call> c, Response> r) { diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/PurchaseOrderFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/PurchaseOrderFragment.java index 76527f09..4840b549 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/PurchaseOrderFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/PurchaseOrderFragment.java @@ -11,14 +11,19 @@ import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.PurchaseOrderAdapter; -import com.example.petstoremobile.api.RetrofitClient; +import com.example.petstoremobile.api.PurchaseOrderApi; import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.dtos.PurchaseOrderDTO; import com.example.petstoremobile.fragments.ListFragment; import com.example.petstoremobile.fragments.listfragments.detailfragments.PurchaseOrderDetailFragment; import java.util.*; + +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; import retrofit2.*; +@AndroidEntryPoint public class PurchaseOrderFragment extends Fragment implements PurchaseOrderAdapter.OnPurchaseOrderClickListener { @@ -28,6 +33,8 @@ public class PurchaseOrderFragment extends Fragment private SwipeRefreshLayout swipeRefresh; private EditText etSearch; + @Inject PurchaseOrderApi api; + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -94,7 +101,7 @@ public class PurchaseOrderFragment extends Fragment private void loadData() { if (swipeRefresh != null) swipeRefresh.setRefreshing(true); - RetrofitClient.getPurchaseOrderApi(requireContext()).getAllPurchaseOrders(0, 100) + api.getAllPurchaseOrders(0, 100) .enqueue(new Callback>() { public void onResponse(Call> c, Response> r) { diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SaleFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SaleFragment.java index c813aa5a..74b8c4ef 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SaleFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SaleFragment.java @@ -15,12 +15,18 @@ import android.widget.ImageButton; import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.SaleAdapter; +import com.example.petstoremobile.api.SaleApi; import com.example.petstoremobile.fragments.ListFragment; import com.example.petstoremobile.fragments.listfragments.detailfragments.RefundDetailFragment; import com.example.petstoremobile.models.Sale; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; + +@AndroidEntryPoint public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickListener { private List saleList = new ArrayList<>(); @@ -30,6 +36,8 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis private EditText etSearch; private ImageButton btnHamburger; + @Inject SaleApi api; + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ServiceFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ServiceFragment.java index 1f204114..afa793de 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ServiceFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ServiceFragment.java @@ -19,7 +19,6 @@ import android.widget.Toast; import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.ServiceAdapter; -import com.example.petstoremobile.api.RetrofitClient; import com.example.petstoremobile.api.ServiceApi; import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.dtos.ServiceDTO; @@ -30,17 +29,21 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; +@AndroidEntryPoint public class ServiceFragment extends Fragment implements ServiceAdapter.OnServiceClickListener { private List serviceList = new ArrayList<>(); private List filteredList = new ArrayList<>(); private ServiceAdapter adapter; private ImageButton hamburger; - private ServiceApi api; + @Inject ServiceApi api; private SwipeRefreshLayout swipeRefreshLayout; private EditText etSearch; @@ -50,7 +53,6 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_service, container, false); - api = RetrofitClient.getServiceApi(requireContext()); hamburger = view.findViewById(R.id.btnHamburger); setupRecyclerView(view); diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SupplierFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SupplierFragment.java index 0d75da78..66b72b8b 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SupplierFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SupplierFragment.java @@ -19,7 +19,6 @@ import android.widget.Toast; import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.SupplierAdapter; -import com.example.petstoremobile.api.RetrofitClient; import com.example.petstoremobile.api.SupplierApi; import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.dtos.SupplierDTO; @@ -30,17 +29,21 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; +@AndroidEntryPoint public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupplierClickListener { private List supplierList = new ArrayList<>(); private List filteredList = new ArrayList<>(); private SupplierAdapter adapter; private ImageButton hamburger; - private SupplierApi api; + @Inject SupplierApi api; private SwipeRefreshLayout swipeRefreshLayout; private EditText etSearch; @@ -50,7 +53,6 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_supplier, container, false); - api = RetrofitClient.getSupplierApi(requireContext()); hamburger = view.findViewById(R.id.btnHamburger); setupRecyclerView(view); diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AdoptionDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AdoptionDetailFragment.java index 47733da2..512427d5 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AdoptionDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AdoptionDetailFragment.java @@ -14,8 +14,13 @@ import com.example.petstoremobile.api.*; import com.example.petstoremobile.dtos.*; import com.example.petstoremobile.fragments.ListFragment; import java.util.*; + +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; import retrofit2.*; +@AndroidEntryPoint public class AdoptionDetailFragment extends Fragment { private TextView tvMode, tvAdoptionId; @@ -33,6 +38,10 @@ public class AdoptionDetailFragment extends Fragment { private final String[] STATUSES = {"Pending", "Approved", "Rejected"}; + @Inject AdoptionApi adoptionApi; + @Inject PetApi petApi; + @Inject CustomerApi customerApi; + @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -84,7 +93,7 @@ public class AdoptionDetailFragment extends Fragment { } private void loadPets() { - RetrofitClient.getPetApi(requireContext()).getAllPets(0, 200) + petApi.getAllPets(0, 200) .enqueue(new Callback>() { public void onResponse(Call> c, Response> r) { @@ -115,7 +124,7 @@ public class AdoptionDetailFragment extends Fragment { } private void loadCustomers() { - RetrofitClient.getCustomerApi(requireContext()).getAllCustomers(0, 200) + customerApi.getAllCustomers(0, 200) .enqueue(new Callback>() { public void onResponse(Call> c, Response> r) { @@ -201,11 +210,10 @@ public class AdoptionDetailFragment extends Fragment { + " customerId=" + customer.getCustomerId() + " date=" + date + " status=" + status); - AdoptionApi api = RetrofitClient.getAdoptionApi(requireContext()); if (isEditing) { - api.updateAdoption(adoptionId, dto).enqueue(simpleCallback("Updated")); + adoptionApi.updateAdoption(adoptionId, dto).enqueue(simpleCallback("Updated")); } else { - api.createAdoption(dto).enqueue(simpleCallback("Saved")); + adoptionApi.createAdoption(dto).enqueue(simpleCallback("Saved")); } } @@ -237,8 +245,7 @@ public class AdoptionDetailFragment extends Fragment { new AlertDialog.Builder(requireContext()) .setTitle("Delete Adoption?") .setPositiveButton("Yes", (d, w) -> - RetrofitClient.getAdoptionApi(requireContext()) - .deleteAdoption(adoptionId) + adoptionApi.deleteAdoption(adoptionId) .enqueue(new Callback() { public void onResponse(Call c, Response r) { navigateBack(); diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AppointmentDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AppointmentDetailFragment.java index eaabc061..51e37666 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AppointmentDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AppointmentDetailFragment.java @@ -14,8 +14,13 @@ import com.example.petstoremobile.api.*; import com.example.petstoremobile.dtos.*; import com.example.petstoremobile.fragments.ListFragment; import java.util.*; + +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; import retrofit2.*; +@AndroidEntryPoint public class AppointmentDetailFragment extends Fragment { private TextView tvMode, tvAppointmentId; @@ -41,6 +46,12 @@ public class AppointmentDetailFragment extends Fragment { private final Integer[] MINUTES = {0,15,30,45}; private final String[] STATUSES = {"Booked","Completed","Cancelled"}; + @Inject AppointmentApi appointmentApi; + @Inject PetApi petApi; + @Inject ServiceApi serviceApi; + @Inject CustomerApi customerApi; + @Inject StoreApi storeApi; + @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_appointment_detail, container, false); @@ -107,7 +118,7 @@ public class AppointmentDetailFragment extends Fragment { } private void loadPets() { - RetrofitClient.getPetApi(requireContext()).getAllPets(0, 200) + petApi.getAllPets(0, 200) .enqueue(new Callback>() { public void onResponse(Call> c, Response> r) { if (r.isSuccessful() && r.body() != null) { @@ -137,7 +148,7 @@ public class AppointmentDetailFragment extends Fragment { } private void loadServices() { - RetrofitClient.getServiceApi(requireContext()).getAllServices(0, 200) + serviceApi.getAllServices(0, 200) .enqueue(new Callback>() { public void onResponse(Call> c, Response> r) { if (r.isSuccessful() && r.body() != null) { @@ -167,7 +178,7 @@ public class AppointmentDetailFragment extends Fragment { } private void loadCustomers() { - RetrofitClient.getCustomerApi(requireContext()).getAllCustomers(0, 200) + customerApi.getAllCustomers(0, 200) .enqueue(new Callback>() { public void onResponse(Call> c, Response> r) { if (r.isSuccessful() && r.body() != null) { @@ -198,7 +209,7 @@ public class AppointmentDetailFragment extends Fragment { } private void loadStores() { - RetrofitClient.getStoreApi(requireContext()).getAllStores(0, 50) + storeApi.getAllStores(0, 50) .enqueue(new Callback>() { public void onResponse(Call> c, Response> r) { if (r.isSuccessful() && r.body() != null) { @@ -228,7 +239,7 @@ public class AppointmentDetailFragment extends Fragment { } private void loadAllAppointments() { - RetrofitClient.getAppointmentApi(requireContext()).getAllAppointments(0, 500) + appointmentApi.getAllAppointments(0, 500) .enqueue(new Callback>() { public void onResponse(Call> c, Response> r) { if (r.isSuccessful() && r.body() != null) @@ -350,11 +361,10 @@ public class AppointmentDetailFragment extends Fragment { + " petId=" + pet.getPetId() + " date=" + date + " time=" + time); - AppointmentApi api = RetrofitClient.getAppointmentApi(requireContext()); if (isEditing) { - api.updateAppointment(appointmentId, dto).enqueue(simpleCallback("Updated")); + appointmentApi.updateAppointment(appointmentId, dto).enqueue(simpleCallback("Updated")); } else { - api.createAppointment(dto).enqueue(simpleCallback("Saved")); + appointmentApi.createAppointment(dto).enqueue(simpleCallback("Saved")); } } @@ -426,8 +436,7 @@ public class AppointmentDetailFragment extends Fragment { new AlertDialog.Builder(requireContext()) .setTitle("Delete Appointment?") .setPositiveButton("Yes", (d, w) -> - RetrofitClient.getAppointmentApi(requireContext()) - .deleteAppointment(appointmentId) + appointmentApi.deleteAppointment(appointmentId) .enqueue(new Callback() { public void onResponse(Call c, Response r) { navigateBack(); } public void onFailure(Call c, Throwable t) { diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/InventoryDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/InventoryDetailFragment.java index f98351e3..c1bb052d 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/InventoryDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/InventoryDetailFragment.java @@ -21,7 +21,6 @@ import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.BlackTextArrayAdapter; import com.example.petstoremobile.api.InventoryApi; import com.example.petstoremobile.api.ProductApi; -import com.example.petstoremobile.api.RetrofitClient; import com.example.petstoremobile.dtos.InventoryDTO; import com.example.petstoremobile.dtos.InventoryRequest; import com.example.petstoremobile.dtos.PageResponse; @@ -32,10 +31,14 @@ import com.example.petstoremobile.fragments.listfragments.InventoryFragment; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; +@AndroidEntryPoint public class InventoryDetailFragment extends Fragment { private TextView tvMode, tvInventoryId, tvProductInfo; @@ -43,8 +46,8 @@ public class InventoryDetailFragment extends Fragment { private android.widget.EditText etQuantity; private Button btnSave, btnDelete, btnBack; - private InventoryApi inventoryApi; - private ProductApi productApi; + @Inject InventoryApi inventoryApi; + @Inject ProductApi productApi; private InventoryFragment inventoryFragment; private boolean isEditing = false; @@ -70,9 +73,6 @@ public class InventoryDetailFragment extends Fragment { Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_inventory_detail, container, false); - inventoryApi = RetrofitClient.getInventoryApi(requireContext()); - productApi = RetrofitClient.getProductApi(requireContext()); - initViews(view); setupProductSearch(); handleArguments(); diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/PetDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/PetDetailFragment.java index c37558a7..7f9a44f1 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/PetDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/PetDetailFragment.java @@ -22,17 +22,20 @@ import android.widget.Toast; import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.BlackTextArrayAdapter; import com.example.petstoremobile.api.PetApi; -import com.example.petstoremobile.api.RetrofitClient; import com.example.petstoremobile.dtos.PetDTO; import com.example.petstoremobile.fragments.ListFragment; import com.example.petstoremobile.fragments.listfragments.PetFragment; import com.example.petstoremobile.utils.ActivityLogger; import com.example.petstoremobile.utils.InputValidator; +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; +@AndroidEntryPoint public class PetDetailFragment extends Fragment { private TextView tvMode, tvPetId; @@ -43,6 +46,8 @@ public class PetDetailFragment extends Fragment { private boolean isEditing = false; private PetFragment petFragment; + @Inject PetApi petApi; + //set the pet fragment to the parent so we refer back to pet view when save or delete is done public void setPetFragment(PetFragment fragment) { this.petFragment = fragment; @@ -92,8 +97,6 @@ public class PetDetailFragment extends Fragment { petDTO.setPetPrice(priceStr); petDTO.setPetStatus(status); - PetApi petApi = RetrofitClient.getPetApi(requireContext()); - //check if the pet is being edited or added if (isEditing) { // Update existing pet @@ -148,7 +151,6 @@ public class PetDetailFragment extends Fragment { .setTitle("Delete Pet") .setMessage("Are you sure you want to delete " + etPetName.getText().toString() + "?") .setPositiveButton("Delete", (dialog, which) -> { - PetApi petApi = RetrofitClient.getPetApi(requireContext()); //if they say yes then delete the pet petApi.deletePet((long) petId).enqueue(new Callback() { @Override @@ -242,4 +244,4 @@ public class PetDetailFragment extends Fragment { spinnerPetStatus.setAdapter(adapter); } -} +} \ No newline at end of file diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ProductDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ProductDetailFragment.java index 40bdc91b..228f2c01 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ProductDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ProductDetailFragment.java @@ -29,11 +29,17 @@ import java.io.FileOutputStream; import java.io.InputStream; import java.math.BigDecimal; import java.util.*; + +import javax.inject.Inject; +import javax.inject.Named; + +import dagger.hilt.android.AndroidEntryPoint; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.RequestBody; import retrofit2.*; +@AndroidEntryPoint public class ProductDetailFragment extends Fragment { private TextView tvMode, tvProductId; @@ -50,6 +56,10 @@ public class ProductDetailFragment extends Fragment { private List categoryList = new ArrayList<>(); private Uri photoUri; + @Inject ProductApi productApi; + @Inject CategoryApi categoryApi; + @Inject @Named("baseUrl") String baseUrl; + private ActivityResultLauncher galleryLauncher; private ActivityResultLauncher cameraLauncher; private ActivityResultLauncher permissionLauncher; @@ -155,7 +165,7 @@ public class ProductDetailFragment extends Fragment { // Helper function to remove the photo private void removePhoto() { if (isEditing) { - RetrofitClient.getProductApi(requireContext()).deleteProductImage(prodId) + productApi.deleteProductImage(prodId) .enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { @@ -187,7 +197,7 @@ public class ProductDetailFragment extends Fragment { } private void loadCategories() { - RetrofitClient.getCategoryApi(requireContext()).getAllCategories(0, 100) + categoryApi.getAllCategories(0, 100) .enqueue(new Callback>() { public void onResponse(Call> c, Response> r) { @@ -243,7 +253,7 @@ public class ProductDetailFragment extends Fragment { //load the product image from the backend private void loadProductImage() { - String imageUrl = RetrofitClient.BASE_URL + String.format(Locale.US, ProductApi.PRODUCT_IMAGE_PATH, prodId); + String imageUrl = baseUrl + String.format(Locale.US, ProductApi.PRODUCT_IMAGE_PATH, prodId); Glide.with(this) .load(imageUrl) .diskCacheStrategy(DiskCacheStrategy.NONE) @@ -262,7 +272,7 @@ public class ProductDetailFragment extends Fragment { RequestBody requestFile = RequestBody.create(file, MediaType.parse(requireContext().getContentResolver().getType(uri))); MultipartBody.Part body = MultipartBody.Part.createFormData("image", file.getName(), requestFile); - RetrofitClient.getProductApi(requireContext()).uploadProductImage(prodId, body) + productApi.uploadProductImage(prodId, body) .enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { @@ -328,11 +338,10 @@ public class ProductDetailFragment extends Fragment { ProductDTO dto = new ProductDTO(name, category.getCategoryId(), desc, price); - ProductApi api = RetrofitClient.getProductApi(requireContext()); if (isEditing) { - api.updateProduct(prodId, dto).enqueue(simpleCallback("Updated")); + productApi.updateProduct(prodId, dto).enqueue(simpleCallback("Updated")); } else { - api.createProduct(dto).enqueue(new Callback() { + productApi.createProduct(dto).enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { if (response.isSuccessful() && response.body() != null) { @@ -375,8 +384,7 @@ public class ProductDetailFragment extends Fragment { new AlertDialog.Builder(requireContext()) .setTitle("Delete Product?") .setPositiveButton("Yes", (d, w) -> - RetrofitClient.getProductApi(requireContext()) - .deleteProduct(prodId) + productApi.deleteProduct(prodId) .enqueue(new Callback() { public void onResponse(Call c, Response r) { navigateBack(); diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ProductSupplierDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ProductSupplierDetailFragment.java index 2bd47432..909df42c 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ProductSupplierDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ProductSupplierDetailFragment.java @@ -14,8 +14,13 @@ import com.example.petstoremobile.dtos.*; import com.example.petstoremobile.fragments.ListFragment; import java.math.BigDecimal; import java.util.*; + +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; import retrofit2.*; +@AndroidEntryPoint public class ProductSupplierDetailFragment extends Fragment { private TextView tvMode; @@ -32,6 +37,10 @@ public class ProductSupplierDetailFragment extends Fragment { private List productList = new ArrayList<>(); private List supplierList = new ArrayList<>(); + @Inject ProductSupplierApi productSupplierApi; + @Inject ProductApi productApi; + @Inject SupplierApi supplierApi; + @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -62,7 +71,7 @@ public class ProductSupplierDetailFragment extends Fragment { } private void loadProducts() { - RetrofitClient.getProductApi(requireContext()).getAllProducts(null, 0, 200) + productApi.getAllProducts(null, 0, 200) .enqueue(new Callback>() { public void onResponse(Call> c, Response> r) { @@ -93,7 +102,7 @@ public class ProductSupplierDetailFragment extends Fragment { } private void loadSuppliers() { - RetrofitClient.getSupplierApi(requireContext()).getAllSuppliers(0, 200) + supplierApi.getAllSuppliers(0, 200) .enqueue(new Callback>() { public void onResponse(Call> c, Response> r) { @@ -164,12 +173,11 @@ public class ProductSupplierDetailFragment extends Fragment { ProductSupplierDTO dto = new ProductSupplierDTO( product.getProdId(), supplier.getSupId(), cost); - ProductSupplierApi api = RetrofitClient.getProductSupplierApi(requireContext()); if (isEditing) { - api.updateProductSupplier(editProductId, editSupplierId, dto) + productSupplierApi.updateProductSupplier(editProductId, editSupplierId, dto) .enqueue(simpleCallback("Updated")); } else { - api.createProductSupplier(dto).enqueue(simpleCallback("Saved")); + productSupplierApi.createProductSupplier(dto).enqueue(simpleCallback("Saved")); } } @@ -200,8 +208,7 @@ public class ProductSupplierDetailFragment extends Fragment { new AlertDialog.Builder(requireContext()) .setTitle("Delete?") .setPositiveButton("Yes", (d, w) -> - RetrofitClient.getProductSupplierApi(requireContext()) - .deleteProductSupplier(editProductId, editSupplierId) + productSupplierApi.deleteProductSupplier(editProductId, editSupplierId) .enqueue(new Callback() { public void onResponse(Call c, Response r) { navigateBack(); diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/PurchaseOrderDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/PurchaseOrderDetailFragment.java index 47dae4d8..eec648e2 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/PurchaseOrderDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/PurchaseOrderDetailFragment.java @@ -9,6 +9,9 @@ import androidx.fragment.app.Fragment; import com.example.petstoremobile.R; import com.example.petstoremobile.fragments.ListFragment; +import dagger.hilt.android.AndroidEntryPoint; + +@AndroidEntryPoint public class PurchaseOrderDetailFragment extends Fragment { private TextView tvId, tvSupplier, tvDate, tvStatus; diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/RefundDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/RefundDetailFragment.java index 41b8f3b2..190c89c5 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/RefundDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/RefundDetailFragment.java @@ -13,11 +13,17 @@ import android.widget.TextView; import android.widget.Toast; import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.BlackTextArrayAdapter; +import com.example.petstoremobile.api.SaleApi; import com.example.petstoremobile.fragments.ListFragment; import com.example.petstoremobile.fragments.listfragments.SaleFragment; import com.example.petstoremobile.utils.ActivityLogger; import com.example.petstoremobile.utils.InputValidator; +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; + +@AndroidEntryPoint public class RefundDetailFragment extends Fragment { private EditText etRefundSaleId, etRefundReason; @@ -27,6 +33,8 @@ public class RefundDetailFragment extends Fragment { private int saleId; private SaleFragment saleFragment; + @Inject SaleApi saleApi; + public void setSaleFragment(SaleFragment fragment) { this.saleFragment = fragment; } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ServiceDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ServiceDetailFragment.java index c7b0a4c5..4176fa38 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ServiceDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ServiceDetailFragment.java @@ -15,7 +15,6 @@ import android.widget.TextView; import android.widget.Toast; import com.example.petstoremobile.R; -import com.example.petstoremobile.api.RetrofitClient; import com.example.petstoremobile.api.ServiceApi; import com.example.petstoremobile.dtos.ServiceDTO; import com.example.petstoremobile.fragments.ListFragment; @@ -23,10 +22,14 @@ import com.example.petstoremobile.fragments.listfragments.ServiceFragment; import com.example.petstoremobile.utils.ActivityLogger; import com.example.petstoremobile.utils.InputValidator; +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; +@AndroidEntryPoint public class ServiceDetailFragment extends Fragment { private TextView tvMode, tvServiceId; @@ -36,6 +39,8 @@ public class ServiceDetailFragment extends Fragment { private boolean isEditing = false; private ServiceFragment serviceFragment; + @Inject ServiceApi serviceApi; + //set the service fragment to the parent so we refer back to service view when save or delete is done public void setServiceFragment(ServiceFragment fragment) { this.serviceFragment = fragment; @@ -79,8 +84,6 @@ public class ServiceDetailFragment extends Fragment { serviceDTO.setServiceDuration(duration); serviceDTO.setServicePrice(price); - ServiceApi serviceApi = RetrofitClient.getServiceApi(requireContext()); - //check if the service is being edited or added if (isEditing) { // Update existing service @@ -135,7 +138,6 @@ public class ServiceDetailFragment extends Fragment { .setTitle("Delete Service") .setMessage("Are you sure you want to delete " + etServiceName.getText().toString() + "?") .setPositiveButton("Delete", (dialog, which) -> { - ServiceApi serviceApi = RetrofitClient.getServiceApi(requireContext()); serviceApi.deleteService((long) serviceId).enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { @@ -205,4 +207,4 @@ public class ServiceDetailFragment extends Fragment { btnDeleteService = view.findViewById(R.id.btnDeleteService); btnBack = view.findViewById(R.id.btnBack); } -} +} \ No newline at end of file diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/SupplierDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/SupplierDetailFragment.java index df5c5520..6125ddc5 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/SupplierDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/SupplierDetailFragment.java @@ -15,7 +15,6 @@ import android.widget.TextView; import android.widget.Toast; import com.example.petstoremobile.R; -import com.example.petstoremobile.api.RetrofitClient; import com.example.petstoremobile.api.SupplierApi; import com.example.petstoremobile.dtos.SupplierDTO; import com.example.petstoremobile.fragments.ListFragment; @@ -23,10 +22,14 @@ import com.example.petstoremobile.fragments.listfragments.SupplierFragment; import com.example.petstoremobile.utils.ActivityLogger; import com.example.petstoremobile.utils.InputValidator; +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; +@AndroidEntryPoint public class SupplierDetailFragment extends Fragment { private TextView tvMode, tvSupId; @@ -36,6 +39,8 @@ public class SupplierDetailFragment extends Fragment { private boolean isEditing = false; private SupplierFragment supplierFragment; + @Inject SupplierApi supplierApi; + //set the supplier fragment to the parent so we refer back to supplier view when save or delete is done public void setSupplierFragment(SupplierFragment fragment) { this.supplierFragment = fragment; @@ -82,8 +87,6 @@ public class SupplierDetailFragment extends Fragment { supplierDTO.setSupEmail(email); supplierDTO.setSupPhone(phone); - SupplierApi supplierApi = RetrofitClient.getSupplierApi(requireContext()); - //check if the supplier is being edited or added if (isEditing) { // Update existing supplier @@ -138,7 +141,6 @@ public class SupplierDetailFragment extends Fragment { .setTitle("Delete Supplier") .setMessage("Are you sure you want to delete " + etSupCompany.getText().toString() + "?") .setPositiveButton("Delete", (dialog, which) -> { - SupplierApi supplierApi = RetrofitClient.getSupplierApi(requireContext()); supplierApi.deleteSupplier((long) supId).enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { @@ -215,4 +217,4 @@ public class SupplierDetailFragment extends Fragment { btnDeleteSupplier = view.findViewById(R.id.btnDeleteSupplier); btnBack = view.findViewById(R.id.btnBack); } -} +} \ No newline at end of file diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/listprofilefragments/PetProfileFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/listprofilefragments/PetProfileFragment.java index c4c47d31..bf51556a 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/listprofilefragments/PetProfileFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/listprofilefragments/PetProfileFragment.java @@ -29,7 +29,6 @@ import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.example.petstoremobile.R; import com.example.petstoremobile.api.PetApi; -import com.example.petstoremobile.api.RetrofitClient; import com.example.petstoremobile.fragments.ListFragment; import com.example.petstoremobile.fragments.listfragments.detailfragments.PetDetailFragment; @@ -40,6 +39,10 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; +import javax.inject.Inject; +import javax.inject.Named; + +import dagger.hilt.android.AndroidEntryPoint; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.RequestBody; @@ -47,6 +50,7 @@ import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; +@AndroidEntryPoint public class PetProfileFragment extends Fragment { private TextView tvPetName, tvPetSpecies, tvPetBreed, tvPetAge, tvPetPrice; @@ -56,6 +60,9 @@ public class PetProfileFragment extends Fragment { private int petId; private boolean hasImage = false; + @Inject PetApi petApi; + @Inject @Named("baseUrl") String baseUrl; + // launchers for camera and gallery private ActivityResultLauncher galleryLauncher; private ActivityResultLauncher cameraLauncher; @@ -201,7 +208,7 @@ public class PetProfileFragment extends Fragment { // Helper function to load pet image from backend private void loadPetImage(int petId) { - String imageUrl = RetrofitClient.BASE_URL + String.format(Locale.US, PetApi.PET_IMAGE_PATH, petId); + String imageUrl = baseUrl + String.format(Locale.US, PetApi.PET_IMAGE_PATH, petId); Glide.with(this) .load(imageUrl) @@ -236,7 +243,6 @@ public class PetProfileFragment extends Fragment { MultipartBody.Part body = MultipartBody.Part.createFormData("image", file.getName(), requestFile); // Call the backend to upload the image - PetApi petApi = RetrofitClient.getPetApi(requireContext()); petApi.uploadPetImage((long) petId, body).enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { @@ -261,7 +267,6 @@ public class PetProfileFragment extends Fragment { } private void deletePetImage() { - PetApi petApi = RetrofitClient.getPetApi(requireContext()); petApi.deletePetImage((long) petId).enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { diff --git a/android/app/src/main/java/com/example/petstoremobile/services/ChatNotificationService.java b/android/app/src/main/java/com/example/petstoremobile/services/ChatNotificationService.java index a1e16fa6..a502d8d0 100644 --- a/android/app/src/main/java/com/example/petstoremobile/services/ChatNotificationService.java +++ b/android/app/src/main/java/com/example/petstoremobile/services/ChatNotificationService.java @@ -8,7 +8,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.example.petstoremobile.api.ChatApi; import com.example.petstoremobile.api.CustomerApi; -import com.example.petstoremobile.api.RetrofitClient; import com.example.petstoremobile.api.auth.TokenManager; import com.example.petstoremobile.dtos.ConversationDTO; import com.example.petstoremobile.dtos.CustomerDTO; @@ -21,11 +20,17 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Named; + +import dagger.hilt.android.AndroidEntryPoint; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; // Service to receive notifications when a new conversation is created +@AndroidEntryPoint public class ChatNotificationService extends Service { private static final String TAG = "ChatNotificationService"; @@ -37,6 +42,11 @@ public class ChatNotificationService extends Service { private final Map customerIdToName = new HashMap<>(); private Long currentUserId; + @Inject CustomerApi customerApi; + @Inject ChatApi chatApi; + @Inject TokenManager tokenManager; + @Inject @Named("baseUrl") String baseUrl; + //When the service starts, connect to the websocket @Override public int onStartCommand(Intent intent, int flags, int startId) { @@ -48,14 +58,11 @@ public class ChatNotificationService extends Service { // helper function to connect to the websocket private void connectWebSocket() { //get the token and role from the shared preferences - TokenManager tm = TokenManager.getInstance(this); - String token = tm.getToken(); - String role = tm.getRole(); - currentUserId = tm.getUserId(); + String token = tokenManager.getToken(); + String role = tokenManager.getRole(); + currentUserId = tokenManager.getUserId(); if (token != null && stompChatManager == null) { - //load customers to have names associated with customer ids - CustomerApi customerApi = RetrofitClient.getCustomerApi(this); customerApi.getAllCustomers(0, 1000).enqueue(new Callback>() { @Override public void onResponse(@NonNull Call> call, @NonNull Response> response) { @@ -78,7 +85,6 @@ public class ChatNotificationService extends Service { private void loadConversationsAndStartStomp(String token, String role) { // Fetch existing conversations - ChatApi chatApi = RetrofitClient.getChatApi(this); chatApi.getAllConversations().enqueue(new Callback>() { @Override public void onResponse(@NonNull Call> call, @NonNull Response> response) { @@ -110,7 +116,7 @@ public class ChatNotificationService extends Service { private void startStomp(String token, String role) { if (stompChatManager != null) return; - stompChatManager = new StompChatManager(token, role); + stompChatManager = new StompChatManager(token, role, baseUrl); // Listen for messages in existing conversations stompChatManager.setMessageListener(message -> { @@ -193,7 +199,6 @@ public class ChatNotificationService extends Service { // Helper function to fetch customer name for a conversation private void fetchCustomerName(Long customerId) { - CustomerApi customerApi = RetrofitClient.getCustomerApi(this); customerApi.getCustomerById(customerId).enqueue(new Callback() { @Override public void onResponse(@NonNull Call call, @NonNull Response response) { @@ -223,4 +228,4 @@ public class ChatNotificationService extends Service { public IBinder onBind(Intent intent) { return null; } -} +} \ No newline at end of file diff --git a/android/app/src/main/java/com/example/petstoremobile/websocket/StompChatManager.java b/android/app/src/main/java/com/example/petstoremobile/websocket/StompChatManager.java index af4264db..f0f5d7ec 100644 --- a/android/app/src/main/java/com/example/petstoremobile/websocket/StompChatManager.java +++ b/android/app/src/main/java/com/example/petstoremobile/websocket/StompChatManager.java @@ -3,7 +3,6 @@ package com.example.petstoremobile.websocket; import android.os.Handler; import android.os.Looper; import android.util.Log; -import com.example.petstoremobile.api.RetrofitClient; import com.example.petstoremobile.dtos.ConversationDTO; import com.example.petstoremobile.dtos.MessageDTO; import com.google.gson.Gson; @@ -54,14 +53,16 @@ public class StompChatManager { private ConnectionListener connectionListener; private final String authToken; private final String role; + private final String baseUrl; private boolean isConnected; private boolean isConnecting; private boolean manualDisconnect; private Long pendingConversationId; - public StompChatManager(String authToken, String role) { + public StompChatManager(String authToken, String role, String baseUrl) { this.authToken = authToken; this.role = role == null ? "" : role.trim().toUpperCase(Locale.ROOT); + this.baseUrl = baseUrl; } public void setMessageListener(MessageListener listener) { @@ -267,16 +268,16 @@ public class StompChatManager { // Make the URL for the websocket connection private String buildWebSocketUrl() { - String baseUrl = RetrofitClient.BASE_URL.endsWith("/") - ? RetrofitClient.BASE_URL.substring(0, RetrofitClient.BASE_URL.length() - 1) - : RetrofitClient.BASE_URL; - if (baseUrl.startsWith("https://")) { - return "wss://" + baseUrl.substring("https://".length()) + "/ws/chat"; + String cleanBaseUrl = baseUrl.endsWith("/") + ? baseUrl.substring(0, baseUrl.length() - 1) + : baseUrl; + if (cleanBaseUrl.startsWith("https://")) { + return "wss://" + cleanBaseUrl.substring("https://".length()) + "/ws/chat"; } - if (baseUrl.startsWith("http://")) { - return "ws://" + baseUrl.substring("http://".length()) + "/ws/chat"; + if (cleanBaseUrl.startsWith("http://")) { + return "ws://" + cleanBaseUrl.substring("http://".length()) + "/ws/chat"; } - return baseUrl + "/ws/chat"; + return cleanBaseUrl + "/ws/chat"; } // Helper to check if the current user is a customer @@ -292,4 +293,4 @@ public class StompChatManager { reconnectHandler.removeCallbacksAndMessages(null); reconnectHandler.postDelayed(this::connect, 1000); } -} +} \ No newline at end of file diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 37562787..91c68295 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -1,4 +1,5 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { alias(libs.plugins.android.application) apply false + alias(libs.plugins.hilt) apply false } \ No newline at end of file diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 18b8ebb8..8e1b4f14 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -8,6 +8,7 @@ material = "1.13.0" activity = "1.12.4" constraintlayout = "2.2.1" swiperefreshlayout = "1.2.0" +hilt = "2.51.1" [libraries] junit = { group = "junit", name = "junit", version.ref = "junit" } @@ -18,7 +19,9 @@ material = { group = "com.google.android.material", name = "material", version.r activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } swiperefreshlayout = { group = "androidx.swiperefreshlayout", name = "swiperefreshlayout", version.ref = "swiperefreshlayout" } +hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } +hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } - +hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }