Adds blog tips button

blinds
Laurent 5 years ago
parent 4b1f3abc4e
commit b448dfaab7
  1. 2
      app/build.gradle
  2. 5
      app/src/main/AndroidManifest.xml
  3. 49
      app/src/main/java/net/pokeranalytics/android/api/BlogPostApi.kt
  4. 13
      app/src/main/java/net/pokeranalytics/android/model/blogpost/BlogPost.kt
  5. 6
      app/src/main/java/net/pokeranalytics/android/ui/fragment/SettingsFragment.kt
  6. 72
      app/src/main/java/net/pokeranalytics/android/ui/modules/feed/FeedFragment.kt
  7. 10
      app/src/main/java/net/pokeranalytics/android/ui/view/rows/SettingsRow.kt
  8. 64
      app/src/main/java/net/pokeranalytics/android/util/Preferences.kt
  9. 1
      app/src/main/java/net/pokeranalytics/android/util/URL.kt
  10. 9
      app/src/main/res/drawable/ic_public.xml
  11. 19
      app/src/main/res/layout/fragment_feed.xml
  12. 3
      app/src/main/res/values/strings.xml

@ -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'

@ -178,6 +178,11 @@
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name="net.pokeranalytics.android.ui.modules.blogposts.BlogPostActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<!-- No screenOrientation="portrait" to fix Oreo crash -->
<activity
android:name="net.pokeranalytics.android.ui.activity.ColorPickerActivity"

@ -0,0 +1,49 @@
package net.pokeranalytics.android.api
import android.content.Context
import com.android.volley.Request
import com.android.volley.toolbox.JsonArrayRequest
import com.android.volley.toolbox.Volley
import org.json.JSONArray
import timber.log.Timber
data class BlogPost(var id: Int, var content: String)
private fun JSONArray.toBlogPosts(): List<BlogPost> {
val posts = mutableListOf<BlogPost>()
(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<BlogPost>) -> (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)
}
}
}

@ -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 = ""
}

@ -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 -> {}
}
}

@ -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<Class<out RealmModel>> =
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<HandHistory>().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<HandHistory>().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
}
}

@ -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
}

@ -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)
}
}
}

@ -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"),

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11,19.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L9,15v1c0,1.1 0.9,2 2,2v1.93zM17.9,17.39c-0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1L8,12v-2h2c0.55,0 1,-0.45 1,-1L11,7h2c1.1,0 2,-0.9 2,-2v-0.41c2.93,1.19 5,4.06 5,7.41 0,2.08 -0.8,3.97 -2.1,5.39z"
android:fillColor="#000000"/>
</vector>

@ -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" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/postButton"
style="@style/PokerAnalyticsTheme.FloatingButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|start"
android:layout_marginStart="16dp"
android:layout_marginBottom="16dp"
android:src="@drawable/ic_public"
android:tint="@color/black"
android:transitionName="floating_action_button_blog_post"
app:fabSize="normal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:contentDescription="@string/posts" />
<com.google.android.material.button.MaterialButton
android:id="@+id/subscribe"

@ -808,5 +808,8 @@
<string name="please_save_hand_history">Please save before sharing</string>
<string name="contact_support">It looks like there is an issue here. Please contact the support to get help.</string>
<string name="product_unavailable">Please wait for a second or retry in a moment...</string>
<string name="posts">posts</string>
<string name="show_blog_tips">Show tips on front page</string>
<string name="blog_tips">App tips</string>
</resources>

Loading…
Cancel
Save