diff --git a/app/src/main/java/org/solovyev/android/calculator/App.java b/app/src/main/java/org/solovyev/android/calculator/App.java index 6e2c4ffe..4650b131 100644 --- a/app/src/main/java/org/solovyev/android/calculator/App.java +++ b/app/src/main/java/org/solovyev/android/calculator/App.java @@ -57,8 +57,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; /** @@ -81,8 +79,6 @@ public final class App { return tag + "/" + subTag; } - @Nonnull - private static final Products products = Products.create().add(ProductTypes.IN_APP, Arrays.asList("ad_free")); @Nonnull private static Languages languages; @Nonnull @@ -93,8 +89,6 @@ public final class App { private static SharedPreferences preferences; @Nonnull private static volatile Ga ga; - @Nonnull - private static volatile Billing billing; @Nullable private static Boolean lg = null; @Nullable @@ -109,15 +103,6 @@ public final class App { private static Bus bus; @Nonnull private static Display display; - @Nonnull - private static final Executor background = Executors.newFixedThreadPool(5, new ThreadFactory() { - @NonNull - private final AtomicInteger counter = new AtomicInteger(); - @Override - public Thread newThread(@Nonnull Runnable r) { - return new Thread(r, "Background #" + counter.getAndIncrement()); - } - }); private App() { throw new AssertionError(); @@ -129,23 +114,6 @@ public final class App { App.preferences = PreferenceManager.getDefaultSharedPreferences(application); App.uiThreadExecutor = application.uiThread; App.ga = new Ga(application, preferences); - App.billing = new Billing(application, new Billing.DefaultConfiguration() { - @Nonnull - @Override - public String getPublicKey() { - return CalculatorSecurity.getPK(); - } - - @Nullable - @Override - public Inventory getFallbackInventory(@Nonnull Checkout checkout, @Nonnull Executor onLoadExecutor) { - if (RobotmediaDatabase.exists(billing.getContext())) { - return new RobotmediaInventory(checkout, onLoadExecutor); - } else { - return null; - } - } - }); App.screenMetrics = new ScreenMetrics(application); App.languages = languages; App.languages.init(); @@ -185,16 +153,6 @@ public final class App { return ga; } - @Nonnull - public static Billing getBilling() { - return billing; - } - - @Nonnull - public static Products getProducts() { - return products; - } - public static boolean isLargeScreen() { return Views.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE, App.getApplication().getResources().getConfiguration()); } diff --git a/app/src/main/java/org/solovyev/android/calculator/AppComponent.java b/app/src/main/java/org/solovyev/android/calculator/AppComponent.java index 3340f076..e8c6c7f8 100644 --- a/app/src/main/java/org/solovyev/android/calculator/AppComponent.java +++ b/app/src/main/java/org/solovyev/android/calculator/AppComponent.java @@ -9,6 +9,8 @@ import org.solovyev.android.calculator.history.BaseHistoryFragment; import org.solovyev.android.calculator.history.EditHistoryFragment; import org.solovyev.android.calculator.onscreen.CalculatorOnscreenService; import org.solovyev.android.calculator.operators.OperatorsFragment; +import org.solovyev.android.calculator.preferences.PreferencesActivity; +import org.solovyev.android.calculator.preferences.PurchaseDialogActivity; import org.solovyev.android.calculator.variables.EditVariableFragment; import org.solovyev.android.calculator.variables.VariablesFragment; @@ -20,6 +22,7 @@ public interface AppComponent { void inject(CalculatorApplication application); void inject(EditorFragment fragment); void inject(BaseUi ui); + void inject(FragmentUi ui); void inject(CalculatorOnscreenService service); void inject(BaseHistoryFragment fragment); void inject(BaseDialogFragment fragment); @@ -35,4 +38,6 @@ public interface AppComponent { void inject(CalculatorReceiver receiver); void inject(DisplayFragment fragment); void inject(KeyboardFragment fragment); + void inject(PurchaseDialogActivity activity); + void inject(PreferencesActivity activity); } diff --git a/app/src/main/java/org/solovyev/android/calculator/AppModule.java b/app/src/main/java/org/solovyev/android/calculator/AppModule.java index 5e4660a3..8e588b21 100644 --- a/app/src/main/java/org/solovyev/android/calculator/AppModule.java +++ b/app/src/main/java/org/solovyev/android/calculator/AppModule.java @@ -12,10 +12,13 @@ import dagger.Module; import dagger.Provides; import jscl.JsclMathEngine; import org.solovyev.android.UiThreadExecutor; +import org.solovyev.android.checkout.*; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.inject.Named; import javax.inject.Singleton; +import java.util.Collections; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; @@ -108,6 +111,34 @@ public class AppModule { return JsclMathEngine.getInstance(); } + @Provides + @Singleton + Billing provideBilling() { + return new Billing(application, new Billing.DefaultConfiguration() { + @Nonnull + @Override + public String getPublicKey() { + return CalculatorSecurity.getPK(); + } + + @Nullable + @Override + public Inventory getFallbackInventory(@Nonnull Checkout checkout, @Nonnull Executor onLoadExecutor) { + if (RobotmediaDatabase.exists(application)) { + return new RobotmediaInventory(checkout, onLoadExecutor); + } else { + return null; + } + } + }); + } + + @Singleton + @Provides + Products provideProducts() { + return Products.create().add(ProductTypes.IN_APP, Collections.singletonList("ad_free")); + } + private static class AppBus extends Bus { @NonNull diff --git a/app/src/main/java/org/solovyev/android/calculator/BaseUi.java b/app/src/main/java/org/solovyev/android/calculator/BaseUi.java index 7b8d76bd..99c8d885 100644 --- a/app/src/main/java/org/solovyev/android/calculator/BaseUi.java +++ b/app/src/main/java/org/solovyev/android/calculator/BaseUi.java @@ -133,7 +133,7 @@ public abstract class BaseUi implements SharedPreferences.OnSharedPreferenceChan ActivityLauncher launcher; protected void onCreate(@Nonnull Activity activity) { - cast(activity.getApplication()).getComponent().inject(this); + inject(cast(activity.getApplication()).getComponent()); layout = Preferences.Gui.layout.getPreferenceNoError(preferences); theme = Preferences.Gui.theme.getPreferenceNoError(preferences); @@ -148,6 +148,10 @@ public abstract class BaseUi implements SharedPreferences.OnSharedPreferenceChan } } + protected void inject(@Nonnull AppComponent component) { + component.inject(this); + } + @Nonnull public Preferences.Gui.Theme getTheme() { return theme; diff --git a/app/src/main/java/org/solovyev/android/calculator/FragmentUi.java b/app/src/main/java/org/solovyev/android/calculator/FragmentUi.java index 8e76893b..12dd8896 100644 --- a/app/src/main/java/org/solovyev/android/calculator/FragmentUi.java +++ b/app/src/main/java/org/solovyev/android/calculator/FragmentUi.java @@ -28,19 +28,17 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; -import org.solovyev.android.checkout.ActivityCheckout; -import org.solovyev.android.checkout.Checkout; +import org.solovyev.android.checkout.CppCheckout; import org.solovyev.android.checkout.Inventory; import org.solovyev.android.checkout.ProductTypes; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import javax.inject.Inject; import java.util.Locale; public class FragmentUi extends BaseUi { - private ActivityCheckout checkout; - @Nullable private AdView adView; @@ -53,6 +51,9 @@ public class FragmentUi extends BaseUi { @Nullable private Boolean adFree = null; + @Inject + CppCheckout checkout; + public FragmentUi(int layoutId) { this.layoutId = layoutId; } @@ -86,7 +87,6 @@ public class FragmentUi extends BaseUi { public void onCreate(@Nonnull Fragment fragment) { final FragmentActivity activity = fragment.getActivity(); super.onCreate(activity); - checkout = Checkout.forActivity(activity, App.getBilling(), App.getProducts()); if (listenersOnCreate) { if (fragment instanceof CalculatorEventListener) { @@ -97,6 +97,12 @@ public class FragmentUi extends BaseUi { checkout.start(); } + @Override + protected void inject(@Nonnull AppComponent component) { + super.inject(component); + component.inject(this); + } + public void onResume(@Nonnull Fragment fragment) { if (adView != null) { adView.resume(); diff --git a/app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesActivity.java b/app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesActivity.java index b015f9cd..260c08e0 100644 --- a/app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesActivity.java +++ b/app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesActivity.java @@ -7,16 +7,14 @@ import android.os.Bundle; import android.support.annotation.StringRes; import android.support.annotation.XmlRes; import android.util.SparseArray; - -import org.solovyev.android.calculator.ActivityUi; -import org.solovyev.android.calculator.App; -import org.solovyev.android.calculator.BaseActivity; -import org.solovyev.android.calculator.Preferences; -import org.solovyev.android.calculator.R; +import org.solovyev.android.calculator.*; import org.solovyev.android.checkout.ActivityCheckout; +import org.solovyev.android.checkout.Billing; import org.solovyev.android.checkout.Checkout; +import org.solovyev.android.checkout.Products; import javax.annotation.Nonnull; +import javax.inject.Inject; import static android.support.v7.app.ActionBar.NAVIGATION_MODE_STANDARD; @@ -38,10 +36,14 @@ public class PreferencesActivity extends BaseActivity implements SharedPreferenc preferences.append(R.xml.preferences_widget, new PrefDef("screen-widget", R.string.prefs_widget_title)); } - @Nonnull - private final ActivityCheckout checkout = Checkout.forActivity(this, App.getBilling(), App.getProducts()); + ActivityCheckout checkout; private boolean paused = true; + @Inject + Billing billing; + @Inject + Products products; + public PreferencesActivity() { super(R.layout.main_empty); } @@ -90,9 +92,16 @@ public class PreferencesActivity extends BaseActivity implements SharedPreferenc getSupportActionBar().setNavigationMode(NAVIGATION_MODE_STANDARD); + checkout = Checkout.forActivity(this, billing, products); checkout.start(); } + @Override + protected void inject(@Nonnull AppComponent component) { + super.inject(component); + component.inject(this); + } + @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (!paused) { diff --git a/app/src/main/java/org/solovyev/android/calculator/preferences/PurchaseDialogActivity.java b/app/src/main/java/org/solovyev/android/calculator/preferences/PurchaseDialogActivity.java index 4e96adb7..92de715e 100644 --- a/app/src/main/java/org/solovyev/android/calculator/preferences/PurchaseDialogActivity.java +++ b/app/src/main/java/org/solovyev/android/calculator/preferences/PurchaseDialogActivity.java @@ -30,28 +30,20 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; - import butterknife.Bind; import butterknife.ButterKnife; import org.solovyev.android.calculator.*; -import org.solovyev.android.checkout.ActivityCheckout; -import org.solovyev.android.checkout.BillingRequests; -import org.solovyev.android.checkout.Checkout; -import org.solovyev.android.checkout.ProductTypes; -import org.solovyev.android.checkout.Purchase; -import org.solovyev.android.checkout.RequestListener; +import org.solovyev.android.checkout.*; import org.solovyev.android.fragments.FragmentUtils; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import javax.inject.Inject; import static org.solovyev.android.calculator.FragmentTab.purchase_dialog; public class PurchaseDialogActivity extends BaseActivity { - @Nonnull - private final ActivityCheckout checkout = Checkout.forActivity(this, App.getBilling(), App.getProducts()); - @Nonnull private final RequestListener purchaseListener = new RequestListener() { @Override @@ -65,6 +57,12 @@ public class PurchaseDialogActivity extends BaseActivity { } }; + @Inject + Billing billing; + @Inject + Products products; + ActivityCheckout checkout; + public PurchaseDialogActivity() { super(R.layout.cpp_dialog); } @@ -75,10 +73,17 @@ public class PurchaseDialogActivity extends BaseActivity { FragmentUtils.createFragment(this, PurchaseDialogFragment.class, R.id.dialog_layout, "purchase-dialog"); + checkout = Checkout.forActivity(this, billing, products); checkout.start(); checkout.createPurchaseFlow(purchaseListener); } + @Override + protected void inject(@Nonnull AppComponent component) { + super.inject(component); + component.inject(this); + } + @Override protected void onStart() { super.onStart(); diff --git a/app/src/main/java/org/solovyev/android/checkout/CppCheckout.java b/app/src/main/java/org/solovyev/android/checkout/CppCheckout.java new file mode 100644 index 00000000..59e8fc59 --- /dev/null +++ b/app/src/main/java/org/solovyev/android/checkout/CppCheckout.java @@ -0,0 +1,39 @@ +package org.solovyev.android.checkout; + +import android.support.annotation.NonNull; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * App-wide {@link Checkout} which counts how many times it has been started. + */ +@Singleton +public class CppCheckout extends Checkout { + private int started = 0; + + @Inject + public CppCheckout(@NonNull Billing billing, @NonNull Products products) { + super(null, billing, products); + } + + @Override + public void stop() { + Check.isMainThread(); + Check.isTrue(started > 0, "Must be started first"); + started--; + if (started == 0) { + super.stop(); + } + started = Math.max(0, started); + } + + @Override + public void start(Listener listener) { + Check.isMainThread(); + started++; + if (started == 1) { + super.start(listener); + } + } +}