Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.6.10'
ext.kotlin_version = '1.6.21'

repositories {
google()
Expand Down
23 changes: 10 additions & 13 deletions core/src/main/java/io/snabble/sdk/Coupons.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import android.os.Looper
import android.os.Parcelable
import androidx.annotation.Keep
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.google.gson.GsonBuilder
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
Expand Down Expand Up @@ -72,8 +71,8 @@ enum class CouponSource {
class Coupons (
private val project: Project
) : Iterable<Coupon>, LiveData<List<Coupon>>() {
val source: LiveData<CouponSource> = MutableLiveData(CouponSource.Bundled)
val isLoading: LiveData<Boolean> = MutableLiveData(false)
val source: LiveData<CouponSource> = MutableAccessibleLiveData(CouponSource.Bundled)
val isLoading: LiveData<Boolean> = MutableAccessibleLiveData(false)

fun filter(type: CouponType): List<Coupon> =
value?.filter { it.type == type } ?: emptyList()
Expand All @@ -89,24 +88,22 @@ class Coupons (

fun update() {
if (isLoading.value == true) return
fun <T> LiveData<T>.setAsap(value: T) {
if (Looper.getMainLooper().thread.id == Thread.currentThread().id) {
(this as MutableLiveData<T>).value = value
} else {
(this as MutableLiveData<T>).postValue(value)
}

fun <T> LiveData<T>.set(value: T) {
(this as MutableAccessibleLiveData<T>).value = value
}

project.urls["coupons"]?.let { path ->
val couponsUrl = Snabble.absoluteUrl(path)
isLoading.setAsap(true)
isLoading.set(true)

val request = Request.Builder()
.url(couponsUrl)
.build()
val couponCall = project.okHttpClient.newCall(request)
couponCall.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
isLoading.setAsap(false)
isLoading.set(false)
}

override fun onResponse(call: Call, response: Response) {
Expand All @@ -116,9 +113,9 @@ class Coupons (
postValue(localizedResponse.coupons.filter { coupon ->
coupon.isValid
})
source.setAsap(CouponSource.Online)
source.set(CouponSource.Online)
}
isLoading.setAsap(false)
isLoading.set(false)
}
})
}
Expand Down
6 changes: 1 addition & 5 deletions core/src/main/java/io/snabble/sdk/OkHttpClientFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import okhttp3.Cache;
import okhttp3.CertificatePinner;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;

class OkHttpClientFactory {
private static final String[] PINS = new String[] {
Expand Down Expand Up @@ -37,10 +36,7 @@ static OkHttpClient createOkHttpClient(Application application) {
builder.retryOnConnectionFailure(true);
builder.pingInterval(5, TimeUnit.SECONDS); // workaround for https://github.com/square/okhttp/issues/3146

HttpLoggingInterceptor logging = new HttpLoggingInterceptor(Logger::i);

logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
builder.addInterceptor(logging);
builder.addInterceptor(new OkHttpLogger(Logger::i));

Config config = Snabble.getInstance().getConfig();
builder.addInterceptor(new UserAgentInterceptor(application));
Expand Down
54 changes: 54 additions & 0 deletions core/src/main/java/io/snabble/sdk/OkHttpLogger.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package io.snabble.sdk

import okhttp3.Interceptor
import okhttp3.Response
import okhttp3.logging.HttpLoggingInterceptor
import java.io.IOException
import java.util.concurrent.TimeUnit

class OkHttpLogger @JvmOverloads constructor(
private val logger: HttpLoggingInterceptor.Logger = HttpLoggingInterceptor.Logger.DEFAULT
) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()

val requestBody = request.body

val connection = chain.connection()
var requestStartMessage = "--> ${request.method} ${request.url}"
if (connection != null) {
requestStartMessage += " " + connection.protocol()
}
if (requestBody != null) {
requestStartMessage += " (${requestBody.contentLength()}-byte body)"
}
if (request.headers.size > 0) {
requestStartMessage += " with headers: " + request.headers.joinToString { it.first }
}
logger.log(requestStartMessage)

val startNs = System.nanoTime()
val response: Response
try {
response = chain.proceed(request)
} catch (e: Exception) {
logger.log("<-- HTTP FAILED: $e")
throw e
}

val tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)

val responseBody = response.body!!
val contentLength = responseBody.contentLength()
var bodySize = if (contentLength != -1L) "$contentLength-byte" else "unknown-length"
if (response.networkResponse == null) {
bodySize = "cache-hit, $bodySize"
} else if (response.cacheResponse != null) {
bodySize = "cache-miss, $bodySize"
}
logger.log("<-- ${response.code}${if (response.message.isEmpty()) "" else ' ' + response.message} ${response.request.url} (${tookMs}ms, $bodySize body)")

return response
}
}
37 changes: 20 additions & 17 deletions ui/src/main/java/io/snabble/sdk/ui/checkout/PaymentStatusView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@ import android.animation.LayoutTransition
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.activity.OnBackPressedCallback
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.widget.addTextChangedListener
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
Expand Down Expand Up @@ -67,7 +66,9 @@ class PaymentStatusView @JvmOverloads constructor(

private var isStopped: Boolean = false
private val project: Project
get() = requireNotNull(Snabble.checkedInProject.value)
private val checkout: Checkout
get() = project.checkout

private val backPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
Expand All @@ -79,16 +80,19 @@ class PaymentStatusView @JvmOverloads constructor(
private var lastState: CheckoutState? = null

private var paymentOriginCandidate: PaymentOriginCandidate? = null
private var paymentOriginCandidateHelper: PaymentOriginCandidateHelper
private val paymentOriginCandidateHelper by lazy { PaymentOriginCandidateHelper(project) }
private var ignoreStateChanges = false
private var hasShownSEPAInput = false

init {
clipChildren = false

project = requireNotNull(Snabble.checkedInProject.value)
checkout = project.checkout
paymentOriginCandidateHelper = PaymentOriginCandidateHelper(project)
if (!isInEditMode) {
initViews()
}
}

private fun initViews() {
paymentOriginCandidateHelper.addPaymentOriginCandidateAvailableListener(this)

back.isEnabled = false
Expand All @@ -101,6 +105,8 @@ class PaymentStatusView @JvmOverloads constructor(
if (state == CheckoutState.PAYMENT_APPROVED) {
executeUiAction(SnabbleUI.Event.SHOW_CHECKOUT_DONE)
}

project.coupons.update()
}

checkout.state.observeView(this) {
Expand All @@ -114,14 +120,9 @@ class PaymentStatusView @JvmOverloads constructor(
rating1.setOnClickListener {
ratingMessage = ""
inputBadRatingLayout.isVisible = true
inputBadRatingLayout.editText
?.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
override fun afterTextChanged(s: Editable) {
ratingMessage = s.toString()
}
})
inputBadRatingLayout.editText?.addTextChangedListener { s ->
ratingMessage = s.toString()
}
}

rating2.setOnClickListener {
Expand All @@ -135,10 +136,10 @@ class PaymentStatusView @JvmOverloads constructor(
addIbanLayout.isVisible = false
addIbanButton.setOnClickListener {
val paymentOriginCandidate = paymentOriginCandidate
val intent = Intent(getContext(), SEPACardInputActivity::class.java)
val intent = Intent(context, SEPACardInputActivity::class.java)
intent.putExtra(SEPACardInputActivity.ARG_PAYMENT_ORIGIN_CANDIDATE, paymentOriginCandidate)
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
getContext()?.startActivity(intent)
context?.startActivity(intent)
addIbanLayout.isVisible = false
hasShownSEPAInput = true
}
Expand Down Expand Up @@ -318,7 +319,9 @@ class PaymentStatusView @JvmOverloads constructor(
override fun onAttachedToWindow() {
super.onAttachedToWindow()

startPollingForPaymentOriginCandidate()
if (!isInEditMode) {
startPollingForPaymentOriginCandidate()
}
}

override fun onDetachedFromWindow() {
Expand Down
7 changes: 3 additions & 4 deletions ui/src/main/res/layout/snabble_fragment_payment_status.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<io.snabble.sdk.ui.checkout.PaymentStatusView xmlns:android="http://schemas.android.com/apk/res/android"
<io.snabble.sdk.ui.checkout.PaymentStatusView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="match_parent">

</io.snabble.sdk.ui.checkout.PaymentStatusView>
android:layout_height="match_parent"/>
56 changes: 45 additions & 11 deletions utils/src/main/java/io/snabble/sdk/utils/Dispatch.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
Expand All @@ -26,27 +27,60 @@ public static void setMainThreadHandler(MainThreadHandler handler) {
}

public static Future<?> background(Runnable runnable) {
return executorService.submit(runnable);
return executorService.submit(() -> {
try {
runnable.run();
} catch (Throwable t){
Log.e("Dispatch", "Crash on background thread", t);
throw t;
}
});
}

public static Future<?> io(Runnable runnable) {
return ioScheduler.submit(runnable);
return ioScheduler.submit(() -> {
try {
runnable.run();
} catch (Throwable t){
Log.e("Dispatch", "Crash on I/O thread", t);
throw t;
}
});
}

public static Future<?> background(Runnable runnable, long delayMs) {
return executorService.schedule(runnable, delayMs, TimeUnit.MILLISECONDS);
return executorService.schedule(() -> {
try {
runnable.run();
} catch (Throwable t){
Log.e("Dispatch", "Crash on background thread", t);
throw t;
}
}, delayMs, TimeUnit.MILLISECONDS);
}

public static void mainThread(Runnable runnable) {
if (Looper.myLooper() == Looper.getMainLooper()) {
runnable.run();
} else {
if (mainThreadHandler != null) {
mainThreadHandler.handle(runnable);
return;
}
try {
if (Looper.myLooper() == Looper.getMainLooper()) {
runnable.run();
} else {
if (mainThreadHandler != null) {
mainThreadHandler.handle(() -> {
try {
runnable.run();
} catch (Throwable t){
Log.e("Dispatch", "Crash on main thread", t);
throw t;
}
});
return;
}

handler.post(runnable);
handler.post(runnable);
}
} catch (Throwable t){
Log.e("Dispatch", "Crash on main thread", t);
throw t;
}
}

Expand Down