From b448dfaab75bbdde1578b9623e6c71b700271119 Mon Sep 17 00:00:00 2001 From: Laurent Date: Thu, 11 Feb 2021 12:15:28 +0100 Subject: [PATCH] Adds blog tips button --- app/build.gradle | 2 +- app/src/main/AndroidManifest.xml | 5 ++ .../pokeranalytics/android/api/BlogPostApi.kt | 49 +++++++++++++ .../android/model/blogpost/BlogPost.kt | 13 ++++ .../android/ui/fragment/SettingsFragment.kt | 6 ++ .../android/ui/modules/feed/FeedFragment.kt | 72 ++++++++++++++++--- .../android/ui/view/rows/SettingsRow.kt | 10 ++- .../android/util/Preferences.kt | 64 ++++++++++++++++- .../net/pokeranalytics/android/util/URL.kt | 1 + app/src/main/res/drawable/ic_public.xml | 9 +++ app/src/main/res/layout/fragment_feed.xml | 19 ++++- app/src/main/res/values/strings.xml | 3 + 12 files changed, 238 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/net/pokeranalytics/android/api/BlogPostApi.kt create mode 100644 app/src/main/java/net/pokeranalytics/android/model/blogpost/BlogPost.kt create mode 100644 app/src/main/res/drawable/ic_public.xml diff --git a/app/build.gradle b/app/build.gradle index d39fd84d..8b0e9a8a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -115,7 +115,7 @@ dependencies { // Android implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.core:core-ktx:1.3.1' - implementation 'com.google.android.material:material:1.2.1' + implementation 'com.google.android.material:material:1.3.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' implementation 'androidx.work:work-runtime-ktx:2.4.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0d1d9899..dedeb749 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -178,6 +178,11 @@ android:launchMode="singleTop" android:screenOrientation="portrait" /> + + { + + val posts = mutableListOf() + (0 until this.length()).forEach { index -> + val jo = this.getJSONObject(index) + val post = BlogPost(jo.getInt("id"), jo.getJSONObject("content").getString("rendered")) + posts.add(post) + } + return posts +} + +class BlogPostApi { + + companion object { + + private const val tipsLastPostsURL = "https://www.poker-analytics.net/blog/wp-json/wp/v2/posts/?categories=109\n" + + fun getLatestPosts(context: Context, callback: (List) -> (Unit)) { + + val queue = Volley.newRequestQueue(context) + + val jsonObjectRequest = JsonArrayRequest( + Request.Method.GET, tipsLastPostsURL, null, + { response -> +// Timber.d("posts = $response") + callback(response.toBlogPosts()) + }, + { error -> + Timber.w("Error while retrieving blog posts: $error") + } + ) + + queue.add(jsonObjectRequest) + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/blogpost/BlogPost.kt b/app/src/main/java/net/pokeranalytics/android/model/blogpost/BlogPost.kt new file mode 100644 index 00000000..605e5bb7 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/model/blogpost/BlogPost.kt @@ -0,0 +1,13 @@ +package net.pokeranalytics.android.model.blogpost + +import com.google.gson.annotations.SerializedName + +class BlogPost { + + @SerializedName("id") + var id: Int = 0 + + @SerializedName("level") + var content: String = "" + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/SettingsFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/SettingsFragment.kt index d0535dde..86c6b4f7 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/SettingsFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/SettingsFragment.kt @@ -153,6 +153,7 @@ class SettingsFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRep override fun boolForRow(row: RowRepresentable): Boolean { return when (row) { SettingsRow.STOP_NOTIFICATION -> Preferences.showStopNotifications(requireContext()) + SettingsRow.SHOULD_SHOW_BLOG_TIPS -> Preferences.shouldShowBlogTips(requireContext()) else -> false } } @@ -183,6 +184,7 @@ class SettingsFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRep 3 -> parentActivity?.openUrl(URL.FACEBOOK.value) } } + SettingsRow.BLOG_TIPS -> parentActivity?.openUrl(URL.BLOG_TIPS.value) SettingsRow.POKER_RUMBLE -> parentActivity?.openUrl(URL.POKER_RUMBLE.value) SettingsRow.DISCORD -> parentActivity?.openUrl(URL.DISCORD.value) SettingsRow.PRIVACY_POLICY -> parentActivity?.openUrl(URL.PRIVACY_POLICY.value) @@ -223,6 +225,10 @@ class SettingsFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRep } Preferences.setShowStopNotifications(show, requireContext()) } + SettingsRow.SHOULD_SHOW_BLOG_TIPS -> { + val show = value as Boolean + Preferences.showBlogTips(show, requireContext()) + } else -> {} } } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/feed/FeedFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/feed/FeedFragment.kt index a1fd6be3..7e72f3c5 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/feed/FeedFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/feed/FeedFragment.kt @@ -9,12 +9,15 @@ import androidx.core.app.ActivityOptionsCompat import androidx.core.view.isVisible import androidx.interpolator.view.animation.FastOutSlowInInterpolator import com.android.billingclient.api.Purchase +import com.google.android.material.badge.BadgeDrawable +import com.google.android.material.badge.BadgeUtils import com.google.android.material.tabs.TabLayout import io.realm.RealmModel import io.realm.RealmResults import io.realm.Sort import io.realm.kotlin.where import net.pokeranalytics.android.R +import net.pokeranalytics.android.api.BlogPostApi import net.pokeranalytics.android.databinding.FragmentFeedBinding import net.pokeranalytics.android.exceptions.PAIllegalStateException import net.pokeranalytics.android.model.LiveData @@ -25,6 +28,7 @@ import net.pokeranalytics.android.model.realm.handhistory.HandHistory import net.pokeranalytics.android.ui.activity.BillingActivity import net.pokeranalytics.android.ui.activity.components.RequestCode import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate +import net.pokeranalytics.android.ui.extensions.openUrl import net.pokeranalytics.android.ui.fragment.components.FilterableFragment import net.pokeranalytics.android.ui.modules.data.EditableDataActivity import net.pokeranalytics.android.ui.modules.datalist.DataListActivity @@ -36,6 +40,7 @@ import net.pokeranalytics.android.ui.modules.session.SessionActivity import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager import net.pokeranalytics.android.util.Preferences +import net.pokeranalytics.android.util.URL import net.pokeranalytics.android.util.billing.AppGuard import net.pokeranalytics.android.util.billing.PurchaseListener import net.pokeranalytics.android.util.extensions.count @@ -77,6 +82,8 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate, PurchaseLis private var selectedTransaction: Transaction? = null private var selectedTransactionPosition: Int = -1 + private var badgeDrawable: BadgeDrawable? = null + override val observedEntities: List> = listOf(Session::class.java, Transaction::class.java, HandHistory::class.java) @@ -121,7 +128,6 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate, PurchaseLis if (v.id == R.id.menuRecyclerView) { activity?.menuInflater?.inflate(R.menu.menu_session, menu) } - } override fun onRowLongClick(itemView: View, row: RowRepresentable, position: Int) { @@ -184,10 +190,6 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate, PurchaseLis } - override fun onDestroy() { - super.onDestroy() - } - override fun onDestroyView() { super.onDestroyView() AppGuard.unregisterListener(this) @@ -268,6 +270,27 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate, PurchaseLis } } + this.view?.viewTreeObserver?.addOnGlobalLayoutListener { + Timber.d(">>> addOnGlobalLayoutListener called") + retrieveLatestBlogPosts() // attempt to retrieve blog tips on display + } + + displayPostButton() + binding.postButton.setOnClickListener { + Preferences.setBlogTipsTapped(requireContext()) + parentActivity?.openUrl(URL.BLOG_TIPS.value) + displayPostButton() + } + + binding.postButton.viewTreeObserver.addOnGlobalLayoutListener { + val badgeDrawable = BadgeDrawable.create(requireContext()) + BadgeUtils.attachBadgeDrawable(badgeDrawable, binding.postButton) +// badgeDrawable.number = 2 + badgeDrawable.horizontalOffset = 30 + badgeDrawable.verticalOffset = 20 + this.badgeDrawable = badgeDrawable + } + // Tabs binding.tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { override fun onTabSelected(tab: TabLayout.Tab) { @@ -379,9 +402,9 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate, PurchaseLis handHistoryFilter?.results() ?: run { getRealm().where().findAll() } this.realmHandHistories = this.realmHandHistories.sort("date", Sort.DESCENDING) - this.realmHandHistories.forEach { - Timber.d("date = ${it.date}, year=${it.year}, month = ${it.month}, day = ${it.dayOfMonth}") - } +// this.realmHandHistories.forEach { +// Timber.d("date = ${it.date}, year=${it.year}, month = ${it.month}, day = ${it.dayOfMonth}") +// } var hhDistinctDates = handHistoryFilter?.results("year", "month", "dayOfMonth") ?: getRealm().where().distinct("year", "month", "dayOfMonth").findAll() @@ -557,4 +580,37 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate, PurchaseLis this.showSubscriptionButton() } + private fun retrieveLatestBlogPosts() { + + val now = Date().time + if (true) { +// if (Preferences.getLastBlogTipsRetrievalDate(requireContext()) + 24 * 3600 * 1000 < now) { + + BlogPostApi.getLatestPosts(requireContext()) { posts -> + Preferences.setLastBlogTipsRetrievalDate(now, requireContext()) + + var count = 0 + if (posts.isNotEmpty()) { + Preferences.setLatestRetrievedBlogPostId(posts.first().id, requireContext()) + + val id = Preferences.getLatestDisplayedBlogPostId(requireContext()) + + count = posts.count { it.id > id } + } + displayPostButton(count) + } + } + + } + + private fun displayPostButton(newCount: Int = 0) { + var show = false + if (Preferences.shouldShowBlogTips(requireContext()) && newCount > 0) { + show = true + this.badgeDrawable?.number = newCount + } + this.binding.postButton.isVisible = show + this.badgeDrawable?.isVisible = show + } + } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/rows/SettingsRow.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/rows/SettingsRow.kt index 46e980f5..68f127f6 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/rows/SettingsRow.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/rows/SettingsRow.kt @@ -29,6 +29,8 @@ enum class SettingsRow : RowRepresentable { // Follow FOLLOW_US, POKER_RUMBLE, + SHOULD_SHOW_BLOG_TIPS, + BLOG_TIPS, // Preferences LANGUAGE, @@ -70,10 +72,10 @@ enum class SettingsRow : RowRepresentable { } rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.information)) - rows.addAll(arrayListOf(SUBSCRIPTION, VERSION, RATE_APP, CONTACT_US, BUG_REPORT, DISCORD)) + rows.addAll(arrayListOf(SUBSCRIPTION, VERSION, RATE_APP, CONTACT_US, BUG_REPORT)) rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.follow_us)) - rows.addAll(arrayListOf(FOLLOW_US)) + rows.addAll(arrayListOf(FOLLOW_US, DISCORD, BLOG_TIPS, SHOULD_SHOW_BLOG_TIPS)) rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.preferences)) rows.addAll(arrayListOf(CURRENCY)) @@ -116,6 +118,8 @@ enum class SettingsRow : RowRepresentable { PRIVACY_POLICY -> R.string.privacy_policy TERMS_OF_USE -> R.string.terms_of_use FOLLOW_US -> R.string.follow_us + BLOG_TIPS -> R.string.blog_tips + SHOULD_SHOW_BLOG_TIPS -> R.string.show_blog_tips LANGUAGE -> R.string.language CURRENCY -> R.string.currency EXPORT_CSV_SESSIONS -> R.string.sessions_csv @@ -135,7 +139,7 @@ enum class SettingsRow : RowRepresentable { VERSION, SUBSCRIPTION -> RowViewType.TITLE_VALUE.ordinal LANGUAGE, CURRENCY -> RowViewType.TITLE_VALUE_ARROW.ordinal FOLLOW_US -> RowViewType.ROW_FOLLOW_US.ordinal - STOP_NOTIFICATION -> RowViewType.TITLE_SWITCH.ordinal + STOP_NOTIFICATION, SHOULD_SHOW_BLOG_TIPS -> RowViewType.TITLE_SWITCH.ordinal STOP_NOTIFICATION_MESSAGE -> RowViewType.INFO.ordinal else -> RowViewType.TITLE_ARROW.ordinal } diff --git a/app/src/main/java/net/pokeranalytics/android/util/Preferences.kt b/app/src/main/java/net/pokeranalytics/android/util/Preferences.kt index 440822ce..e6c3d978 100644 --- a/app/src/main/java/net/pokeranalytics/android/util/Preferences.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/Preferences.kt @@ -36,7 +36,11 @@ class Preferences { SHOW_STOP_NOTIFICATIONS("showStopNotifications"), ADD_NEW_TRANSACTION_TYPES("addNewTransactionTypes"), SHOW_VILLAIN_CARDS("showVillainCards"), - BANKROLL_RESULT_CAPTURE_TYPE("bankrollResultCaptureType_") + BANKROLL_RESULT_CAPTURE_TYPE("bankrollResultCaptureType_"), + LATEST_BLOG_POST_ID_RETRIEVED("latestBlogPostIdRetrieved"), + LATEST_BLOG_POST_ID_DISPLAYED("latestBlogPostIdDisplayed"), + LAST_BLOG_TIPS_RETRIEVAL("lastBlogTipsRetrieval"), + SHOW_BLOG_TIPS("showBlogTips"), } enum class FeedMessage { @@ -113,6 +117,29 @@ class Preferences { return preferences.getBoolean(key.identifier, defaultValue ?: false) } + private fun setInt(key: PreferenceKey, value: Int, context: Context) { + val preferences = PreferenceManager.getDefaultSharedPreferences(context) + val editor = preferences.edit() + editor.putInt(key.identifier, value) + editor.apply() + } + + private fun getInt(key: PreferenceKey, context: Context): Int { + val preferences = PreferenceManager.getDefaultSharedPreferences(context) + return preferences.getInt(key.identifier, -1) + } + + private fun setLong(key: PreferenceKey, value: Long, context: Context) { + val preferences = PreferenceManager.getDefaultSharedPreferences(context) + val editor = preferences.edit() + editor.putLong(key.identifier, value) + editor.apply() + } + + private fun getLong(key: PreferenceKey, context: Context): Long { + val preferences = PreferenceManager.getDefaultSharedPreferences(context) + return preferences.getLong(key.identifier, 0L) + } fun setShowVillainCards(show: Boolean, context: Context) { setBoolean(Keys.SHOW_VILLAIN_CARDS, show, context) @@ -213,7 +240,40 @@ class Preferences { else -> ResultCaptureType.values()[ordinal] } } - } + + fun setLatestRetrievedBlogPostId(id: Int, context: Context) { + setInt(Keys.LATEST_BLOG_POST_ID_RETRIEVED, id, context) + } + + private fun getLatestRetrievedBlogPostId(context: Context): Int { + return getInt(Keys.LATEST_BLOG_POST_ID_RETRIEVED, context) + } + + fun setBlogTipsTapped(context: Context) { + setInt(Keys.LATEST_BLOG_POST_ID_RETRIEVED, getLatestRetrievedBlogPostId(context), context) + } + + fun getLatestDisplayedBlogPostId(context: Context): Long { + return getLong(Keys.LATEST_BLOG_POST_ID_DISPLAYED, context) + } + + fun showBlogTips(show: Boolean, context: Context) { + setBoolean(Keys.SHOW_BLOG_TIPS, show, context) + } + + fun shouldShowBlogTips(context: Context): Boolean { + return getBoolean(Keys.SHOW_BLOG_TIPS, context, true) + } + + fun setLastBlogTipsRetrievalDate(date: Long, context: Context) { + setLong(Keys.LAST_BLOG_TIPS_RETRIEVAL, date, context) + } + + fun getLastBlogTipsRetrievalDate(context: Context): Long { + return getLong(Keys.LAST_BLOG_TIPS_RETRIEVAL, context) + } + + } } diff --git a/app/src/main/java/net/pokeranalytics/android/util/URL.kt b/app/src/main/java/net/pokeranalytics/android/util/URL.kt index 7c2b9e52..9f88ada7 100644 --- a/app/src/main/java/net/pokeranalytics/android/util/URL.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/URL.kt @@ -14,6 +14,7 @@ enum class URL(var value: String) { INSTAGRAM("https://www.instagram.com/pokeranalytics"), TWITTER("https://twitter.com/paapptweet"), FACEBOOK("https://www.facebook.com/171053452998758"), + BLOG_TIPS("https://www.poker-analytics.net/blog/category/tips/#main"), // Support SUPPORT_EMAIL("support@pokeranalytics.net"), diff --git a/app/src/main/res/drawable/ic_public.xml b/app/src/main/res/drawable/ic_public.xml new file mode 100644 index 00000000..70fa66d4 --- /dev/null +++ b/app/src/main/res/drawable/ic_public.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/fragment_feed.xml b/app/src/main/res/layout/fragment_feed.xml index 5a2c59ab..78afc002 100644 --- a/app/src/main/res/layout/fragment_feed.xml +++ b/app/src/main/res/layout/fragment_feed.xml @@ -104,7 +104,24 @@ android:transitionName="floating_action_button" app:fabSize="normal" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" /> + app:layout_constraintEnd_toEndOf="parent" + android:contentDescription="@string/add_manually" /> + + Please save before sharing It looks like there is an issue here. Please contact the support to get help. Please wait for a second or retry in a moment... + posts + Show tips on front page + App tips