diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..fb7f4a8 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..a0de2a1 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..82d4462 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..97f2bc5 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,61 @@ +plugins { + id 'com.android.application' +} + +android { + compileSdk 32 + + dataBinding { + enabled=true + } + viewBinding{ + enabled=true + } + + defaultConfig { + applicationId "com.example.shoppingcart" + minSdk 23 + targetSdk 32 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + + implementation 'androidx.appcompat:appcompat:1.5.1' + implementation 'com.google.android.material:material:1.6.1' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + +// recyclerview + implementation "androidx.recyclerview:recyclerview:1.2.1" + //mvvm + // ViewModel + implementation "androidx.lifecycle:lifecycle-viewmodel:2.5.1" // LiveData + implementation "androidx.lifecycle:lifecycle-livedata:2.5.1" + +// navigation + // Java language implementation + implementation "androidx.navigation:navigation-fragment:2.5.2" + implementation "androidx.navigation:navigation-ui:2.5.2" + +// glide for imageview + implementation 'com.github.bumptech.glide:glide:4.14.1' + annotationProcessor 'com.github.bumptech.glide:compiler:4.14.1' +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/com/example/shoppingcart/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/example/shoppingcart/ExampleInstrumentedTest.java new file mode 100644 index 0000000..df3c491 --- /dev/null +++ b/app/src/androidTest/java/com/example/shoppingcart/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.example.shoppingcart; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.example.shoppingcart", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..ed88ed6 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/shoppingcart/adapters/CartListAdapter.java b/app/src/main/java/com/example/shoppingcart/adapters/CartListAdapter.java new file mode 100644 index 0000000..ab55496 --- /dev/null +++ b/app/src/main/java/com/example/shoppingcart/adapters/CartListAdapter.java @@ -0,0 +1,84 @@ +package com.example.shoppingcart.adapters; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.DiffUtil; +import androidx.recyclerview.widget.ListAdapter; +import androidx.recyclerview.widget.RecyclerView; + +import com.example.shoppingcart.databinding.CartRowBinding; +import com.example.shoppingcart.models.CartItem; + +import java.util.List; + +public class CartListAdapter extends ListAdapter { + + CartInterface cartInterface; + + public CartListAdapter(CartInterface cartInterface) { + super(CartItem.itemCallback); + this.cartInterface = cartInterface; + } + + @NonNull + @Override + public CartViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + CartRowBinding cartRowBinding = CartRowBinding.inflate(inflater,parent,false); + return new CartViewHolder(cartRowBinding); + } + + @Override + public void onBindViewHolder(@NonNull CartViewHolder holder, int position) { + holder.cartRowBinding.setCartItem(getItem(position)); + + //if binding are pendings + holder.cartRowBinding.executePendingBindings(); + } + + class CartViewHolder extends RecyclerView.ViewHolder{ + + CartRowBinding cartRowBinding; + + public CartViewHolder(@NonNull CartRowBinding cartRowBinding) { + super(cartRowBinding.getRoot()); + this.cartRowBinding= cartRowBinding; + + cartRowBinding.deleteCartIV.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + cartRowBinding.spinner.setSelection((getItem(getAdapterPosition()).getQuantity())-1); + cartInterface.deleteItem(getItem(getAdapterPosition())); + notifyDataSetChanged(); + } + }); + + //quanity of spinner + cartRowBinding.spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + int quantity = position + 1; + if(quantity == getItem(getAdapterPosition()).getQuantity()){ + return; + } + cartInterface.changeQuantity(getItem(getAdapterPosition()), quantity); + } + + @Override + public void onNothingSelected(AdapterView parent) { + + } + }); + } + } + + public interface CartInterface{ + + void deleteItem(CartItem cartItem); + void changeQuantity(CartItem cartItem,int quantity); + } +} diff --git a/app/src/main/java/com/example/shoppingcart/adapters/ShopListAdapter.java b/app/src/main/java/com/example/shoppingcart/adapters/ShopListAdapter.java new file mode 100644 index 0000000..78deae6 --- /dev/null +++ b/app/src/main/java/com/example/shoppingcart/adapters/ShopListAdapter.java @@ -0,0 +1,62 @@ +package com.example.shoppingcart.adapters; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.AsyncDifferConfig; +import androidx.recyclerview.widget.DiffUtil; +import androidx.recyclerview.widget.ListAdapter; +import androidx.recyclerview.widget.RecyclerView; + +import com.example.shoppingcart.databinding.ShopRowBinding; +import com.example.shoppingcart.models.Product; + +public class ShopListAdapter extends ListAdapter { + + ShopInterface shopInterface; + + public ShopListAdapter(ShopInterface shopInterface) { + super(Product.itemCallback); + this.shopInterface = shopInterface; + } + + @NonNull + @Override + public ShopViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); + ShopRowBinding shopRowBinding = ShopRowBinding.inflate(layoutInflater,parent,false); + shopRowBinding.setShopInterface(shopInterface); + return new ShopViewHolder(shopRowBinding); + } + + @Override + public void onBindViewHolder(@NonNull ShopViewHolder holder, int position) { + Product product = getItem(position); + holder.shopRowBinding.setProduct(product); + } + + class ShopViewHolder extends RecyclerView.ViewHolder{ + + ShopRowBinding shopRowBinding; + + public ShopViewHolder(ShopRowBinding binding) { + super(binding.getRoot()); + this.shopRowBinding = binding; + + /* differently way by adding variable in show_row and added onclick in LinearLayout*/ +// this.shopRowBinding.getRoot().setOnClickListener(new View.OnClickListener() { +// @Override +// public void onClick(View v) { +// +// } +// }); + } + } + + public interface ShopInterface{ + void addItem(Product product); + void onItemClick(Product product); + } +} diff --git a/app/src/main/java/com/example/shoppingcart/models/CartItem.java b/app/src/main/java/com/example/shoppingcart/models/CartItem.java new file mode 100644 index 0000000..ae8cdc8 --- /dev/null +++ b/app/src/main/java/com/example/shoppingcart/models/CartItem.java @@ -0,0 +1,72 @@ +package com.example.shoppingcart.models; + +import android.widget.Spinner; + +import androidx.annotation.NonNull; +import androidx.databinding.BindingAdapter; +import androidx.recyclerview.widget.DiffUtil; + +import java.util.Objects; + +public class CartItem { + + private Product product; + private int quantity; + + public CartItem(Product product, int quantity) { + this.product = product; + this.quantity = quantity; + } + + public Product getProduct() { + return product; + } + + public void setProduct(Product product) { + this.product = product; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } + + @Override + public String toString() { + return "CartItem{" + + "product=" + product + + ", quantity=" + quantity + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CartItem cartItem = (CartItem) o; + return getQuantity() == cartItem.getQuantity() && getProduct().equals(cartItem.getProduct()); + } + + //for getting spinner value of no of quantity added in cart + @BindingAdapter("android:setVal") + public static void getSelectedSpinnerValue(Spinner spinner, int quantity){ + spinner.setSelection(quantity - 1, true); + + } + + //for DiffUtil.ItemCallback diffCallback for setting adapter constructor + public static DiffUtil.ItemCallback itemCallback = new DiffUtil.ItemCallback() { + @Override + public boolean areItemsTheSame(@NonNull CartItem oldItem, @NonNull CartItem newItem) { + return oldItem.getQuantity()==(newItem.getQuantity()); + } + + @Override + public boolean areContentsTheSame(@NonNull CartItem oldItem, @NonNull CartItem newItem) { + return oldItem.equals(newItem); + } + }; +} diff --git a/app/src/main/java/com/example/shoppingcart/models/Product.java b/app/src/main/java/com/example/shoppingcart/models/Product.java new file mode 100644 index 0000000..d80b81f --- /dev/null +++ b/app/src/main/java/com/example/shoppingcart/models/Product.java @@ -0,0 +1,108 @@ +package com.example.shoppingcart.models; + +import android.widget.ImageView; + +import androidx.annotation.NonNull; +import androidx.databinding.BindingAdapter; +import androidx.recyclerview.widget.DiffUtil; + +import com.bumptech.glide.Glide; + +import java.util.Objects; + +public class Product { + + String id; + String name; + double price; + boolean isAvailable; + String imageUrl; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Product product = (Product) o; + return Double.compare(product.getPrice(), getPrice()) == 0 && isAvailable() == product.isAvailable() && getId().equals(product.getId()) && getName().equals(product.getName()) && getImageUrl().equals(product.getImageUrl()); + } + + public static DiffUtil.ItemCallback itemCallback = new DiffUtil.ItemCallback() { + @Override + public boolean areItemsTheSame(@NonNull Product oldItem, @NonNull Product newItem) { + return oldItem.getId().equals(newItem.getId()); + } + + @Override + public boolean areContentsTheSame(@NonNull Product oldItem, @NonNull Product newItem) { + return oldItem.equals(newItem); + } + }; + + @Override + public String toString() { + return "Product{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + ", price=" + price + + ", isAvailable=" + isAvailable + + ", imageUrl='" + imageUrl + '\'' + + '}'; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public double getPrice() { + return price; + } + + public void setPrice(double price) { + this.price = price; + } + + public boolean isAvailable() { + return isAvailable; + } + + public void setAvailable(boolean available) { + isAvailable = available; + } + + public String getImageUrl() { + return imageUrl; + } + + public void setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } + + public Product(String id, String name, double price, boolean isAvailable, String imageUrl) { + this.id = id; + this.name = name; + this.price = price; + this.isAvailable = isAvailable; + this.imageUrl = imageUrl; + } + + @BindingAdapter("android:productImage") + public static void loadImage(ImageView imageView,String imageUrl){ + Glide.with(imageView) + .load(imageUrl) + .fitCenter() + .into(imageView); + + } +} diff --git a/app/src/main/java/com/example/shoppingcart/repositories/CartRepository.java b/app/src/main/java/com/example/shoppingcart/repositories/CartRepository.java new file mode 100644 index 0000000..bfe9d17 --- /dev/null +++ b/app/src/main/java/com/example/shoppingcart/repositories/CartRepository.java @@ -0,0 +1,112 @@ +package com.example.shoppingcart.repositories; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; + +import com.example.shoppingcart.models.CartItem; +import com.example.shoppingcart.models.Product; + +import java.util.ArrayList; +import java.util.List; + +public class CartRepository { + + //to get the list of items inside cart + private MutableLiveData> mutableCart = new MutableLiveData<>(); + + //get total price for cart + private MutableLiveData mutableTotalPrice = new MutableLiveData<>(); + + //connect shopfragment to shopRepo , we use shopView model to access this method + public LiveData> getCart(){ + if (mutableCart.getValue() == null){ + initCart(); + } + + return mutableCart; + } + + public void initCart() { + mutableCart.setValue(new ArrayList<>()); + calculateCartTotal(); + } + + public boolean addItemToCart(Product product){ + if (mutableCart.getValue() == null){ + initCart(); + } + List cartItemList = new ArrayList<>(mutableCart.getValue()); + + for (CartItem cartItem : cartItemList) { + if (cartItem.getProduct().getId().equals(product.getId())) { + if (cartItem.getQuantity() == 5) { + return false; + } + + int index = cartItemList.indexOf(cartItem); + cartItem.setQuantity(cartItem.getQuantity() + 1); + cartItemList.set(index, cartItem); + + mutableCart.setValue(cartItemList); + return true; + } + } + CartItem cartItems = new CartItem(product,1); + cartItemList.add(cartItems); + + mutableCart.setValue(cartItemList); + calculateCartTotal(); + return true; + } + + public void removeItemFromCart(CartItem cartItem){ + if (mutableCart.getValue() == null){ + return; + } + List cartItemList = new ArrayList<>(mutableCart.getValue()); + if (cartItemList.contains(cartItem)){ + int index = cartItemList.indexOf(cartItem); + if(cartItemList.get(index).getQuantity()>1) { + cartItem.setQuantity(cartItem.getQuantity() - 1); + cartItemList.set(index, cartItem); + mutableCart.setValue(cartItemList); + }else{ + cartItemList.remove(cartItem); + mutableCart.setValue(cartItemList); + } + } + else { + cartItemList.remove(cartItem); + mutableCart.setValue(cartItemList); + } + calculateCartTotal(); + } + + public void changeQuantity(CartItem cartItem, int quantity){ + if (mutableCart.getValue() == null){ + return; + } + List cartItemList = new ArrayList<>(mutableCart.getValue()); + CartItem updatedItem = new CartItem(cartItem.getProduct(),quantity); + cartItemList.set(cartItemList.indexOf(cartItem),updatedItem); + mutableCart.setValue(cartItemList); + calculateCartTotal(); + } + + private void calculateCartTotal(){ + if (mutableCart.getValue() == null) return; + double total = 0.0; + List cartItemList = mutableCart.getValue(); + for (CartItem cartItem : cartItemList){ + total += cartItem.getProduct().getPrice() * cartItem.getQuantity(); + } + mutableTotalPrice.setValue(total); + } + + public LiveData getTotalPrice(){ + if (mutableTotalPrice.getValue() == null){ + mutableTotalPrice.setValue(0.0); + } + return mutableTotalPrice; + } +} diff --git a/app/src/main/java/com/example/shoppingcart/repositories/ShopRepository.java b/app/src/main/java/com/example/shoppingcart/repositories/ShopRepository.java new file mode 100644 index 0000000..7b72fed --- /dev/null +++ b/app/src/main/java/com/example/shoppingcart/repositories/ShopRepository.java @@ -0,0 +1,53 @@ +package com.example.shoppingcart.repositories; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; + +import com.example.shoppingcart.models.Product; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class ShopRepository { + + private MutableLiveData> mutableProductList; + + public LiveData> getProducts(){ + if(mutableProductList==null){ + mutableProductList = new MutableLiveData<>(); + loadProducts(); + } + return mutableProductList; + } + + private void loadProducts() { + List productList = new ArrayList<>(); + + //fakeData + productList.add(new Product(String.valueOf(UUID.randomUUID()),"iMac 21",1299,true, + "https://www.apple.com/newsroom/images/product/imac/standard/Apple_imac-magickeyboardnum-magicmouse2-macos-wallpaper_08042020_big.jpg.large.jpg")); + productList.add(new Product(String.valueOf(UUID.randomUUID()),"iPad Air",1199,true, + "https://store.storeimages.cdn-apple.com/4668/as-images.apple.com/is/ipad-air-witb-pink-cell-202203_FMT_WHH?wid=562&hei=744&fmt=jpeg&qlt=90&.v=1645646566364.jpg")); + productList.add(new Product(String.valueOf(UUID.randomUUID()),"iPad Pro",1159,true, + "https://cdn.shopify.com/s/files/1/0529/4053/8039/products/31Mreycv_NL.jpg?v=1650883781.jpg")); + productList.add(new Product(String.valueOf(UUID.randomUUID()),"iphone 14 Pro",1869,true, + "https://m.media-amazon.com/images/I/71ZDY57yTQL._SX522_.jpg")); + productList.add(new Product(String.valueOf(UUID.randomUUID()),"iphone 14 Max",1849,true, + "https://d2d22nphq0yz8t.cloudfront.net/88e6cc4b-eaa1-4053-af65-563d88ba8b26/https://media.croma.com/image/upload/v1662655662/Croma%20Assets/Communication/Mobiles/Images/261979_oq7vjv.png/mxw_640,f_auto.jpg")); + productList.add(new Product(String.valueOf(UUID.randomUUID()),"iphone 14 Pro Max",1879,true, + "https://www.apple.com/newsroom/images/product/iphone/geo/Apple-iPhone-14-Pro-iPhone-14-Pro-Max-silver-220907-geo_inline.jpg.large.jpg")); + productList.add(new Product(String.valueOf(UUID.randomUUID()),"iphone 14",1829,true, + "https://www.xda-developers.com/files/2022/09/Apple-iPhone-14-Plus-Purple.jpg")); + productList.add(new Product(String.valueOf(UUID.randomUUID()),"iphone 13 Pro",1685,true, + "https://img.giznext.com/assets/model/2/11247/apple-iphone-13-pro-34630b234a544052a6a3792d5e0672.jpg")); + productList.add(new Product(String.valueOf(UUID.randomUUID()),"iphone 13 Max",1675,true, + "https://m.media-amazon.com/images/I/619m8rLBQSL._SL1500_.jpg")); + productList.add(new Product(String.valueOf(UUID.randomUUID()),"iphone 13 Pro Max",1699,true, + "https://www.apple.com/newsroom/images/product/iphone/geo/Apple_iPhone-13-Pro_iPhone-13-Pro-Max_GEO_09142021_inline.jpg.slideshow-large_2x.jpg")); + productList.add(new Product(String.valueOf(UUID.randomUUID()),"iphone X Pro",1359,true, + "https://store.storeimages.cdn-apple.com/4982/as-images.apple.com/is/refurb-iphone-11-pro-max-space-gray-2019?wid=400&hei=400&fmt=jpeg&qlt=90&.v=1611101628000.jpg")); + + mutableProductList.setValue(productList); + } +} diff --git a/app/src/main/java/com/example/shoppingcart/viewModel/ShopViewModel.java b/app/src/main/java/com/example/shoppingcart/viewModel/ShopViewModel.java new file mode 100644 index 0000000..d459891 --- /dev/null +++ b/app/src/main/java/com/example/shoppingcart/viewModel/ShopViewModel.java @@ -0,0 +1,62 @@ +package com.example.shoppingcart.viewModel; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +import com.example.shoppingcart.models.CartItem; +import com.example.shoppingcart.models.Product; +import com.example.shoppingcart.repositories.CartRepository; +import com.example.shoppingcart.repositories.ShopRepository; + +import java.util.List; + +public class ShopViewModel extends ViewModel { + + ShopRepository shopRepository = new ShopRepository(); + + CartRepository cartRepository = new CartRepository(); + + //sameviewModel for getting into product detail fragment + public MutableLiveData mutableProduct = new MutableLiveData<>(); + + public LiveData> getProducts(){ + return shopRepository.getProducts(); + } + + //sameviewModel for getting into product detail fragment + public void setProduct(Product product){ + mutableProduct.setValue(product); + } + + public LiveData getProduct(){ + return mutableProduct; + } + + public LiveData> getCartItems(){ + return cartRepository.getCart(); + } + + public boolean addItemToCart(Product product){ + return cartRepository.addItemToCart(product); + } + + //delete Item from cart + public void removeItemFromCart(CartItem cartItem){ + cartRepository.removeItemFromCart(cartItem); + } + + //quanity of spinner in cart + public void changeQuantity(CartItem cartItem, int quantity){ + cartRepository.changeQuantity(cartItem,quantity); + } + + //for total in cart + public LiveData getTotalPrice(){ + return cartRepository.getTotalPrice(); + } + + public void resetCart(){ + cartRepository.initCart(); + } +} diff --git a/app/src/main/java/com/example/shoppingcart/views/BlankFragment.java b/app/src/main/java/com/example/shoppingcart/views/BlankFragment.java new file mode 100644 index 0000000..1752ca0 --- /dev/null +++ b/app/src/main/java/com/example/shoppingcart/views/BlankFragment.java @@ -0,0 +1,66 @@ +package com.example.shoppingcart.views; + +import android.os.Bundle; + +import androidx.fragment.app.Fragment; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.example.shoppingcart.R; + +/** + * A simple {@link Fragment} subclass. + * Use the {@link BlankFragment#newInstance} factory method to + * create an instance of this fragment. + */ +public class BlankFragment extends Fragment { + + // TODO: Rename parameter arguments, choose names that match + // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER + private static final String ARG_PARAM1 = "param1"; + private static final String ARG_PARAM2 = "param2"; + + // TODO: Rename and change types of parameters + private String mParam1; + private String mParam2; + + public BlankFragment() { + // Required empty public constructor + } + + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param param1 Parameter 1. + * @param param2 Parameter 2. + * @return A new instance of fragment BlankFragment. + */ + // TODO: Rename and change types and number of parameters + public static BlankFragment newInstance(String param1, String param2) { + BlankFragment fragment = new BlankFragment(); + Bundle args = new Bundle(); + args.putString(ARG_PARAM1, param1); + args.putString(ARG_PARAM2, param2); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + mParam1 = getArguments().getString(ARG_PARAM1); + mParam2 = getArguments().getString(ARG_PARAM2); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_blank, container, false); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/shoppingcart/views/CartFragment.java b/app/src/main/java/com/example/shoppingcart/views/CartFragment.java new file mode 100644 index 0000000..12e1a98 --- /dev/null +++ b/app/src/main/java/com/example/shoppingcart/views/CartFragment.java @@ -0,0 +1,91 @@ +package com.example.shoppingcart.views; + +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProvider; +import androidx.navigation.NavController; +import androidx.navigation.Navigation; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.RecyclerView; + +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.example.shoppingcart.R; +import com.example.shoppingcart.adapters.CartListAdapter; +import com.example.shoppingcart.databinding.FragmentCartBinding; +import com.example.shoppingcart.models.CartItem; +import com.example.shoppingcart.viewModel.ShopViewModel; + +import java.util.List; + +public class CartFragment extends Fragment implements CartListAdapter.CartInterface { + + private static final String TAG = "CartFragment"; + ShopViewModel shopViewModel; + FragmentCartBinding fragmentCartBinding; + NavController navController; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment +// return inflater.inflate(R.layout.fragment_cart, container, false); + //viewBinding - only using view's id in this java file + fragmentCartBinding = FragmentCartBinding.inflate(inflater,container,false); + return fragmentCartBinding.getRoot(); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + +// CartListAdapter cartListAdapter = new CartListAdapter(CartItem.itemCallback); +// instead of this, removed the parameter of constructor & added {CartItem.itemCallBack} directly in super method + CartListAdapter cartListAdapter = new CartListAdapter(this); + navController = Navigation.findNavController(view); + + fragmentCartBinding.cartRV.setAdapter(cartListAdapter); + fragmentCartBinding.cartRV.addItemDecoration(new DividerItemDecoration(requireContext(), RecyclerView.VERTICAL)); + + shopViewModel = new ViewModelProvider(requireActivity()).get(ShopViewModel.class); + shopViewModel.getCartItems().observe(getViewLifecycleOwner(), new Observer>() { + @Override + public void onChanged(List cartItems) { + cartListAdapter.submitList(cartItems); + fragmentCartBinding.continueBT.setEnabled(cartItems.size()>0); + } + }); + fragmentCartBinding.continueBT.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + navController.navigate(R.id.action_cartFragment_to_orderFragment); + } + }); + + shopViewModel.getTotalPrice().observe(getViewLifecycleOwner(), new Observer() { + @Override + public void onChanged(Double aDouble) { + fragmentCartBinding.textView.setText("Total: $ "+aDouble); + } + }); + } + + @Override + public void deleteItem(CartItem cartItem) { + Log.d(TAG, "deleteItem: "+cartItem.getProduct().getName()); + shopViewModel.removeItemFromCart(cartItem); + } + + @Override + public void changeQuantity(CartItem cartItem, int quantity) { + shopViewModel.changeQuantity(cartItem,quantity); + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/shoppingcart/views/MainActivity.java b/app/src/main/java/com/example/shoppingcart/views/MainActivity.java new file mode 100644 index 0000000..51e20c4 --- /dev/null +++ b/app/src/main/java/com/example/shoppingcart/views/MainActivity.java @@ -0,0 +1,93 @@ +package com.example.shoppingcart.views; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProvider; +import androidx.navigation.NavController; +import androidx.navigation.Navigation; +import androidx.navigation.fragment.NavHostFragment; +import androidx.navigation.ui.NavigationUI; + +import android.os.Bundle; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.TextView; + +import com.example.shoppingcart.R; +import com.example.shoppingcart.models.CartItem; +import com.example.shoppingcart.viewModel.ShopViewModel; + +import java.util.List; + +public class MainActivity extends AppCompatActivity { + + private static final String TAG = "MainActivity"; + NavController navController; + NavHostFragment navHostFragment; + ShopViewModel shopViewModel; + private int cartQuantity = 0; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + //navigation + navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment); + navController = navHostFragment.getNavController(); +// navController = Navigation.findNavController(this,R.id.nav_host_fragment); + + shopViewModel = new ViewModelProvider(this).get(ShopViewModel.class); + shopViewModel.getCartItems().observe(this, new Observer>() { + @Override + public void onChanged(List cartItems) { + Log.d(TAG, "onChanged: "+cartItems.size()); + int quantity = 0; + for(CartItem cartItem : cartItems){ + quantity += cartItem.getQuantity(); + } + cartQuantity = quantity; + invalidateOptionsMenu(); + } + }); + + //for title of the screen + NavigationUI.setupActionBarWithNavController(this,navController); + } + + //menu + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main_menu,menu); + + //for cart icon number + MenuItem menuItem = menu.findItem(R.id.cartFragment); + View actionView = menuItem.getActionView(); + + TextView cartBadgeText = actionView.findViewById(R.id.cart_badge_TV); + cartBadgeText.setText(""+cartQuantity); + cartBadgeText.setVisibility(cartQuantity == 0 ? View.GONE : View.VISIBLE); + + actionView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onOptionsItemSelected(menuItem); + } + }); + return true; + } + + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem item) { + return NavigationUI.onNavDestinationSelected(item,navController) || super.onOptionsItemSelected(item); + } + + @Override + public boolean onSupportNavigateUp() { + navController.navigateUp(); + return super.onSupportNavigateUp(); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/shoppingcart/views/OrderFragment.java b/app/src/main/java/com/example/shoppingcart/views/OrderFragment.java new file mode 100644 index 0000000..19cf227 --- /dev/null +++ b/app/src/main/java/com/example/shoppingcart/views/OrderFragment.java @@ -0,0 +1,50 @@ +package com.example.shoppingcart.views; + +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; +import androidx.navigation.NavController; +import androidx.navigation.Navigation; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.example.shoppingcart.R; +import com.example.shoppingcart.databinding.FragmentOrderBinding; +import com.example.shoppingcart.viewModel.ShopViewModel; + + +public class OrderFragment extends Fragment { + + NavController navController; + FragmentOrderBinding fragmentOrderBinding; + ShopViewModel shopViewModel; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment +// return inflater.inflate(R.layout.fragment_order, container, false); + fragmentOrderBinding = FragmentOrderBinding.inflate(inflater,container,false); + return fragmentOrderBinding.getRoot(); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + navController = Navigation.findNavController(view); + shopViewModel =new ViewModelProvider(requireActivity()).get(ShopViewModel.class); + fragmentOrderBinding.continueBT.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + shopViewModel.resetCart(); + navController.navigate(R.id.action_orderFragment_to_shopFragment); + + } + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/shoppingcart/views/ProductDetailFragment.java b/app/src/main/java/com/example/shoppingcart/views/ProductDetailFragment.java new file mode 100644 index 0000000..f3bc652 --- /dev/null +++ b/app/src/main/java/com/example/shoppingcart/views/ProductDetailFragment.java @@ -0,0 +1,81 @@ +package com.example.shoppingcart.views; + +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; + +import com.bumptech.glide.Glide; +import com.example.shoppingcart.R; +import com.example.shoppingcart.databinding.FragmentProductDetailBinding; +import com.example.shoppingcart.viewModel.ShopViewModel; + +public class ProductDetailFragment extends Fragment { + +// FragmentProductDetailBinding fragmentProductDetailBinding; + ShopViewModel shopViewModel; + ImageView productImage; + TextView productName, prodcutPrice, prodcutAvailability; + Button addToCart; + View view; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + view = inflater.inflate(R.layout.fragment_product_detail, container, false); + initView(); + return view; +// fragmentProductDetailBinding = FragmentProductDetailBinding.inflate(inflater,container,false); +// return fragmentProductDetailBinding.getRoot(); + } + + private void initView() { + productImage = view.findViewById(R.id.product_image_IV); + productName = view.findViewById(R.id.product_name_text_TV); + prodcutPrice = view.findViewById(R.id.price_text_TV); + prodcutAvailability = view.findViewById(R.id.available_text_TV); + addToCart = view.findViewById(R.id.add_to_cart_BTN); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + shopViewModel = new ViewModelProvider(requireActivity()).get(ShopViewModel.class); + Glide.with(getActivity()) + .load(shopViewModel.getProduct().getValue().getImageUrl()) + .into(productImage); +// productImage.setImageURI(shopViewModel.getProduct().getValue().getImageUrl()); + productName.setText(shopViewModel.getProduct().getValue().getName()); + prodcutPrice.setText((int) shopViewModel.getProduct().getValue().getPrice()+"$"); + if (shopViewModel.getProduct().getValue().isAvailable()) { + prodcutAvailability.setText("Available"); + addToCart.setVisibility(View.VISIBLE); + addToCart.setEnabled(true); + } + else { + prodcutAvailability.setText("Out of Stock"); + } + + onClickAddToCart(); +// fragmentProductDetailBinding.setShopViewModel(shopViewModel); + } + + private void onClickAddToCart() { + addToCart.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + shopViewModel.addItemToCart(shopViewModel.getProduct().getValue()); + } + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/shoppingcart/views/ShopFragment.java b/app/src/main/java/com/example/shoppingcart/views/ShopFragment.java new file mode 100644 index 0000000..de167f3 --- /dev/null +++ b/app/src/main/java/com/example/shoppingcart/views/ShopFragment.java @@ -0,0 +1,92 @@ +package com.example.shoppingcart.views; + +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProvider; +import androidx.navigation.NavController; +import androidx.navigation.Navigation; +import androidx.recyclerview.widget.DividerItemDecoration; + +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.example.shoppingcart.R; +import com.example.shoppingcart.adapters.ShopListAdapter; +import com.example.shoppingcart.databinding.FragmentShopBinding; +import com.example.shoppingcart.models.Product; +import com.example.shoppingcart.viewModel.ShopViewModel; +import com.google.android.material.snackbar.Snackbar; + +import java.util.List; + +public class ShopFragment extends Fragment implements ShopListAdapter.ShopInterface { + + private static final String TAG = "ShopFragment"; + FragmentShopBinding fragmentShopBinding; + ShopListAdapter shopListAdapter; + private ShopViewModel shopViewModel; + private NavController navController; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + + fragmentShopBinding = FragmentShopBinding.inflate(inflater,container,false); + fragmentShopBinding.shopRV.addItemDecoration(new DividerItemDecoration(requireContext(),DividerItemDecoration.VERTICAL)); + fragmentShopBinding.shopRV.addItemDecoration(new DividerItemDecoration(requireContext(),DividerItemDecoration.HORIZONTAL)); + return fragmentShopBinding.getRoot(); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + //settingAdapter + shopListAdapter = new ShopListAdapter(this); + fragmentShopBinding.shopRV.setAdapter(shopListAdapter); + + navController = Navigation.findNavController(view); + shopViewModel = new ViewModelProvider(requireActivity()).get(ShopViewModel.class); + shopViewModel.getProducts().observe(getViewLifecycleOwner(), new Observer>() { + @Override + public void onChanged(List products) { + shopListAdapter.submitList(products); + } + }); + } + + //setup the onclick on this method by view binding in shop_row -> add to cart button + @Override + public void addItem(Product product) { + Log.d(TAG, "addItem: "+product.toString()); + boolean isAdded = shopViewModel.addItemToCart(product); + Log.d(TAG, "addItem: "+product.getName() + isAdded); + if (isAdded){ + Snackbar.make(requireView(), product.getName()+" added ", Snackbar.LENGTH_LONG) + .setAction("Checkout", new View.OnClickListener() { + @Override + public void onClick(View v) { + navController.navigate(R.id.action_shopFragment_to_cartFragment); + } + }).show(); + } + else { + Snackbar.make(requireView(), "Max quantity added ", Snackbar.LENGTH_LONG) + .show(); + + } + } + + @Override + public void onItemClick(Product product) { + Log.d(TAG, "onItemClick: "+product.toString()); + shopViewModel.setProduct(product); + navController.navigate(R.id.action_shopFragment_to_productDetailFragment2); + } +} \ No newline at end of file diff --git a/app/src/main/res/anim/slide_in_left.xml b/app/src/main/res/anim/slide_in_left.xml new file mode 100644 index 0000000..f9869bd --- /dev/null +++ b/app/src/main/res/anim/slide_in_left.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/slide_in_right.xml b/app/src/main/res/anim/slide_in_right.xml new file mode 100644 index 0000000..ababcc5 --- /dev/null +++ b/app/src/main/res/anim/slide_in_right.xml @@ -0,0 +1,13 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/slide_out_left.xml b/app/src/main/res/anim/slide_out_left.xml new file mode 100644 index 0000000..1525d6b --- /dev/null +++ b/app/src/main/res/anim/slide_out_left.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/slide_out_right.xml b/app/src/main/res/anim/slide_out_right.xml new file mode 100644 index 0000000..b10819e --- /dev/null +++ b/app/src/main/res/anim/slide_out_right.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/badge_shape.xml b/app/src/main/res/drawable/badge_shape.xml new file mode 100644 index 0000000..2b03a29 --- /dev/null +++ b/app/src/main/res/drawable/badge_shape.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_baseline_delete_24.xml b/app/src/main/res/drawable/ic_baseline_delete_24.xml new file mode 100644 index 0000000..5b730a9 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_delete_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_shopping_cart_24.xml b/app/src/main/res/drawable/ic_baseline_shopping_cart_24.xml new file mode 100644 index 0000000..aeda902 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_shopping_cart_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/iphone11.jpeg b/app/src/main/res/drawable/iphone11.jpeg new file mode 100644 index 0000000..2d499c4 Binary files /dev/null and b/app/src/main/res/drawable/iphone11.jpeg differ diff --git a/app/src/main/res/font/open_sans.xml b/app/src/main/res/font/open_sans.xml new file mode 100644 index 0000000..a5d4182 --- /dev/null +++ b/app/src/main/res/font/open_sans.xml @@ -0,0 +1,13 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/font/open_sans_italic.ttf b/app/src/main/res/font/open_sans_italic.ttf new file mode 100644 index 0000000..790286f Binary files /dev/null and b/app/src/main/res/font/open_sans_italic.ttf differ diff --git a/app/src/main/res/font/open_sans_regular.ttf b/app/src/main/res/font/open_sans_regular.ttf new file mode 100644 index 0000000..1dc226d Binary files /dev/null and b/app/src/main/res/font/open_sans_regular.ttf differ diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..b1b3795 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/cart_action_item.xml b/app/src/main/res/layout/cart_action_item.xml new file mode 100644 index 0000000..22771ce --- /dev/null +++ b/app/src/main/res/layout/cart_action_item.xml @@ -0,0 +1,37 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/cart_row.xml b/app/src/main/res/layout/cart_row.xml new file mode 100644 index 0000000..9732e2e --- /dev/null +++ b/app/src/main/res/layout/cart_row.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_blank.xml b/app/src/main/res/layout/fragment_blank.xml new file mode 100644 index 0000000..0de42e8 --- /dev/null +++ b/app/src/main/res/layout/fragment_blank.xml @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_cart.xml b/app/src/main/res/layout/fragment_cart.xml new file mode 100644 index 0000000..d63e542 --- /dev/null +++ b/app/src/main/res/layout/fragment_cart.xml @@ -0,0 +1,46 @@ + + + + + + + + +