├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── app ├── build.gradle ├── misc │ └── res │ │ ├── feature1024x500.png │ │ ├── logo.png │ │ ├── logo256x256.png │ │ ├── logo512x512.png │ │ └── logo512x512.xcf ├── proguard-rules.txt └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── org │ │ └── solovyev │ │ └── android │ │ └── checkout │ │ └── app │ │ ├── BannerActivity.java │ │ ├── BuyConsumeFragmentActivity.java │ │ ├── CheckoutApplication.java │ │ ├── Encryption.java │ │ ├── HistoryActivity.java │ │ ├── MainActivity.java │ │ ├── SkusActivity.java │ │ ├── StaticActivity.java │ │ └── SubscriptionsActivity.java │ └── res │ ├── drawable-hdpi │ └── ic_launcher.png │ ├── drawable-mdpi │ └── ic_launcher.png │ ├── drawable-xhdpi │ └── ic_launcher.png │ ├── drawable-xxhdpi │ ├── ic_launcher.png │ └── top_shadow.9.png │ ├── layout │ ├── activity_banner.xml │ ├── activity_fragment.xml │ ├── activity_history.xml │ ├── activity_main.xml │ ├── activity_skus.xml │ ├── activity_static.xml │ ├── activity_subscriptions.xml │ ├── fragment_buy_consume.xml │ ├── purchase.xml │ ├── sku.xml │ ├── subscription.xml │ └── use_case.xml │ ├── menu │ └── banner.xml │ ├── values-ru │ └── strings.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── lib ├── build.gradle ├── lint.xml ├── proguard-rules.txt └── src │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ ├── com │ │ ├── android │ │ │ └── vending │ │ │ │ └── billing │ │ │ │ ├── InAppBillingService.java │ │ │ │ └── InAppBillingServiceImpl.java │ │ └── google │ │ │ └── android │ │ │ └── gms │ │ │ └── internal │ │ │ └── play_billing │ │ │ └── InAppBillingServiceFactory.java │ │ └── org │ │ └── solovyev │ │ └── android │ │ └── checkout │ │ ├── ActivityCheckout.java │ │ ├── Base64.java │ │ ├── Base64DecoderException.java │ │ ├── BaseInventory.java │ │ ├── BasePurchaseVerifier.java │ │ ├── BasePurchasesRequest.java │ │ ├── Billing.java │ │ ├── BillingException.java │ │ ├── BillingRequests.java │ │ ├── BillingSupportedRequest.java │ │ ├── Cache.java │ │ ├── CancellableExecutor.java │ │ ├── CancellableRequestListener.java │ │ ├── ChangePurchaseRequest.java │ │ ├── Check.java │ │ ├── Checkout.java │ │ ├── CheckoutInventory.java │ │ ├── ConcurrentCache.java │ │ ├── ConsumePurchaseRequest.java │ │ ├── CustomUiCheckout.java │ │ ├── DefaultLogger.java │ │ ├── DefaultPurchaseVerifier.java │ │ ├── EmptyLogger.java │ │ ├── EmptyRequestListener.java │ │ ├── FallingBackInventory.java │ │ ├── FragmentCheckout.java │ │ ├── GetPurchaseHistoryRequest.java │ │ ├── GetPurchasesRequest.java │ │ ├── GetSkuDetailsRequest.java │ │ ├── IntentStarter.java │ │ ├── Inventory.java │ │ ├── Logger.java │ │ ├── MainThread.java │ │ ├── MainThreadLogger.java │ │ ├── MainThreadRequestListener.java │ │ ├── MapCache.java │ │ ├── PendingRequests.java │ │ ├── PlayStoreBroadcastReceiver.java │ │ ├── PlayStoreListener.java │ │ ├── ProductTypes.java │ │ ├── Purchase.java │ │ ├── PurchaseComparator.java │ │ ├── PurchaseFlow.java │ │ ├── PurchaseRequest.java │ │ ├── PurchaseVerifier.java │ │ ├── Purchases.java │ │ ├── Request.java │ │ ├── RequestException.java │ │ ├── RequestListener.java │ │ ├── RequestListenerWrapper.java │ │ ├── RequestRunnable.java │ │ ├── RequestType.java │ │ ├── ResponseCodes.java │ │ ├── RobotmediaDatabase.java │ │ ├── RobotmediaInventory.java │ │ ├── SafeCache.java │ │ ├── SameThreadExecutor.java │ │ ├── Security.java │ │ ├── Sku.java │ │ ├── Skus.java │ │ └── UiCheckout.java │ └── test │ └── java │ └── org │ └── solovyev │ └── android │ └── checkout │ ├── ActivityCheckoutTest.java │ ├── AsyncServiceConnector.java │ ├── BasePurchaseVerifierTest.java │ ├── BillingDB.java │ ├── BillingSupportedRequestTest.java │ ├── BillingTest.java │ ├── CacheKeyTest.java │ ├── CacheTestBase.java │ ├── CheckoutInventoryTest.java │ ├── CheckoutTest.java │ ├── ConcurrentCacheTest.java │ ├── ConsumePurchaseRequestTest.java │ ├── DefaultPurchaseVerifierTest.java │ ├── FailingCacheCheckoutInventoryTest.java │ ├── FallingBackInventoryTest.java │ ├── GetPurchaseHistoryRequestTest.java │ ├── GetPurchasesRequestTest.java │ ├── GetSkuDetailsRequestTest.java │ ├── InventoryProductTest.java │ ├── InventoryProductsTest.java │ ├── InventoryTestBase.java │ ├── MainThreadRequestListenerTest.java │ ├── MapCacheTest.java │ ├── PendingRequestsTest.java │ ├── PurchaseFlowTest.java │ ├── PurchaseRequestTest.java │ ├── PurchaseTest.java │ ├── PurchasesTest.java │ ├── RequestTestBase.java │ ├── RobotmediaDatabaseTest.java │ ├── RobotmediaInventoryTest.java │ ├── SkuTest.java │ ├── SkusTest.java │ ├── TestServiceConnector.java │ ├── Tests.java │ └── Transaction.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | out/ 4 | build/ 5 | atlassian-ide-plugin.xml 6 | local.properties 7 | .gradle -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | jdk: 3 | - oraclejdk8 4 | language: android 5 | android: 6 | components: 7 | - tools # to get the new `repository-11.xml` 8 | - tools # to install Android SDK tools 9 | - build-tools-29.0.2 10 | - android-29 11 | - extra-android-support 12 | - extra-google-google_play_services 13 | - extra-google-m2repository 14 | - extra-android-m2repository 15 | 16 | script: ./gradlew clean check assembleDebug 17 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2014 serso aka se.solovyev 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | --------------------------------------------------------------------- 16 | 17 | Contact details 18 | 19 | Email: se.solovyev@gmail.com 20 | Site: http://se.solovyev.org -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 serso aka se.solovyev 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 17 | * Contact details 18 | * 19 | * Email: se.solovyev@gmail.com 20 | * Site: http://se.solovyev.org 21 | */ 22 | 23 | apply plugin: 'com.android.application' 24 | apply plugin: 'kotlin-android' 25 | 26 | android { 27 | defaultConfig { 28 | compileSdk versions.sdk.compile 29 | minSdkVersion versions.sdk.min 30 | targetSdkVersion versions.sdk.target 31 | versionCode versions.code 32 | versionName versions.name 33 | } 34 | buildTypes { 35 | release { 36 | minifyEnabled true 37 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.txt' 38 | } 39 | } 40 | compileOptions { 41 | sourceCompatibility JavaVersion.VERSION_11 42 | targetCompatibility JavaVersion.VERSION_11 43 | } 44 | namespace 'org.solovyev.android.checkout.app' 45 | publishing { 46 | singleVariant('release') { 47 | withSourcesJar() 48 | withJavadocJar() 49 | } 50 | } 51 | lint { 52 | abortOnError false 53 | } 54 | } 55 | 56 | dependencies { 57 | implementation "org.jetbrains.kotlin:kotlin-stdlib:1.9.21" 58 | implementation "androidx.annotation:annotation:1.7.1" 59 | implementation "androidx.appcompat:appcompat:1.6.1" 60 | implementation "androidx.recyclerview:recyclerview:1.3.2" 61 | implementation project(':lib') 62 | } 63 | 64 | afterEvaluate { 65 | publishing { 66 | publications { 67 | releaseApp(MavenPublication) { 68 | from components.release 69 | 70 | artifact source: file('build/outputs/mapping/release/mapping.txt'), classifier: 'proguard', extension: 'txt' 71 | artifact source: file('build/outputs/bundle/release/app-release.aab'), classifier: 'signed', extension: 'aab' 72 | 73 | artifactId = 'checkout-app' 74 | configurePublication(releaseApp, 'apk') 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/misc/res/feature1024x500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serso/android-checkout/4176fc4c039a8b8ef8002af00c3d623341dcb368/app/misc/res/feature1024x500.png -------------------------------------------------------------------------------- /app/misc/res/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serso/android-checkout/4176fc4c039a8b8ef8002af00c3d623341dcb368/app/misc/res/logo.png -------------------------------------------------------------------------------- /app/misc/res/logo256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serso/android-checkout/4176fc4c039a8b8ef8002af00c3d623341dcb368/app/misc/res/logo256x256.png -------------------------------------------------------------------------------- /app/misc/res/logo512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serso/android-checkout/4176fc4c039a8b8ef8002af00c3d623341dcb368/app/misc/res/logo512x512.png -------------------------------------------------------------------------------- /app/misc/res/logo512x512.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serso/android-checkout/4176fc4c039a8b8ef8002af00c3d623341dcb368/app/misc/res/logo512x512.xcf -------------------------------------------------------------------------------- /app/proguard-rules.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serso/android-checkout/4176fc4c039a8b8ef8002af00c3d623341dcb368/app/proguard-rules.txt -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 42 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 55 | 58 | 59 | 64 | 67 | 68 | 73 | 76 | 77 | 82 | 85 | 86 | 91 | 94 | 95 | 100 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /app/src/main/java/org/solovyev/android/checkout/app/BannerActivity.java: -------------------------------------------------------------------------------- 1 | package org.solovyev.android.checkout.app; 2 | 3 | import android.content.Intent; 4 | import android.net.Uri; 5 | import android.os.Bundle; 6 | import android.view.Menu; 7 | import android.view.MenuItem; 8 | import android.view.View; 9 | 10 | import androidx.annotation.Nullable; 11 | import androidx.appcompat.app.AppCompatActivity; 12 | import androidx.core.app.ActivityCompat; 13 | 14 | import org.solovyev.android.checkout.ActivityCheckout; 15 | import org.solovyev.android.checkout.Billing; 16 | import org.solovyev.android.checkout.Checkout; 17 | import org.solovyev.android.checkout.EmptyRequestListener; 18 | import org.solovyev.android.checkout.Inventory; 19 | import org.solovyev.android.checkout.ProductTypes; 20 | import org.solovyev.android.checkout.Purchase; 21 | 22 | import javax.annotation.Nonnull; 23 | 24 | 25 | /** 26 | * Shows some text and an advertisement below it. The ad banner is removed when user purchases 27 | * "ad_free" product. 28 | */ 29 | public class BannerActivity extends AppCompatActivity implements View.OnClickListener { 30 | 31 | private static final String AD_FREE = "ad_free"; 32 | View mAd; 33 | private ActivityCheckout mCheckout; 34 | private boolean mAdFree = true; 35 | 36 | @Override 37 | protected void onCreate(@Nullable Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | 40 | setContentView(R.layout.activity_banner); 41 | mAd = ActivityCompat.requireViewById(this, R.id.ad); 42 | mAd.setOnClickListener(this); 43 | 44 | final Billing billing = CheckoutApplication.get(this).getBilling(); 45 | mCheckout = Checkout.forActivity(this, billing); 46 | mCheckout.start(); 47 | mCheckout.loadInventory(Inventory.Request.create().loadAllPurchases(), new InventoryCallback()); 48 | } 49 | 50 | @Override 51 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 52 | mCheckout.onActivityResult(requestCode, resultCode, data); 53 | super.onActivityResult(requestCode, resultCode, data); 54 | } 55 | 56 | @Override 57 | public boolean onCreateOptionsMenu(Menu menu) { 58 | getMenuInflater().inflate(R.menu.banner, menu); 59 | return super.onCreateOptionsMenu(menu); 60 | } 61 | 62 | @Override 63 | public boolean onPrepareOptionsMenu(Menu menu) { 64 | final boolean show = super.onPrepareOptionsMenu(menu); 65 | menu.findItem(R.id.menu_buy_ad_free).setEnabled(!mAdFree); 66 | return show; 67 | } 68 | 69 | @Override 70 | public boolean onOptionsItemSelected(MenuItem item) { 71 | if (item.getItemId() == R.id.menu_buy_ad_free) { 72 | buyAdFree(); 73 | return true; 74 | } 75 | return super.onOptionsItemSelected(item); 76 | } 77 | 78 | private void buyAdFree() { 79 | mCheckout.startPurchaseFlow(ProductTypes.IN_APP, AD_FREE, null, new PurchaseListener()); 80 | } 81 | 82 | @Override 83 | protected void onDestroy() { 84 | mCheckout.stop(); 85 | super.onDestroy(); 86 | } 87 | 88 | private void showAd() { 89 | mAdFree = false; 90 | mAd.setVisibility(View.VISIBLE); 91 | } 92 | 93 | private void hideAd() { 94 | mAdFree = true; 95 | mAd.setVisibility(View.GONE); 96 | } 97 | 98 | @Override 99 | public void onClick(View v) { 100 | if (v.getId() == R.id.ad) { 101 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/serso/android-checkout"))); 102 | } 103 | } 104 | 105 | private class PurchaseListener extends EmptyRequestListener { 106 | @Override 107 | public void onSuccess(@Nonnull Purchase purchase) { 108 | hideAd(); 109 | } 110 | } 111 | 112 | private class InventoryCallback implements Inventory.Callback { 113 | @Override 114 | public void onLoaded(@Nonnull Inventory.Products products) { 115 | final Inventory.Product product = products.get(ProductTypes.IN_APP); 116 | if (!product.supported) { 117 | // billing is not supported, user can't purchase anything. Don't show ads in this 118 | // case 119 | return; 120 | } 121 | if (product.isPurchased(AD_FREE)) { 122 | return; 123 | } 124 | showAd(); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /app/src/main/java/org/solovyev/android/checkout/app/CheckoutApplication.java: -------------------------------------------------------------------------------- 1 | package org.solovyev.android.checkout.app; 2 | 3 | import org.solovyev.android.checkout.Billing; 4 | import org.solovyev.android.checkout.PlayStoreListener; 5 | 6 | import android.app.Activity; 7 | import android.app.Application; 8 | import android.widget.Toast; 9 | 10 | import javax.annotation.Nonnull; 11 | 12 | public class CheckoutApplication extends Application { 13 | 14 | @Nonnull 15 | private final Billing mBilling = new Billing(this, new Billing.DefaultConfiguration() { 16 | @Nonnull 17 | @Override 18 | public String getPublicKey() { 19 | // encrypted public key of the app. Plain version can be found in Google Play's Developer 20 | // Console in Service & APIs section under "YOUR LICENSE KEY FOR THIS APPLICATION" title. 21 | // A naive encryption algorithm is used to "protect" the key. See more about key protection 22 | // here: https://developer.android.com/google/play/billing/billing_best_practices.html#key 23 | final String s = "PixnMSYGLjg7Ah0xDwYILlVZUy0sIiBoMi4jLDcoXTcNLiQjKgtlIC48NiRcHxwKHEcYEyZrPyMWXFRpV10VES9ENz" + 24 | "g1Hj06HTV1MCAHJlpgEDcmOxFDEkA8OiQRKjEQDxhRWVVEMBYmNl1AJghcKUAYVT15KSQgBQABMgwqKSlqF1gZBA4fAw5rMyxKI" + 25 | "w9LJFc7AhxZGjoPATgRUiUjKSsOWyRKDi4nIA9lKgAGOhMLDF06CwoKGFR6Wj0hGwReS10NXzQTIREhKlkuMz4XDTwUQjRCJUA+" + 26 | "VjQVPUIoPicOLQJCLxs8RjZnJxY1OQNLKgQCPj83AyBEFSAJEk5UClYjGxVLNBU3FS4DCztENQMuOk5rFVclKz88AAApPgADGFx" + 27 | "EEV5eQAF7QBhdQEE+Bzc5MygCAwlEFzclKRB7FB0uFgwPKgAvLCk2OyFiKxkgIy8BBQYjFy4/E1ktJikrEVlKJVYIHh16NDwtDC" + 28 | "U0Vg8JNzoQBwQWOwk1GzZ4FT8fWicwITcRJi8="; 29 | return Encryption.decrypt(s, "se.solovyev@gmail.com"); 30 | } 31 | }); 32 | 33 | /** 34 | * Returns an instance of {@link CheckoutApplication} attached to the passed activity. 35 | */ 36 | public static CheckoutApplication get(Activity activity) { 37 | return (CheckoutApplication) activity.getApplication(); 38 | } 39 | 40 | @Override 41 | public void onCreate() { 42 | super.onCreate(); 43 | mBilling.addPlayStoreListener(new PlayStoreListener() { 44 | @Override 45 | public void onPurchasesChanged() { 46 | Toast.makeText(CheckoutApplication.this, R.string.purchases_changed, Toast.LENGTH_LONG).show(); 47 | } 48 | }); 49 | } 50 | 51 | @Nonnull 52 | public Billing getBilling() { 53 | return mBilling; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/org/solovyev/android/checkout/app/Encryption.java: -------------------------------------------------------------------------------- 1 | package org.solovyev.android.checkout.app; 2 | 3 | import android.util.Base64; 4 | 5 | import javax.annotation.Nonnull; 6 | 7 | /** 8 | * Simple (and stupid) encryption algorithm. Should not be used in real apps. 9 | */ 10 | final class Encryption { 11 | 12 | @Nonnull 13 | static String decrypt(@Nonnull String message, @Nonnull String salt) { 14 | return xor(new String(Base64.decode(message, 0)), salt); 15 | } 16 | 17 | @Nonnull 18 | static String encrypt(@Nonnull String message, @Nonnull String salt) { 19 | return new String(Base64.encode(xor(message, salt).getBytes(), 0)); 20 | } 21 | 22 | /** 23 | * Encrypts or decrypts a base-64 string using a XOR cipher. 24 | */ 25 | @Nonnull 26 | private static String xor(@Nonnull String message, @Nonnull String salt) { 27 | final char[] m = message.toCharArray(); 28 | final int ml = m.length; 29 | 30 | final char[] s = salt.toCharArray(); 31 | final int sl = s.length; 32 | 33 | final char[] res = new char[ml]; 34 | for (int i = 0; i < ml; i++) { 35 | res[i] = (char) (m[i] ^ s[i % sl]); 36 | } 37 | return new String(res); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/org/solovyev/android/checkout/app/MainActivity.java: -------------------------------------------------------------------------------- 1 | package org.solovyev.android.checkout.app; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.TextView; 10 | 11 | import androidx.annotation.Nullable; 12 | import androidx.annotation.StringRes; 13 | import androidx.appcompat.app.AppCompatActivity; 14 | import androidx.core.app.ActivityCompat; 15 | import androidx.core.view.ViewCompat; 16 | import androidx.recyclerview.widget.LinearLayoutManager; 17 | import androidx.recyclerview.widget.RecyclerView; 18 | 19 | /** 20 | * Shows a list of use cases covered in the app. 21 | */ 22 | public class MainActivity extends AppCompatActivity { 23 | 24 | RecyclerView mRecycler; 25 | 26 | @Override 27 | protected void onCreate(@Nullable Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_main); 30 | mRecycler = ActivityCompat.requireViewById(this, R.id.recycler); 31 | 32 | mRecycler.setLayoutManager(new LinearLayoutManager(this)); 33 | mRecycler.setAdapter(new Adapter(this)); 34 | } 35 | 36 | private enum UseCase { 37 | STATIC(StaticActivity.class, R.string.use_case_title_static, R.string.use_case_desc_static), 38 | BANNER(BannerActivity.class, R.string.use_case_title_banner, R.string.use_case_desc_banner), 39 | SKUS(SkusActivity.class, R.string.use_case_title_skus, R.string.use_case_desc_skus), 40 | SUBSCRIPTIONS(SubscriptionsActivity.class, R.string.use_case_title_subscriptions, R.string.use_case_desc_subscriptions), 41 | FRAGMENT(BuyConsumeFragmentActivity.class, R.string.use_case_title_fragment, R.string.use_case_desc_fragment), 42 | HISTORY(HistoryActivity.class, R.string.use_case_title_history, R.string.use_case_desc_history); 43 | 44 | // activity to be started on click 45 | final Class activity; 46 | @StringRes 47 | final int title; 48 | @StringRes 49 | final int description; 50 | 51 | UseCase(Class activity, @StringRes int title, @StringRes int description) { 52 | this.activity = activity; 53 | this.title = title; 54 | this.description = description; 55 | } 56 | } 57 | 58 | static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 59 | 60 | private final Activity mActivity; 61 | TextView mTitle; 62 | TextView mDescription; 63 | @Nullable 64 | private UseCase mUseCase; 65 | 66 | 67 | ViewHolder(Activity activity, View view) { 68 | super(view); 69 | mActivity = activity; 70 | mTitle = ViewCompat.requireViewById(view, R.id.use_case_title); 71 | mDescription = ViewCompat.requireViewById(view, R.id.use_case_description); 72 | 73 | view.setOnClickListener(this); 74 | } 75 | 76 | void onBind(UseCase useCase) { 77 | mUseCase = useCase; 78 | mTitle.setText(useCase.title); 79 | mDescription.setText(useCase.description); 80 | } 81 | 82 | @Override 83 | public void onClick(View v) { 84 | if (mUseCase == null) { 85 | return; 86 | } 87 | mActivity.startActivity(new Intent(mActivity, mUseCase.activity)); 88 | } 89 | } 90 | 91 | private static class Adapter extends RecyclerView.Adapter { 92 | 93 | private final Activity mActivity; 94 | private final UseCase[] mUseCases = UseCase.values(); 95 | private final LayoutInflater mInflater; 96 | 97 | private Adapter(Activity activity) { 98 | mActivity = activity; 99 | mInflater = LayoutInflater.from(activity); 100 | } 101 | 102 | @Override 103 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 104 | final View view = mInflater.inflate(R.layout.use_case, parent, false); 105 | return new ViewHolder(mActivity, view); 106 | } 107 | 108 | @Override 109 | public void onBindViewHolder(ViewHolder holder, int position) { 110 | holder.onBind(mUseCases[position]); 111 | } 112 | 113 | @Override 114 | public int getItemCount() { 115 | return mUseCases.length; 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serso/android-checkout/4176fc4c039a8b8ef8002af00c3d623341dcb368/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serso/android-checkout/4176fc4c039a8b8ef8002af00c3d623341dcb368/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serso/android-checkout/4176fc4c039a8b8ef8002af00c3d623341dcb368/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serso/android-checkout/4176fc4c039a8b8ef8002af00c3d623341dcb368/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/top_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serso/android-checkout/4176fc4c039a8b8ef8002af00c3d623341dcb368/app/src/main/res/drawable-xxhdpi/top_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_banner.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | 23 | 24 | 25 | 32 | 33 | 37 | 38 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_history.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_skus.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_static.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 18 | 19 | 23 | 24 | 30 | 31 |