In This tutorial we are going to cover basics of using different dependencies in our simple app. In production, most features used in this app won’t be available
In the previous part, we learnt how to create the supermarket price comparison backend using php and mysql as the database. In this part, we are going to learn how to create the android part using android studio.
The app will consist of several activities and fragments and a main Navigation drawer to help in easy navigation. The home fragment will list all products available in the database in a recycler view.
The Admin activity will enable the main administrator to login and add sub-admins (supermarket admins), who will later login through the supermarket Admin activity and add products with their respective prices.
1. In Android Studio, create a new project from File ⇒ New Project and fill all the required details.
2. Create three packages named Adapter, Model under src folder.
3. Open build.gradle and add volley library support by adding
[php]
compile ‘com.android.support:appcompat-v7:25.0.0’
compile ‘com.android.support:design:25.0.0’
compile ‘com.android.support:cardview-v7:25.0.0’
compile ‘com.android.support:recyclerview-v7:25.0.0’
compile ‘com.rengwuxian.materialedittext:library:2.1.4’
compile ‘com.mcxiaoke.volley:library:1.0.19’
compile ‘com.github.rey5137:material:1.2.4’
compile ‘com.squareup.picasso:picasso:2.5.2’
compile ‘com.android.support:support-v4:25.0.0’
compile ‘com.android.support:support-vector-drawable:25.0.0’
compile ‘com.squareup.retrofit2:retrofit:2.1.0’
compile ‘com.squareup.retrofit2:converter-gson:2.1.0’
compile ‘com.github.rey5137:material:1.2.4’
[/php]
4. Open colors.xml located under res ⇒ values and add the color values. If you don’t find colors.xml, create a new file with the name.
[xml]
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#9C27B0</color>
<color name="colorPrimaryDark">#9C27B0</color>
<color name="colorAccent">#9C27B0</color>
</resources>
[/xml]
5. Create a class named Config.java and add below code. In this class we declare the urls. While testing you need to replace the ip address with your localhost pc ip.
[java]
package com.kedevelopers.supermarketprices;
/**
* Created by BEN on 11/17/2016.
*/
public class Config {
//Data URL
public static final String KE_URL = "http://192.168.137.1";
public static final String SELLER = "http://192.168.137.1/supermarket_api/v1/get-sellers";
public static final String URL_ADD_USER ="http://192.168.137.1/supermarket_api/v1/register" ;
public static final String URL_SUP_USER ="http://192.168.137.1/supermarket_api/v1/login" ;
public static final String ADD_URL ="http://192.168.137.1/supermarket_api/v1/add-product" ;
public static final String ADD_URL_PRICE ="http://192.168.137.1/supermarket_api/v1/add-product-price" ;
public static final String DELETE_USER_URL = "http://192.168.137.1/supermarket_api/v1/delete-user";
}
[/java]
6. Create a class named AppController.java. This class extends from Applicationwhich should be executed on app launch. In this class we initiate all the volley core objects.
[java]package com.kedevelopers.supermarketprices;
/**
* Created by BEN on 11/17/2016.
*/
import android.app.Application;
import android.content.Context;
import android.text.TextUtils;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;
public class AppController extends Application {
public static final String TAG = AppController.class
.getSimpleName();
private RequestQueue mRequestQueue;
private ImageLoader mImageLoader;
private static AppController mInstance;
@Override
public void onCreate() {
super.onCreate();
mInstance = this;
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
}
public static synchronized AppController getInstance() {
return mInstance;
}
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
mRequestQueue = Volley.newRequestQueue(getApplicationContext());
}
return mRequestQueue;
}
public <T> void addToRequestQueue(Request<T> req, String tag) {
// set the default tag if tag is empty
req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
getRequestQueue().add(req);
}
public <T> void addToRequestQueue(Request<T> req) {
req.setTag(TAG);
getRequestQueue().add(req);
}
public void cancelPendingRequests(Object tag) {
if (mRequestQueue != null) {
mRequestQueue.cancelAll(tag);
}
}
}
[/java]
7. Now open AndroidManifest.xml and add INTERNET permission. Add the AppController class to<application> tag. Also add other activities (creating activities with android studio will automatically add them in the manifest) which we are going to create shortly. I am keeping MainActivity as launcher activity as it should be seen on app launch.
[xml]
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.kedevelopers.supermarketprices">
<uses-permission android:name="android.permission.INTERNET" />
<!– To auto-complete the email text field in the login form with the user’s emails –>
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<application
android:name=".AppController"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ProductView"
android:label="@string/title_activity_product_view"
android:parentActivityName=".MainActivity"
android:theme="@style/AppTheme.NoActionBar">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.kedevelopers.supermarketprices.MainActivity" />
</activity>
<activity
android:name=".LoginActivity"
android:label="@string/title_activity_login" />
<activity
android:name=".SettingsActivity"
android:label="@string/title_activity_settings" />
<activity
android:name=".AdminAddUser"
android:label="@string/title_activity_admin_add_user"
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".SupermarketAdmin"
android:label="@string/title_activity_supermarket_admin" />
<activity
android:name=".AddProduct"
android:label="@string/title_activity_add_product"
android:theme="@style/AppTheme.NoActionBar">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.kedevelopers.supermarketprices.SupermarketAdminProducts" />
</activity>
<activity
android:name=".SupermarketAdminProducts"
android:label="@string/title_activity_supermarket_admin_products"
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".ListUsers"
android:label="@string/title_activity_list_users"
android:theme="@style/AppTheme.NoActionBar"></activity>
</application>
</manifest>
[/xml]
8. Am only going to put the MainActivity.Java code below. The Rest of the code is downloadable below
[java]
package com.kedevelopers.supermarketprices;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.SearchView;
import android.util.Log;
import android.view.View;
import android.support.design.widget.NavigationView;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.kedevelopers.supermarketprices.Adapter.ProductsAdapter;
import com.kedevelopers.supermarketprices.Model.Product;
import com.rey.material.app.Dialog;
import com.rey.material.app.DialogFragment;
import com.rey.material.app.SimpleDialog;
import com.rey.material.app.ThemeManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener, SearchView.OnQueryTextListener {
private ArrayList<Product> listProduct;
private ProgressBar progressBar1;
private RecyclerView recyclerView;
private RecyclerView.LayoutManager layoutManager;
private ProductsAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
//Initializing Views
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
progressBar1 = (ProgressBar) findViewById(R.id.progressBar1);
progressBar1.setVisibility(View.INVISIBLE);
recyclerView.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
loadJSON();
recyclerView.setAdapter(adapter);
final FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
loadJSON();
}
});
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.setDrawerListener(toggle);
toggle.syncState();
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
}
@Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
final MenuItem item = menu.findItem(R.id.menu_search);
final SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
searchView.setOnQueryTextListener(this);
MenuItemCompat.setOnActionExpandListener(item,
new MenuItemCompat.OnActionExpandListener() {
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
// Do something when collapsed
adapter.setFilter(listProduct);
return true; // Return true to collapse action view
}
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
// Do something when expanded
return true; // Return true to expand action view
}
});
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
ShowDialog("Supermarket Price Comparison App", "This app helps compare prices of different products listed by different supermarkets in Kenya");
return true;
}
return super.onOptionsItemSelected(item);
}
@SuppressWarnings("StatementWithEmptyBody")
@Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
if (id == R.id.nav_home) {
// Handle the camera action
} else if (id == R.id.nav_admin) {
Intent intent = new Intent(MainActivity.this, LoginActivity.class);
startActivity(intent);
} else if (id == R.id.nav_s_admin) {
Intent intent = new Intent(MainActivity.this, SupermarketAdmin.class);
startActivity(intent);
} else if (id == R.id.nav_slideshow) {
Intent sharingIntent = new Intent(Intent.ACTION_SEND);
sharingIntent.setType("text/plain");
String shareBody = "This app is terrific – you can file compare prices on different commodities from different supermarkets";
sharingIntent.putExtra(Intent.EXTRA_SUBJECT, "Get the app");
sharingIntent.putExtra(Intent.EXTRA_TEXT, shareBody);
MainActivity.this.startActivity(Intent.createChooser(sharingIntent, "Share via"));
} else if (id == R.id.nav_manage) {
} else if (id == R.id.nav_share) {
Intent intent = new Intent(MainActivity.this, SettingsActivity.class);
startActivity(intent);
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
final List<Product> filteredModelList = filter(listProduct, newText);
adapter.setFilter(filteredModelList);
return true;
}
private List<Product> filter(List<Product> models, String query) {
query = query.toLowerCase();
final List<Product> filteredModelList = new ArrayList<>();
for (Product model : models) {
final String text = model.getName().toLowerCase();
if (text.contains(query)) {
filteredModelList.add(model);
}
}
return filteredModelList;
}
private void loadJSON() {
progressBar1.setVisibility(View.VISIBLE);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Config.KE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
RequestInterface request = retrofit.create(RequestInterface.class);
Call<JSONResponse> call = request.getJSON();
call.enqueue(new Callback<JSONResponse>() {
@Override
public void onResponse(Call<JSONResponse> call, retrofit2.Response<JSONResponse> response) {
JSONResponse jsonResponse = response.body();
listProduct = new ArrayList<>(Arrays.asList(jsonResponse.getProducts()));
Log.d("Prods", listProduct.toString());
adapter = new ProductsAdapter(listProduct, getApplicationContext());
recyclerView.setAdapter(adapter);
progressBar1.setVisibility(View.INVISIBLE);
}
@Override
public void onFailure(Call<JSONResponse> call, Throwable t) {
// Log.d("Error",t.getMessage());
}
});
}
private void ShowDialog(String title, String message) {
Dialog.Builder builder;
boolean isLightTheme = ThemeManager.getInstance().getCurrentTheme() == 0;
builder = new SimpleDialog.Builder(isLightTheme ? R.style.SimpleDialogLight : R.style.SimpleDialog) {
@Override
public void onPositiveActionClicked(DialogFragment fragment) {
Toast.makeText(MainActivity.this, "Agreed", Toast.LENGTH_SHORT).show();
super.onPositiveActionClicked(fragment);
}
@Override
public void onNegativeActionClicked(DialogFragment fragment) {
Toast.makeText(MainActivity.this, "Disagreed", Toast.LENGTH_SHORT).show();
super.onNegativeActionClicked(fragment);
}
};
((SimpleDialog.Builder) builder).message(message)
.title(title)
.positiveAction("OK");
DialogFragment fragment = DialogFragment.newInstance(builder);
fragment.show(getSupportFragmentManager(), null);
}
}
[/java]
For a beginner it will be always difficult to run this project for the first time. But don’t worry, the following steps will helps you testing this app. (The ip address looks like 192.168.0.100)
⇒ Make sure that both devices (the device running the PHP project and the android device) are on the same wifi network.
⇒ Give correct username , password and database name of MySQL in Config.php
⇒ Replace the URL ip address in Config.java with your machine ip address. You can get the ip address by running ipconfig in cmd
⇒Right click wamp server and put it online. This will enable android device within the same wifi location have access to the data.