improved extra features purchase flow

This commit is contained in:
Mariotaku Lee 2017-03-28 11:15:58 +08:00
parent d58337ab79
commit 3d219368cd
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
13 changed files with 65 additions and 52 deletions

View File

@ -1 +1 @@
2cf5b4a092585e624e14f0bb0d7eaff6d0f338bb e2cd7e2688f9099318c7bf4653c15eb1e7c212cf

View File

@ -493,6 +493,10 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
val df = RetweetProtectedStatusWarnFragment() val df = RetweetProtectedStatusWarnFragment()
df.show(supportFragmentManager, df.show(supportFragmentManager,
"retweet_protected_status_warning_message") "retweet_protected_status_warning_message")
} else if (scheduleInfo != null && !extraFeaturesService.isEnabled(ExtraFeaturesService.FEATURE_SCHEDULE_STATUS)) {
ExtraFeaturesIntroductionDialogFragment.show(supportFragmentManager,
feature = ExtraFeaturesService.FEATURE_SCHEDULE_STATUS,
requestCode = REQUEST_PURCHASE_EXTRA_FEATURES)
} else { } else {
updateStatus() updateStatus()
} }
@ -549,13 +553,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
} }
R.id.schedule -> { R.id.schedule -> {
val controller = statusScheduleController ?: return true val controller = statusScheduleController ?: return true
if (extraFeaturesService.isEnabled(ExtraFeaturesService.FEATURE_SCHEDULE_STATUS)) { startActivityForResult(controller.createSetScheduleIntent(), REQUEST_SET_SCHEDULE)
startActivityForResult(controller.createSetScheduleIntent(), REQUEST_SET_SCHEDULE)
} else {
ExtraFeaturesIntroductionDialogFragment.show(supportFragmentManager,
feature = ExtraFeaturesService.FEATURE_SCHEDULE_STATUS,
requestCode = REQUEST_PURCHASE_EXTRA_FEATURES)
}
} }
else -> { else -> {
val intent = item.intent val intent = item.intent

View File

@ -47,7 +47,7 @@ class SelectableUsersAdapter(
private val inflater: LayoutInflater = LayoutInflater.from(context) private val inflater: LayoutInflater = LayoutInflater.from(context)
private val checkedState: MutableMap<UserKey, Boolean> = ArrayMap() private val checkedState: MutableMap<UserKey, Boolean> = ArrayMap()
private val lockedState: MutableMap<UserKey, Boolean> = ArrayMap() private val lockedState: MutableMap<UserKey, Boolean> = ArrayMap()
var itemCheckedListener: ((Int, Boolean) -> Unit)? = null var itemCheckedListener: ((Int, Boolean) -> Boolean)? = null
var data: List<ParcelableUser>? = null var data: List<ParcelableUser>? = null
set(value) { set(value) {
@ -174,7 +174,10 @@ class SelectableUsersAdapter(
fun setItemChecked(position: Int, value: Boolean) { fun setItemChecked(position: Int, value: Boolean) {
val userKey = getUserKey(position) val userKey = getUserKey(position)
setCheckState(userKey, value) setCheckState(userKey, value)
itemCheckedListener?.invoke(position, value) if (!(itemCheckedListener?.invoke(position, value) ?: true)) {
setCheckState(userKey, !value)
notifyItemChanged(position)
}
} }
fun clearCheckState() { fun clearCheckState() {

View File

@ -135,14 +135,8 @@ class DraftsFragment : BaseFragment(), LoaderCallbacks<Cursor?>, OnItemClickList
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.scheduled_statuses -> { R.id.scheduled_statuses -> {
if (extraFeaturesService.isEnabled(ExtraFeaturesService.FEATURE_SCHEDULE_STATUS)) { val scheduleManageIntent = statusScheduleController?.createManageIntent() ?: return true
val scheduleManageIntent = statusScheduleController?.createManageIntent() startActivity(scheduleManageIntent)
startActivity(scheduleManageIntent)
} else {
ExtraFeaturesIntroductionDialogFragment.show(childFragmentManager,
ExtraFeaturesService.FEATURE_SCHEDULE_STATUS,
requestCode = REQUEST_PURCHASE_EXTRA_FEATURES)
}
return true return true
} }
} }

View File

@ -1,10 +1,11 @@
package org.mariotaku.twidere.fragment.filter package org.mariotaku.twidere.fragment.filter
import android.app.Activity
import android.app.Dialog import android.app.Dialog
import android.content.Context import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.support.v4.app.LoaderManager import android.support.v4.app.LoaderManager
import android.support.v4.content.Loader import android.support.v4.content.Loader
import android.support.v7.app.AlertDialog import android.support.v7.app.AlertDialog
@ -20,6 +21,7 @@ import nl.komponents.kovenant.task
import nl.komponents.kovenant.ui.alwaysUi import nl.komponents.kovenant.ui.alwaysUi
import org.mariotaku.ktextension.* import org.mariotaku.ktextension.*
import org.mariotaku.twidere.R import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.REQUEST_PURCHASE_EXTRA_FEATURES
import org.mariotaku.twidere.activity.BaseActivity import org.mariotaku.twidere.activity.BaseActivity
import org.mariotaku.twidere.adapter.SelectableUsersAdapter import org.mariotaku.twidere.adapter.SelectableUsersAdapter
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter
@ -27,14 +29,14 @@ import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter.IndicatorPosi
import org.mariotaku.twidere.constant.IntentConstants import org.mariotaku.twidere.constant.IntentConstants
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_COUNT import org.mariotaku.twidere.constant.IntentConstants.EXTRA_COUNT
import org.mariotaku.twidere.extension.applyTheme import org.mariotaku.twidere.extension.applyTheme
import org.mariotaku.twidere.fragment.AbsContentListRecyclerViewFragment import org.mariotaku.twidere.fragment.*
import org.mariotaku.twidere.fragment.BaseDialogFragment
import org.mariotaku.twidere.fragment.MessageDialogFragment
import org.mariotaku.twidere.fragment.ProgressDialogFragment
import org.mariotaku.twidere.loader.CursorSupportUsersLoader import org.mariotaku.twidere.loader.CursorSupportUsersLoader
import org.mariotaku.twidere.loader.iface.IExtendedLoader import org.mariotaku.twidere.loader.iface.IExtendedLoader
import org.mariotaku.twidere.model.ParcelableUser import org.mariotaku.twidere.model.ParcelableUser
import org.mariotaku.twidere.model.analyzer.PurchaseFinished
import org.mariotaku.twidere.util.Analyzer
import org.mariotaku.twidere.util.DataStoreUtils import org.mariotaku.twidere.util.DataStoreUtils
import org.mariotaku.twidere.util.premium.ExtraFeaturesService
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
/** /**
@ -59,6 +61,16 @@ abstract class BaseFiltersImportFragment : AbsContentListRecyclerViewFragment<Se
loaderManager.initLoader(0, loaderArgs, this) loaderManager.initLoader(0, loaderArgs, this)
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
REQUEST_PURCHASE_EXTRA_FEATURES -> {
if (resultCode == Activity.RESULT_OK) {
Analyzer.log(PurchaseFinished.create(data!!))
}
}
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_filters_import, menu) inflater.inflate(R.menu.menu_filters_import, menu)
} }
@ -68,7 +80,7 @@ abstract class BaseFiltersImportFragment : AbsContentListRecyclerViewFragment<Se
val userCount = adapter.userCount val userCount = adapter.userCount
menu.setItemAvailability(R.id.select_none, checkedCount > 0) menu.setItemAvailability(R.id.select_none, checkedCount > 0)
menu.setItemAvailability(R.id.select_all, checkedCount < userCount) menu.setItemAvailability(R.id.select_all, checkedCount < userCount)
menu.setItemAvailability(R.id.invert_selection, checkedCount > 0 && checkedCount < userCount) menu.setItemAvailability(R.id.invert_selection, checkedCount in 1 until userCount)
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -94,6 +106,12 @@ abstract class BaseFiltersImportFragment : AbsContentListRecyclerViewFragment<Se
Toast.makeText(context, R.string.message_toast_no_user_selected, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.message_toast_no_user_selected, Toast.LENGTH_SHORT).show()
return true return true
} }
if (!extraFeaturesService.isEnabled(ExtraFeaturesService.FEATURE_FILTERS_IMPORT)) {
ExtraFeaturesIntroductionDialogFragment.show(fragmentManager,
feature = ExtraFeaturesService.FEATURE_FILTERS_IMPORT,
requestCode = REQUEST_PURCHASE_EXTRA_FEATURES)
return true
}
val df = ImportConfirmDialogFragment() val df = ImportConfirmDialogFragment()
df.arguments = Bundle { df.arguments = Bundle {
this[EXTRA_COUNT] = adapter.checkedCount this[EXTRA_COUNT] = adapter.checkedCount
@ -169,7 +187,13 @@ abstract class BaseFiltersImportFragment : AbsContentListRecyclerViewFragment<Se
override fun onCreateAdapter(context: Context): SelectableUsersAdapter { override fun onCreateAdapter(context: Context): SelectableUsersAdapter {
val adapter = SelectableUsersAdapter(context, Glide.with(this)) val adapter = SelectableUsersAdapter(context, Glide.with(this))
adapter.itemCheckedListener = { _, _ -> adapter.itemCheckedListener = listener@ { position, value ->
if (!extraFeaturesService.isEnabled(ExtraFeaturesService.FEATURE_FILTERS_IMPORT)) {
ExtraFeaturesIntroductionDialogFragment.show(fragmentManager,
feature = ExtraFeaturesService.FEATURE_FILTERS_IMPORT,
requestCode = REQUEST_PURCHASE_EXTRA_FEATURES)
return@listener false
}
val count = adapter.checkedCount val count = adapter.checkedCount
val actionBar = (activity as BaseActivity).supportActionBar val actionBar = (activity as BaseActivity).supportActionBar
actionBar?.subtitle = if (count > 0) { actionBar?.subtitle = if (count > 0) {
@ -178,6 +202,7 @@ abstract class BaseFiltersImportFragment : AbsContentListRecyclerViewFragment<Se
null null
} }
activity.supportInvalidateOptionsMenu() activity.supportInvalidateOptionsMenu()
return@listener true
} }
return adapter return adapter
} }

View File

@ -1,14 +1,12 @@
package org.mariotaku.twidere.fragment.filter package org.mariotaku.twidere.fragment.filter
import android.net.Uri import android.net.Uri
import org.mariotaku.twidere.provider.TwidereDataStore import org.mariotaku.twidere.provider.TwidereDataStore.Filters
class FilteredKeywordsFragment : BaseFiltersFragment() { class FilteredKeywordsFragment : BaseFiltersFragment() {
override val contentUri: Uri override val contentUri: Uri = Filters.Keywords.CONTENT_URI
get() = TwidereDataStore.Filters.Keywords.CONTENT_URI
override val contentColumns: Array<String> override val contentColumns: Array<String> = Filters.Keywords.COLUMNS
get() = TwidereDataStore.Filters.Keywords.COLUMNS
} }

View File

@ -1,15 +1,12 @@
package org.mariotaku.twidere.fragment.filter package org.mariotaku.twidere.fragment.filter
import android.net.Uri import android.net.Uri
import org.mariotaku.twidere.fragment.filter.BaseFiltersFragment import org.mariotaku.twidere.provider.TwidereDataStore.Filters
import org.mariotaku.twidere.provider.TwidereDataStore
class FilteredLinksFragment : BaseFiltersFragment() { class FilteredLinksFragment : BaseFiltersFragment() {
override val contentColumns: Array<String> override val contentColumns: Array<String> = Filters.Links.COLUMNS
get() = TwidereDataStore.Filters.Links.COLUMNS
override val contentUri: Uri override val contentUri: Uri = Filters.Links.CONTENT_URI
get() = TwidereDataStore.Filters.Links.CONTENT_URI
} }

View File

@ -29,7 +29,6 @@ import org.mariotaku.twidere.activity.UserSelectorActivity
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_ACCOUNT_HOST import org.mariotaku.twidere.constant.IntentConstants.EXTRA_ACCOUNT_HOST
import org.mariotaku.twidere.constant.nameFirstKey import org.mariotaku.twidere.constant.nameFirstKey
import org.mariotaku.twidere.fragment.AddUserFilterDialogFragment import org.mariotaku.twidere.fragment.AddUserFilterDialogFragment
import org.mariotaku.twidere.fragment.ExtraFeaturesIntroductionDialogFragment
import org.mariotaku.twidere.model.FiltersData import org.mariotaku.twidere.model.FiltersData
import org.mariotaku.twidere.model.ParcelableUser import org.mariotaku.twidere.model.ParcelableUser
import org.mariotaku.twidere.model.UserKey import org.mariotaku.twidere.model.UserKey
@ -41,7 +40,6 @@ import org.mariotaku.twidere.util.ThemeUtils
import org.mariotaku.twidere.util.UserColorNameManager import org.mariotaku.twidere.util.UserColorNameManager
import org.mariotaku.twidere.util.content.ContentResolverUtils import org.mariotaku.twidere.util.content.ContentResolverUtils
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import org.mariotaku.twidere.util.premium.ExtraFeaturesService
import javax.inject.Inject import javax.inject.Inject
class FilteredUsersFragment : BaseFiltersFragment() { class FilteredUsersFragment : BaseFiltersFragment() {
@ -105,31 +103,22 @@ class FilteredUsersFragment : BaseFiltersFragment() {
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
var isExtraFeatures: Boolean = false
val intent = Intent(context, AccountSelectorActivity::class.java) val intent = Intent(context, AccountSelectorActivity::class.java)
intent.putExtra(EXTRA_SINGLE_SELECTION, true) intent.putExtra(EXTRA_SINGLE_SELECTION, true)
intent.putExtra(EXTRA_SELECT_ONLY_ITEM_AUTOMATICALLY, true) intent.putExtra(EXTRA_SELECT_ONLY_ITEM_AUTOMATICALLY, true)
val requestCode = when (item.itemId) { val requestCode = when (item.itemId) {
R.id.add_user_single, R.id.add_user -> REQUEST_ADD_USER_SELECT_ACCOUNT R.id.add_user_single, R.id.add_user -> REQUEST_ADD_USER_SELECT_ACCOUNT
R.id.import_from_blocked_users -> { R.id.import_from_blocked_users -> {
isExtraFeatures = true
REQUEST_IMPORT_BLOCKS_SELECT_ACCOUNT REQUEST_IMPORT_BLOCKS_SELECT_ACCOUNT
} }
R.id.import_from_muted_users -> { R.id.import_from_muted_users -> {
isExtraFeatures = true
intent.putExtra(EXTRA_ACCOUNT_HOST, USER_TYPE_TWITTER_COM) intent.putExtra(EXTRA_ACCOUNT_HOST, USER_TYPE_TWITTER_COM)
REQUEST_IMPORT_MUTES_SELECT_ACCOUNT REQUEST_IMPORT_MUTES_SELECT_ACCOUNT
} }
else -> return false else -> return false
} }
if (!isExtraFeatures || extraFeaturesService.isEnabled(ExtraFeaturesService.FEATURE_FILTERS_IMPORT)) { startActivityForResult(intent, requestCode)
startActivityForResult(intent, requestCode)
} else {
ExtraFeaturesIntroductionDialogFragment.show(childFragmentManager,
feature = ExtraFeaturesService.FEATURE_FILTERS_IMPORT,
requestCode = REQUEST_PURCHASE_EXTRA_FEATURES)
}
return true return true
} }

View File

@ -140,8 +140,8 @@ class MessageNewConversationFragment : BaseFragment(), LoaderCallbacks<List<Parc
val roundRadius = resources.getDimension(R.dimen.element_spacing_xsmall) val roundRadius = resources.getDimension(R.dimen.element_spacing_xsmall)
val spanPadding = resources.getDimension(R.dimen.element_spacing_xsmall) val spanPadding = resources.getDimension(R.dimen.element_spacing_xsmall)
usersAdapter.itemCheckedListener = itemChecked@ { pos, checked -> usersAdapter.itemCheckedListener = itemChecked@ { pos, checked ->
val text: Editable = editParticipants.editableText ?: return@itemChecked val text: Editable = editParticipants.editableText ?: return@itemChecked false
val user = usersAdapter.getUser(pos) ?: return@itemChecked val user = usersAdapter.getUser(pos) ?: return@itemChecked false
if (checked) { if (checked) {
text.getSpans(0, text.length, PendingQuerySpan::class.java).forEach { pending -> text.getSpans(0, text.length, PendingQuerySpan::class.java).forEach { pending ->
val start = text.getSpanStart(pending) val start = text.getSpanStart(pending)
@ -173,6 +173,7 @@ class MessageNewConversationFragment : BaseFragment(), LoaderCallbacks<List<Parc
} }
editParticipants.clearComposingText() editParticipants.clearComposingText()
updateCheckState() updateCheckState()
return@itemChecked true
} }
} }

View File

@ -7,6 +7,7 @@ import org.mariotaku.kpreferences.KPreferences
import org.mariotaku.twidere.util.* import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import org.mariotaku.twidere.util.media.MediaPreloader import org.mariotaku.twidere.util.media.MediaPreloader
import org.mariotaku.twidere.util.premium.ExtraFeaturesService
import org.mariotaku.twidere.util.schedule.StatusScheduleController import org.mariotaku.twidere.util.schedule.StatusScheduleController
import javax.inject.Inject import javax.inject.Inject
@ -35,6 +36,8 @@ abstract class BaseAbstractTask<Params, Result, Callback>(val context: Context)
@Inject @Inject
lateinit var userColorNameManager: UserColorNameManager lateinit var userColorNameManager: UserColorNameManager
@Inject @Inject
lateinit var extraFeaturesService: ExtraFeaturesService
@Inject
lateinit var scheduleControllerFactory: StatusScheduleController.Factory lateinit var scheduleControllerFactory: StatusScheduleController.Factory
val scheduleController: StatusScheduleController? val scheduleController: StatusScheduleController?

View File

@ -51,6 +51,7 @@ import org.mariotaku.twidere.provider.TwidereDataStore.Drafts
import org.mariotaku.twidere.task.BaseAbstractTask import org.mariotaku.twidere.task.BaseAbstractTask
import org.mariotaku.twidere.util.* import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.io.ContentLengthInputStream import org.mariotaku.twidere.util.io.ContentLengthInputStream
import org.mariotaku.twidere.util.premium.ExtraFeaturesService
import java.io.Closeable import java.io.Closeable
import java.io.File import java.io.File
import java.io.FileNotFoundException import java.io.FileNotFoundException
@ -257,9 +258,12 @@ class UpdateStatusTask(
): UpdateStatusResult { ): UpdateStatusResult {
stateCallback.onUpdatingStatus() stateCallback.onUpdatingStatus()
if (!extraFeaturesService.isEnabled(ExtraFeaturesService.FEATURE_SCHEDULE_STATUS)) {
throw SchedulerNotFoundException(context.getString(R.string.error_message_scheduler_not_available))
}
val controller = scheduleController ?: run { val controller = scheduleController ?: run {
throw SchedulerNotFoundException("No scheduler found") throw SchedulerNotFoundException(context.getString(R.string.error_message_scheduler_not_available))
} }
controller.scheduleStatus(statusUpdate, pendingUpdate, scheduleInfo) controller.scheduleStatus(statusUpdate, pendingUpdate, scheduleInfo)

View File

@ -51,7 +51,6 @@ abstract class ExtraFeaturesService {
const val FEATURE_FILTERS_IMPORT = "import_filters" const val FEATURE_FILTERS_IMPORT = "import_filters"
const val FEATURE_FILTERS_SUBSCRIPTION = "filters_subscriptions" const val FEATURE_FILTERS_SUBSCRIPTION = "filters_subscriptions"
const val FEATURE_SYNC_DATA = "sync_data" const val FEATURE_SYNC_DATA = "sync_data"
const val FEATURE_SCHEDULE_STATUS = "schedule_status" const val FEATURE_SCHEDULE_STATUS = "schedule_status"
fun newInstance(context: Context): ExtraFeaturesService { fun newInstance(context: Context): ExtraFeaturesService {

View File

@ -368,6 +368,7 @@
<string name="error_message_no_content">No content</string> <string name="error_message_no_content">No content</string>
<string name="error_message_rate_limit">Twitter\'s rate limit exceeded, please retry <xliff:g id="time">%s</xliff:g></string> <string name="error_message_rate_limit">Twitter\'s rate limit exceeded, please retry <xliff:g id="time">%s</xliff:g></string>
<string name="error_message_rate_limit_with_action">Twitter\'s rate limit exceeded while <xliff:g id="action">%1$s</xliff:g>, please retry <xliff:g id="time">%2$s</xliff:g></string> <string name="error_message_rate_limit_with_action">Twitter\'s rate limit exceeded while <xliff:g id="action">%1$s</xliff:g>, please retry <xliff:g id="time">%2$s</xliff:g></string>
<string name="error_message_scheduler_not_available">Tweet schedule not available</string>
<string name="error_message_status_too_long">Tweet too long.</string> <string name="error_message_status_too_long">Tweet too long.</string>
<string name="error_message_tweet_shorten_failed">Tweet shorten failed.</string> <string name="error_message_tweet_shorten_failed">Tweet shorten failed.</string>
<string name="error_message_tweet_shortener_not_found">Tweet shortener not found, maybe it was uninstalled.</string> <string name="error_message_tweet_shortener_not_found">Tweet shortener not found, maybe it was uninstalled.</string>
@ -599,6 +600,7 @@
<string name="message_auto_refresh_confirm">Enable auto refresh to get new tweets automatically?</string> <string name="message_auto_refresh_confirm">Enable auto refresh to get new tweets automatically?</string>
<string name="message_blocked_user">Blocked <xliff:g id="user">%s</xliff:g>.</string> <string name="message_blocked_user">Blocked <xliff:g id="user">%s</xliff:g>.</string>
<string name="message_buffer_delete_schedule_confirm">Delete this schedule?</string> <string name="message_buffer_delete_schedule_confirm">Delete this schedule?</string>
<string name="message_buffer_disconnect_confirm">Disconnect from Buffer?</string>
<string name="message_clear_messages_confirm">Clear all messages?</string> <string name="message_clear_messages_confirm">Clear all messages?</string>
<string name="message_conversation_created">Conversation created.</string> <string name="message_conversation_created">Conversation created.</string>
<string name="message_destroy_conversation_confirm">Leave this conversation?</string> <string name="message_destroy_conversation_confirm">Leave this conversation?</string>