Added reports API

Migrated to a MVVM architecture on sample app
This commit is contained in:
Gilbert Kimutai 2019-02-20 05:57:06 +03:00
parent 5b18bb626b
commit e975393b5e
42 changed files with 1354 additions and 110 deletions

View File

@ -39,6 +39,13 @@ android {
}
ext {
arch_version = '1.1.1'
support_lib_version = '28.0.0'
dagger_version = '2.14.1'
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
@ -56,5 +63,22 @@ dependencies {
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation project(path: ':woodroid')
implementation 'de.hdodenhof:circleimageview:2.1.0'
implementation 'com.romandanylyk:pageindicatorview:0.2.0@aar'
// Android architecture components
implementation "android.arch.lifecycle:extensions:$arch_version"
annotationProcessor "android.arch.lifecycle:compiler:$arch_version"
implementation "android.arch.paging:runtime:1.0.1"
// Dagger.
implementation "com.google.dagger:dagger:$dagger_version"
implementation "com.google.dagger:dagger-android:$dagger_version"
implementation "com.google.dagger:dagger-android-support:$dagger_version"
annotationProcessor "com.google.dagger:dagger-android-processor:$dagger_version"
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
}

View File

@ -33,7 +33,7 @@
android:theme="@style/AppTheme.NoActionBar">
</activity>
<activity
android:name=".ui.ShopActivity"
android:name=".ui.product.ShopActivity"
android:label="@string/title_activity_shop"
android:theme="@style/AppTheme.NoActionBar">
</activity>

View File

@ -1,11 +1,14 @@
package me.gilo.wc;
import android.app.Application;
import dagger.android.AndroidInjector;
import dagger.android.DaggerApplication;
import io.github.inflationx.calligraphy3.CalligraphyConfig;
import io.github.inflationx.calligraphy3.CalligraphyInterceptor;
import io.github.inflationx.viewpump.ViewPump;
import me.gilo.wc.di.DaggerAppComponent;
public class WcApp extends Application {
public class WcApp extends DaggerApplication {
@Override
public void onCreate() {
@ -21,4 +24,9 @@ public class WcApp extends Application {
}
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return DaggerAppComponent.create();
}
}

View File

@ -7,7 +7,7 @@ import android.view.View;
import android.widget.TextView;
import me.gilo.wc.R;
import me.gilo.wc.ui.coupon.CouponsActivity;
import me.gilo.wc.ui.ShopActivity;
import me.gilo.wc.ui.product.ShopActivity;
public class MenuViewHolder extends RecyclerView.ViewHolder {

View File

@ -0,0 +1,25 @@
package me.gilo.wc.common;
import android.annotation.SuppressLint;
import android.arch.lifecycle.ViewModel;
import android.arch.lifecycle.ViewModelProvider;
import android.arch.lifecycle.ViewModelProviders;
import dagger.android.support.DaggerAppCompatActivity;
import javax.inject.Inject;
/**
* This Activity is to be inherited by any activity to initiate the injection.
*/
@SuppressLint("Registered")
public class BaseActivity extends DaggerAppCompatActivity {
@Inject
public ViewModelProvider.Factory viewModelFactory;
public <T extends ViewModel> T getViewModel(final Class<T> cls) {
return ViewModelProviders.of(this, viewModelFactory).get(cls);
}
}

View File

@ -0,0 +1,20 @@
package me.gilo.wc.common;
public class NetworkException extends Exception{
public NetworkException() {
super();
}
public NetworkException(String message) {
super(message);
}
public NetworkException(String message, Throwable cause) {
super(message, cause);
}
public NetworkException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,12 @@
package me.gilo.wc.common;
import android.support.v7.widget.RecyclerView;
/**
* Created by amrro <amr.elghobary@gmail.com> on 9/15/17.
* General interface callback for handling clicks inside {@link RecyclerView}
*/
public interface OnItemClickedListener<T> {
void onClicked(T item);
}

View File

@ -0,0 +1,78 @@
package me.gilo.wc.common;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.List;
@SuppressWarnings({"WeakerAccess", "ConstantConditions"})
public final class Resource<T> {
@Nullable
private final T data;
@Nullable
private final Exception error;
Status status = Status.LOADING;
public Resource(@NonNull T data) {
this(data, null);
}
public Resource(@NonNull Status status) {
this(null, null);
this.status = status;
}
public Resource(@NonNull Exception exception) {
this(null, exception);
this.status = Status.ERROR;
}
private Resource(@Nullable T value, @Nullable Exception error) {
this.data = value;
this.error = error;
if (error != null){
status = Status.ERROR;
}else if (data != null){
if (data instanceof List){
if (((List) data).size() == 0){
status = Status.EMPTY;
}else {
status = status.SUCCESS;
}
}else {
status = Status.SUCCESS;
}
}else {
status = Status.LOADING;
}
}
public boolean isSuccessful() {
return data != null && error == null;
}
@NonNull
public T data() {
if (error != null) {
throw new IllegalStateException("error is not null. Call isSuccessful() first.");
}
return data;
}
@NonNull
public Exception error() {
if (data != null) {
throw new IllegalStateException("data is not null. Call isSuccessful() first.");
}
return error;
}
@NonNull
public Status status() {
return status;
}
}

View File

@ -0,0 +1,12 @@
package me.gilo.wc.common;
public enum Status {
EMPTY,
SUCCESS,
ERROR,
LOADING;
public Status isLoading(){
return LOADING;
}
}

View File

@ -0,0 +1,39 @@
package me.gilo.wc.common;
import android.arch.lifecycle.LiveData;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import java.io.IOException;
public class WooLiveData<T> extends LiveData<Resource<T>> implements Callback<T> {
public WooLiveData() {
setValue(new Resource<>(Status.LOADING));
}
@Override
public void onResponse(Call<T> call, Response<T> response) {
if (response.isSuccessful()){
setValue(new Resource<>(response.body()));
}else{
String error = null;
try {
error = response.errorBody().string();
} catch (IOException e) {
e.printStackTrace();
}
if (error == null){
error = "Something went wrong";
}
setValue(new Resource<>(new NetworkException(error)));
}
}
@Override
public void onFailure(Call<T> call, Throwable t) {
setValue(new Resource<>( new NetworkException(t)));
}
}

View File

@ -0,0 +1,17 @@
package me.gilo.wc.di;
import dagger.Module;
import dagger.android.ContributesAndroidInjector;
import me.gilo.wc.MainActivity;
import me.gilo.wc.ui.product.ShopActivity;
@Module
abstract class ActivitiesModule {
@ContributesAndroidInjector
abstract MainActivity contributesMainActivity();
@ContributesAndroidInjector
abstract ShopActivity contributesShopActivity();
}

View File

@ -0,0 +1,23 @@
package me.gilo.wc.di;
import dagger.Component;
import dagger.android.AndroidInjector;
import dagger.android.DaggerApplication;
import dagger.android.support.AndroidSupportInjectionModule;
import me.gilo.wc.WcApp;
import javax.inject.Singleton;
@Singleton
@Component(modules = {
AndroidSupportInjectionModule.class,
ViewModelModule.class,
ActivitiesModule.class,
})
interface AppComponent extends AndroidInjector<DaggerApplication> {
void inject(WcApp app);
}

View File

@ -0,0 +1,24 @@
package me.gilo.wc.di;
import dagger.Module;
import dagger.Provides;
import me.gilo.wc.WcApp;
import javax.inject.Singleton;
@Module
public class AppModule {
WcApp app;
void AppModule(WcApp application) {
app = application;
}
@Provides
@Singleton
WcApp providesApplication() {
return app;
}
}

View File

@ -0,0 +1,14 @@
package me.gilo.wc.di;
import android.arch.lifecycle.ViewModel;
import dagger.MapKey;
import java.lang.annotation.*;
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@MapKey
@interface ViewModelKey {
Class<? extends ViewModel> value();
}

View File

@ -0,0 +1,23 @@
package me.gilo.wc.di;
import android.arch.lifecycle.ViewModel;
import android.arch.lifecycle.ViewModelProvider;
import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoMap;
import me.gilo.wc.utils.ViewModelFactory;
import me.gilo.wc.viewmodels.ProductViewModel;
@SuppressWarnings("WeakerAccess")
@Module
public abstract class ViewModelModule {
@Binds
@IntoMap
@ViewModelKey(ProductViewModel.class)
abstract ViewModel bindUserViewModel(ProductViewModel viewModel);
@Binds
abstract ViewModelProvider.Factory bindViewModelFactory(ViewModelFactory factory);
}

View File

@ -0,0 +1,25 @@
package me.gilo.wc.repo;
import me.gilo.wc.common.WooLiveData;
import me.gilo.woodroid.models.Product;
import javax.inject.Inject;
import java.util.List;
public class ProductRepository extends WoocommerceRepository {
@Inject
public ProductRepository() {
}
public WooLiveData<List<Product>> products() {
final WooLiveData<List<Product>> callBack = new WooLiveData();
woocommerce.ProductRepository().products().enqueue(callBack);
return callBack;
}
}

View File

@ -0,0 +1,13 @@
package me.gilo.wc.repo;
import me.gilo.woodroid.Woocommerce;
public class WoocommerceRepository {
Woocommerce woocommerce = new Woocommerce.Builder()
.setSiteUrl("http://157.230.131.179")
.setApiVersion(Woocommerce.API_V3)
.setConsumerKey("ck_26c61abd7eeff238d87dc56585bf26cb2d1a1ec3")
.setConsumerSecret("cs_062e8e3a7ae0ce08fdebc0c39f8f834d5e87598e")
.build();
}

View File

@ -1,79 +0,0 @@
package me.gilo.wc.ui
import android.os.Bundle
import android.support.v7.widget.GridLayoutManager
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_shop.*
import kotlinx.android.synthetic.main.content_coupon.*
import kotlinx.android.synthetic.main.content_shop.*
import me.gilo.wc.R
import me.gilo.wc.adapter.ProductAdapter
import me.gilo.woodroid.Woocommerce
import me.gilo.woodroid.models.Product
import me.gilo.woodroid.models.filters.ProductFilter
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.util.*
import kotlin.collections.HashMap
class ShopActivity : BaseActivity() {
lateinit var adapter : ProductAdapter
lateinit var products: ArrayList<Product>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_shop)
setSupportActionBar(toolbar)
title = "Shop"
val layoutManager = GridLayoutManager(baseContext, 2)
rvShop.layoutManager = layoutManager
rvShop.isNestedScrollingEnabled = false
products = ArrayList<Product>()
adapter = ProductAdapter(products)
rvShop.adapter = adapter
products()
}
//Not best practise, but works for purposes of demo
private fun products() {
val woocommerce = Woocommerce.Builder()
.setSiteUrl("http://157.230.131.179")
.setApiVersion(Woocommerce.API_V3)
.setConsumerKey("ck_26c61abd7eeff238d87dc56585bf26cb2d1a1ec3")
.setConsumerSecret("cs_062e8e3a7ae0ce08fdebc0c39f8f834d5e87598e")
.build()
val filters = ProductFilter()
filters.per_page = 3
woocommerce.ProductRepository().products(filters).enqueue(object : Callback<List<Product>> {
override fun onResponse(call: Call<List<Product>>, response: Response<List<Product>>) {
if (response.isSuccessful) {
val productsResponse = response.body()
for (product in productsResponse!!) {
products.add(product)
}
adapter.notifyDataSetChanged()
}else{
Toast.makeText(baseContext, "" + response.code() + " : " + response.message(), Toast.LENGTH_SHORT).show()
}
}
override fun onFailure(call: Call<List<Product>>, t: Throwable) {
}
})
}
}

View File

@ -0,0 +1,114 @@
package me.gilo.wc.ui.product
import android.content.Context
import android.os.Bundle
import android.support.v7.widget.GridLayoutManager
import android.widget.Toast
import io.github.inflationx.viewpump.ViewPumpContextWrapper
import kotlinx.android.synthetic.main.activity_shop.*
import kotlinx.android.synthetic.main.content_shop.*
import me.gilo.wc.R
import me.gilo.wc.adapter.ProductAdapter
import me.gilo.wc.common.BaseActivity
import me.gilo.wc.common.Status
import me.gilo.wc.ui.state.ProgressDialogFragment
import me.gilo.wc.viewmodels.ProductViewModel
import me.gilo.woodroid.models.Product
import org.json.JSONObject
import java.util.*
class ShopActivity : BaseActivity() {
lateinit var adapter: ProductAdapter
lateinit var products: ArrayList<Product>
lateinit var viewModel: ProductViewModel
val TAG = this::getLocalClassName
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(ViewPumpContextWrapper.wrap(newBase))
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_shop)
setSupportActionBar(toolbar)
viewModel = getViewModel(ProductViewModel::class.java)
title = "Shop"
val layoutManager = GridLayoutManager(baseContext, 2)
rvShop.layoutManager = layoutManager
rvShop.isNestedScrollingEnabled = false
products = ArrayList()
adapter = ProductAdapter(products)
rvShop.adapter = adapter
products()
}
private fun products() {
viewModel.products().observe(this, android.arch.lifecycle.Observer { response ->
when (response!!.status()) {
Status.LOADING -> {
showLoading("Performing log in", "This will only take a short while")
}
Status.SUCCESS -> {
stopShowingLoading()
val productsResponse = response.data()
for (product in productsResponse) {
products.add(product)
}
adapter.notifyDataSetChanged()
}
Status.ERROR -> {
stopShowingLoading()
var message: String
var loginError = JSONObject(response.error().message)
if (loginError.has("status_message")) {
message = loginError.getString("status_message")
} else {
message = response.error().message.toString()
}
Toast.makeText(baseContext, message, Toast.LENGTH_LONG).show()
}
Status.EMPTY -> {
stopShowingLoading()
}
}
})
}
private lateinit var progressDialog: ProgressDialogFragment
fun showLoading(title: String, message: String) {
val manager = supportFragmentManager
progressDialog = ProgressDialogFragment.newInstance(title, message)
progressDialog.isCancelable = false
progressDialog.show(manager, "progress")
}
fun showLoading() {
showLoading("This will only take a sec", "Loading")
}
fun stopShowingLoading() {
progressDialog.dismiss()
}
}

View File

@ -0,0 +1,131 @@
package me.gilo.wc.utils;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import android.support.annotation.StringRes;
import android.util.Base64;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/*
*
*/
public class AppUtils {
Context context;
String token;
String expiry;
public static final String MY_PREFS_NAME = "StarterApp";
public AppUtils(Context context) {
this.context = context;
}
public static void showToast(Context context, @StringRes int text, boolean isLong) {
showToast(context, context.getString(text), isLong);
}
public static void showToast(Context context, String text, boolean isLong) {
Toast.makeText(context, text, isLong ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT).show();
}
public void saveToken(String token, String expiry){
SharedPreferences.Editor editor = context.getSharedPreferences(MY_PREFS_NAME, context.MODE_PRIVATE).edit();
editor.putString("token", token);
editor.putString("expiry", expiry);
editor.putBoolean("loggedIn", true);
editor.apply();
}
public String getToken() {
SharedPreferences prefs = context.getSharedPreferences(MY_PREFS_NAME, context.MODE_PRIVATE);
return prefs.getString("token", null);
}
public String getExpiry() {
SharedPreferences prefs = context.getSharedPreferences(MY_PREFS_NAME, context.MODE_PRIVATE);
return prefs.getString("expiry", null);
}
public boolean isLoggedIn(){
SharedPreferences prefs = context.getSharedPreferences(MY_PREFS_NAME, context.MODE_PRIVATE);
return prefs.getBoolean("loggedIn", false);
}
public void logOut(){
SharedPreferences.Editor editor = context.getSharedPreferences(MY_PREFS_NAME, context.MODE_PRIVATE).edit();
editor.putString("token", "");
editor.putString("expiry", "");
editor.putBoolean("loggedIn", false);
editor.apply();
}
public static Date getCurrentDateTime(){
Date currentDate = Calendar.getInstance().getTime();
return currentDate;
}
public static String getFormattedDateString(Date date) {
try {
SimpleDateFormat spf = new SimpleDateFormat("EEE MMM d HH:mm:ss zzz yyyy");
String dateString = spf.format(date);
Date newDate = spf.parse(dateString);
spf= new SimpleDateFormat("dd MMM yyyy HH:mm:ss");
return spf.format(newDate);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
public static String generateHash(String password) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA-512");
md.update(password.getBytes());
byte byteData[] = md.digest();
String base64 = Base64.encodeToString(byteData, Base64.NO_WRAP);
return base64;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
public static void showMessage(Context context, String message) {
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
}
public static void openKeyboard(final Context context) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
}
}
}, 500);
}
public static void hideKeyboard(Activity activity) {
InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
}
}

View File

@ -0,0 +1,81 @@
package me.gilo.wc.utils;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
import java.util.Calendar;
public class DateTextWatcher implements TextWatcher {
private String current = "";
private String ddmmyyyy = "DDMMYYYY";
private Calendar cal = Calendar.getInstance();
EditText date;
public DateTextWatcher(EditText date){
this.date = date;
date.addTextChangedListener(this);
}
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (!s.toString().equals(current)) {
String clean = s.toString().replaceAll("[^\\d.]|\\.", "");
String cleanC = current.replaceAll("[^\\d.]|\\.", "");
int cl = clean.length();
int sel = cl;
for (int i = 2; i <= cl && i < 6; i += 2) {
sel++;
}
//Fix for pressing delete next to a forward slash
if (clean.equals(cleanC)) sel--;
if (clean.length() < 8){
clean = clean + ddmmyyyy.substring(clean.length());
}else{
//This part makes sure that when we finish entering numbers
//the date is correct, fixing it otherwise
int day = Integer.parseInt(clean.substring(0,2));
int mon = Integer.parseInt(clean.substring(2,4));
int year = Integer.parseInt(clean.substring(4,8));
mon = mon < 1 ? 1 : mon > 12 ? 12 : mon;
cal.set(Calendar.MONTH, mon-1);
year = (year<1900)?1900:(year>2100)?2100:year;
cal.set(Calendar.YEAR, year);
// ^ first set year for the line below to work correctly
//with leap years - otherwise, date e.g. 29/02/2012
//would be automatically corrected to 28/02/2012
day = (day > cal.getActualMaximum(Calendar.DATE))? cal.getActualMaximum(Calendar.DATE):day;
clean = String.format("%02d%02d%02d",day, mon, year);
}
clean = String.format("%s/%s/%s", clean.substring(0, 2),
clean.substring(2, 4),
clean.substring(4, 8));
sel = sel < 0 ? 0 : sel;
current = clean;
date.setText(current);
date.setSelection(sel < current.length() ? sel : current.length());
}
}
@Override
public void afterTextChanged(Editable editable) {
}
}

View File

@ -0,0 +1,5 @@
package me.gilo.wc.utils;
public interface OnItemClickListener<T> {
void onItemClick(T data);
}

View File

@ -0,0 +1,62 @@
package me.gilo.wc.utils;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
private OnItemClickListener mListener;
private OnRecyclerViewItemClickListener recyclerViewItemClickListener;
public interface OnItemClickListener {
void onItemClick(View view, int position);
}
public interface OnRecyclerViewItemClickListener {
void onItemClick(View parentView, View childView, int position);
}
GestureDetector mGestureDetector;
public RecyclerItemClickListener(Context context, OnItemClickListener listener) {
mListener = listener;
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
}
public RecyclerItemClickListener(Context context, OnRecyclerViewItemClickListener listener) {
recyclerViewItemClickListener = listener;
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
}
@Override
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
View childView = view.findChildViewUnder(e.getX(), e.getY());
if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
mListener.onItemClick(childView, view.getChildPosition(childView));
} else if (childView != null && recyclerViewItemClickListener != null && mGestureDetector.onTouchEvent(e)) {
recyclerViewItemClickListener.onItemClick(view, childView, view.getChildPosition(childView));
}
return false;
}
@Override
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean b) {
}
}

View File

@ -0,0 +1,21 @@
package me.gilo.wc.utils;
import java.text.DecimalFormat;
/**
* Created by gilo on 1/31/16.
*/
public class StringFormatter {
public static String formatPrice(float price) {
DecimalFormat formatter = new DecimalFormat("#,###.00");
String formattedText = formatter.format(price);
return "$" + formattedText;
}
public static String formatNumber(float price) {
DecimalFormat formatter = new DecimalFormat("#,###.00");
String formattedText = formatter.format(price);
return "" + formattedText;
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.gilo.wc.utils;
import android.arch.lifecycle.ViewModel;
import android.arch.lifecycle.ViewModelProvider;
import android.support.annotation.NonNull;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import java.util.Map;
@Singleton
public class ViewModelFactory implements ViewModelProvider.Factory {
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;
@Inject
ViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
this.creators = creators;
}
@NonNull
@SuppressWarnings("unchecked")
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
Provider<? extends ViewModel> creator = creators.get(modelClass);
if (creator == null) {
for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : creators.entrySet()) {
if (modelClass.isAssignableFrom(entry.getKey())) {
creator = entry.getValue();
break;
}
}
}
if (creator == null) {
throw new IllegalArgumentException("unknown model class " + modelClass);
}
try {
return (T) creator.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,23 @@
package me.gilo.wc.viewmodels;
import android.arch.lifecycle.ViewModel;
import me.gilo.wc.common.WooLiveData;
import me.gilo.wc.repo.ProductRepository;
import me.gilo.woodroid.models.Product;
import javax.inject.Inject;
import java.util.List;
public final class ProductViewModel extends ViewModel {
private final ProductRepository productRepository;
@Inject
ProductViewModel(ProductRepository productRepository) {
this.productRepository = productRepository;
}
public WooLiveData<List<Product>> products() {
return productRepository.products();
}
}

View File

@ -5,7 +5,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.ShopActivity">
tools:context=".ui.product.ShopActivity">
<android.support.design.widget.AppBarLayout
android:layout_height="wrap_content"

View File

@ -7,7 +7,7 @@
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:showIn="@layout/activity_shop"
tools:context=".ui.ShopActivity">
tools:context=".ui.product.ShopActivity">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"

View File

@ -1,9 +1,6 @@
package me.gilo.woodroid;
import me.gilo.woodroid.repo.CouponRepository;
import me.gilo.woodroid.repo.CustomerRepository;
import me.gilo.woodroid.repo.OrderRepository;
import me.gilo.woodroid.repo.ProductRepository;
import me.gilo.woodroid.repo.*;
import me.gilo.woodroid.repo.order.OrderNoteRepository;
import me.gilo.woodroid.repo.order.RefundRepository;
import me.gilo.woodroid.repo.product.*;
@ -29,6 +26,8 @@ public class Woocommerce {
final OrderRepository orderRepository;
final ProductRepository productRepository;
final ReportsRepository reportsRepository;
enum ApiVersion {
API_VERSION1{
@ -67,6 +66,8 @@ public class Woocommerce {
orderRepository = new OrderRepository(baseUrl, consumerKey, consumerSecret);
productRepository = new ProductRepository(baseUrl, consumerKey, consumerSecret);
reportsRepository = new ReportsRepository(baseUrl, consumerKey, consumerSecret);
}
@ -153,4 +154,8 @@ public class Woocommerce {
public ProductRepository ProductRepository() {
return productRepository;
}
public ReportsRepository ReportsRepository() {
return reportsRepository;
}
}

View File

@ -0,0 +1,44 @@
package me.gilo.woodroid.data.api;
import me.gilo.woodroid.models.report.*;
import retrofit2.Call;
import retrofit2.http.*;
import java.util.List;
import java.util.Map;
public interface ReportAPI {
@GET("reports/sales")
Call<List<SalesTotal>> sales();
@GET("reports/sales")
Call<List<SalesTotal>> sales(@QueryMap Map<String, String> filter);
@GET("reports/top_sellers")
Call<List<TopSellerProducts>> top_sellers();
@GET(" /wp-json/wc/v3/reports/top_sellers")
Call<List<TopSellerProducts>> top_sellers(@QueryMap Map<String, String> filter);
@GET("reports/coupons/totals")
Call<List<CouponsTotal>> coupons_totals();
@GET("reports/customers/totals")
Call<List<CustomersTotal>> customers_totals();
@GET("reports/orders/totals")
Call<List<OrdersTotal>> orders_totals();
@GET("reports/products/totals")
Call<List<ProductsTotal>> products_totals();
@GET("reports/reviews/totals")
Call<List<ReviewsTotal>> reviews_totals();
}

View File

@ -0,0 +1,29 @@
package me.gilo.woodroid.models.filters;
import java.util.HashMap;
import java.util.Map;
public class Filter {
String context;
Map<String, String> filters = new HashMap<>();
public String getContext() {
return context;
}
public void setContext(String context) {
this.context = context;
addFilter("context", context);
}
public void addFilter(String filter, Object value) {
filters.put(filter, value.toString());
}
public Map<String, String> getFilters() {
return filters;
}
}

View File

@ -3,23 +3,10 @@ package me.gilo.woodroid.models.filters;
import java.util.HashMap;
import java.util.Map;
public class OrderNoteFilter {
public class OrderNoteFilter extends Filter{
String context;
String type;
Map<String, String> filters = new HashMap<>();
public String getContext() {
return context;
}
public void setContext(String context) {
this.context = context;
addFilter("context", context);
}
public String getType() {
return type;
}
@ -29,12 +16,4 @@ public class OrderNoteFilter {
addFilter("type", type);
}
public void addFilter(String filter, Object value) {
filters.put(filter, value.toString());
}
public Map<String, String> getFilters() {
return filters;
}
}

View File

@ -0,0 +1,38 @@
package me.gilo.woodroid.models.filters;
public class ReportsDateFilter extends Filter{
String period;
String date_min;
String date_max;
public String getPeriod() {
return period;
}
public void setPeriod(String period) {
this.period = period;
addFilter("period", period);
}
public String getDate_min() {
return date_min;
}
public void setDate_min(String date_min) {
this.date_min = date_min;
addFilter("date_min", date_min);
}
public String getDate_max() {
return date_max;
}
public void setDate_max(String date_max) {
this.date_max = date_max;
addFilter("date_max", date_max);
}
}

View File

@ -0,0 +1,32 @@
package me.gilo.woodroid.models.report;
public class CouponsTotal {
String slug;
String name;
String total;
public String getSlug() {
return slug;
}
public void setSlug(String slug) {
this.slug = slug;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTotal() {
return total;
}
public void setTotal(String total) {
this.total = total;
}
}

View File

@ -0,0 +1,32 @@
package me.gilo.woodroid.models.report;
public class CustomersTotal {
String slug;
String name;
String total;
public String getSlug() {
return slug;
}
public void setSlug(String slug) {
this.slug = slug;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTotal() {
return total;
}
public void setTotal(String total) {
this.total = total;
}
}

View File

@ -0,0 +1,32 @@
package me.gilo.woodroid.models.report;
public class OrdersTotal {
String slug;
String name;
String total;
public String getSlug() {
return slug;
}
public void setSlug(String slug) {
this.slug = slug;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTotal() {
return total;
}
public void setTotal(String total) {
this.total = total;
}
}

View File

@ -0,0 +1,32 @@
package me.gilo.woodroid.models.report;
public class ProductsTotal {
String slug;
String name;
String total;
public String getSlug() {
return slug;
}
public void setSlug(String slug) {
this.slug = slug;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTotal() {
return total;
}
public void setTotal(String total) {
this.total = total;
}
}

View File

@ -0,0 +1,32 @@
package me.gilo.woodroid.models.report;
public class ReviewsTotal {
String slug;
String name;
String total;
public String getSlug() {
return slug;
}
public void setSlug(String slug) {
this.slug = slug;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTotal() {
return total;
}
public void setTotal(String total) {
this.total = total;
}
}

View File

@ -0,0 +1,13 @@
package me.gilo.woodroid.models.report;
public class SalesPeriodTotal {
private String shipping;
private String discount;
private int orders;
private String tax;
private int customers;
private int items;
private String sales;
}

View File

@ -0,0 +1,115 @@
package me.gilo.woodroid.models.report;
import java.util.Map;
public class SalesTotal {
private int total_discount;
private String net_sales;
private String total_customers;
private Map<String, SalesPeriodTotal> totals;
private int total_orders;
private String total_tax;
private int total_items;
private String totals_grouped_by;
private String total_shipping;
private String average_sales;
private String total_sales;
private int total_refunds;
public int getTotal_discount() {
return total_discount;
}
public void setTotal_discount(int total_discount) {
this.total_discount = total_discount;
}
public String getNet_sales() {
return net_sales;
}
public void setNet_sales(String net_sales) {
this.net_sales = net_sales;
}
public String getTotal_customers() {
return total_customers;
}
public void setTotal_customers(String total_customers) {
this.total_customers = total_customers;
}
public Map<String, SalesPeriodTotal> getTotals() {
return totals;
}
public void setTotals(Map<String, SalesPeriodTotal> totals) {
this.totals = totals;
}
public int getTotal_orders() {
return total_orders;
}
public void setTotal_orders(int total_orders) {
this.total_orders = total_orders;
}
public String getTotal_tax() {
return total_tax;
}
public void setTotal_tax(String total_tax) {
this.total_tax = total_tax;
}
public int getTotal_items() {
return total_items;
}
public void setTotal_items(int total_items) {
this.total_items = total_items;
}
public String getTotals_grouped_by() {
return totals_grouped_by;
}
public void setTotals_grouped_by(String totals_grouped_by) {
this.totals_grouped_by = totals_grouped_by;
}
public String getTotal_shipping() {
return total_shipping;
}
public void setTotal_shipping(String total_shipping) {
this.total_shipping = total_shipping;
}
public String getAverage_sales() {
return average_sales;
}
public void setAverage_sales(String average_sales) {
this.average_sales = average_sales;
}
public String getTotal_sales() {
return total_sales;
}
public void setTotal_sales(String total_sales) {
this.total_sales = total_sales;
}
public int getTotal_refunds() {
return total_refunds;
}
public void setTotal_refunds(int total_refunds) {
this.total_refunds = total_refunds;
}
}

View File

@ -0,0 +1,32 @@
package me.gilo.woodroid.models.report;
public class TopSellerProducts {
String title;
int product_id;
int quantity;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getProduct_id() {
return product_id;
}
public void setProduct_id(int product_id) {
this.product_id = product_id;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
}

View File

@ -0,0 +1,55 @@
package me.gilo.woodroid.repo;
import me.gilo.woodroid.data.api.ReportAPI;
import me.gilo.woodroid.models.filters.ReportsDateFilter;
import me.gilo.woodroid.models.report.*;
import retrofit2.Call;
import java.util.List;
public class ReportsRepository extends WooRepository{
private final ReportAPI apiService;
public ReportsRepository(String baseUrl, String consumerKey, String consumerSecret) {
super( baseUrl, consumerKey, consumerSecret);
apiService = retrofit.create(ReportAPI.class);
}
public Call<List<SalesTotal>> sales() {
return apiService.sales();
}
public Call<List<SalesTotal>> sales(ReportsDateFilter reportsDateFilter) {
return apiService.sales(reportsDateFilter.getFilters());
}
public Call<List<TopSellerProducts>> top_sellers() {
return apiService.top_sellers();
}
public Call<List<TopSellerProducts>> top_sellers(ReportsDateFilter reportsDateFilter) {
return apiService.top_sellers(reportsDateFilter.getFilters());
}
public Call<List<CouponsTotal>> coupons_totals() {
return apiService.coupons_totals();
}
public Call<List<CustomersTotal>> customer_totals() {
return apiService.customers_totals();
}
public Call<List<OrdersTotal>> order_totals() {
return apiService.orders_totals();
}
public Call<List<ProductsTotal>> product_totals() {
return apiService.products_totals();
}
public Call<List<ReviewsTotal>> review_totals() {
return apiService.reviews_totals();
}
}