Skip to content

Commit 8e7b3ab

Browse files
committed
refactor: improve code health and reduce complexity in media logic
- Refactor ArtResolver to reduce cyclomatic complexity and nesting. - Simplify LibraryTreeLoader domain item mapping and search logic. - Break down GramophonePlaybackService.onCreate and convertMetadata. - Clean up CoilArtPipeline ResolutionInterceptor. - Organize GramophoneApplication initialization steps. Addresses issues reported by CodeScene.
1 parent 36c0883 commit 8e7b3ab

5 files changed

Lines changed: 609 additions & 721 deletions

File tree

app/src/main/java/org/akanework/gramophone/logic/GramophoneApplication.kt

Lines changed: 106 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -103,87 +103,11 @@ class GramophoneApplication : Application(), SingletonImageLoader.Factory,
103103
// disk read and write on first launch, but unavoidable as threads would race setDefaultNightMode
104104
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
105105
if (BuildConfig.DEBUG) {
106-
// Use StrictMode to find anti-pattern issues
107-
StrictMode.setThreadPolicy(
108-
ThreadPolicy.Builder()
109-
.detectAll()
110-
.let {
111-
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.UPSIDE_DOWN_CAKE ||
112-
Build.VERSION.SDK_INT == Build.VERSION_CODES.VANILLA_ICE_CREAM
113-
) {
114-
it.permitExplicitGc() // platform bug, now fixed
115-
} else it
116-
}
117-
.let {
118-
if (Debug.isDebuggerConnected() || isAlpsBoostFwkPresent())
119-
it.permitDiskReads()
120-
else it
121-
}
122-
.penaltyLog()
123-
.penaltyDialog()
124-
.build()
125-
)
126-
StrictMode.setVmPolicy(
127-
VmPolicy.Builder()
128-
.detectAll()
129-
// detectAll does in fact not detect everything :)
130-
.let {
131-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
132-
it.detectImplicitDirectBoot()
133-
} else it
134-
}
135-
.penaltyLog()
136-
.penaltyDeath()
137-
.build()
138-
)
139-
FragmentStrictMode.defaultPolicy = FragmentStrictMode.Policy.Builder()
140-
.detectFragmentReuse()
141-
.detectFragmentTagUsage()
142-
.detectRetainInstanceUsage()
143-
.detectSetUserVisibleHint()
144-
//.detectTargetFragmentUsage() TODO onDisplayPreferenceDialog()
145-
.detectWrongFragmentContainer()
146-
.detectWrongNestedHierarchy()
147-
.penaltyDeath()
148-
.build()
106+
setupStrictMode()
107+
setupFragmentStrictMode()
149108
}
150109
android.util.Log.d(TAG, "GramophoneApplication.onCreate()")
151-
if (!android.util.Log.isLoggable(TAG, android.util.Log.INFO)) {
152-
Log.setLogger(object : Log.Logger {
153-
override fun d(
154-
tag: String,
155-
message: String,
156-
throwable: Throwable?
157-
) {
158-
android.util.Log.e(tag, "[DEBUG] $message", throwable)
159-
}
160-
161-
override fun i(
162-
tag: String,
163-
message: String,
164-
throwable: Throwable?
165-
) {
166-
android.util.Log.e(tag, "[INFO] $message", throwable)
167-
}
168-
169-
override fun w(
170-
tag: String,
171-
message: String,
172-
throwable: Throwable?
173-
) {
174-
android.util.Log.e(tag, "[WARN] $message", throwable)
175-
}
176-
177-
override fun e(
178-
tag: String,
179-
message: String,
180-
throwable: Throwable?
181-
) {
182-
android.util.Log.e(tag, "[ERROR] $message", throwable)
183-
}
184-
185-
})
186-
}
110+
setupMedia3Logger()
187111
uacManager = UacManager(this)
188112
Flags.PLAYLIST_EDITING = prefs.getBooleanStrict("playlist_editing", false)
189113
reader = FlowReader(
@@ -196,20 +120,8 @@ class GramophoneApplication : Application(), SingletonImageLoader.Factory,
196120
recentlyAddedFilterSecondFlow,
197121
MutableStateFlow(true), "gramophoneAlbumCover"
198122
)
199-
// Set application theme when launching.
200-
when (prefs.getString("theme_mode", "0")) {
201-
"0" -> {
202-
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
203-
}
204-
205-
"1" -> {
206-
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
207-
}
208-
209-
"2" -> {
210-
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
211-
}
212-
}
123+
setupTheme(prefs)
124+
213125
// This is a separate thread to avoid disk read on main thread and improve startup time
214126
CoroutineScope(Dispatchers.Default).launch {
215127
onSharedPreferenceChanged(prefs, null) // reload all values
@@ -225,14 +137,84 @@ class GramophoneApplication : Application(), SingletonImageLoader.Factory,
225137
}
226138
}
227139

140+
private fun setupStrictMode() {
141+
StrictMode.setThreadPolicy(
142+
ThreadPolicy.Builder()
143+
.detectAll()
144+
.let {
145+
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.UPSIDE_DOWN_CAKE ||
146+
Build.VERSION.SDK_INT == Build.VERSION_CODES.VANILLA_ICE_CREAM
147+
) {
148+
it.permitExplicitGc() // platform bug, now fixed
149+
} else it
150+
}
151+
.let {
152+
if (Debug.isDebuggerConnected() || isAlpsBoostFwkPresent())
153+
it.permitDiskReads()
154+
else it
155+
}
156+
.penaltyLog()
157+
.penaltyDialog()
158+
.build()
159+
)
160+
StrictMode.setVmPolicy(
161+
VmPolicy.Builder()
162+
.detectAll()
163+
.let {
164+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
165+
it.detectImplicitDirectBoot()
166+
} else it
167+
}
168+
.penaltyLog()
169+
.penaltyDeath()
170+
.build()
171+
)
172+
}
173+
174+
private fun setupFragmentStrictMode() {
175+
FragmentStrictMode.defaultPolicy = FragmentStrictMode.Policy.Builder()
176+
.detectFragmentReuse()
177+
.detectFragmentTagUsage()
178+
.detectRetainInstanceUsage()
179+
.detectSetUserVisibleHint()
180+
.detectWrongFragmentContainer()
181+
.detectWrongNestedHierarchy()
182+
.penaltyDeath()
183+
.build()
184+
}
185+
186+
private fun setupMedia3Logger() {
187+
if (!android.util.Log.isLoggable(TAG, android.util.Log.INFO)) {
188+
Log.setLogger(object : Log.Logger {
189+
override fun d(tag: String, message: String, throwable: Throwable?) {
190+
android.util.Log.e(tag, "[DEBUG] $message", throwable)
191+
}
192+
override fun i(tag: String, message: String, throwable: Throwable?) {
193+
android.util.Log.e(tag, "[INFO] $message", throwable)
194+
}
195+
override fun w(tag: String, message: String, throwable: Throwable?) {
196+
android.util.Log.e(tag, "[WARN] $message", throwable)
197+
}
198+
override fun e(tag: String, message: String, throwable: Throwable?) {
199+
android.util.Log.e(tag, "[ERROR] $message", throwable)
200+
}
201+
})
202+
}
203+
}
204+
205+
private fun setupTheme(prefs: SharedPreferences) {
206+
when (prefs.getString("theme_mode", "0")) {
207+
"0" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
208+
"1" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
209+
"2" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
210+
}
211+
}
212+
228213
override fun onSharedPreferenceChanged(prefs: SharedPreferences, key: String?) {
229214
runBlocking {
230215
if (key == null || key == "mediastore_filter") {
231216
minSongLengthSecondsFlow.emit(
232-
prefs.getInt(
233-
"mediastore_filter",
234-
resources.getInteger(R.integer.filter_default_sec)
235-
).toLong()
217+
prefs.getInt("mediastore_filter", resources.getInteger(R.integer.filter_default_sec)).toLong()
236218
)
237219
}
238220
if (key == null || key == "folderFilter") {
@@ -257,46 +239,38 @@ class GramophoneApplication : Application(), SingletonImageLoader.Factory,
257239
add(CoilArtPipeline.ArtResourceKeyer())
258240
add(CoilArtPipeline.ArtResourceFetcher.Factory())
259241
}
260-
.run {
261-
if (!BuildConfig.DEBUG) this else
262-
logger(object : Logger {
263-
override var minLevel = Logger.Level.Verbose
264-
override fun log(
265-
tag: String,
266-
level: Logger.Level,
267-
message: String?,
268-
throwable: Throwable?
269-
) {
270-
if (level < minLevel) return
271-
val println = { it: String ->
272-
when (level) {
273-
Logger.Level.Verbose -> Log.d(tag, it)
274-
Logger.Level.Debug -> Log.d(tag, it)
275-
Logger.Level.Info -> Log.i(tag, it)
276-
Logger.Level.Warn -> Log.w(tag, it)
277-
Logger.Level.Error -> Log.e(tag, it)
278-
}
279-
}
280-
if (message != null) {
281-
println(message)
282-
}
283-
// Let's keep the log readable and ignore normal events' stack traces.
284-
if (throwable != null && throwable !is NullRequestDataException
285-
&& (throwable !is IOException
286-
|| throwable.message != "No album art found")
287-
) {
288-
println(Log.getThrowableString(throwable)!!)
289-
}
290-
}
291-
})
242+
.apply {
243+
if (BuildConfig.DEBUG) {
244+
logger(createDebugLogger())
245+
}
292246
}
293247
.build()
294248
}
295249

250+
private fun createDebugLogger() = object : Logger {
251+
override var minLevel = Logger.Level.Verbose
252+
override fun log(tag: String, level: Logger.Level, message: String?, throwable: Throwable?) {
253+
if (level < minLevel) return
254+
message?.let { printLog(tag, level, it) }
255+
if (throwable != null && throwable !is NullRequestDataException &&
256+
(throwable !is IOException || throwable.message != "No album art found")
257+
) {
258+
printLog(tag, level, Log.getThrowableString(throwable)!!)
259+
}
260+
}
261+
262+
private fun printLog(tag: String, level: Logger.Level, msg: String) {
263+
when (level) {
264+
Logger.Level.Verbose -> Log.d(tag, msg)
265+
Logger.Level.Debug -> Log.d(tag, msg)
266+
Logger.Level.Info -> Log.i(tag, msg)
267+
Logger.Level.Warn -> Log.w(tag, msg)
268+
Logger.Level.Error -> Log.e(tag, msg)
269+
}
270+
}
271+
}
272+
296273
override fun uncaughtException(t: Thread, e: Throwable) {
297-
// TODO convert to notification that opens BugHandlerActivity on click, and let JVM
298-
// go through the normal exception process (to get stats from play). disadvantage: we can't
299-
// cheat the statistic that way
300274
val exceptionMessage = Log.getThrowableString(e)
301275
val threadName = Thread.currentThread().name
302276
Log.e(TAG, "Error on thread $threadName:\n $exceptionMessage")

0 commit comments

Comments
 (0)