diff --git a/app/build.gradle b/app/build.gradle index 65eb202..ccd18c8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' + } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8f8b5ef..ed925bf 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -33,7 +33,7 @@ android:theme="@style/AppTheme.NoActionBar"> diff --git a/app/src/main/java/me/gilo/wc/WcApp.java b/app/src/main/java/me/gilo/wc/WcApp.java index da53faf..8905ec6 100644 --- a/app/src/main/java/me/gilo/wc/WcApp.java +++ b/app/src/main/java/me/gilo/wc/WcApp.java @@ -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 applicationInjector() { + return DaggerAppComponent.create(); + } } diff --git a/app/src/main/java/me/gilo/wc/adapter/viewholder/MenuViewHolder.java b/app/src/main/java/me/gilo/wc/adapter/viewholder/MenuViewHolder.java index 5272fbe..a3f2b06 100644 --- a/app/src/main/java/me/gilo/wc/adapter/viewholder/MenuViewHolder.java +++ b/app/src/main/java/me/gilo/wc/adapter/viewholder/MenuViewHolder.java @@ -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 { diff --git a/app/src/main/java/me/gilo/wc/common/BaseActivity.java b/app/src/main/java/me/gilo/wc/common/BaseActivity.java new file mode 100644 index 0000000..ee21ddf --- /dev/null +++ b/app/src/main/java/me/gilo/wc/common/BaseActivity.java @@ -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 getViewModel(final Class cls) { + return ViewModelProviders.of(this, viewModelFactory).get(cls); + } +} diff --git a/app/src/main/java/me/gilo/wc/common/NetworkException.java b/app/src/main/java/me/gilo/wc/common/NetworkException.java new file mode 100644 index 0000000..8dca6d3 --- /dev/null +++ b/app/src/main/java/me/gilo/wc/common/NetworkException.java @@ -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); + } +} diff --git a/app/src/main/java/me/gilo/wc/common/OnItemClickedListener.java b/app/src/main/java/me/gilo/wc/common/OnItemClickedListener.java new file mode 100644 index 0000000..eb79371 --- /dev/null +++ b/app/src/main/java/me/gilo/wc/common/OnItemClickedListener.java @@ -0,0 +1,12 @@ +package me.gilo.wc.common; + +import android.support.v7.widget.RecyclerView; + +/** + * Created by amrro on 9/15/17. + * General interface callback for handling clicks inside {@link RecyclerView} + */ + +public interface OnItemClickedListener { + void onClicked(T item); +} diff --git a/app/src/main/java/me/gilo/wc/common/Resource.java b/app/src/main/java/me/gilo/wc/common/Resource.java new file mode 100644 index 0000000..a04918f --- /dev/null +++ b/app/src/main/java/me/gilo/wc/common/Resource.java @@ -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 { + @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; + } + + +} diff --git a/app/src/main/java/me/gilo/wc/common/Status.java b/app/src/main/java/me/gilo/wc/common/Status.java new file mode 100644 index 0000000..b840b91 --- /dev/null +++ b/app/src/main/java/me/gilo/wc/common/Status.java @@ -0,0 +1,12 @@ +package me.gilo.wc.common; + +public enum Status { + EMPTY, + SUCCESS, + ERROR, + LOADING; + + public Status isLoading(){ + return LOADING; + } +} diff --git a/app/src/main/java/me/gilo/wc/common/WooLiveData.java b/app/src/main/java/me/gilo/wc/common/WooLiveData.java new file mode 100644 index 0000000..036f186 --- /dev/null +++ b/app/src/main/java/me/gilo/wc/common/WooLiveData.java @@ -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 extends LiveData> implements Callback { + + public WooLiveData() { + setValue(new Resource<>(Status.LOADING)); + } + + @Override + public void onResponse(Call call, Response 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 call, Throwable t) { + setValue(new Resource<>( new NetworkException(t))); + } +} diff --git a/app/src/main/java/me/gilo/wc/di/ActivitiesModule.java b/app/src/main/java/me/gilo/wc/di/ActivitiesModule.java new file mode 100644 index 0000000..fbafee2 --- /dev/null +++ b/app/src/main/java/me/gilo/wc/di/ActivitiesModule.java @@ -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(); + +} diff --git a/app/src/main/java/me/gilo/wc/di/AppComponent.java b/app/src/main/java/me/gilo/wc/di/AppComponent.java new file mode 100644 index 0000000..ef0db86 --- /dev/null +++ b/app/src/main/java/me/gilo/wc/di/AppComponent.java @@ -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 { + + void inject(WcApp app); + +} diff --git a/app/src/main/java/me/gilo/wc/di/AppModule.java b/app/src/main/java/me/gilo/wc/di/AppModule.java new file mode 100644 index 0000000..011620f --- /dev/null +++ b/app/src/main/java/me/gilo/wc/di/AppModule.java @@ -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; + } + +} diff --git a/app/src/main/java/me/gilo/wc/di/ViewModelKey.java b/app/src/main/java/me/gilo/wc/di/ViewModelKey.java new file mode 100644 index 0000000..3e1e5fc --- /dev/null +++ b/app/src/main/java/me/gilo/wc/di/ViewModelKey.java @@ -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 value(); +} diff --git a/app/src/main/java/me/gilo/wc/di/ViewModelModule.java b/app/src/main/java/me/gilo/wc/di/ViewModelModule.java new file mode 100644 index 0000000..576364a --- /dev/null +++ b/app/src/main/java/me/gilo/wc/di/ViewModelModule.java @@ -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); +} diff --git a/app/src/main/java/me/gilo/wc/repo/ProductRepository.java b/app/src/main/java/me/gilo/wc/repo/ProductRepository.java new file mode 100644 index 0000000..f103cb9 --- /dev/null +++ b/app/src/main/java/me/gilo/wc/repo/ProductRepository.java @@ -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> products() { + final WooLiveData> callBack = new WooLiveData(); + + woocommerce.ProductRepository().products().enqueue(callBack); + return callBack; + } + + +} diff --git a/app/src/main/java/me/gilo/wc/repo/WoocommerceRepository.java b/app/src/main/java/me/gilo/wc/repo/WoocommerceRepository.java new file mode 100644 index 0000000..af13713 --- /dev/null +++ b/app/src/main/java/me/gilo/wc/repo/WoocommerceRepository.java @@ -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(); +} diff --git a/app/src/main/java/me/gilo/wc/ui/ShopActivity.kt b/app/src/main/java/me/gilo/wc/ui/ShopActivity.kt deleted file mode 100644 index 98e21aa..0000000 --- a/app/src/main/java/me/gilo/wc/ui/ShopActivity.kt +++ /dev/null @@ -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 - - 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() - - 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> { - override fun onResponse(call: Call>, response: Response>) { - - 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>, t: Throwable) { - - } - }) - - } - -} diff --git a/app/src/main/java/me/gilo/wc/ui/product/ShopActivity.kt b/app/src/main/java/me/gilo/wc/ui/product/ShopActivity.kt new file mode 100644 index 0000000..3c4293a --- /dev/null +++ b/app/src/main/java/me/gilo/wc/ui/product/ShopActivity.kt @@ -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 + + 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() + } + +} diff --git a/app/src/main/java/me/gilo/wc/utils/AppUtils.java b/app/src/main/java/me/gilo/wc/utils/AppUtils.java new file mode 100644 index 0000000..899c16a --- /dev/null +++ b/app/src/main/java/me/gilo/wc/utils/AppUtils.java @@ -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); + } +} \ No newline at end of file diff --git a/app/src/main/java/me/gilo/wc/utils/DateTextWatcher.java b/app/src/main/java/me/gilo/wc/utils/DateTextWatcher.java new file mode 100644 index 0000000..e30c20d --- /dev/null +++ b/app/src/main/java/me/gilo/wc/utils/DateTextWatcher.java @@ -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) { + + } +} diff --git a/app/src/main/java/me/gilo/wc/utils/OnItemClickListener.java b/app/src/main/java/me/gilo/wc/utils/OnItemClickListener.java new file mode 100644 index 0000000..b7ee433 --- /dev/null +++ b/app/src/main/java/me/gilo/wc/utils/OnItemClickListener.java @@ -0,0 +1,5 @@ +package me.gilo.wc.utils; + +public interface OnItemClickListener { + void onItemClick(T data); +} diff --git a/app/src/main/java/me/gilo/wc/utils/RecyclerItemClickListener.java b/app/src/main/java/me/gilo/wc/utils/RecyclerItemClickListener.java new file mode 100644 index 0000000..4268c07 --- /dev/null +++ b/app/src/main/java/me/gilo/wc/utils/RecyclerItemClickListener.java @@ -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) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/me/gilo/wc/utils/StringFormatter.java b/app/src/main/java/me/gilo/wc/utils/StringFormatter.java new file mode 100644 index 0000000..0a36a0d --- /dev/null +++ b/app/src/main/java/me/gilo/wc/utils/StringFormatter.java @@ -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; + } +} diff --git a/app/src/main/java/me/gilo/wc/utils/ViewModelFactory.java b/app/src/main/java/me/gilo/wc/utils/ViewModelFactory.java new file mode 100644 index 0000000..7ba60fe --- /dev/null +++ b/app/src/main/java/me/gilo/wc/utils/ViewModelFactory.java @@ -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, Provider> creators; + + @Inject + ViewModelFactory(Map, Provider> creators) { + this.creators = creators; + } + + @NonNull + @SuppressWarnings("unchecked") + @Override + public T create(@NonNull Class modelClass) { + Provider creator = creators.get(modelClass); + if (creator == null) { + for (Map.Entry, Provider> 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); + } + } +} diff --git a/app/src/main/java/me/gilo/wc/viewmodels/ProductViewModel.java b/app/src/main/java/me/gilo/wc/viewmodels/ProductViewModel.java new file mode 100644 index 0000000..ce68eac --- /dev/null +++ b/app/src/main/java/me/gilo/wc/viewmodels/ProductViewModel.java @@ -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> products() { + return productRepository.products(); + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_shop.xml b/app/src/main/res/layout/activity_shop.xml index 7f1157a..d3bcd80 100644 --- a/app/src/main/res/layout/activity_shop.xml +++ b/app/src/main/res/layout/activity_shop.xml @@ -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"> + tools:context=".ui.product.ShopActivity"> > sales(); + + @GET("reports/sales") + Call> sales(@QueryMap Map filter); + + + @GET("reports/top_sellers") + Call> top_sellers(); + + @GET(" /wp-json/wc/v3/reports/top_sellers") + Call> top_sellers(@QueryMap Map filter); + + + @GET("reports/coupons/totals") + Call> coupons_totals(); + + + @GET("reports/customers/totals") + Call> customers_totals(); + + + @GET("reports/orders/totals") + Call> orders_totals(); + + @GET("reports/products/totals") + Call> products_totals(); + + @GET("reports/reviews/totals") + Call> reviews_totals(); + +} \ No newline at end of file diff --git a/woodroid/src/main/java/me/gilo/woodroid/models/filters/Filter.java b/woodroid/src/main/java/me/gilo/woodroid/models/filters/Filter.java new file mode 100644 index 0000000..426873b --- /dev/null +++ b/woodroid/src/main/java/me/gilo/woodroid/models/filters/Filter.java @@ -0,0 +1,29 @@ +package me.gilo.woodroid.models.filters; + +import java.util.HashMap; +import java.util.Map; + +public class Filter { + + String context; + + Map 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 getFilters() { + return filters; + } +} diff --git a/woodroid/src/main/java/me/gilo/woodroid/models/filters/OrderNoteFilter.java b/woodroid/src/main/java/me/gilo/woodroid/models/filters/OrderNoteFilter.java index fc78498..6536bfe 100644 --- a/woodroid/src/main/java/me/gilo/woodroid/models/filters/OrderNoteFilter.java +++ b/woodroid/src/main/java/me/gilo/woodroid/models/filters/OrderNoteFilter.java @@ -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 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 getFilters() { - return filters; - } } diff --git a/woodroid/src/main/java/me/gilo/woodroid/models/filters/ReportsDateFilter.java b/woodroid/src/main/java/me/gilo/woodroid/models/filters/ReportsDateFilter.java new file mode 100644 index 0000000..011e403 --- /dev/null +++ b/woodroid/src/main/java/me/gilo/woodroid/models/filters/ReportsDateFilter.java @@ -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); + } +} diff --git a/woodroid/src/main/java/me/gilo/woodroid/models/report/CouponsTotal.java b/woodroid/src/main/java/me/gilo/woodroid/models/report/CouponsTotal.java new file mode 100644 index 0000000..1592fc8 --- /dev/null +++ b/woodroid/src/main/java/me/gilo/woodroid/models/report/CouponsTotal.java @@ -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; + } +} diff --git a/woodroid/src/main/java/me/gilo/woodroid/models/report/CustomersTotal.java b/woodroid/src/main/java/me/gilo/woodroid/models/report/CustomersTotal.java new file mode 100644 index 0000000..a39032f --- /dev/null +++ b/woodroid/src/main/java/me/gilo/woodroid/models/report/CustomersTotal.java @@ -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; + } +} diff --git a/woodroid/src/main/java/me/gilo/woodroid/models/report/OrdersTotal.java b/woodroid/src/main/java/me/gilo/woodroid/models/report/OrdersTotal.java new file mode 100644 index 0000000..1e5b2f8 --- /dev/null +++ b/woodroid/src/main/java/me/gilo/woodroid/models/report/OrdersTotal.java @@ -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; + } +} diff --git a/woodroid/src/main/java/me/gilo/woodroid/models/report/ProductsTotal.java b/woodroid/src/main/java/me/gilo/woodroid/models/report/ProductsTotal.java new file mode 100644 index 0000000..7eb75a0 --- /dev/null +++ b/woodroid/src/main/java/me/gilo/woodroid/models/report/ProductsTotal.java @@ -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; + } +} diff --git a/woodroid/src/main/java/me/gilo/woodroid/models/report/ReviewsTotal.java b/woodroid/src/main/java/me/gilo/woodroid/models/report/ReviewsTotal.java new file mode 100644 index 0000000..b1612db --- /dev/null +++ b/woodroid/src/main/java/me/gilo/woodroid/models/report/ReviewsTotal.java @@ -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; + } +} diff --git a/woodroid/src/main/java/me/gilo/woodroid/models/report/SalesPeriodTotal.java b/woodroid/src/main/java/me/gilo/woodroid/models/report/SalesPeriodTotal.java new file mode 100644 index 0000000..8d0cc4c --- /dev/null +++ b/woodroid/src/main/java/me/gilo/woodroid/models/report/SalesPeriodTotal.java @@ -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; + +} diff --git a/woodroid/src/main/java/me/gilo/woodroid/models/report/SalesTotal.java b/woodroid/src/main/java/me/gilo/woodroid/models/report/SalesTotal.java new file mode 100644 index 0000000..b37f34d --- /dev/null +++ b/woodroid/src/main/java/me/gilo/woodroid/models/report/SalesTotal.java @@ -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 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 getTotals() { + return totals; + } + + public void setTotals(Map 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; + } +} diff --git a/woodroid/src/main/java/me/gilo/woodroid/models/report/TopSellerProducts.java b/woodroid/src/main/java/me/gilo/woodroid/models/report/TopSellerProducts.java new file mode 100644 index 0000000..1463277 --- /dev/null +++ b/woodroid/src/main/java/me/gilo/woodroid/models/report/TopSellerProducts.java @@ -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; + } +} diff --git a/woodroid/src/main/java/me/gilo/woodroid/repo/ReportsRepository.java b/woodroid/src/main/java/me/gilo/woodroid/repo/ReportsRepository.java new file mode 100644 index 0000000..3586056 --- /dev/null +++ b/woodroid/src/main/java/me/gilo/woodroid/repo/ReportsRepository.java @@ -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> sales() { + return apiService.sales(); + } + + public Call> sales(ReportsDateFilter reportsDateFilter) { + return apiService.sales(reportsDateFilter.getFilters()); + } + + public Call> top_sellers() { + return apiService.top_sellers(); + } + + public Call> top_sellers(ReportsDateFilter reportsDateFilter) { + return apiService.top_sellers(reportsDateFilter.getFilters()); + } + + public Call> coupons_totals() { + return apiService.coupons_totals(); + } + + public Call> customer_totals() { + return apiService.customers_totals(); + } + + public Call> order_totals() { + return apiService.orders_totals(); + } + + public Call> product_totals() { + return apiService.products_totals(); + } + + public Call> review_totals() { + return apiService.reviews_totals(); + } + +}