Skip to content

Commit

Permalink
Add documentation to all utilities
Browse files Browse the repository at this point in the history
  • Loading branch information
wingio committed Dec 5, 2023
1 parent 50a5a87 commit ecac4b3
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 10 deletions.
2 changes: 1 addition & 1 deletion app/src/main/java/xyz/wingio/dimett/Dimett.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class Dimett : Application() {
override fun onCreate() {
super.onCreate()

// Add gif support (https://coil-kt.github.io/coil/gifs/)
val imageLoader = ImageLoader.Builder(this)
.components {
if (Build.VERSION.SDK_INT >= 28) {
Expand All @@ -46,7 +47,6 @@ class Dimett : Application() {
loggerModule
)
}

}

}
13 changes: 12 additions & 1 deletion app/src/main/java/xyz/wingio/dimett/utils/CustomTabUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ import android.content.Intent
import android.net.Uri
import androidx.browser.customtabs.CustomTabsIntent

/**
* Cached result of [defaultBrowserPackage]
*/
private var mDefaultBrowserPackage: String? = null

@Suppress("DEPRECATION")
/**
* Gets the package name for the default browser app on the users device
*/
private val Context.defaultBrowserPackage: String?
@SuppressLint("QueryPermissionsNeeded")
get() {
Expand All @@ -25,6 +30,12 @@ private val Context.defaultBrowserPackage: String?
} else mDefaultBrowserPackage
}

/**
* Open a Chrome custom tab
*
* @param url Url of the desired webpage
* @param force Whether or not to force a custom tab, avoids certain links being opened in a non browser app
*/
fun Context.openCustomTab(url: String, force: Boolean) = CustomTabsIntent.Builder().build().run {
if (force) intent.setPackage(defaultBrowserPackage)
launchUrl(this@openCustomTab, Uri.parse(url))
Expand Down
16 changes: 16 additions & 0 deletions app/src/main/java/xyz/wingio/dimett/utils/EmojiUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,40 @@ import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import java.util.regex.Pattern

/**
* Collection of utilities for dealing with emojis
*/
object EmojiUtils : KoinComponent {

private val context: Context by inject()
private val json: Json by inject()

/**
* Parses `assets/emoji.json` and returns a map of emoji codepoints to the Twemoji versions resource name located in `res/raw`
*/
val emojis by lazy {
val _json = String(context.assets.open("emoji.json").readBytes())
json.decodeFromString<EmojiJson>(_json).emoji
}

/**
* An autogenerated regex pattern matching every supported emoji
*
* @see [xyz.wingio.dimett.ast.addUnicodeEmojiRule]
*/
val regex by lazy {
"^(${
emojis.keys.sortedByDescending { it.length }.joinToString("|") { emoji ->
Pattern.quote(emoji)
}
})"
}

}

/**
* Usable representation of `assets/emoji.json`
*/
@Serializable
data class EmojiJson(
val emoji: Map<String, String>
Expand Down
57 changes: 56 additions & 1 deletion app/src/main/java/xyz/wingio/dimett/utils/Logger.kt
Original file line number Diff line number Diff line change
@@ -1,24 +1,76 @@
package xyz.wingio.dimett.utils

import android.util.Log
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import xyz.wingio.dimett.BuildConfig

/**
* More readable logger implementation
*
* @param tag Tag to be shown in logcat
*/
class Logger(
private val tag: String
) {

/**
* The lowest log level, usually used for very frequent logs
*
* @param msg The message to be printed
* @param throwable Optional error to be printed alongside the message
*/
fun verbose(msg: String?, throwable: Throwable? = null) = log(msg, throwable, Level.VERBOSE)

/**
* Second lowest log level, used for intentional debug messages
*
* @param msg The message to be printed
* @param throwable Optional error to be printed alongside the message
*/
fun debug(msg: String?, throwable: Throwable? = null) = log(msg, throwable, Level.DEBUG)

/**
* Middle log level, used for printing basic information
*
* @param msg The message to be printed
* @param throwable Optional error to be printed alongside the message
*/
fun info(msg: String?, throwable: Throwable? = null) = log(msg, throwable, Level.INFO)

/**
* Second highest log level, used when something could potentially be going wrong
*
* @param msg The message to be printed
* @param throwable Optional error to be printed alongside the message
*/
fun warn(msg: String?, throwable: Throwable? = null) = log(msg, throwable, Level.WARN)

/**
* Highest log level, used when something has gone wrong or an exception was thrown
*
* @param msg The message to be printed
* @param throwable Optional error to be printed alongside the message
*/
fun error(msg: String?, throwable: Throwable? = null) = log(msg, throwable, Level.ERROR)

inline fun <reified T> logSerializable(obj: T) = log(
/**
* Prints a JSON representation of an object
*
* @param obj The object to print, must be annotated with [Serializable]
*/
inline fun <reified T> logSerializable(obj: @Serializable T) = log(
msg = Json.Default.encodeToString(obj)
)

/**
* Logs a message at the specified [level] if the app's build is debuggable
*
* @param msg The message to be printed
* @param throwable Optional error to be printed alongside the message
* @param level Level to log this message at, see [Level]
*/
fun log(msg: String?, throwable: Throwable? = null, level: Level = Level.INFO) {
if (!BuildConfig.DEBUG) return
when (level) {
Expand All @@ -30,6 +82,9 @@ class Logger(
}
}

/**
* Levels used when logging messages
*/
enum class Level {
VERBOSE,
DEBUG,
Expand Down
27 changes: 24 additions & 3 deletions app/src/main/java/xyz/wingio/dimett/utils/NavUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.annotation.StringRes
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.pager.PagerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocal
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
Expand All @@ -18,24 +19,44 @@ import xyz.wingio.dimett.ui.screens.feed.FeedTab
import xyz.wingio.dimett.ui.screens.profile.ProfileTab
import xyz.wingio.dimett.ui.screens.notifications.NotificationsTab

/**
* Tabs that appear on the [main screen][xyz.wingio.dimett.ui.screens.main.MainScreen]
*/
enum class RootTab(val tab: Tab) {
FEED(FeedTab()),
EXPLORE(ExploreTab()),
NOTIFICATIONS(NotificationsTab()),
PROFILE(ProfileTab()),
}

/**
* Safely navigate to the given [screen] from the root [Navigator]
*
* @param screen Where to navigate
*/
tailrec fun Navigator.navigate(screen: Screen) {
return if (parent == null && items.firstOrNull { it.key == screen.key } == null) try {
return if (
parent == null // Is the root navigator
&& items.firstOrNull { it.key == screen.key } == null // Doesn't already have the screen in the navigation stack
) try {
push(screen)
} catch (_: Throwable) {
}
} catch (_: Throwable) {}
else parent!!.navigate(screen)
}

/**
* [CompositionLocal] instance for [PagerState]
*/
@OptIn(ExperimentalFoundationApi::class)
val LocalPagerState = compositionLocalOf<PagerState> { error("No PagerState provided") }

/**
* Convenience function for providing tab options with alternating selected and unselected icons
*
* @param name String resource id for the name of this tab
* @param icon Icon to be shown when the tab is not selected (Usually outlined)
* @param iconSelected Icon to be shown when the tab is selected (Usually filled)
*/
@Composable
@SuppressLint("ComposableNaming")
@OptIn(ExperimentalFoundationApi::class)
Expand Down
18 changes: 16 additions & 2 deletions app/src/main/java/xyz/wingio/dimett/utils/TextUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ import xyz.wingio.dimett.ast.rendercontext.DefaultRenderContext
import xyz.wingio.syntakts.Syntakts
import xyz.wingio.syntakts.compose.rememberRendered

/**
* Required in order to support custom emotes and Twemoji
*
* @param textStyle Used for calculating the size of the emoji
*/
@Composable
fun inlineContent(
textStyle: TextStyle = LocalTextStyle.current
Expand Down Expand Up @@ -70,13 +75,22 @@ fun inlineContent(
}
}


/**
* Retrieves a string resource and formats it
*
* @param string Resource id for the desired string
* @param args Formatting arguments
* ---
* Ex: "%1$s has favorited your post" with user.username
* @param syntakts The [Syntakts] instance used to render the text
* @param actionHandler Ran whenever any text is clicked
*/
@Composable
fun getString(
@StringRes string: Int,
vararg args: Any,
syntakts: Syntakts<DefaultRenderContext> = StringSyntakts,
actionHandler: (String) -> Unit = {}
actionHandler: (actionName: String) -> Unit = {}
): AnnotatedString {
val _string = stringResource(string, *args)
return syntakts.rememberRendered(
Expand Down
41 changes: 39 additions & 2 deletions app/src/main/java/xyz/wingio/dimett/utils/Utils.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package xyz.wingio.dimett.utils

import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.icu.text.CompactDecimalFormat
Expand All @@ -22,6 +23,9 @@ import kotlin.collections.forEach
import kotlin.collections.mutableMapOf
import kotlin.collections.set

/**
* Convert the html returned from the server into plain text that can be parsed on device
*/
val String.plain: String
get() = replace("<br> ", "<br>&nbsp;")
.replace("<br /> ", "<br />&nbsp;")
Expand All @@ -31,6 +35,9 @@ val String.plain: String
.trimEnd()
.toString()

/**
* Converts the list to a map (shortcode: url)
*/
fun List<CustomEmoji>.toEmojiMap(): Map<String, String> {
val map = mutableMapOf<String, String>()
forEach {
Expand All @@ -39,6 +46,9 @@ fun List<CustomEmoji>.toEmojiMap(): Map<String, String> {
return map
}

/**
* Converts the list to a map (username: id)
*/
fun List<Mention>.toMentionMap(): Map<String, String> {
val map = mutableMapOf<String, String>()
forEach {
Expand All @@ -47,12 +57,20 @@ fun List<Mention>.toMentionMap(): Map<String, String> {
return map
}

/**
* Injects the default [Logger]
*/
fun getLogger(): Logger = GlobalContext.get().get(named("default"))

/**
* Converts a string to an [AnnotatedString]
*/
fun String.toAnnotatedString() = AnnotatedString(this)

/**
* Removes the mention for the replied user from the start of the text
*
* @param post The post to process
*/
fun processPostContent(post: Post): String {
val repliedTo = post.mentions.firstOrNull { mention ->
Expand All @@ -68,19 +86,38 @@ fun processPostContent(post: Post): String {
} ?: ""
}

fun Context.getResId(emojiCode: String) =
resources.getIdentifier(emojiCode, "raw", BuildConfig.APPLICATION_ID)
/**
* Get the resource id for a raw resource from its name
*
* @param resName Name of the raw resource (without the file extension)
*/
@SuppressLint("DiscouragedApi")
fun Context.getResId(resName: String) =
resources.getIdentifier(resName, "raw", BuildConfig.APPLICATION_ID)

/**
* Converts a number into a more compact form (1,121 -> 1.1k)
*
* @param number The number to format
*/
fun formatNumber(number: Int): String =
CompactDecimalFormat.getInstance(Locale.getDefault(), CompactDecimalFormat.CompactStyle.SHORT)
.format(number)

/**
* Opens the share dialog with the specified [string]
*
* @param string The text to share, such as a link
*/
fun Context.shareText(string: String) = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, string)
startActivity(Intent.createChooser(this, getString(R.string.action_share)))
}

/**
* Runs the [block] on the main (UI) thread
*/
fun mainThread(block: () -> Unit) {
Handler(Looper.getMainLooper()).post(block)
}

0 comments on commit ecac4b3

Please sign in to comment.