~gardenapple/mitch

ref: 651b21d26b49d9b334662de2712d27236907226b mitch/app/src/main/java/ua/gardenapple/itchupdater/ItchWebsiteUtils.kt -rw-r--r-- 7.1 KiB
651b21d2gardenapple Update to latest Android Studio and Gradle stuff 2 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
package ua.gardenapple.itchupdater

import android.content.Context
import android.net.Uri
import android.webkit.CookieManager
import androidx.preference.PreferenceManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.Request
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import ua.gardenapple.itchupdater.ui.MitchWebView
import java.io.IOException
import java.net.URLEncoder

object ItchWebsiteUtils {
    val LOGIN_PAGE_URI: Uri = Uri.parse("https://itch.io/login")
    val STORE_ANDROID_PAGE_URI: Uri = Uri.parse("https://itch.io/games/platform-android")
    val STORE_PAGE_URI: Uri = Uri.parse("https://itch.io/games")

    private val gameBgColorPattern = Regex("root[{]--itchio_ui_bg: (#?\\w+);")
    private val gameButtonColorPattern = Regex("--itchio_button_color: (#?\\w+);")
    private val gameButtonFgColorPattern = Regex("--itchio_button_fg_color: (#?\\w+);")

    private val userBgColorPattern = Regex("--itchio_gray_back: (#?\\w+);")
    private val userFgColorPattern = Regex("--itchio_border_radius: ?\\w+;color:(#?\\w+);")


    fun isItchWebPage(uri: Uri): Boolean {
        return uri.host != null && (uri.host == "itch.io" ||
                uri.host!!.endsWith(".itch.io") ||
                uri.host!!.endsWith(".itch.zone") ||
                uri.host!!.endsWith(".hwcdn.net"))
    }

    fun isStorePage(htmlDoc: Document): Boolean {
        return htmlDoc.body().attr("data-page_name") == "view_game"
    }

    fun isDownloadPage(htmlDoc: Document): Boolean {
        return htmlDoc.body().attr("data-page_name") == "game_download"
    }

    fun isPurchasePage(htmlDoc: Document): Boolean {
        return htmlDoc.body().attr("data-page_name") == "game_purchase"
    }

    fun isUserPage(htmlDoc: Document): Boolean {
        return htmlDoc.body().attr("data-page_name") == "user"
    }

    fun isDevlogPage(htmlDoc: Document): Boolean {
        return when (htmlDoc.body().attr("data-page_name")) {
            "game.devlog" -> true
            "game.devlog_post" -> true
            else -> false
        }
    }

    fun isGamePage(htmlDoc: Document): Boolean {
        return isDevlogPage(htmlDoc) || isStorePage(htmlDoc) ||
                isPurchasePage(htmlDoc) || isDownloadPage(htmlDoc)
    }

    fun hasGameDownloadLinks(htmlDoc: Document): Boolean {
        return htmlDoc.body().selectFirst(".download_btn") != null
    }

    /**
     * @return true if the page is a store page or devlog page, or stylized community profile
     */
    fun isStylizedPage(htmlDoc: Document): Boolean {
        return htmlDoc.getElementById("game_theme") != null ||
                htmlDoc.getElementById("user_theme") != null
    }

    /**
     * @return true if the screen is small enough where itch.io starts introducing the bottom navbar AND we're looking at a store page now
     */
    fun siteHasNavbar(webView: MitchWebView, htmlDoc: Document): Boolean {
        return webView.contentWidth < 650 && htmlDoc.getElementById("user_tools") != null
    }

    /**
     * @return If htmlDoc is a store page or download page, will return the associated gameID. Otherwise, the behavior is undefined.
     */
    fun getGameId(htmlDoc: Document): Int? {
        return htmlDoc.head().selectFirst("[name=\"itch:path\"]")
            ?.attr("content")
            ?.substringAfter("games/")?.toInt()
    }


    suspend fun fetchAndParse(url: String): Document = withContext(Dispatchers.IO) {
        val request = Request.Builder().run {
            url(url)
            CookieManager.getInstance()?.getCookie(url)?.let { cookie ->
                addHeader("Cookie", cookie)
            }
            build()
        }
        var html = ""
        Mitch.httpClient.newCall(request).execute().use { response ->
            if (!response.isSuccessful)
                throw IOException("Unexpected response $response")
            html = response.body!!.string()
        }

        return@withContext Jsoup.parse(html)
    }

    /**
     * @return the URL that the user will see by default on the Browse tab.
     */
    fun getMainBrowsePage(context: Context): String {
        val prefs = PreferenceManager.getDefaultSharedPreferences(context)
        return when (prefs.getString("preference_start_page", "android")) {
            "android" -> "https://itch.io/games/platform-android"
            "web" -> "https://itch.io/games/platform-web"
            "web_touch" -> "https://itch.io/games/input-touchscreen/platform-web"
            "all_games" -> "https://itch.io/games"
            "library" -> "https://itch.io/my-collections"
            "feed" -> "https://itch.io/my-feed"
            else -> "https://itch.io"
        }
    }

    /**
     * @param searchQuery an itch.io search query, input from the user
     * @return the URL for search results on itch.io
     */
    fun getSearchUrl(searchQuery: String): String {
        val searchQueryEncoded = URLEncoder.encode(searchQuery, "utf-8")
        return "https://itch.io/search?q=$searchQueryEncoded"
    }

    /*
     * Methods for working with custom game themes or user themes
     */

    fun getBackgroundUIColor(doc: Document): Int? {
        val gameThemeCSS = doc.getElementById("game_theme")?.html()
        if (gameThemeCSS != null) {
            val foundColors = gameBgColorPattern.find(gameThemeCSS)
            if (foundColors != null)
                return Utils.parseCssColor(foundColors.groupValues[1])
        }

        val userThemeCSS = doc.getElementById("user_theme")?.html()
        if (userThemeCSS != null) {
            return Utils.parseCssColor("#333333")
        }
        return null
    }

    fun getAccentUIColor(doc: Document): Int? {
        val gameThemeCSS = doc.getElementById("game_theme")?.html()
        if (gameThemeCSS != null) {
            val foundColors = gameButtonColorPattern.find(gameThemeCSS)
            if (foundColors != null)
                return Utils.parseCssColor(foundColors.groupValues[1])
        }

        val userThemeCSS = doc.getElementById("user_theme")?.html()
        if (userThemeCSS != null) {
            val foundColors = userFgColorPattern.find(userThemeCSS)
            if (foundColors != null)
                return Utils.parseCssColor(foundColors.groupValues[1])
        }
        return null
    }


    fun getAccentFgUIColor(doc: Document): Int? {
        val gameThemeCSS = doc.getElementById("game_theme")?.html()
        if (gameThemeCSS != null) {
            val foundColors = gameButtonFgColorPattern.find(gameThemeCSS)
            if (foundColors != null)
                return Utils.parseCssColor(foundColors.groupValues[1])
        }

        val userThemeCSS = doc.getElementById("user_theme")?.html()
        if (userThemeCSS != null) {
            val foundColors = userBgColorPattern.find(userThemeCSS)
            if (foundColors != null)
                return Utils.parseCssColor(foundColors.groupValues[1])
        }
        return null
    }

    fun isDarkTheme(doc: Document): Boolean {
        return doc.selectFirst(".main_layout")?.hasClass("dark_theme") ?: false
    }

    fun getLoggedInUserName(doc: Document): String? {
        return doc.selectFirst(".user_name")?.html()
    }
}