diff --git a/build.gradle b/build.gradle index 6761f15..e15b91d 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,8 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - + ext.kotlin_version = '1.2.50' + repositories { google() jcenter() @@ -37,6 +38,7 @@ buildscript { classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.8.2' classpath 'com.dicedmelon.gradle:jacoco-android:0.1.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/button-merchant/src/main/java/com/usebutton/merchant/ButtonInternalImpl.java b/button-merchant/src/main/java/com/usebutton/merchant/ButtonInternalImpl.java index 9d61168..40e5fa7 100644 --- a/button-merchant/src/main/java/com/usebutton/merchant/ButtonInternalImpl.java +++ b/button-merchant/src/main/java/com/usebutton/merchant/ButtonInternalImpl.java @@ -82,7 +82,7 @@ public void trackOrder(ButtonRepository buttonRepository, DeviceManager manager, @NonNull Order order, @Nullable final UserActivityListener listener) { if (buttonRepository.getApplicationId() == null) { if (listener != null) { - listener.onError(new ApplicationIdNotFoundException()); + listener.onResult(new ApplicationIdNotFoundException()); } return; @@ -93,14 +93,14 @@ public void trackOrder(ButtonRepository buttonRepository, DeviceManager manager, @Override public void onTaskComplete(@Nullable Object object) { if (listener != null) { - listener.onSuccess(); + listener.onResult(null); } } @Override public void onTaskError(Throwable throwable) { if (listener != null) { - listener.onError(throwable); + listener.onResult(throwable); } } }; @@ -137,12 +137,12 @@ public void handlePostInstallIntent(final ButtonRepository buttonRepository, DeviceManager deviceManager) { if (buttonRepository.getApplicationId() == null) { - listener.onNoPostInstallIntent(new ApplicationIdNotFoundException()); + listener.onResult(null, new ApplicationIdNotFoundException()); return; } if (deviceManager.isOldInstallation() || buttonRepository.checkedDeferredDeepLink()) { - listener.onNoPostInstallIntent(null); + listener.onResult(null, null); return; } @@ -162,16 +162,16 @@ public void onTaskComplete(@Nullable PostInstallLink postInstallLink) { setAttributionToken(buttonRepository, attribution.getBtnRef()); } - listener.onPostInstallIntent(deepLinkIntent); + listener.onResult(deepLinkIntent, null); return; } - listener.onNoPostInstallIntent(null); + listener.onResult(null, null); } @Override public void onTaskError(Throwable throwable) { - listener.onNoPostInstallIntent(throwable); + listener.onResult(null, throwable); } }, deviceManager); } diff --git a/button-merchant/src/main/java/com/usebutton/merchant/PostInstallIntentListener.java b/button-merchant/src/main/java/com/usebutton/merchant/PostInstallIntentListener.java index 87be077..d6f9d58 100644 --- a/button-merchant/src/main/java/com/usebutton/merchant/PostInstallIntentListener.java +++ b/button-merchant/src/main/java/com/usebutton/merchant/PostInstallIntentListener.java @@ -26,22 +26,22 @@ package com.usebutton.merchant; import android.content.Intent; -import android.support.annotation.NonNull; import android.support.annotation.Nullable; /** * Callbacks for post-install intent handler. */ public interface PostInstallIntentListener { - - /** - * A post-install url was found. You are responsible for navigating the user - * -to the appropriate content by starting the attached intent. - */ - void onPostInstallIntent(@NonNull Intent intent); - /** - * No post-install url was found. Continue with your normal launch sequence. + * This callback is used to notify the application that the request to check for a post-app + * install deep link is complete. If a deep link was found, it will be returned here. If an + * error was encountered while making the request, it will optionally be returned here. + * + * @param intent if a post-install link was found it will be returned here in the intent data. + * The intent can be started to navigate the user to the appropriate location in the app. If no + * post-install intent was found, this param will be {@code null}. + * @param t if an error was encountered while making the request it will be returned in this + * param, otherwise {@code null}. */ - void onNoPostInstallIntent(@Nullable Throwable t); + void onResult(@Nullable Intent intent, @Nullable Throwable t); } diff --git a/button-merchant/src/main/java/com/usebutton/merchant/UserActivityListener.java b/button-merchant/src/main/java/com/usebutton/merchant/UserActivityListener.java index 512a979..7f2ac03 100644 --- a/button-merchant/src/main/java/com/usebutton/merchant/UserActivityListener.java +++ b/button-merchant/src/main/java/com/usebutton/merchant/UserActivityListener.java @@ -33,7 +33,5 @@ * @see ButtonMerchant#trackOrder(android.content.Context, Order, UserActivityListener) */ public interface UserActivityListener { - void onSuccess(); - - void onError(@Nullable Throwable t); + void onResult(@Nullable Throwable t); } diff --git a/button-merchant/src/test/java/com/usebutton/merchant/ButtonInternalImplTest.java b/button-merchant/src/test/java/com/usebutton/merchant/ButtonInternalImplTest.java index 5ff1755..f426f90 100644 --- a/button-merchant/src/test/java/com/usebutton/merchant/ButtonInternalImplTest.java +++ b/button-merchant/src/test/java/com/usebutton/merchant/ButtonInternalImplTest.java @@ -121,7 +121,7 @@ public void trackOrder_nullApplicationId() { buttonInternal.trackOrder(buttonRepository, mock(DeviceManager.class), mock(Order.class), listener); - verify(listener).onError(any(ApplicationIdNotFoundException.class)); + verify(listener).onResult(any(ApplicationIdNotFoundException.class)); verify(buttonRepository, never()).postUserActivity(any(DeviceManager.class), any(Order.class), any(Task.Listener.class)); @@ -160,7 +160,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { buttonInternal.trackOrder(buttonRepository, mock(DeviceManager.class), mock(Order.class), listener); - verify(listener).onSuccess(); + verify(listener).onResult(null); } @Test @@ -184,7 +184,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { buttonInternal.trackOrder(buttonRepository, mock(DeviceManager.class), mock(Order.class), listener); - verify(listener).onError(throwable); + verify(listener).onResult(throwable); } @Test @@ -297,8 +297,7 @@ public void handlePostInstallIntent_returnValidPostInstallLink_setSourceToken() listenerArgumentCaptor.getValue().onTaskComplete(postInstallLink); - verify(postInstallIntentListener).onPostInstallIntent(any(Intent.class)); - verify(postInstallIntentListener, never()).onNoPostInstallIntent(any(Throwable.class)); + verify(postInstallIntentListener).onResult(any(Intent.class), (Throwable) isNull()); verify(buttonRepository).setSourceToken("valid_source_token"); } @@ -324,8 +323,7 @@ public void handlePostInstallIntent_returnInvalidPostInstallLink_doNotSetSourceT listenerArgumentCaptor.getValue().onTaskComplete(postInstallLink); - verify(postInstallIntentListener, never()).onPostInstallIntent(any(Intent.class)); - verify(postInstallIntentListener).onNoPostInstallIntent((Throwable) isNull()); + verify(postInstallIntentListener).onResult(null, null); verify(buttonRepository, never()).setSourceToken(anyString()); } @@ -343,9 +341,8 @@ public void handlePostInstallIntent_nullApplicationId_throwException() { verify(buttonRepository, never()).getPendingLink(any(Task.Listener.class), any(DeviceManager.class)); - verify(postInstallIntentListener, never()).onPostInstallIntent(any(Intent.class)); verify(buttonRepository, never()).setSourceToken(anyString()); - verify(postInstallIntentListener).onNoPostInstallIntent( + verify(postInstallIntentListener).onResult((Intent) isNull(), any(ApplicationIdNotFoundException.class)); } @@ -368,8 +365,8 @@ public void handlePostInstallIntent_throwButtonNetworkException() { listenerArgumentCaptor.getValue().onTaskError(new ButtonNetworkException("")); - verify(postInstallIntentListener).onNoPostInstallIntent(any(ButtonNetworkException.class)); - verify(postInstallIntentListener, never()).onPostInstallIntent(any(Intent.class)); + verify(postInstallIntentListener).onResult((Intent) isNull(), + any(ButtonNetworkException.class)); } @Test @@ -404,7 +401,7 @@ public void handlePostInstallIntent_oldInstallation_doNotUpdateCheckDeferredDeep "com.usebutton.merchant", deviceManager); - verify(postInstallIntentListener).onNoPostInstallIntent((Throwable) isNull()); + verify(postInstallIntentListener).onResult(null, null); verify(buttonRepository, never()).updateCheckDeferredDeepLink(anyBoolean()); } @@ -422,7 +419,7 @@ public void handlePostInstallIntent_checkedDeferredDeepLink_doNotUpdateCheckDefe "com.usebutton.merchant", deviceManager); - verify(postInstallIntentListener).onNoPostInstallIntent((Throwable) isNull()); + verify(postInstallIntentListener).onResult((Intent) isNull(), (Throwable) isNull()); verify(buttonRepository, never()).updateCheckDeferredDeepLink(anyBoolean()); } -} \ No newline at end of file +} diff --git a/sample/build.gradle b/sample/build.gradle index c6bfea0..3105200 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -24,6 +24,7 @@ */ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' def BUTTON_MERCHANT_API_KEY = hasProperty('buttonMerchantAppId') ? '"' + buttonMerchantAppId + '"' : '"app-1234567890abcdef"' @@ -59,6 +60,7 @@ dependencies { implementation "com.android.support:appcompat-v7:$supportLibVersion" implementation 'com.android.support.constraint:constraint-layout:1.1.0' implementation "com.android.support:design:$supportLibVersion" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" testImplementation "junit:junit:$junitVersion" androidTestImplementation "com.android.support.test:runner:$testRunnerVersion" androidTestImplementation "com.android.support.test.espresso:espresso-core:$espressoVersion" diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index 12285b7..06b40db 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -60,6 +60,11 @@ + + diff --git a/sample/src/main/java/com/usebutton/merchant/sample/KotlinActivity.kt b/sample/src/main/java/com/usebutton/merchant/sample/KotlinActivity.kt new file mode 100644 index 0000000..b5cd6b1 --- /dev/null +++ b/sample/src/main/java/com/usebutton/merchant/sample/KotlinActivity.kt @@ -0,0 +1,128 @@ +package com.usebutton.merchant.sample + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import android.support.v7.widget.Toolbar +import android.util.Log +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.widget.TextView +import android.widget.Toast +import com.usebutton.merchant.ButtonMerchant +import com.usebutton.merchant.Order.Builder +import com.usebutton.merchant.sample.R.id +import java.util.Random + +class KotlinActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + val toolbar = findViewById(R.id.toolbar) + setSupportActionBar(toolbar) + + ButtonMerchant.trackIncomingIntent(this, intent) + val token = ButtonMerchant.getAttributionToken(this) + Log.d(TAG, "Attribution Token is $token") + + val textView = findViewById(R.id.attribution_token) + textView.text = ButtonMerchant.getAttributionToken(this) + + checkForPostInstallIntent() + initTrackNewIntentButton() + initTackOrderButton() + initClearDataButton() + initAttributionTokenListener() + } + + private fun checkForPostInstallIntent() { + ButtonMerchant.handlePostInstallIntent(this) { intent, t -> + if (intent != null) { + startActivity(intent) + } else if (t != null) { + Log.e(TAG, "Error checking post install intent", t) + } + } + } + + private fun initTrackNewIntentButton() { + findViewById(id.track_new_intent).setOnClickListener { v -> + val intent = Intent() + intent.data = Uri.parse(TEST_URL + Random().nextInt(100000)) + ButtonMerchant.trackIncomingIntent(v.context, intent) + } + } + + private fun initTackOrderButton() { + findViewById(id.track_order).setOnClickListener { + val order = Builder("order-id-123") + .setAmount(8999) + .setCurrencyCode("USD") + .build() + ButtonMerchant.trackOrder(this, order) { t -> + if (t == null) { + toastify("Order track success") + } else { + toastify("Order track error") + } + } + } + } + + private fun initClearDataButton() { + findViewById(id.clear_all_data).setOnClickListener { + ButtonMerchant.clearAllData(this) + Log.d(TAG, "Cleared all data") + + val token = ButtonMerchant.getAttributionToken(this) + Log.d(TAG, "Attribution Token is $token") + + val textView = findViewById(id.attribution_token) + textView.text = ButtonMerchant.getAttributionToken(this) + } + } + + private fun initAttributionTokenListener() { + ButtonMerchant.addAttributionTokenListener(this) { token -> + runOnUiThread { + findViewById(id.attribution_token).text = token + } + } + } + + private fun toastify(message: String) { + runOnUiThread { + Toast.makeText(this@KotlinActivity, message, Toast.LENGTH_SHORT).show() + } + } + + companion object { + private const val TAG = "KotlinActivity" + private const val TEST_URL = "https://sample-merchant.usebutton.com/?btn_ref=srctok-test" + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.main_menu, menu) + val kotlinItem = menu.findItem(R.id.action_switch_kotlin) + val javaItem = menu.findItem(R.id.action_switch_java) + kotlinItem.isVisible = false + javaItem.isVisible = true + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.action_switch_kotlin -> { + } + R.id.action_switch_java -> { + val i = Intent(this@KotlinActivity, MainActivity::class.java) + finish() + startActivity(i) + } + } + return super.onOptionsItemSelected(item) + } +} diff --git a/sample/src/main/java/com/usebutton/merchant/sample/MainActivity.java b/sample/src/main/java/com/usebutton/merchant/sample/MainActivity.java index 62a6817..090024b 100644 --- a/sample/src/main/java/com/usebutton/merchant/sample/MainActivity.java +++ b/sample/src/main/java/com/usebutton/merchant/sample/MainActivity.java @@ -34,6 +34,8 @@ import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.widget.TextView; import android.widget.Toast; @@ -68,17 +70,13 @@ protected void onCreate(Bundle savedInstanceState) { ButtonMerchant.handlePostInstallIntent(this, new PostInstallIntentListener() { @Override - public void onPostInstallIntent(@NonNull Intent intent) { - if (intent.getData() != null) { - Log.d(TAG, "onPostInstallIntent: " + intent + " btn_ref: " - + intent.getData().getQueryParameter("btn_ref")); + public void onResult(@Nullable Intent intent, @Nullable Throwable t) { + if (intent != null) { + startActivity(intent); + } else if (t != null) { + Log.e(TAG, "Error checking post install intent", t); } } - - @Override - public void onNoPostInstallIntent(@Nullable Throwable t) { - Log.d(TAG, "onNoPostInstallIntent", t); - } }); final Context context = this; @@ -92,23 +90,17 @@ public void onClick(View v) { .build(); ButtonMerchant.trackOrder(context, order, new UserActivityListener() { @Override - public void onSuccess() { + public void onResult(@Nullable final Throwable t) { runOnUiThread(new Runnable() { @Override public void run() { - Toast.makeText(context, "Order track success", - Toast.LENGTH_SHORT).show(); - } - }); - } - - @Override - public void onError(@Nullable Throwable t) { - runOnUiThread(new Runnable() { - @Override - public void run() { - Toast.makeText(MainActivity.this, "Order track error", - Toast.LENGTH_SHORT).show(); + if (t == null) { + Toast.makeText(context, "Order track success", + Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(MainActivity.this, "Order track error", + Toast.LENGTH_SHORT).show(); + } } }); } @@ -156,4 +148,30 @@ public void run() { } }); } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main_menu, menu); + MenuItem javaItem = menu.findItem(R.id.action_switch_java); + MenuItem kotlinItem = menu.findItem(R.id.action_switch_kotlin); + javaItem.setVisible(false); + kotlinItem.setVisible(true); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_switch_java: + break; + case R.id.action_switch_kotlin: + Intent i = new Intent(MainActivity.this, KotlinActivity.class); + finish(); + startActivity(i); + break; + default: + break; + } + return super.onOptionsItemSelected(item); + } } diff --git a/sample/src/main/res/menu/main_menu.xml b/sample/src/main/res/menu/main_menu.xml new file mode 100644 index 0000000..5174efb --- /dev/null +++ b/sample/src/main/res/menu/main_menu.xml @@ -0,0 +1,39 @@ + + + + + + + \ No newline at end of file diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml index b13179d..57551df 100644 --- a/sample/src/main/res/values/strings.xml +++ b/sample/src/main/res/values/strings.xml @@ -26,4 +26,6 @@ Button Merchant Settings + Switch to Java + Switch to Kotlin