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
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.a494studios.koreanconjugator.settings;

import android.app.Activity;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.preference.Preference;

import com.a494studios.koreanconjugator.utils.Utils;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.PurchaseHistoryRecord;
import com.android.billingclient.api.PurchaseHistoryResponseListener;

import java.util.List;

public class AdFreeCheckListener implements PurchaseHistoryResponseListener {
Activity activity;
Preference preference;

public AdFreeCheckListener(Activity activity, Preference preference) {
this.activity = activity;
this.preference = preference;
}

@Override
public void onPurchaseHistoryResponse(@NonNull BillingResult result, @Nullable List<PurchaseHistoryRecord> list) {
String msg;
int responseCode = result.getResponseCode();
switch (responseCode) {
case BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED:
case BillingClient.BillingResponseCode.OK:
boolean isAdFree = list.get(0).getSkus().get(0).equals(Utils.SKU_AD_FREE);
Utils.setAdFree(activity, isAdFree);
if (isAdFree) {
msg = "Ad-free purchase activated, thank you for supporting Hanji!";
} else {
msg = "Ad-free purchase not found";
}
break;
case BillingClient.BillingResponseCode.BILLING_UNAVAILABLE:
case BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED:
msg = "You're device is not compatible. If you purchased an upgrade please contact support for a refund";
break;
case BillingClient.BillingResponseCode.ITEM_NOT_OWNED:
msg = "Ad-free purchase not found";
break;
case BillingClient.BillingResponseCode.SERVICE_DISCONNECTED:
case BillingClient.BillingResponseCode.SERVICE_TIMEOUT:
case BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE:
msg = "Unable to connect to Google Play store. Please try again later";
break;
default:
msg = "An error occurred. Please try again later";
break;
}

activity.runOnUiThread(() -> {
Toast.makeText(activity, msg, Toast.LENGTH_LONG).show();
preference.setSummary("Click here to check your ad-free status");
});
}
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,43 @@
package com.a494studios.koreanconjugator.settings;


import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceFragment;

import com.a494studios.koreanconjugator.CustomApplication;
import com.a494studios.koreanconjugator.R;
import com.a494studios.koreanconjugator.utils.Utils;
import com.android.billingclient.api.BillingClient;

/**
* A simple {@link PreferenceFragment} subclass.
*
*/
public class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);

Activity activity = getActivity();
Preference adFreeCheck = findPreference("adFreeCheck");
adFreeCheck.setOnPreferenceClickListener(preference -> {
adFreeCheck.setSummary("Checking ad-free status...");

AdFreeCheckListener listener = new AdFreeCheckListener(activity, adFreeCheck);
BillingClient client = CustomApplication.getBillingClient();
client.queryPurchaseHistoryAsync(BillingClient.SkuType.INAPP, listener);

return true;
});
}

@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
// Empty on purpose
// Empty on purpose
}

@Override
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/res/xml/preferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
<intent android:targetClass="com.a494studios.koreanconjugator.settings.FavoritesActivity"
android:targetPackage="com.a494studios.koreanconjugator"/>
</Preference>
<Preference
android:persistent="false"
android:key="adFreeCheck"
android:title="Check Ad-free Status"
android:summary="Click here to check your ad-free status"/>
</PreferenceCategory>
<PreferenceCategory android:title="Legal Information">
<Preference
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package com.a494studios.koreanconjugator.settings;


import android.preference.Preference;

import androidx.fragment.app.FragmentActivity;

import com.a494studios.koreanconjugator.utils.Utils;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.PurchaseHistoryRecord;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.shadows.ShadowToast;

import java.util.ArrayList;
import java.util.Collections;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@RunWith(RobolectricTestRunner.class)
public class AdFreeCheckListenerTest {
private FragmentActivity activity;
private Preference preference;
AdFreeCheckListener listener;

@Before
public void init() {
activity = Robolectric.buildActivity(FragmentActivity.class)
.create()
.start()
.resume()
.get();

preference = mock(Preference.class);
listener = new AdFreeCheckListener(activity, preference);
}

@Test
public void enablesAdFreeWhenOK() {
// Setup mock
PurchaseHistoryRecord record = mock(PurchaseHistoryRecord.class);
ArrayList<String> skus = new ArrayList<>(Collections.singletonList(Utils.SKU_AD_FREE));
when(record.getSkus()).thenReturn(skus);

String msg = "Ad-free purchase activated, thank you for supporting Hanji!";
testListener(BillingClient.BillingResponseCode.OK, msg, record);
assertTrue(Utils.isAdFree(activity));
}

@Test
public void disableWhenNotOwned() {
// Setup mock
PurchaseHistoryRecord record = mock(PurchaseHistoryRecord.class);
ArrayList<String> skus = new ArrayList<>(Collections.singletonList("foobar"));
when(record.getSkus()).thenReturn(skus);

String msg = "Ad-free purchase not found";
testListener(BillingClient.BillingResponseCode.OK, msg, record);
assertFalse(Utils.isAdFree(activity));
}

@Test
public void handlesNotOwned() {
String msg = "Ad-free purchase not found";
testListener(BillingClient.BillingResponseCode.ITEM_NOT_OWNED, msg);
assertNull(Utils.isAdFree(activity));
}

@Test
public void handlesIncompatableDevice() {
String msg = "You're device is not compatible. If you purchased an upgrade please contact support for a refund";
testListener(BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED, msg);
assertNull(Utils.isAdFree(activity));
}

@Test
public void handlesDisconnect() {
String msg = "Unable to connect to Google Play store. Please try again later";
testListener(BillingClient.BillingResponseCode.SERVICE_DISCONNECTED, msg);
assertNull(Utils.isAdFree(activity));
}

@Test
public void handlesError() {
String msg = "An error occurred. Please try again later";
testListener(BillingClient.BillingResponseCode.ERROR, msg);
assertNull(Utils.isAdFree(activity));
}

private void testListener(int code, String message) {
testListener(code, message, null);
}

private void testListener(int code, String message, PurchaseHistoryRecord record) {
// Setup mocks
BillingResult result = BillingResult
.newBuilder()
.setResponseCode(code)
.build();
if(record == null) {
record = mock(PurchaseHistoryRecord.class);
}
assertNull(Utils.isAdFree(activity));

listener.onPurchaseHistoryResponse(result, Collections.singletonList(record));

verify(preference, times(1)).setSummary("Click here to check your ad-free status");
assertTrue(ShadowToast.showedToast(message));
}
}