Android Ecommerce Shopping App With PayPal Payment Integration Part 1

In this tutorial, we are going to learn how to create android Ecommerce shopping cart with PayPal payment integration.

This is going to be a long tutorial but I guess we will all learn something new after we go through it to the end.

First, we will focus on android shopping cart. After we are done, we will then focus our attention on how to integrate PayPal payment in android for the shop checkout.

Android PayPal integration is important if you are planning to sell physical good in your mobile shop. But if your plan is to sell digital goods and services, Google suggestion is to use In-app purchase.

I wrote a detailed blog post on Android In-App Billing v3  I will suggest you read it.

Also checkout paid version of complete android ecommerce shopping app

It is important for us to have a good understanding of what we are planning to create. I have added some screen-shots below.

ANDROID PROJECT SCREENSHOT

androidpaypal

 

CREATE NEW ANDROID PROJECT

Lets start to soil our hands in code. Start up your IDE. For this tutorial, I am using the following tools and environment, feel free to use what works for you.

Windows 10

Android Studio

Sony Xperia ZL

Min SDK 14

Target SDK 23

To create a new android application project, follow the steps as stipulated below.

Go to File menu

Click on New menu

Click on Android Application

Enter Project name: AndroidPayExample

Package: com.inducesmile.androidPayExample

Select Blank Activity

Name your activity: SplashActivity

Keep other default selections

Continue to click on next button until Finish button is active, then click on Finish Button.

 

UPDATE ANDROIDMANIFEST.XML

We are going to update our project Manifest.xml file. First, we will add Internet permission since we will make network calls in this application. The default Application class will be extended. So add the name of the custom application class in the name attribute of the application element.

<uses-permission android:name="android.permission.INTERNET" />

The updated code is as shown below.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.inducesmile.androidpayexample">
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        android:name=".network.CustomApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <meta-data
            android:name="com.google.android.gms.wallet.api.enabled"
            android:value="true" />
        <activity android:name=".SplashActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".ShoppingActivity" />
        <activity android:name=".ProductActivity" />
        <activity android:name=".CheckoutActivity" />
        <activity android:name=".PaymentsActivity" />
        <activity android:name=".PayPalCheckoutActivity"></activity>
    </application>
</manifest>

 

UPDATE DEPENDENCIES IN BUILD.GRADLE FILE

In this android shopping cart project with Paypal integration, we are going to make use of third party android libraries. Below is the update build.gradle app level file.

apply plugin: 'com.android.application'
android {
    compileSdkVersion 24
    buildToolsVersion "24.0.0"
    defaultConfig {
        applicationId "com.inducesmile.androidpayexample"
        minSdkVersion 16
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.google.android.gms:play-services-wallet:9.4.0'
    compile 'com.android.support:support-v4:24.1.1'
    compile 'com.android.support:appcompat-v7:24.1.1'
    compile 'com.intuit.sdp:sdp-android:1.0.3'
    compile 'com.android.support:design:24.1.1'
    compile 'com.mcxiaoke.volley:library:1.0.19'
    compile 'com.google.code.gson:gson:2.6.1'
    compile('com.paypal.sdk:paypal-android-sdk:2.14.4') {
        exclude group: 'io.card'
    }
}

 

STRINGS.XML

We are going to update our project strings.xml file located in the values folder inside the res folder. Open the file and add the code below to it.

<resources>
    <string name="app_name">Android Pay Example</string>
    <string name="android_pay">Android Pay. Simple way to pay</string>
    <string name="add_to_cart">ADD TO CART</string>
    <string name="description_example">It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.</string>
    <string name="product_size">Size: 40</string>
    <string name="product_color">Color: Black</string>
    <string name="product_price">Price: 80$</string>
    <string name="action_cart">Shopping cart</string>
    <string name="quantity">1</string>
    <string name="product_name">Sleek black top</string>
    <string name="price">100 $</string>
    <string name="remove_from_cart">Remove</string>
    <string name="continue_shopping">Continue shopping</string>
    <string name="checkout">Checkout</string>
    <string name="sub_total">Sub total:</string>
    <string name="server_error">Failed to payment information to server</string>
    <string name="successful_payment">Payment successfully uploaded</string>
    <string name="pay_with_paypal">Pay with PayPal</string>
</resources>

 

COLORS.XML

Open the colors.xml file in the same location as the strings.xml file and add the code below to the file.

<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#CDDC39</color>
<color name="colorPrimaryDark">#AFB42B</color>
<color name="colorPrimaryLight">#F0F4C3</color>
<color name="colorAccent">#795548</color>
<color name="colorPrimaryText">#212121</color>
<color name="colorSecondaryText">#757575</color>
<color name="icons">#ffffff</color>
<color name="colorDivider">#BDBDBD</color>
</resources>

Now, we will proceed with the intro splash screen activity page. This is the first page you will see when you first starts the application.

 

Activity_Splash.xml

Go to res folder > layout > activity_splash.xml and open the file. Add the code below to the file.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:background="@color/colorPrimaryLight"
    tools:context="com.inducesmile.androidpayexample.SplashActivity">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="16dp"
        android:gravity="center">
        <ImageView
            android:id="@+id/logo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/paylogo"
            android:contentDescription="@string/app_name"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="12dp"
            android:textStyle="italic"
            android:textSize="13dp"
            android:text="@string/android_pay"/>
    </LinearLayout>
</RelativeLayout>

 

SplashActivity.java

Once this page is loaded, it will take three seconds before it redirects to the ShoppingActivity page. The content of this file is simple and easy to understand. Open SplashActivity.java and add the code below to the file.

import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Handler;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
public class SplashActivity extends AppCompatActivity {
    private static final String TAG = SplashActivity.class.getSimpleName();
    private final int SPLASH_DISPLAY_LENGTH = 3000;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        ActionBar actionBar = getSupportActionBar();
        if(null != actionBar){
            actionBar.hide();
        }
        new Handler().postDelayed(new Runnable(){
            @Override
            public void run(){
                Intent startActivityIntent = new Intent(SplashActivity.this, ShoppingActivity.class);
                startActivity(startActivityIntent);
                SplashActivity.this.finish();
            }
        }, SPLASH_DISPLAY_LENGTH);
    }
}

 

Activity_shopping.xml

The activity_shopping.xml is the main front shop design. It will contain a top ImageView and below it will be RecyclerView that will hold products on sale. Open activity_shopping in the layout folder and add the code below to the file.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.inducesmile.androidpayexample.ShoppingActivity">
    <FrameLayout
        android:id="@+id/frame_layout"
        android:layout_width="match_parent"
        android:layout_height="@dimen/_200sdp"
        android:layout_centerHorizontal="true"
        android:layout_alignParentTop="true"
        android:padding="0dp">
        <ImageView
            android:id="@+id/recent_news_image"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:adjustViewBounds="true"
            android:background="@drawable/trending"
            android:contentDescription="@string/app_name"/>
    </FrameLayout>
    <android.support.v7.widget.RecyclerView
        android:id="@+id/product_list"
        android:layout_below="@+id/frame_layout"
        android:layout_centerHorizontal="true"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:scrollbars="none" />
</RelativeLayout>

 

ShoppingActivity.java

In ShoppingActivity class, we will obtain the instance of our RecyclerView control. GridLayoutManager is used and we will create a RecyclerView adapter that will bind the data source to the View. The data is objects stored in a ListView. Feel free to fetch your data from local or remote data storage.

Open ShoppingActivity class and add the code below to it.

import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import com.inducesmile.androidpayexample.adapters.ShopRecyclerViewAdapter;
import com.inducesmile.androidpayexample.entities.ProductObject;
import com.inducesmile.androidpayexample.helpers.SpacesItemDecoration;
import java.util.ArrayList;
import java.util.List;
public class ShoppingActivity extends AppCompatActivity {
    private static final String TAG = ShoppingActivity.class.getSimpleName();
    private RecyclerView shoppingRecyclerView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_shopping);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        shoppingRecyclerView = (RecyclerView)findViewById(R.id.product_list);
        GridLayoutManager mGrid = new GridLayoutManager(ShoppingActivity.this, 2);
        shoppingRecyclerView.setLayoutManager(mGrid);
        shoppingRecyclerView.setHasFixedSize(true);
        shoppingRecyclerView.addItemDecoration(new SpacesItemDecoration(2, 12, false));
        ShopRecyclerViewAdapter shopAdapter = new ShopRecyclerViewAdapter(ShoppingActivity.this, getAllProductsOnSale());
        shoppingRecyclerView.setAdapter(shopAdapter);
    }
    private List<ProductObject> getAllProductsOnSale(){
        List<ProductObject> products = new ArrayList<ProductObject>();
        products.add(new ProductObject(1, "Sleek Black Top", R.drawable.productonesmall, "Beautiful sleek black top for casual outfit and evening walk", 20, 38, "Black"));
        products.add(new ProductObject(1, "Flare Black Gown", R.drawable.producttwo, "Beautiful sleek black top for casual outfit and evening walk", 20, 38, "Black"));
        products.add(new ProductObject(1, "Flare White Blouse", R.drawable.productthree, "Beautiful sleek black top for casual outfit and evening walk", 20, 38, "White"));
        products.add(new ProductObject(1, "Blue Swed Gown", R.drawable.productfour, "Beautiful sleek black top for casual outfit and evening walk", 20, 38, "Dark Blue"));
        products.add(new ProductObject(1, "Spotted Gown", R.drawable.productfive, "Beautiful sleek black top for casual outfit and evening walk", 20, 38, "Spotted Green"));
        products.add(new ProductObject(1, "Flare Wax Gown", R.drawable.productsix, "Beautiful sleek black top for casual outfit and evening walk", 20, 38, "Multi-color"));
        return products;
    }
}

 

ShopRecyclerViewAdapter.java

As stated above, the ShoppingActivity class will make use of this adapter class to bind its data to the RecyclerView. Each data will be associated with an item in the RecyclerView.

Create a package folder and name it adapters. Inside this package, create a new java file and name it ShopRecyclerViewAdapter.java.

Open this file and add the code below to it.

import android.content.Context;
import android.content.Intent;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.inducesmile.androidpayexample.ProductActivity;
import com.inducesmile.androidpayexample.R;
import com.inducesmile.androidpayexample.entities.ProductObject;
import java.util.List;
public class ShopRecyclerViewAdapter extends RecyclerView.Adapter<ShopRecyclerViewHolder>{
    private static final String TAG = ShopRecyclerViewAdapter.class.getSimpleName();
    private Context context;
    private List<ProductObject> allProducts;
    public ShopRecyclerViewAdapter(Context context, List<ProductObject> allProducts) {
        this.context = context;
        this.allProducts = allProducts;
    }
    @Override
    public ShopRecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.product_listing, parent, false);
        ShopRecyclerViewHolder productHolder = new ShopRecyclerViewHolder(layoutView);
        return productHolder;
    }
    @Override
    public void onBindViewHolder(ShopRecyclerViewHolder holder, int position) {
        final ProductObject singleProduct = allProducts.get(position);
        holder.productName.setText(singleProduct.getProductName());
        holder.produceImage.setImageResource(singleProduct.getProductImage());
        holder.produceImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent productIntent = new Intent(context, ProductActivity.class);
                GsonBuilder builder = new GsonBuilder();
                Gson gson = builder.create();
                String stringObjectRepresentation = gson.toJson(singleProduct);
                productIntent.putExtra("PRODUCT", stringObjectRepresentation);
                context.startActivity(productIntent);
            }
        });
    }
    @Override
    public int getItemCount() {
        return allProducts.size();
    }
}

 

ShopRecyclerViewHolder.java

We will create a ViewHolder class that the above adapter will make use of. Create a new java file in adapters package folder and name it ShopRecyclerViewHolder.java.

Open the file and add the code below to it.

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.inducesmile.androidpayexample.R;
public class ShopRecyclerViewHolder extends RecyclerView.ViewHolder {
    public ImageView produceImage;
    public TextView productName;
    public ShopRecyclerViewHolder(View itemView) {
        super(itemView);
        produceImage =(ImageView)itemView.findViewById(R.id.product_image);
        productName = (TextView)itemView.findViewById(R.id.product_name);
    }
}

 

Activity_Product.xml

This is a single product page in our android shopping cart project. If you select a product, it will take you to the product page where the detailed information about the product is shown.

Open the activity_product.xml file in the layout folder and add the code below to it.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:background="@color/icons"
    tools:context="com.inducesmile.androidpayexample.ProductActivity">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="5"
        android:orientation="vertical">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:paddingRight="@dimen/_8sdp"
                android:orientation="vertical"
                android:layout_weight="1">
                <TextView
                    android:id="@+id/product_size"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="@dimen/_13sdp"
                    android:layout_marginTop="@dimen/_48sdp"
                    android:text="@string/product_size"
                    android:textColor="@color/colorAccent"/>
                <TextView
                    android:id="@+id/product_color"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="@dimen/_13sdp"
                    android:layout_marginTop="@dimen/_32sdp"
                    android:text="@string/product_color"
                    android:textColor="@color/colorAccent"/>
                <TextView
                    android:id="@+id/product_price"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="@dimen/_13sdp"
                    android:layout_marginTop="@dimen/_32sdp"
                    android:text="@string/product_price"
                    android:textColor="@color/colorAccent"/>
            </LinearLayout>
            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1">
                <ImageView
                    android:id="@+id/full_product_image"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:contentDescription="@string/android_pay"
                    android:src="@drawable/productonebig"
                    android:scaleType="centerCrop"/>
            </LinearLayout>
        </LinearLayout>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"
        android:gravity="bottom"
        android:paddingBottom="@dimen/_16sdp"
        android:orientation="vertical">
        <TextView
            android:id="@+id/product_description"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:lineSpacingMultiplier="1.2"
            android:textColor="@color/colorPrimaryText"
            android:text="@string/description_example"
            android:textSize="@dimen/_12sdp"/>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical">
        <Button
            android:id="@+id/add_to_cart"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/colorPrimaryDark"
            android:textColor="@color/icons"
            android:text="@string/add_to_cart"
            android:textSize="@dimen/_16sdp"
            android:padding="@dimen/_16sdp"/>
    </LinearLayout>
</LinearLayout>

 

ProductActivity.java

We will get all the instances of the View components associated with the product in this class and thereafter, populate it will the value stored in intent object.

Another important Button that we will add in the Product Activity page is add to cart. Once this button is clicked it will add the product to the cart. The cart item count increases or decreases if an item is added or removed from the cart.

Open this file and add the code below to it.

import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.inducesmile.androidpayexample.entities.ProductObject;
import com.inducesmile.androidpayexample.helpers.MySharedPreference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ProductActivity extends AppCompatActivity {
    private static final String TAG = ProductActivity.class.getSimpleName();
    private TextView productSize, productColor, productPrice, productDescription;
    private ImageView productImage;
    private Gson gson;
    private int cartProductNumber = 0;
    private MySharedPreference sharedPreference;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_product);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        sharedPreference = new MySharedPreference(ProductActivity.this);
        productImage = (ImageView)findViewById(R.id.full_product_image);
        productSize = (TextView)findViewById(R.id.product_size);
        productColor = (TextView)findViewById(R.id.product_color);
        productPrice = (TextView)findViewById(R.id.product_price);
        productDescription = (TextView)findViewById(R.id.product_description);
        GsonBuilder builder = new GsonBuilder();
        gson = builder.create();
        String productInStringFormat = getIntent().getExtras().getString("PRODUCT");
        final ProductObject singleProduct = gson.fromJson(productInStringFormat, ProductObject.class);
        if(singleProduct != null){
            setTitle(singleProduct.getProductName());
            productImage.setImageResource(singleProduct.getProductImage());
            productSize.setText("Size: " + String.valueOf(singleProduct.getProductSize()));
            productColor.setText("Color: " + singleProduct.getProductColor());
            productPrice.setText("Price: " + String.valueOf(new Double(singleProduct.getProductPrice()).intValue()) + " $");
            productDescription.setText(Html.fromHtml("<strong>Product Description</strong><br/>" + singleProduct.getProductDescription()));
        }
        Button addToCartButton = (Button)findViewById(R.id.add_to_cart);
        assert addToCartButton != null;
        addToCartButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //increase product count
                String productsFromCart = sharedPreference.retrieveProductFromCart();
                if(productsFromCart.equals("")){
                    List<ProductObject> cartProductList = new ArrayList<ProductObject>();
                    cartProductList.add(singleProduct);
                    String cartValue = gson.toJson(cartProductList);
                    sharedPreference.addProductToTheCart(cartValue);
                    cartProductNumber = cartProductList.size();
                }else{
                    String productsInCart = sharedPreference.retrieveProductFromCart();
                    ProductObject[] storedProducts = gson.fromJson(productsInCart, ProductObject[].class);
                    List<ProductObject> allNewProduct = convertObjectArrayToListObject(storedProducts);
                    allNewProduct.add(singleProduct);
                    String addAndStoreNewProduct = gson.toJson(allNewProduct);
                    sharedPreference.addProductToTheCart(addAndStoreNewProduct);
                    cartProductNumber = allNewProduct.size();
                }
                sharedPreference.addProductCount(cartProductNumber);
                invalidateCart();
            }
        });
    }
    private List<ProductObject> convertObjectArrayToListObject(ProductObject[] allProducts){
        List<ProductObject> mProduct = new ArrayList<ProductObject>();
        Collections.addAll(mProduct, allProducts);
        return mProduct;
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        MenuItem menuItem = menu.findItem(R.id.action_shop);
        int mCount = sharedPreference.retrieveProductCount();
        menuItem.setIcon(buildCounterDrawable(mCount, R.drawable.cart));
        return true;
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_shop) {
            Intent checkoutIntent = new Intent(ProductActivity.this, CheckoutActivity.class);
            startActivity(checkoutIntent);
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
    private Drawable buildCounterDrawable(int count, int backgroundImageId) {
        LayoutInflater inflater = LayoutInflater.from(this);
        View view = inflater.inflate(R.layout.shopping_layout, null);
        view.setBackgroundResource(backgroundImageId);
        if (count == 0) {
            View counterTextPanel = view.findViewById(R.id.counterValuePanel);
            counterTextPanel.setVisibility(View.GONE);
        } else {
            TextView textView = (TextView) view.findViewById(R.id.count);
            textView.setText("" + count);
        }
        view.measure(
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
        view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
        view.setDrawingCacheEnabled(true);
        view.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
        Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
        view.setDrawingCacheEnabled(false);
        return new BitmapDrawable(getResources(), bitmap);
    }
    private void invalidateCart() {
        invalidateOptionsMenu();
    }
}

 

Activity_checkout.xml

The activity_checkout.xml is the layout file for the CheckoutActivity class. It uses a RecyclerView control to list all products that have been added to the cart. It also contain two button – first is for continue shopping and the second is to proceed with checkout.

Open this layout file and add the code below to it.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    android:background="@color/colorPrimaryLight"
    tools:context="com.inducesmile.androidpayexample.CheckoutActivity">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_weight="9">
        <android.support.v7.widget.RecyclerView
            android:id="@+id/checkout_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:scrollbars="none" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_weight="1">
        <TextView
            android:id="@+id/sub_total"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="@dimen/_16sdp"
            android:textStyle="bold"
            android:text="@string/sub_total"/>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:gravity="bottom"
        android:layout_weight="1">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <Button
                android:id="@+id/shopping"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:background="@color/colorPrimaryDark"
                android:textColor="@color/icons"
                android:text="@string/continue_shopping"
                android:layout_weight="1"/>
            <Button
                android:id="@+id/checkout"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:background="@color/colorPrimaryDark"
                android:layout_marginLeft="@dimen/_8sdp"
                android:textColor="@color/icons"
                android:text="@string/checkout"
                android:layout_weight="1"/>
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

 

CheckoutActivity.java

The Checkout Activity class is as shown below. It makes use of CheckRecyclerViewAdapter of data binding.

import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.inducesmile.androidpayexample.adapters.CheckRecyclerViewAdapter;
import com.inducesmile.androidpayexample.entities.ProductObject;
import com.inducesmile.androidpayexample.helpers.MySharedPreference;
import com.inducesmile.androidpayexample.helpers.SimpleDividerItemDecoration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CheckoutActivity extends AppCompatActivity {
    private static final String TAG = CheckoutActivity.class.getSimpleName();
    private RecyclerView checkRecyclerView;
    private TextView subTotal;
    private double mSubTotal = 0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_checkout);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        setTitle("Over Cart");
        subTotal = (TextView )findViewById(R.id.sub_total);
        checkRecyclerView = (RecyclerView)findViewById(R.id.checkout_list);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(CheckoutActivity.this);
        checkRecyclerView.setLayoutManager(linearLayoutManager);
        checkRecyclerView.setHasFixedSize(true);
        checkRecyclerView.addItemDecoration(new SimpleDividerItemDecoration(CheckoutActivity.this));
        // get content of cart
        MySharedPreference mShared = new MySharedPreference(CheckoutActivity.this);
        GsonBuilder builder = new GsonBuilder();
        Gson gson = builder.create();
        ProductObject[] addCartProducts = gson.fromJson(mShared.retrieveProductFromCart(), ProductObject[].class);
        List<ProductObject> productList = convertObjectArrayToListObject(addCartProducts);
        CheckRecyclerViewAdapter mAdapter = new CheckRecyclerViewAdapter(CheckoutActivity.this, productList);
        checkRecyclerView.setAdapter(mAdapter);
        mSubTotal = getTotalPrice(productList);
        subTotal.setText("Subtotal excluding tax and shipping: " + String.valueOf(mSubTotal) + " $");
        Button shoppingButton = (Button)findViewById(R.id.shopping);
        assert shoppingButton != null;
        shoppingButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent shoppingIntent = new Intent(CheckoutActivity.this, ShoppingActivity.class);
                startActivity(shoppingIntent);
            }
        });
        Button checkButton = (Button)findViewById(R.id.checkout);
        assert checkButton != null;
        checkButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent paymentIntent = new Intent(CheckoutActivity.this, PayPalCheckoutActivity.class);
                paymentIntent.putExtra("TOTAL_PRICE", mSubTotal);
                startActivity(paymentIntent);
            }
        });
    }
    private List<ProductObject> convertObjectArrayToListObject(ProductObject[] allProducts){
        List<ProductObject> mProduct = new ArrayList<ProductObject>();
        Collections.addAll(mProduct, allProducts);
        return mProduct;
    }
    private int returnQuantityByProductName(String productName, List<ProductObject> mProducts){
        int quantityCount = 0;
        for(int i = 0; i < mProducts.size(); i++){
            ProductObject pObject = mProducts.get(i);
            if(pObject.getProductName().trim().equals(productName.trim())){
                quantityCount++;
            }
        }
        return quantityCount;
    }
    private double getTotalPrice(List<ProductObject> mProducts){
        double totalCost = 0;
        for(int i = 0; i < mProducts.size(); i++){
            ProductObject pObject = mProducts.get(i);
            totalCost = totalCost + pObject.getProductPrice();
        }
        return totalCost;
    }
}

 

CheckRecyclerViewAdapter.java

In the adapters package folder, create a new java file and name it CheckRecyclerViewAdapter.java. Open the file and add the code below to it.

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.inducesmile.androidpayexample.R;
import com.inducesmile.androidpayexample.entities.ProductObject;
import java.util.List;
public class CheckRecyclerViewAdapter extends RecyclerView.Adapter<CheckRecyclerViewHolder> {
    private Context context;
    private List<ProductObject> mProductObject;
    public CheckRecyclerViewAdapter(Context context, List<ProductObject> mProductObject) {
        this.context = context;
        this.mProductObject = mProductObject;
    }
    @Override
    public CheckRecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.check_layout, parent, false);
        CheckRecyclerViewHolder productHolder = new CheckRecyclerViewHolder(layoutView);
        return productHolder;
    }
    @Override
    public void onBindViewHolder(CheckRecyclerViewHolder holder, int position) {
        //get product quantity
        holder.quantity.setText("1");
        holder.productName.setText(mProductObject.get(position).getProductName());
        holder.productPrice.setText(String.valueOf(mProductObject.get(position).getProductPrice()) + " $");
        holder.removeProduct.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(context, "Do you want to remove product from cart", Toast.LENGTH_LONG).show();
            }
        });
    }
    @Override
    public int getItemCount() {
        return mProductObject.size();
    }
}

 

CheckRecyclerViewHolder.java

Create another java file in adapters package folder and name it CheckRecyclerViewHolder.java. Open this file and add the code below to it.

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
import com.inducesmile.androidpayexample.R;
public class CheckRecyclerViewHolder extends RecyclerView.ViewHolder{
    public TextView quantity, productName, productPrice, removeProduct;
    public CheckRecyclerViewHolder(View itemView) {
        super(itemView);
        quantity = (TextView)itemView.findViewById(R.id.quantity);
        productName =(TextView)itemView.findViewById(R.id.product_name);
        productPrice = (TextView)itemView.findViewById(R.id.product_price);
        removeProduct = (TextView)itemView.findViewById(R.id.remove_from_cart);
    }
}

 

Adapters Layout files

As you have seen from the adapter classes that we create that each of the adapters inflates a layout file. The layout files below are associated with the adapter classes and the checkout menu icon.

 

check_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="@dimen/_16sdp"
    android:orientation="horizontal">
    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_weight="1">
        <TextView
            android:id="@+id/quantity"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="@dimen/_15sdp"
            android:text="@string/quantity"
            android:textColor="@color/colorPrimaryText"
            android:layout_gravity="left"/>
    </LinearLayout>
    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_weight="5">
        <TextView
            android:id="@+id/product_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="@dimen/_15sdp"
            android:textStyle="bold"
            android:text="@string/product_name"
            android:textColor="@color/colorAccent"
            android:layout_gravity="left"/>
    </LinearLayout>
    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_weight="2">
        <TextView
            android:id="@+id/product_price"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="@dimen/_13sdp"
            android:text="@string/price"
            android:textStyle="bold"
            android:textColor="@color/colorPrimaryText"
            android:layout_gravity="center"/>
        <TextView
            android:id="@+id/remove_from_cart"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/_8sdp"
            android:layout_gravity="center"
            android:textSize="@dimen/_14sdp"
            android:textStyle="bold"
            android:text="@string/remove_from_cart"
            />
    </LinearLayout>
</LinearLayout>

 

Product_listing.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/frame_layout"
    android:layout_width="match_parent"
    android:layout_height="@dimen/_200sdp"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true"
    android:padding="0dp">
    <ImageView
        android:id="@+id/product_image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:adjustViewBounds="true"
        android:scaleType="centerCrop"
        android:contentDescription="@string/app_name" />
    <TextView
        android:id="@+id/product_name"
        android:layout_width="match_parent"
        android:layout_height="@dimen/_48sdp"
        android:layout_gravity="bottom"
        android:background="@color/colorPrimaryLight"
        android:gravity="center"
        android:padding="@dimen/_8sdp"
        android:ellipsize = "end"
        android:singleLine="true"
        android:textColor="@color/colorSecondaryText"
        android:textSize="@dimen/_12sdp" />
</FrameLayout>

 

Shopping_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/counterPanel"
    android:layout_width="32dp"
    android:layout_height="32dp"
    android:background="@drawable/cart">
    <RelativeLayout
        android:id="@+id/counterValuePanel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" >
        <ImageView
            android:id="@+id/counterBackground"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/unread_background" />
        <TextView
            android:id="@+id/count"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="1"
            android:textSize="8sp"
            android:layout_centerInParent="true"
            android:textColor="#FFFFFF" />
    </RelativeLayout>
</FrameLayout>

 

Create network package folder

We will need to verify if the payment we receive from PayPal payment is authentic. This is important in order to eliminate elements of fraud.

Create a new package folder and name it network. Create the following three java classes in this package. We will use this classes to make network calls.

 

CustomApplication.java

import android.app.Application;
import com.android.volley.RequestQueue;
public class CustomApplication extends Application{
    private RequestQueue requestQueue;
    @Override
    public void onCreate() {
        super.onCreate();
        requestQueue = VolleySingleton.getInstance(getApplicationContext()).getRequestQueue();
    }
    public RequestQueue getVolleyRequestQueue(){
        return requestQueue;
    }
}

 

GsonRequest.java

import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.toolbox.HttpHeaderParser;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
public class GsonRequest<T> extends Request<T> {
    private Gson mGson = new Gson();
    private Class<T> clazz;
    private Map<String, String> headers;
    private Map<String, String> params;
    private Response.Listener<T> listener;
    /**
     * Make a GET request and return a parsed object from JSON.
     */
    public GsonRequest(int method, String url, Class<T> clazz, Response.Listener<T> listener, Response.ErrorListener errorListener) {
        super(method, url, errorListener);
        this.clazz = clazz;
        this.listener = listener;
        mGson = new Gson();
    }
    /**
     * Make a POST request and return a parsed object from JSON.
     */
    public GsonRequest(int method, String url, Class<T> clazz, Map<String, String> params, Response.Listener<T> listener, Response.ErrorListener errorListener) {
        super(method, url, errorListener);
        this.clazz = clazz;
        this.params = params;
        this.listener = listener;
        this.headers = null;
        mGson = new Gson();
    }
    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        return headers != null ? headers : super.getHeaders();
    }
    @Override
    protected Map<String, String> getParams() throws AuthFailureError {
        return params;
    }
    @Override
    protected void deliverResponse(T response) {
        listener.onResponse(response);
    }
    @Override
    protected Response<T> parseNetworkResponse(NetworkResponse response) {
        try {
            String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
            return Response.success(mGson.fromJson(json, clazz), HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        } catch (JsonSyntaxException e) {
            return Response.error(new ParseError(e));
        }
    }
}

 

VolleySingleton.java

import android.content.Context;
import android.graphics.Bitmap;
import android.util.LruCache;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;
public class VolleySingleton {
    private static VolleySingleton mInstance;
    private RequestQueue mRequestQueue;
    private ImageLoader mImageLoader;
    private static Context mCtx;
    private VolleySingleton(Context context) {
        mCtx = context;
        mRequestQueue = getRequestQueue();
        mImageLoader = new ImageLoader(mRequestQueue, new ImageLoader.ImageCache() {
            private final LruCache<String, Bitmap> cache = new LruCache<String, Bitmap>(20);
            @Override
            public Bitmap getBitmap(String url) {
                return cache.get(url);
            }
            @Override
            public void putBitmap(String url, Bitmap bitmap) {
                cache.put(url, bitmap);
            }
        });
    }
    public static synchronized VolleySingleton getInstance(Context context) {
        if (mInstance == null) {
            mInstance = new VolleySingleton(context);
        }
        return mInstance;
    }
    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
        }
        return mRequestQueue;
    }
    public <T> void addToRequestQueue(Request<T> req) {
        getRequestQueue().add(req);
    }
    public ImageLoader getImageLoader() {
        return mImageLoader;
    }
}

This is where we will stop in the first part of this tutorial, we will finish up our android shopping cart application with PayPal Payment integration in part 2.

If you love this tutorial, stay back for part 2.

Remember to subscribe with your email address to be among the first to receive my new android blog post once it is published.

If you have a question or suggestion kindly use the comment box below.

The complete source code for this application will be made available after the second and final part of the tutorial.

OTHER INTERESTING POSTS:

5 Comments

    • Henry

Add a Comment