diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/constant/IntentConstants.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/constant/IntentConstants.java index 33da50f38..c578833f1 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/constant/IntentConstants.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/constant/IntentConstants.java @@ -208,5 +208,5 @@ public interface IntentConstants { String EXTRA_SIMPLE_LAYOUT = "simple_layout"; String EXTRA_API_CONFIG = "api_config"; String EXTRA_COUNT = "count"; - + String EXTRA_REQUEST_CODE = "request_code"; } diff --git a/twidere/src/google/kotlin/org/mariotaku/twidere/activity/GooglePlayInAppPurchaseActivity.kt b/twidere/src/google/kotlin/org/mariotaku/twidere/activity/GooglePlayInAppPurchaseActivity.kt index 6db45f53a..7a3530f1f 100644 --- a/twidere/src/google/kotlin/org/mariotaku/twidere/activity/GooglePlayInAppPurchaseActivity.kt +++ b/twidere/src/google/kotlin/org/mariotaku/twidere/activity/GooglePlayInAppPurchaseActivity.kt @@ -6,12 +6,15 @@ import android.os.Bundle import android.support.v4.app.DialogFragment import com.anjlab.android.iab.v3.BillingProcessor import com.anjlab.android.iab.v3.Constants.* +import com.anjlab.android.iab.v3.SkuDetails import com.anjlab.android.iab.v3.TransactionDetails import nl.komponents.kovenant.task import nl.komponents.kovenant.ui.alwaysUi import nl.komponents.kovenant.ui.failUi import nl.komponents.kovenant.ui.successUi import org.mariotaku.twidere.Constants +import org.mariotaku.twidere.constant.EXTRA_CURRENCY +import org.mariotaku.twidere.constant.EXTRA_PRICE import org.mariotaku.twidere.constant.IntentConstants.INTENT_PACKAGE_PREFIX import org.mariotaku.twidere.constant.RESULT_NOT_PURCHASED import org.mariotaku.twidere.constant.RESULT_SERVICE_UNAVAILABLE @@ -82,8 +85,9 @@ class GooglePlayInAppPurchaseActivity : BaseActivity(), BillingProcessor.IBillin } } - private fun handlePurchased(details: TransactionDetails) { - setResult(RESULT_OK) + private fun handlePurchased(sku: SkuDetails, transaction: TransactionDetails) { + val data = Intent().putExtra(EXTRA_PRICE, sku.priceValue).putExtra(EXTRA_CURRENCY, sku.currency) + setResult(RESULT_OK, data) finish() } @@ -93,11 +97,16 @@ class GooglePlayInAppPurchaseActivity : BaseActivity(), BillingProcessor.IBillin val dfRef = WeakReference(ProgressDialogFragment.show(it.supportFragmentManager, "consume_purchase_progress")) task { val activity = weakThis.get() ?: throw PurchaseException(BILLING_RESPONSE_RESULT_USER_CANCELED) - activity.billingProcessor.loadOwnedPurchasesFromGoogle() - val details = activity.billingProcessor.getPurchaseTransactionDetails(activity.productId) - return@task details ?: throw PurchaseException(BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED) - }.successUi { details -> - weakThis.get()?.handlePurchased(details) + val productId = activity.productId + val bp = activity.billingProcessor + bp.loadOwnedPurchasesFromGoogle() + val skuDetails = bp.getPurchaseListingDetails(productId) + ?: throw PurchaseException(BILLING_RESPONSE_RESULT_ERROR) + val transactionDetails = bp.getPurchaseTransactionDetails(productId) + ?: throw PurchaseException(BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED) + return@task Pair(skuDetails, transactionDetails) + }.successUi { result -> + weakThis.get()?.handlePurchased(result.first, result.second) }.failUi { error -> if (error is PurchaseException) { weakThis.get()?.handleError(error.code) @@ -122,10 +131,13 @@ class GooglePlayInAppPurchaseActivity : BaseActivity(), BillingProcessor.IBillin val activity = weakThis.get() ?: throw PurchaseException(BILLING_RESPONSE_RESULT_USER_CANCELED) val bp = activity.billingProcessor bp.loadOwnedPurchasesFromGoogle() - val result = bp.getPurchaseTransactionDetails(activity.productId) - return@task result ?: throw PurchaseException(BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED) - }.successUi { details -> - weakThis.get()?.handlePurchased(details) + val skuDetails = bp.getPurchaseListingDetails(productId) + ?: throw PurchaseException(BILLING_RESPONSE_RESULT_ERROR) + val transactionDetails = bp.getPurchaseTransactionDetails(productId) + ?: throw PurchaseException(BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED) + return@task Pair(skuDetails, transactionDetails) + }.successUi { result -> + weakThis.get()?.handlePurchased(result.first, result.second) }.failUi { error -> if (error is PurchaseException) { weakThis.get()?.handleError(error.code) @@ -149,6 +161,7 @@ class GooglePlayInAppPurchaseActivity : BaseActivity(), BillingProcessor.IBillin BILLING_RESPONSE_RESULT_USER_CANCELED -> Activity.RESULT_CANCELED BILLING_RESPONSE_RESULT_SERVICE_UNAVAILABLE -> RESULT_SERVICE_UNAVAILABLE BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED -> RESULT_NOT_PURCHASED + BILLING_RESPONSE_RESULT_ERROR -> RESULT_NOT_PURCHASED else -> billingResponse } return resultCode diff --git a/twidere/src/google/kotlin/org/mariotaku/twidere/util/FabricAnalyzer.kt b/twidere/src/google/kotlin/org/mariotaku/twidere/util/FabricAnalyzer.kt index 891412652..dfb76fb73 100644 --- a/twidere/src/google/kotlin/org/mariotaku/twidere/util/FabricAnalyzer.kt +++ b/twidere/src/google/kotlin/org/mariotaku/twidere/util/FabricAnalyzer.kt @@ -22,6 +22,7 @@ package org.mariotaku.twidere.util import android.accounts.Account import android.accounts.AccountManager import android.accounts.OnAccountsUpdateListener +import android.app.Activity import android.app.Application import android.os.Build import com.crashlytics.android.Crashlytics @@ -32,9 +33,12 @@ import org.mariotaku.ktextension.configure import org.mariotaku.twidere.BuildConfig import org.mariotaku.twidere.Constants import org.mariotaku.twidere.TwidereConstants.ACCOUNT_TYPE +import org.mariotaku.twidere.model.analyzer.Purchase import org.mariotaku.twidere.model.analyzer.Search import org.mariotaku.twidere.model.analyzer.Share import org.mariotaku.twidere.model.analyzer.SignIn +import java.math.BigDecimal +import java.util.* /** * Created by mariotaku on 15/7/8. @@ -81,6 +85,20 @@ class FabricAnalyzer : Analyzer(), Constants { putAttributes(event) }) } + is Purchase -> { + answers.logPurchase(configure(PurchaseEvent()) { + putItemName(event.productName) + putSuccess(event.resultCode == Activity.RESULT_OK) + if (!event.price.isNaN() && event.currency != null) { + putCurrency(Currency.getInstance(event.currency) ?: Currency.getInstance(Locale.getDefault())) + putItemPrice(BigDecimal(event.price)) + } + event.forEachValues { name, value -> + putCustomAttribute(name, value) + } + putAttributes(event) + }) + } else -> { answers.logCustom(configure(CustomEvent(event.name)) { putAttributes(event) diff --git a/twidere/src/main/java/org/mariotaku/twidere/Constants.java b/twidere/src/main/java/org/mariotaku/twidere/Constants.java index 4d7e7afde..62ce6b89a 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/Constants.java +++ b/twidere/src/main/java/org/mariotaku/twidere/Constants.java @@ -108,6 +108,9 @@ public interface Constants extends TwidereConstants { @Preference(type = STRING, exportable = false) String KEY_DEVICE_SERIAL = "device_serial"; + // Intent constants + String EXTRA_PRODUCT_TYPE = "product_type"; + @SuppressWarnings("SpellCheckingInspection") String GOOGLE_APIS_SERVER_CLIENT_ID = "223623398518-1p34hsndj7couh2c9c2f8909amh9euhf.apps.googleusercontent.com"; @SuppressWarnings("SpellCheckingInspection") diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/constant/ExtraFeatureConstants.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/constant/ExtraFeatureConstants.kt index 842368ea7..5d48137cd 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/constant/ExtraFeatureConstants.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/constant/ExtraFeatureConstants.kt @@ -4,4 +4,8 @@ package org.mariotaku.twidere.constant * Created by mariotaku on 2017/1/1. */ const val RESULT_SERVICE_UNAVAILABLE = 1 -const val RESULT_NOT_PURCHASED = 8 \ No newline at end of file +const val RESULT_INTERNAL_ERROR = 6 +const val RESULT_NOT_PURCHASED = 8 + +const val EXTRA_PRICE = "price" +const val EXTRA_CURRENCY = "currency" \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/DraftExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/DraftExtensions.kt index 117eeeec3..8fdbd1d78 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/DraftExtensions.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/DraftExtensions.kt @@ -242,3 +242,11 @@ private class BodyPartHandler(private val context: Context, private val draft: D } } + +fun draftActionTypeString(@Draft.Action action: String?): String { + return when (action) { + Draft.Action.QUOTE -> "quote" + Draft.Action.REPLY -> "reply" + else -> "tweet" + } +} diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/ParcelableMediaExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/ParcelableMediaExtensions.kt new file mode 100644 index 000000000..c0f705ad7 --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/ParcelableMediaExtensions.kt @@ -0,0 +1,20 @@ +package org.mariotaku.twidere.extension.model + +import org.mariotaku.twidere.model.ParcelableMedia + +/** + * Created by mariotaku on 2017/1/7. + */ + +fun parcelableMediaTypeString(@ParcelableMedia.Type type: Int): String { + if (type <= 0) return "none" + return when (type) { + ParcelableMedia.Type.IMAGE -> "image" + ParcelableMedia.Type.VIDEO -> "video" + ParcelableMedia.Type.ANIMATED_GIF -> "gif" + ParcelableMedia.Type.CARD_ANIMATED_GIF -> "gif" + ParcelableMedia.Type.EXTERNAL_PLAYER -> "external" + ParcelableMedia.Type.VARIABLE_TYPE -> "variable" + else -> "unknown" + } +} \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/ParcelableStatusExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/ParcelableStatusExtensions.kt new file mode 100644 index 000000000..165890101 --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/ParcelableStatusExtensions.kt @@ -0,0 +1,9 @@ +package org.mariotaku.twidere.extension.model + +import org.mariotaku.twidere.model.ParcelableStatus + +/** + * Created by mariotaku on 2017/1/7. + */ +val ParcelableStatus.media_type: Int + get() = (media ?: quoted_media)?.firstOrNull()?.type ?: 0 \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/ExtraFeaturesIntroductionDialogFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/ExtraFeaturesIntroductionDialogFragment.kt index 5c81932f9..70cb9e25e 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/ExtraFeaturesIntroductionDialogFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/ExtraFeaturesIntroductionDialogFragment.kt @@ -5,6 +5,7 @@ import android.os.Bundle import android.support.v7.app.AlertDialog import android.view.View import org.mariotaku.twidere.R +import org.mariotaku.twidere.constant.IntentConstants.EXTRA_REQUEST_CODE import org.mariotaku.twidere.util.premium.ExtraFeaturesService /** @@ -26,7 +27,15 @@ class ExtraFeaturesIntroductionDialogFragment : BaseDialogFragment() { builder.setTitle(R.string.title_extra_features) builder.setView(R.layout.dialog_extra_features_introduction) builder.setPositiveButton(R.string.action_purchase) { dialog, which -> - startActivity(extraFeaturesService.createPurchaseIntent(context)) + val requestCode = arguments?.getInt(EXTRA_REQUEST_CODE) ?: 0 + val purchaseIntent = extraFeaturesService.createPurchaseIntent(context) + if (requestCode == 0) { + startActivity(purchaseIntent) + } else if (parentFragment != null) { + parentFragment.startActivityForResult(purchaseIntent, requestCode) + } else { + activity.startActivityForResult(purchaseIntent, requestCode) + } } builder.setNegativeButton(R.string.action_later) { dialog, which -> diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/PermissionRequestDialog.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/PermissionRequestDialog.kt index ba21fa179..a01193c88 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/PermissionRequestDialog.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/PermissionRequestDialog.kt @@ -8,8 +8,7 @@ import android.support.v7.app.AlertDialog import org.mariotaku.ktextension.Bundle import org.mariotaku.ktextension.set import org.mariotaku.twidere.R -import org.mariotaku.twidere.constant.IntentConstants.EXTRA_MESSAGE -import org.mariotaku.twidere.constant.IntentConstants.EXTRA_PERMISSIONS +import org.mariotaku.twidere.constant.IntentConstants.* /** * Created by mariotaku on 2016/12/13. @@ -36,7 +35,7 @@ class PermissionRequestDialog : BaseDialogFragment() { } companion object { - const val EXTRA_REQUEST_CODE = "request_code" + fun show(fragmentManager: FragmentManager, message: String, permissions: Array, requestCode: Int): PermissionRequestDialog { val df = PermissionRequestDialog() df.arguments = Bundle { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/StatusFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/StatusFragment.kt index eeb1e70d5..e5cb29969 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/StatusFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/StatusFragment.kt @@ -91,11 +91,13 @@ import org.mariotaku.twidere.annotation.Referral import org.mariotaku.twidere.constant.* import org.mariotaku.twidere.constant.KeyboardShortcutConstants.* import org.mariotaku.twidere.extension.model.getAccountType +import org.mariotaku.twidere.extension.model.media_type import org.mariotaku.twidere.loader.ConversationLoader import org.mariotaku.twidere.loader.ParcelableStatusLoader import org.mariotaku.twidere.menu.FavoriteItemProvider import org.mariotaku.twidere.model.* import org.mariotaku.twidere.model.analyzer.Share +import org.mariotaku.twidere.model.analyzer.StatusView import org.mariotaku.twidere.model.message.FavoriteTaskEvent import org.mariotaku.twidere.model.message.StatusListChangedEvent import org.mariotaku.twidere.model.util.* @@ -208,7 +210,7 @@ class StatusFragment : BaseSupportFragment(), LoaderCallbacks { + private val statusActivityLoaderCallback = object : LoaderCallbacks { override fun onCreateLoader(id: Int, args: Bundle): Loader { val accountKey = args.getParcelable(EXTRA_ACCOUNT_KEY) val statusId = args.getString(EXTRA_STATUS_ID) @@ -435,6 +437,7 @@ class StatusFragment : BaseSupportFragment(), LoaderCallbacks(EXTRA_ACCOUNT_KEY)) startActivityForResult(intent, REQUEST_ADD_USER_SELECT_ACCOUNT) } + REQUEST_PURCHASE_EXTRA_FEATURES -> { + Analyzer.log(Purchase.fromActivityResult(Purchase.NAME_EXTRA_FEATURES, resultCode, data)) + } } } @@ -123,6 +130,9 @@ class FilteredUsersFragment : BaseFiltersFragment() { startActivityForResult(intent, requestCode) } else { val df = ExtraFeaturesIntroductionDialogFragment() + df.arguments = Bundle { + putInt(EXTRA_REQUEST_CODE, REQUEST_PURCHASE_EXTRA_FEATURES) + } df.show(childFragmentManager, "extra_features_introduction") } return true diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/premium/ExtraFeaturesIntroductionCardFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/premium/ExtraFeaturesIntroductionCardFragment.kt index 0a95851d9..ec6d1559e 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/premium/ExtraFeaturesIntroductionCardFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/premium/ExtraFeaturesIntroductionCardFragment.kt @@ -13,6 +13,8 @@ import org.mariotaku.twidere.R import org.mariotaku.twidere.constant.RESULT_NOT_PURCHASED import org.mariotaku.twidere.constant.RESULT_SERVICE_UNAVAILABLE import org.mariotaku.twidere.fragment.BaseSupportFragment +import org.mariotaku.twidere.model.analyzer.Purchase +import org.mariotaku.twidere.util.Analyzer import org.mariotaku.twidere.util.premium.ExtraFeaturesService /** @@ -50,7 +52,12 @@ class ExtraFeaturesIntroductionCardFragment : BaseSupportFragment() { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { when (requestCode) { REQUEST_PURCHASE -> { - activity?.recreate() + Analyzer.log(Purchase.fromActivityResult(Purchase.NAME_EXTRA_FEATURES, resultCode, data)) + when (resultCode) { + Activity.RESULT_OK -> { + activity?.recreate() + } + } } REQUEST_RESTORE_PURCHASE -> { when (resultCode) { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/model/analyzer/Purchase.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/model/analyzer/Purchase.kt new file mode 100644 index 000000000..764bfd92e --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/model/analyzer/Purchase.kt @@ -0,0 +1,48 @@ +package org.mariotaku.twidere.model.analyzer + +import android.app.Activity +import android.content.Intent +import org.mariotaku.twidere.constant.* +import org.mariotaku.twidere.util.Analyzer + +/** + * Created by mariotaku on 2017/1/7. + */ + +data class Purchase(val productName: String) : Analyzer.Event { + override val name: String = "Purchase" + override var accountType: String? = null + var resultCode: Int = Activity.RESULT_OK + var price: Double = Double.NaN + var currency: String? = null + + override fun forEachValues(action: (String, String?) -> Unit) { + if (resultCode != Activity.RESULT_OK) { + action("Fail reason", getFailReason(resultCode)) + } + } + + companion object { + const val NAME_EXTRA_FEATURES = "Enhanced Features" + + internal fun getFailReason(resultCode: Int): String { + return when (resultCode) { + Activity.RESULT_CANCELED -> "cancelled" + RESULT_SERVICE_UNAVAILABLE -> "service unavailable" + RESULT_INTERNAL_ERROR -> "internal error" + RESULT_NOT_PURCHASED -> "not purchased" + else -> "unknown" + } + } + + fun fromActivityResult(name: String, resultCode: Int, data: Intent?): Purchase { + val result = Purchase(name) + result.resultCode = resultCode + if (data != null) { + result.price = data.getDoubleExtra(EXTRA_PRICE, Double.NaN) + result.currency = data.getStringExtra(EXTRA_CURRENCY) + } + return result + } + } +} diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/model/analyzer/StatusView.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/model/analyzer/StatusView.kt new file mode 100644 index 000000000..12a3c0149 --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/model/analyzer/StatusView.kt @@ -0,0 +1,22 @@ +package org.mariotaku.twidere.model.analyzer + +import org.mariotaku.twidere.annotation.AccountType +import org.mariotaku.twidere.extension.model.parcelableMediaTypeString +import org.mariotaku.twidere.model.ParcelableMedia +import org.mariotaku.twidere.util.Analyzer + +/** + * Created by mariotaku on 2017/1/7. + */ + +data class StatusView( + @AccountType override val accountType: String? = null, + @ParcelableMedia.Type val mediaType: Int +) : Analyzer.Event { + + override val name: String = "Status View" + + override fun forEachValues(action: (String, String?) -> Unit) { + action("Media Type", parcelableMediaTypeString(mediaType)) + } +} diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/model/analyzer/UpdateStatus.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/model/analyzer/UpdateStatus.kt index d615a6fb7..a00c29057 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/model/analyzer/UpdateStatus.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/model/analyzer/UpdateStatus.kt @@ -1,6 +1,8 @@ package org.mariotaku.twidere.model.analyzer import org.mariotaku.twidere.annotation.AccountType +import org.mariotaku.twidere.extension.model.draftActionTypeString +import org.mariotaku.twidere.extension.model.parcelableMediaTypeString import org.mariotaku.twidere.model.Draft import org.mariotaku.twidere.model.ParcelableMedia import org.mariotaku.twidere.util.Analyzer @@ -30,29 +32,10 @@ data class UpdateStatus( get() = "Tweet" override fun forEachValues(action: (String, String?) -> Unit) { - action("Status Type", actionTypeString(actionType)) - action("Media Type", mediaTypeString(mediaType)) + action("Status Type", draftActionTypeString(actionType)) + action("Media Type", parcelableMediaTypeString(mediaType)) action("Location Type", locationType) action("Success", success.toString()) } - fun actionTypeString(@Draft.Action action: String?): String { - return when (action) { - Draft.Action.QUOTE -> "quote" - Draft.Action.REPLY -> "reply" - else -> "tweet" - } - } - - fun mediaTypeString(@ParcelableMedia.Type type: Int): String { - return when (type) { - ParcelableMedia.Type.IMAGE -> "image" - ParcelableMedia.Type.VIDEO -> "video" - ParcelableMedia.Type.ANIMATED_GIF -> "gif" - ParcelableMedia.Type.CARD_ANIMATED_GIF -> "gif" - ParcelableMedia.Type.EXTERNAL_PLAYER -> "external" - ParcelableMedia.Type.VARIABLE_TYPE -> "variable" - else -> "unknown" - } - } }