├── WaveExample
├── settings.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── app
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── values
│ │ │ │ │ ├── strings.xml
│ │ │ │ │ ├── colors.xml
│ │ │ │ │ ├── dimens.xml
│ │ │ │ │ └── styles.xml
│ │ │ │ ├── drawable-v21
│ │ │ │ │ └── selector_accent.xml
│ │ │ │ ├── values-w820dp
│ │ │ │ │ └── dimens.xml
│ │ │ │ ├── drawable
│ │ │ │ │ └── selector_accent.xml
│ │ │ │ └── layout
│ │ │ │ │ ├── act_project_list.xml
│ │ │ │ │ └── item_product.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── derekcsm
│ │ │ │ │ └── wave_example
│ │ │ │ │ ├── _base
│ │ │ │ │ └── Constants.java
│ │ │ │ │ ├── _api
│ │ │ │ │ ├── WaveApi.kt
│ │ │ │ │ ├── ApiBuilder.kt
│ │ │ │ │ └── OkhttpHeadersInterceptor.kt
│ │ │ │ │ ├── projectlist
│ │ │ │ │ ├── adapter
│ │ │ │ │ │ ├── ProductsAdapterItem.kt
│ │ │ │ │ │ ├── ProductViewHolder.kt
│ │ │ │ │ │ └── ProductsAdapter.kt
│ │ │ │ │ ├── ProductListContract.kt
│ │ │ │ │ ├── ProductListPresenter.kt
│ │ │ │ │ └── ProductListActivity.kt
│ │ │ │ │ └── _model
│ │ │ │ │ └── Product.kt
│ │ │ └── AndroidManifest.xml
│ │ ├── test
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── derekcsm
│ │ │ │ └── wave_example
│ │ │ │ └── ExampleUnitTest.java
│ │ └── androidTest
│ │ │ └── java
│ │ │ └── com
│ │ │ └── derekcsm
│ │ │ └── wave_example
│ │ │ └── ExampleInstrumentedTest.java
│ ├── proguard-rules.pro
│ └── build.gradle
├── build.gradle
├── gradle.properties
├── gradlew.bat
├── .gitignore
└── gradlew
└── README.md
/WaveExample/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/WaveExample/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derekcsm/mobile-challenge/master/WaveExample/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/WaveExample/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derekcsm/mobile-challenge/master/WaveExample/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/WaveExample/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derekcsm/mobile-challenge/master/WaveExample/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/WaveExample/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derekcsm/mobile-challenge/master/WaveExample/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/WaveExample/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derekcsm/mobile-challenge/master/WaveExample/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/WaveExample/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derekcsm/mobile-challenge/master/WaveExample/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/WaveExample/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Wave Example
3 |
4 | $%1$s
5 |
6 |
--------------------------------------------------------------------------------
/WaveExample/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/WaveExample/app/src/main/res/drawable-v21/selector_accent.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
--------------------------------------------------------------------------------
/WaveExample/app/src/main/kotlin/com/derekcsm/wave_example/_base/Constants.java:
--------------------------------------------------------------------------------
1 | package com.derekcsm.wave_example._base;
2 |
3 | public interface Constants {
4 |
5 | String endpoint = "api.waveapps.com";
6 | String ACCESS_TOKEN = "6W9hcvwRvyyZgPu9Odq7ko8DSY8Nfm";
7 | String BUSINESS_ID = "89746d57-c25f-4cec-9c63-34d7780b044b";
8 | }
9 |
--------------------------------------------------------------------------------
/WaveExample/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/WaveExample/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #37dcd6
4 | #008a94
5 | #eb9e34
6 |
7 | #56eb9e34
8 |
9 | #de000000
10 | #de000000
11 |
12 |
--------------------------------------------------------------------------------
/WaveExample/app/src/main/kotlin/com/derekcsm/wave_example/_api/WaveApi.kt:
--------------------------------------------------------------------------------
1 | package com.derekcsm.wave_example._api
2 |
3 | import com.derekcsm.wave_example._model.Product
4 | import retrofit2.http.GET
5 | import retrofit2.http.Path
6 | import rx.Observable
7 |
8 | interface WaveApi {
9 |
10 | @GET("businesses/{business_id}/products")
11 | fun getProducts(@Path("business_id") businessId: String): Observable>
12 | }
--------------------------------------------------------------------------------
/WaveExample/app/src/main/kotlin/com/derekcsm/wave_example/projectlist/adapter/ProductsAdapterItem.kt:
--------------------------------------------------------------------------------
1 | package com.derekcsm.wave_example.projectlist.adapter
2 |
3 | import android.support.annotation.IntDef
4 |
5 | class ProductsAdapterItem(var `object`: T?, @ProductsAdapterItem.ViewType var viewType: Int) {
6 |
7 | companion object {
8 | const val PRODUCT = 0.toLong()
9 | }
10 |
11 | @IntDef(PRODUCT)
12 | annotation class ViewType
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/WaveExample/app/src/main/kotlin/com/derekcsm/wave_example/_model/Product.kt:
--------------------------------------------------------------------------------
1 | package com.derekcsm.wave_example._model
2 |
3 | import com.squareup.moshi.Json
4 |
5 | data class Product(
6 | var id: Int = 0,
7 | var url: String,
8 | var name: String,
9 | var price: Float = 0.toFloat(),
10 | var description: String? = null,
11 | @Json(name = "is_active") var isActive: Boolean = false,
12 | @Json(name = "date_created") var dateCreated: String,
13 | @Json(name = "date_modified") var dateModified: String
14 | )
--------------------------------------------------------------------------------
/WaveExample/app/src/test/java/com/derekcsm/wave_example/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.derekcsm.wave_example;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/WaveExample/app/src/main/res/drawable/selector_accent.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/WaveExample/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
7 | 112sp
8 | 56sp
9 | 45sp
10 | 34sp
11 | 24sp
12 | 20sp
13 | 16sp
14 | 14sp
15 | 12sp
16 |
17 |
--------------------------------------------------------------------------------
/WaveExample/app/src/main/kotlin/com/derekcsm/wave_example/projectlist/ProductListContract.kt:
--------------------------------------------------------------------------------
1 | package com.derekcsm.wave_example.projectlist
2 |
3 | import android.content.Context
4 | import android.os.Bundle
5 | import com.derekcsm.wave_example.projectlist.adapter.ProductsAdapter
6 |
7 | interface ProductListContract {
8 |
9 | interface View {
10 | fun getProductsAdapter(): ProductsAdapter
11 |
12 | fun showLoading()
13 |
14 | fun hideLoading()
15 |
16 | fun getContext(): Context
17 | }
18 |
19 | interface UserActionsListener {
20 | fun KEY_SAVED_PRODUCTS() : String
21 |
22 | fun fetchProductsFromApi()
23 |
24 | fun restoreInstanceState(savedState: Bundle)
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/WaveExample/app/src/main/res/layout/act_project_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/WaveExample/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
13 |
14 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/WaveExample/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/derekcs/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/WaveExample/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.0.5-2'
3 | repositories {
4 | jcenter()
5 | }
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:2.2.3'
8 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
19 | }
20 | }
21 |
22 | task clean(type: Delete) {
23 | delete rootProject.buildDir
24 | }
25 |
26 | ext {
27 | androidSupportVersion = '24.2.1'
28 | }
29 |
--------------------------------------------------------------------------------
/WaveExample/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/WaveExample/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/WaveExample/app/src/main/res/layout/item_product.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
24 |
25 |
--------------------------------------------------------------------------------
/WaveExample/app/src/androidTest/java/com/derekcsm/wave_example/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.derekcsm.wave_example;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.derekcsm.wave_example", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/WaveExample/app/src/main/kotlin/com/derekcsm/wave_example/_api/ApiBuilder.kt:
--------------------------------------------------------------------------------
1 | package com.derekcsm.wave_example._api
2 |
3 | import com.derekcsm.wave_example._base.Constants
4 | import okhttp3.HttpUrl
5 | import okhttp3.OkHttpClient
6 | import retrofit2.Retrofit
7 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory
8 | import retrofit2.converter.moshi.MoshiConverterFactory
9 |
10 | class ApiBuilder {
11 |
12 | fun create(): WaveApi {
13 |
14 | var okHttpClientBuilder: OkHttpClient.Builder = OkHttpClient.Builder()
15 | okHttpClientBuilder.addInterceptor(OkHttpHeadersInterceptor())
16 | var okHttpClient = okHttpClientBuilder.build()
17 |
18 | val retrofit = Retrofit.Builder()
19 | .baseUrl(getBaseUrl())
20 | .client(okHttpClient)
21 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
22 | .addConverterFactory(MoshiConverterFactory.create())
23 | .build()
24 |
25 | return retrofit.create(WaveApi::class.java)
26 | }
27 |
28 | private fun getBaseUrl(): HttpUrl {
29 | return HttpUrl.Builder()
30 | .scheme("https")
31 | .host(Constants.endpoint)
32 | .build()
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/WaveExample/app/src/main/kotlin/com/derekcsm/wave_example/projectlist/adapter/ProductViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.derekcsm.wave_example.projectlist.adapter
2 |
3 | import android.content.Context
4 | import android.support.v7.widget.RecyclerView
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import android.widget.TextView
9 | import butterknife.bindView
10 | import com.derekcsm.wave_example.R
11 | import com.derekcsm.wave_example._model.Product
12 | import java.text.NumberFormat
13 |
14 | class ProductViewHolder private constructor(itemView: View) : RecyclerView.ViewHolder(itemView) {
15 |
16 | val tvTitle: TextView by bindView(R.id.tv_product_title)
17 | val tvPrice: TextView by bindView(R.id.tv_price)
18 |
19 | init {
20 | }
21 |
22 | fun onBind(item: ProductsAdapterItem, listener: ProductsAdapter.ClickListener) {
23 | val product = item.`object` as Product
24 |
25 | tvTitle.text = product.name
26 |
27 | val formatter = NumberFormat.getNumberInstance()
28 | formatter.minimumFractionDigits = 2
29 | formatter.maximumFractionDigits = 2
30 | val formattedPrice = formatter.format(product.price)
31 | tvPrice.text = itemView.context.getString(R.string.price, formattedPrice)
32 |
33 | itemView.setOnClickListener { listener.onProductClicked(item) }
34 | }
35 |
36 | companion object {
37 | fun create(context: Context, viewGroup: ViewGroup): ProductViewHolder {
38 | return ProductViewHolder(LayoutInflater.from(context).inflate(R.layout.item_product, viewGroup, false))
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/WaveExample/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 |
4 | android {
5 | compileSdkVersion 24
6 | buildToolsVersion '23.0.3'
7 | defaultConfig {
8 | applicationId 'com.derekcsm.wave_example'
9 | minSdkVersion 19
10 | targetSdkVersion 24
11 | versionCode 1
12 | versionName '1.0'
13 | testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
14 | }
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 | sourceSets {
22 | main.java.srcDirs += 'src/main/kotlin'
23 | }
24 | }
25 |
26 | dependencies {
27 | compile fileTree(dir: 'libs', include: ['*.jar'])
28 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
29 | exclude group: 'com.android.support', module: 'support-annotations'
30 | })
31 |
32 | // Kotlin
33 | compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
34 |
35 | // RX
36 | compile 'io.reactivex:rxandroid:1.2.1'
37 | compile 'io.reactivex:rxjava:1.1.5'
38 |
39 | // Google Android / Support / Niceties
40 | compile "com.android.support:appcompat-v7:$androidSupportVersion"
41 | compile "com.android.support:support-v4:$androidSupportVersion"
42 | compile "com.android.support:recyclerview-v7:$androidSupportVersion"
43 | compile 'com.jakewharton:butterknife:7.0.1'
44 | compile 'com.jakewharton:kotterknife:0.1.0-SNAPSHOT'
45 |
46 | // Networking
47 | compile 'com.squareup.okhttp3:okhttp:3.5.0'
48 | compile 'com.squareup.retrofit2:retrofit:2.1.0'
49 | compile 'com.squareup.retrofit2:converter-moshi:2.1.0'
50 | compile "com.squareup.retrofit2:adapter-rxjava:2.1.0"
51 |
52 | // JSON
53 | compile 'com.squareup.moshi:moshi:1.2.0'
54 |
55 | // Unit testing
56 | testCompile 'junit:junit:4.12'
57 | }
58 | repositories {
59 | mavenCentral()
60 | }
61 |
--------------------------------------------------------------------------------
/WaveExample/app/src/main/kotlin/com/derekcsm/wave_example/_api/OkhttpHeadersInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.derekcsm.wave_example._api
2 |
3 | import android.util.Log
4 | import com.derekcsm.wave_example._base.Constants
5 | import okhttp3.Interceptor
6 | import okhttp3.Response
7 | import java.io.IOException
8 |
9 | class OkHttpHeadersInterceptor constructor() : Interceptor {
10 | private val TAG = "OkHttpHeadersIntercept"
11 |
12 | @Throws(IOException::class)
13 | override fun intercept(chain: Interceptor.Chain): Response {
14 | val builder = chain.request().newBuilder()
15 | builder.addHeader("Connection", "Keep-Alive")
16 | builder.addHeader("Accept-Charset", "UTF-8")
17 | if (chain.request().header("Accept") == null)
18 | builder.addHeader("Accept", "application/json")
19 | builder.addHeader("Content-Type", "application/json")
20 |
21 | // add authorization header to request and proceed
22 | builder.addHeader("Authorization", "Bearer " + Constants.ACCESS_TOKEN)
23 | var response = chain.proceed(builder.build())
24 |
25 | // -- TODO
26 | // -- everything below this point is not doing anything
27 | // -- if I were to implement Oauth2 correctly we'd have refresh/proceed setup
28 |
29 | Log.d(TAG, "intercept: response=" + response.code() + " url=" + response.request().url().toString())
30 | if ((response.code() == 401) and !response.request().url().toString().contains("/oauth/token")) {
31 | //response = chain.proceed(retryOAuthAuthentication(builder))
32 | }
33 |
34 | // Hypothetically speaking if after retrying auth, still 401 then notify unauthorized
35 | if ((response.code() == 401) and response.request().url().toString().contains("oauth/token")) {
36 | Log.d(TAG, "intercept NOTIFY UNAUTHORIZED: response=" + response.code()
37 | + " url=" + response.request().url().toString())
38 | }
39 | return response
40 | }
41 |
42 | private fun retryOAuthAuthentication() {
43 | // in a perfect world this should refresh the auth token but I just don't have time in this example
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/WaveExample/app/src/main/kotlin/com/derekcsm/wave_example/projectlist/adapter/ProductsAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.derekcsm.wave_example.projectlist.adapter
2 |
3 | import android.support.v7.widget.RecyclerView
4 | import android.view.ViewGroup
5 | import com.derekcsm.wave_example._model.Product
6 | import java.util.*
7 |
8 | class ProductsAdapter(private val listener: ProductsAdapter.ClickListener) :
9 | RecyclerView.Adapter() {
10 |
11 | private var productsAdapterItems = ArrayList>()
12 |
13 | init {
14 | }
15 |
16 | fun addItems(ProductsAdapterItems: ArrayList>) {
17 | this.productsAdapterItems = ProductsAdapterItems
18 | }
19 |
20 | fun getItems(): ArrayList> {
21 | return productsAdapterItems
22 | }
23 |
24 | override fun getItemCount(): Int {
25 | return productsAdapterItems.size
26 | }
27 |
28 | override fun getItemViewType(position: Int): Int {
29 | return productsAdapterItems[position].viewType
30 | }
31 |
32 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
33 | when (viewType.toLong()) {
34 | ProductsAdapterItem.PRODUCT -> return ProductViewHolder.create(parent.context, parent)
35 | else -> throw IllegalStateException("Incorrect view type: " + viewType)
36 | }
37 | }
38 |
39 | @Suppress("UNCHECKED_CAST")
40 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
41 | val currentItem = productsAdapterItems[position]
42 |
43 | when (holder.itemViewType.toLong()) {
44 | ProductsAdapterItem.PRODUCT -> (holder as ProductViewHolder).onBind(
45 | currentItem as ProductsAdapterItem, listener)
46 | else -> throw IllegalStateException("Incorrect view type: " + holder.itemViewType)
47 | }
48 | }
49 |
50 | override fun onAttachedToRecyclerView(recyclerView: RecyclerView?) {
51 | super.onAttachedToRecyclerView(recyclerView)
52 | }
53 |
54 | interface ClickListener {
55 | fun onProductClicked(item: ProductsAdapterItem)
56 | }
57 | }
--------------------------------------------------------------------------------
/WaveExample/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/WaveExample/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io
2 |
3 | ### Windows ###
4 | # Windows image file caches
5 | Thumbs.db
6 | ehthumbs.db
7 |
8 | # Folder config file
9 | Desktop.ini
10 |
11 | # Recycle Bin used on file shares
12 | $RECYCLE.BIN/
13 |
14 | # Windows Installer files
15 | *.cab
16 | *.msi
17 | *.msm
18 | *.msp
19 |
20 | # Windows shortcuts
21 | *.lnk
22 |
23 |
24 | ### OSX ###
25 | .DS_Store
26 | .AppleDouble
27 | .LSOverride
28 |
29 | # Icon must end with two \r
30 | Icon
31 |
32 |
33 | # Thumbnails
34 | ._*
35 |
36 | # Files that might appear in the root of a volume
37 | .DocumentRevisions-V100
38 | .fseventsd
39 | .Spotlight-V100
40 | .TemporaryItems
41 | .Trashes
42 | .VolumeIcon.icns
43 |
44 | # Directories potentially created on remote AFP share
45 | .AppleDB
46 | .AppleDesktop
47 | Network Trash Folder
48 | Temporary Items
49 | .apdisk
50 |
51 |
52 | ### Linux ###
53 | *~
54 |
55 | # KDE directory preferences
56 | .directory
57 |
58 | # Linux trash folder which might appear on any partition or disk
59 | .Trash-*
60 |
61 |
62 | ### Intellij ###
63 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
64 |
65 | *.iml
66 |
67 | ## Directory-based project format:
68 | .idea/
69 | # if you remove the above rule, at least ignore the following:
70 |
71 | # User-specific stuff:
72 | # .idea/workspace.xml
73 | # .idea/tasks.xml
74 | # .idea/dictionaries
75 |
76 | # Sensitive or high-churn files:
77 | # .idea/dataSources.ids
78 | # .idea/dataSources.xml
79 | # .idea/sqlDataSources.xml
80 | # .idea/dynamic.xml
81 | # .idea/uiDesigner.xml
82 |
83 | # Gradle:
84 | # .idea/gradle.xml
85 | # .idea/libraries
86 |
87 | # Mongo Explorer plugin:
88 | # .idea/mongoSettings.xml
89 |
90 | ## File-based project format:
91 | *.ipr
92 | *.iws
93 |
94 | ## Plugin-specific files:
95 |
96 | # IntelliJ
97 | /out/
98 |
99 | # mpeltonen/sbt-idea plugin
100 | .idea_modules/
101 |
102 | # JIRA plugin
103 | atlassian-ide-plugin.xml
104 |
105 | # Crashlytics plugin (for Android Studio and IntelliJ)
106 | com_crashlytics_export_strings.xml
107 | crashlytics.properties
108 | crashlytics-build.properties
109 |
110 |
111 | ### Android ###
112 | # Built application files
113 | *.apk
114 | *.ap_
115 |
116 | # Files for the Dalvik VM
117 | *.dex
118 |
119 | # Java class files
120 | *.class
121 |
122 | # Generated files
123 | bin/
124 | gen/
125 |
126 | # Gradle files
127 | .gradle/
128 | build/
129 | /*/build/
130 |
131 | # Local configuration file (sdk path, etc)
132 | local.properties
133 |
134 | # Proguard folder generated by Eclipse
135 | proguard/
136 |
137 | # Log Files
138 | *.log
139 |
140 | ### Android Patch ###
141 | gen-external-apklibs
--------------------------------------------------------------------------------
/WaveExample/app/src/main/kotlin/com/derekcsm/wave_example/projectlist/ProductListPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.derekcsm.wave_example.projectlist
2 |
3 | import android.os.Bundle
4 | import android.util.Log
5 | import android.widget.Toast
6 | import com.derekcsm.wave_example._api.ApiBuilder
7 | import com.derekcsm.wave_example._api.WaveApi
8 | import com.derekcsm.wave_example._base.Constants
9 | import com.derekcsm.wave_example._model.Product
10 | import com.derekcsm.wave_example.projectlist.adapter.ProductsAdapterItem
11 | import com.squareup.moshi.Moshi
12 | import rx.Subscriber
13 | import rx.android.schedulers.AndroidSchedulers
14 | import rx.schedulers.Schedulers
15 | import java.util.*
16 |
17 | class ProductListPresenter constructor(mView: ProductListContract.View) :
18 | ProductListContract.UserActionsListener {
19 |
20 | private val TAG = "ProductListPresenter"
21 | private val KEY_SAVED_PRODUCTS = "saved_products"
22 | override fun KEY_SAVED_PRODUCTS(): String {
23 | return KEY_SAVED_PRODUCTS
24 | }
25 | private val mView: ProductListContract.View
26 | private val waveApi: WaveApi
27 |
28 | init {
29 | this.mView = mView
30 | waveApi = ApiBuilder().create()
31 | }
32 |
33 | override fun restoreInstanceState(savedState: Bundle) {
34 | var productsAdapterItems = ArrayList>()
35 | val stringProducts = savedState.getStringArrayList(KEY_SAVED_PRODUCTS)
36 |
37 | val moshi: Moshi = Moshi.Builder().build()
38 | val jsonAdapter = moshi.adapter(Product::class.java)
39 |
40 | var i = 0
41 | while (i < stringProducts.size) {
42 |
43 | val nProduct = jsonAdapter.fromJson(stringProducts.get(i))
44 |
45 | val productAdapterItem = ProductsAdapterItem(nProduct,
46 | ProductsAdapterItem.PRODUCT.toInt())
47 | productsAdapterItems.add(productAdapterItem)
48 | i++
49 | }
50 |
51 | mView.getProductsAdapter().addItems(productsAdapterItems)
52 | mView.getProductsAdapter().notifyDataSetChanged()
53 | }
54 |
55 | override fun fetchProductsFromApi() {
56 | waveApi.getProducts(Constants.BUSINESS_ID)
57 | .subscribeOn(Schedulers.io())
58 | .observeOn(AndroidSchedulers.mainThread())
59 | .subscribe(object : Subscriber>() {
60 | override fun onCompleted() {
61 | Log.d(TAG, "onCompleted ")
62 | mView.hideLoading()
63 | }
64 |
65 | override fun onError(e: Throwable) {
66 | Log.d(TAG, "onError " + e.message)
67 | Toast.makeText(mView.getContext(), "Error loading: " + e.message, Toast.LENGTH_LONG).show()
68 | mView.hideLoading()
69 | }
70 |
71 | override fun onNext(products: List?) {
72 | Log.d(TAG, "onNext " + products)
73 | if (products != null) {
74 | populateAdapter(products)
75 | }
76 | }
77 | })
78 | }
79 |
80 | private fun populateAdapter(products: List) {
81 |
82 | val productsAdapterItems = ArrayList>()
83 |
84 | var i = 0
85 | while (i < products.size) {
86 | val productAdapterItem = ProductsAdapterItem(products.get(i),
87 | ProductsAdapterItem.PRODUCT.toInt())
88 | productsAdapterItems.add(productAdapterItem)
89 | i++
90 | }
91 |
92 | mView.getProductsAdapter().addItems(productsAdapterItems)
93 | mView.getProductsAdapter().notifyDataSetChanged()
94 | }
95 |
96 | }
--------------------------------------------------------------------------------
/WaveExample/app/src/main/kotlin/com/derekcsm/wave_example/projectlist/ProductListActivity.kt:
--------------------------------------------------------------------------------
1 | package com.derekcsm.wave_example.projectlist
2 |
3 | import android.content.Context
4 | import android.os.Bundle
5 | import android.support.v4.widget.SwipeRefreshLayout
6 | import android.support.v7.app.AppCompatActivity
7 | import android.support.v7.widget.LinearLayoutManager
8 | import android.support.v7.widget.RecyclerView
9 | import android.widget.Toast
10 | import butterknife.bindView
11 | import com.derekcsm.wave_example.R
12 | import com.derekcsm.wave_example._model.Product
13 | import com.derekcsm.wave_example.projectlist.adapter.ProductsAdapter
14 | import com.derekcsm.wave_example.projectlist.adapter.ProductsAdapterItem
15 | import com.squareup.moshi.Moshi
16 | import java.util.*
17 |
18 |
19 | class ProductListActivity : AppCompatActivity(), ProductListContract.View,
20 | ProductsAdapter.ClickListener, SwipeRefreshLayout.OnRefreshListener {
21 |
22 | private val TAG = "ProductListActivity"
23 | private lateinit var mActionsListener: ProductListContract.UserActionsListener
24 |
25 | val rvProductList: RecyclerView by bindView(R.id.rv_product_list)
26 | val swipeRefreshLayout: SwipeRefreshLayout by bindView(R.id.swiperefresh_product)
27 |
28 | private lateinit var mProductsAdapter: ProductsAdapter
29 | override fun getProductsAdapter(): ProductsAdapter {
30 | return mProductsAdapter
31 | }
32 |
33 | override fun onCreate(savedState: Bundle?) {
34 | super.onCreate(savedState)
35 | setContentView(R.layout.act_project_list)
36 |
37 | mActionsListener = ProductListPresenter(this)
38 |
39 | setupProductAdapter()
40 | swipeRefreshLayout.setOnRefreshListener(this)
41 | swipeRefreshLayout.setColorSchemeResources(R.color.colorAccent, R.color.colorPrimary)
42 |
43 | if (savedState == null) {
44 | showLoading()
45 | mActionsListener.fetchProductsFromApi()
46 | } else if (savedState.containsKey(mActionsListener.KEY_SAVED_PRODUCTS())) {
47 | // restore state
48 | mActionsListener.restoreInstanceState(savedState)
49 |
50 | } else {
51 | showLoading()
52 | mActionsListener.fetchProductsFromApi()
53 | }
54 | }
55 |
56 | override fun onSaveInstanceState(outState: Bundle?) {
57 | val stringProducts = ArrayList()
58 |
59 | val moshi: Moshi = Moshi.Builder().build()
60 | val jsonAdapter = moshi.adapter(Product::class.java)
61 |
62 | var i = 0
63 | while (i < mProductsAdapter.getItems().size) {
64 | stringProducts.add(jsonAdapter.toJson(mProductsAdapter.getItems().get(i).`object` as Product))
65 | i++
66 | }
67 |
68 | outState!!.putStringArrayList(mActionsListener.KEY_SAVED_PRODUCTS(), stringProducts)
69 | super.onSaveInstanceState(outState)
70 | }
71 |
72 | private fun setupProductAdapter() {
73 | mProductsAdapter = ProductsAdapter(this)
74 | rvProductList.layoutManager = LinearLayoutManager(this)
75 | rvProductList.adapter = mProductsAdapter
76 | }
77 |
78 | override fun onRefresh() {
79 | showLoading()
80 | mActionsListener.fetchProductsFromApi()
81 | }
82 |
83 | override fun showLoading() {
84 | swipeRefreshLayout.isRefreshing = true
85 | }
86 |
87 | override fun hideLoading() {
88 | swipeRefreshLayout.isRefreshing = false
89 | }
90 |
91 | override fun onProductClicked(item: ProductsAdapterItem) {
92 | val product = item.`object` as Product
93 | Toast.makeText(this, product.name, Toast.LENGTH_SHORT).show()
94 | }
95 |
96 | override fun getContext(): Context {
97 | return this
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Built by @derekcsm
2 |
3 | Might have got a bit carried away, but hey - this was actually pretty fun and it took around 4ish hours in total.
4 |
5 | The idea here was to build out a really clean app that lives within Android gracefully and feels polished to a decent degree. I built the app using Kotlin with a bunch of nice helper libraries like RXJava to keep things clear and composable. The network request is made through an extensible Retrofit API interface that uses and OkHttp Interceptor to add auth headers to all requests. The Activity uses an MVP pattern, and saves instance state using Moshi Json serializations through a String ArrayList stored in the outState bundle. Errors will display as toast messages, and you can even pull to referesh!
6 |
7 | > Should be noted that proper Oauth2 would implement token refreshing, I've added some notes to the OkHttpHeadersInterceptor class
8 |
9 | ---
10 | # Wave Software Development Challenge
11 | Applicants for the [Mobile engineer](https://wave.bamboohr.co.uk/jobs/view.php?id=6) role at Wave must complete the following challenge, and submit a solution prior to the onsite interview.
12 |
13 | The purpose of this exercise is to create something that we can work on together during the onsite. We do this so that you get a chance to collaborate with Wavers during the interview in a situation where you know something better than us (it's your code, after all!)
14 |
15 | There isn't a hard deadline for this exercise; take as long as you need to complete it. However, in terms of total time spent actively working on the challenge, we ask that you not spend more than a few hours, as we value your time and are happy to leave things open to discussion in the onsite interview.
16 |
17 | You can write your app using your favorite language, tools, platform, etc. Whether that means something native or something hybrid is completely up to you.
18 |
19 | Send your submission to [dev.careers@waveapps.com](dev.careers@waveapps.com). Feel free to email [dev.careers@waveapps.com](dev.careers@waveapps.com) if you have any questions.
20 |
21 | ## Submission Instructions
22 | 1. Fork this project on github. You will need to create an account if you don't already have one.
23 | 1. Complete the project as described below within your fork.
24 | 1. Push all of your changes to your fork on github and submit a pull request.
25 | 1. You should also email [dev.careers@waveapps.com](dev.careers@waveapps.com) and your recruiter to let them know you have submitted a solution. Make sure to include your github username in your email (so we can match applicants with pull requests.)
26 |
27 | ## Alternate Submission Instructions (if you don't want to publicize completing the challenge)
28 | 1. Clone the repository.
29 | 1. Complete your project as described below within your local repository.
30 | 1. Email a patch file to [dev.careers@waveapps.com](dev.careers@waveapps.com).
31 |
32 | ## Project Description
33 | In this project, we're going to be creating a simple app that shows a Wave user the products that they can charge for on their invoices.
34 |
35 | You'll be using the public Wave API in this challenge. You can find the documentation [here](http://docs.waveapps.io/). You will specifically be interested in [the products endpoint](http://docs.waveapps.io/endpoints/products.html#get--businesses-business_id-products-), and [using an access token with the API](http://docs.waveapps.io/oauth/index.html#use-the-access-token-to-access-the-api).
36 |
37 | Your Wave contact will supply you with a business ID and a Wave API token before you begin.
38 |
39 | ### What your application must do:
40 |
41 | 1. Your app must retrieve the list of products for the specific business ID sent to you by your Wave contact
42 | 1. The list of products should be fetched and shown to the user in a list view when the app is launched.
43 | 1. Each item in the list view should show the product name and price (formatted as a dollar amount.)
44 |
45 | You are not required to add any interactivity to the app -- i.e. you do not need to send the user to a detail view when they touch one of the list items.
46 |
47 | Your app is allowed to render nothing if there is no internet connection when it loads.
48 |
49 | Once you're done, please submit a paragraph or two in your `README` about what you are particularly proud of in your implementation, and why.
50 |
51 | ## Evaluation
52 | Evaluation of your submission will be based on the following criteria.
53 |
54 | 1. Did your application fulfill the basic requirements?
55 | 1. Did you document the method for setting up and running your application?
56 | 1. Did you follow the instructions for submission?
57 |
--------------------------------------------------------------------------------
/WaveExample/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------