Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
M
Magisk
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Magisk
Commits
ac2a9da4
Commit
ac2a9da4
authored
Aug 14, 2020
by
topjohnwu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix Markdown rendering
Close #3074
parent
d8b1d798
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
371 additions
and
84 deletions
+371
-84
MarkwonImagePlugin.kt
...ava/com/topjohnwu/magisk/core/utils/MarkwonImagePlugin.kt
+244
-0
Utils.kt
app/src/main/java/com/topjohnwu/magisk/core/utils/Utils.kt
+0
-6
AdaptersGeneric.kt
.../java/com/topjohnwu/magisk/databinding/AdaptersGeneric.kt
+5
-15
NetworkingModule.kt
...src/main/java/com/topjohnwu/magisk/di/NetworkingModule.kt
+7
-7
XAndroid.kt
app/src/main/java/com/topjohnwu/magisk/ktx/XAndroid.kt
+92
-0
XView.kt
app/src/main/java/com/topjohnwu/magisk/ktx/XView.kt
+0
-23
MarkDownWindow.kt
...src/main/java/com/topjohnwu/magisk/view/MarkDownWindow.kt
+20
-32
fragment_install_md2.xml
app/src/main/res/layout/fragment_install_md2.xml
+1
-0
ids.xml
app/src/main/res/values/ids.xml
+2
-1
No files found.
app/src/main/java/com/topjohnwu/magisk/core/utils/MarkwonImagePlugin.kt
0 → 100644
View file @
ac2a9da4
package
com.topjohnwu.magisk.core.utils
import
android.graphics.Bitmap
import
android.graphics.Canvas
import
android.graphics.Paint
import
android.graphics.Rect
import
android.graphics.drawable.BitmapDrawable
import
android.graphics.drawable.Drawable
import
android.graphics.drawable.PictureDrawable
import
android.graphics.drawable.ShapeDrawable
import
android.net.Uri
import
android.text.Spanned
import
android.text.style.DynamicDrawableSpan
import
android.view.View
import
android.widget.TextView
import
androidx.annotation.WorkerThread
import
androidx.core.view.marginLeft
import
androidx.core.view.marginRight
import
com.caverock.androidsvg.SVG
import
com.caverock.androidsvg.SVGParseException
import
com.topjohnwu.magisk.core.ResMgr
import
com.topjohnwu.superuser.internal.WaitRunnable
import
io.noties.markwon.*
import
io.noties.markwon.image.*
import
io.noties.markwon.image.data.DataUriSchemeHandler
import
io.noties.markwon.image.network.OkHttpNetworkSchemeHandler
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.async
import
kotlinx.coroutines.awaitAll
import
kotlinx.coroutines.runBlocking
import
okhttp3.OkHttpClient
import
org.commonmark.node.Image
import
timber.log.Timber
import
java.io.InputStream
// Differences with Markwon stock ImagePlugin:
//
// We assume beforeSetText() will be run in a background thread, and in that method
// we download/decode all drawables before sending the spanned markdown CharSequence
// to the next stage. We also get our surrounding TextView width to properly
// resize our images.
//
// This is required for PrecomputedText to properly take the images into account
// when precomputing the metrics of TextView
//
// Basically, we want nothing to do with AsyncDrawable
class
MarkwonImagePlugin
(
okHttp
:
OkHttpClient
)
:
AbstractMarkwonPlugin
()
{
override
fun
configureSpansFactory
(
builder
:
MarkwonSpansFactory
.
Builder
)
{
builder
.
setFactory
(
Image
::
class
.
java
,
ImageSpanFactory
())
}
@WorkerThread
override
fun
beforeSetText
(
tv
:
TextView
,
markdown
:
Spanned
)
{
if
(
markdown
.
isEmpty
())
return
val
spans
=
markdown
.
getSpans
(
0
,
markdown
.
length
,
ImageSpan
::
class
.
java
)
if
(
spans
==
null
||
spans
.
isEmpty
())
return
// Download and create all drawables in parallel
runBlocking
(
Dispatchers
.
IO
)
{
// Download and decode all drawables in parallel
val
defers
=
spans
.
map
{
async
{
it
.
load
()
}
}
// Get TextView sizes beforehand to resize all images
// We get its parent here as the TextView itself might be hidden
val
parent
=
tv
.
parent
as
View
parent
.
post
(
WaitRunnable
{
// Make sure it is rendered
val
width
=
parent
.
width
-
tv
.
paddingLeft
-
tv
.
paddingRight
-
tv
.
marginLeft
-
tv
.
marginRight
spans
.
forEach
{
it
.
canvasWidth
=
width
}
})
// Make sure all is done before returning
defers
.
awaitAll
()
}
}
private
val
schemeHandlers
=
HashMap
<
String
,
SchemeHandler
>(
3
)
private
val
mediaDecoders
=
HashMap
<
String
,
MediaDecoder
>(
0
)
private
val
defaultMediaDecoder
=
DefaultMediaDecoder
.
create
()
init
{
addSchemeHandler
(
DataUriSchemeHandler
.
create
())
addSchemeHandler
(
OkHttpNetworkSchemeHandler
.
create
(
okHttp
))
addMediaDecoder
(
SVGDecoder
())
}
private
fun
addSchemeHandler
(
schemeHandler
:
SchemeHandler
)
{
for
(
scheme
in
schemeHandler
.
supportedSchemes
())
{
schemeHandlers
[
scheme
]
=
schemeHandler
}
}
private
fun
addMediaDecoder
(
mediaDecoder
:
MediaDecoder
)
{
for
(
type
in
mediaDecoder
.
supportedTypes
())
{
mediaDecoders
[
type
]
=
mediaDecoder
}
}
// Modified from AsyncDrawableLoaderImpl.execute(asyncDrawable)
fun
loadDrawable
(
destination
:
String
):
Drawable
?
{
val
uri
=
Uri
.
parse
(
destination
)
var
drawable
:
Drawable
?
=
null
try
{
val
scheme
=
uri
.
scheme
check
(
scheme
!=
null
&&
scheme
.
isNotEmpty
())
{
"No scheme is found: $destination"
}
// obtain scheme handler
val
schemeHandler
=
schemeHandlers
[
scheme
]
?:
throw
IllegalStateException
(
"No scheme-handler is found: $destination"
)
// handle scheme
val
imageItem
=
schemeHandler
.
handle
(
destination
,
uri
)
// if resulting imageItem needs further decoding -> proceed
drawable
=
if
(
imageItem
.
hasDecodingNeeded
())
{
val
withDecodingNeeded
=
imageItem
.
asWithDecodingNeeded
val
mediaDecoder
=
mediaDecoders
[
withDecodingNeeded
.
contentType
()]
?:
defaultMediaDecoder
mediaDecoder
.
decode
(
withDecodingNeeded
.
contentType
(),
withDecodingNeeded
.
inputStream
()
)
}
else
{
imageItem
.
asWithResult
.
result
()
}
}
catch
(
t
:
Throwable
)
{
Timber
.
e
(
t
,
"Error loading image: $destination"
)
}
// apply intrinsic bounds (but only if they are empty)
if
(
drawable
!=
null
&&
drawable
.
bounds
.
isEmpty
)
DrawableUtils
.
applyIntrinsicBounds
(
drawable
)
return
drawable
}
inner
class
ImageSpanFactory
:
SpanFactory
{
override
fun
getSpans
(
configuration
:
MarkwonConfiguration
,
props
:
RenderProps
):
Any
?
{
val
dest
=
ImageProps
.
DESTINATION
.
require
(
props
)
val
size
=
ImageProps
.
IMAGE_SIZE
.
get
(
props
)
return
ImageSpan
(
dest
,
size
)
}
}
inner
class
ImageSpan
(
private
val
dest
:
String
,
private
val
size
:
ImageSize
?
)
:
DynamicDrawableSpan
(
ALIGN_BOTTOM
)
{
var
canvasWidth
=
0
var
measured
=
false
lateinit
var
draw
:
Drawable
fun
load
()
{
draw
=
loadDrawable
(
dest
)
?:
ShapeDrawable
()
}
override
fun
getDrawable
()
=
draw
private
fun
defaultBounds
():
Rect
{
val
bounds
:
Rect
=
draw
.
bounds
if
(!
bounds
.
isEmpty
)
{
return
bounds
}
val
intrinsicBounds
=
DrawableUtils
.
intrinsicBounds
(
draw
)
if
(!
intrinsicBounds
.
isEmpty
)
{
return
intrinsicBounds
}
return
Rect
(
0
,
0
,
1
,
1
)
}
private
fun
measure
(
paint
:
Paint
)
{
if
(
measured
)
return
if
(
canvasWidth
==
0
)
return
measured
=
true
val
bound
=
SizeResolver
.
resolveImageSize
(
size
,
defaultBounds
(),
canvasWidth
,
paint
.
textSize
)
draw
.
bounds
=
bound
}
override
fun
getSize
(
paint
:
Paint
,
text
:
CharSequence
?,
start
:
Int
,
end
:
Int
,
fm
:
Paint
.
FontMetricsInt
?
):
Int
{
measure
(
paint
)
return
super
.
getSize
(
paint
,
text
,
start
,
end
,
fm
)
}
}
object
SizeResolver
:
ImageSizeResolverDef
()
{
// Expose protected API
public
override
fun
resolveImageSize
(
imageSize
:
ImageSize
?,
imageBounds
:
Rect
,
canvasWidth
:
Int
,
textSize
:
Float
):
Rect
{
return
super
.
resolveImageSize
(
imageSize
,
imageBounds
,
canvasWidth
,
textSize
)
}
}
class
SVGDecoder
:
MediaDecoder
()
{
override
fun
supportedTypes
()
=
listOf
(
"image/svg+xml"
)
override
fun
decode
(
contentType
:
String
?,
inputStream
:
InputStream
):
Drawable
{
val
svg
:
SVG
svg
=
try
{
SVG
.
getFromInputStream
(
inputStream
)
}
catch
(
e
:
SVGParseException
)
{
throw
IllegalStateException
(
"Exception decoding SVG"
,
e
)
}
val
w
=
svg
.
documentWidth
val
h
=
svg
.
documentHeight
if
(
w
==
0f
||
h
==
0f
)
{
val
picture
=
svg
.
renderToPicture
()
return
PictureDrawable
(
picture
)
}
val
density
:
Float
=
ResMgr
.
resource
.
displayMetrics
.
density
val
width
=
(
w
*
density
+
.
5f
).
toInt
()
val
height
=
(
h
*
density
+
.
5f
).
toInt
()
val
bitmap
=
Bitmap
.
createBitmap
(
width
,
height
,
Bitmap
.
Config
.
ARGB_8888
)
val
canvas
=
Canvas
(
bitmap
)
canvas
.
scale
(
density
,
density
)
svg
.
renderToCanvas
(
canvas
)
return
BitmapDrawable
(
ResMgr
.
resource
,
bitmap
)
}
}
}
app/src/main/java/com/topjohnwu/magisk/core/utils/Utils.kt
View file @
ac2a9da4
...
@@ -2,7 +2,6 @@ package com.topjohnwu.magisk.core.utils
...
@@ -2,7 +2,6 @@ package com.topjohnwu.magisk.core.utils
import
android.content.Context
import
android.content.Context
import
android.content.Intent
import
android.content.Intent
import
android.content.res.Resources
import
android.net.Uri
import
android.net.Uri
import
android.os.Environment
import
android.os.Environment
import
android.widget.Toast
import
android.widget.Toast
...
@@ -24,11 +23,6 @@ object Utils {
...
@@ -24,11 +23,6 @@ object Utils {
UiThreadHandler
.
run
{
Toast
.
makeText
(
get
(),
resId
,
duration
).
show
()
}
UiThreadHandler
.
run
{
Toast
.
makeText
(
get
(),
resId
,
duration
).
show
()
}
}
}
fun
dpInPx
(
dp
:
Int
):
Int
{
val
scale
=
get
<
Resources
>().
displayMetrics
.
density
return
(
dp
*
scale
+
0.5
).
toInt
()
}
fun
showSuperUser
():
Boolean
{
fun
showSuperUser
():
Boolean
{
return
Info
.
env
.
isActive
&&
(
Const
.
USER_ID
==
0
return
Info
.
env
.
isActive
&&
(
Const
.
USER_ID
==
0
||
Config
.
suMultiuserMode
!=
Config
.
Value
.
MULTIUSER_MODE_OWNER_MANAGED
)
||
Config
.
suMultiuserMode
!=
Config
.
Value
.
MULTIUSER_MODE_OWNER_MANAGED
)
...
...
app/src/main/java/com/topjohnwu/magisk/databinding/AdaptersGeneric.kt
View file @
ac2a9da4
...
@@ -2,15 +2,13 @@ package com.topjohnwu.magisk.databinding
...
@@ -2,15 +2,13 @@ package com.topjohnwu.magisk.databinding
import
android.view.View
import
android.view.View
import
android.widget.TextView
import
android.widget.TextView
import
androidx.core.text.PrecomputedTextCompat
import
androidx.core.view.isGone
import
androidx.core.view.isGone
import
androidx.core.view.isInvisible
import
androidx.core.view.isInvisible
import
androidx.core.widget.TextViewCompat
import
androidx.databinding.BindingAdapter
import
androidx.databinding.BindingAdapter
import
com.topjohnwu.magisk.ktx.coroutineScope
import
com.topjohnwu.magisk.ktx.get
import
com.topjohnwu.magisk.ktx.get
import
io.noties.markwon.Markwon
import
io.noties.markwon.Markwon
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.launch
import
kotlinx.coroutines.launch
@BindingAdapter
(
"gone"
)
@BindingAdapter
(
"gone"
)
...
@@ -33,18 +31,10 @@ fun setInvisibleUnless(view: View, invisibleUnless: Boolean) {
...
@@ -33,18 +31,10 @@ fun setInvisibleUnless(view: View, invisibleUnless: Boolean) {
setInvisible
(
view
,
invisibleUnless
.
not
())
setInvisible
(
view
,
invisibleUnless
.
not
())
}
}
@BindingAdapter
(
"precomputedText"
)
fun
setPrecomputedText
(
tv
:
TextView
,
text
:
CharSequence
)
{
GlobalScope
.
launch
(
Dispatchers
.
Default
)
{
val
pre
=
PrecomputedTextCompat
.
create
(
text
,
TextViewCompat
.
getTextMetricsParams
(
tv
))
tv
.
post
{
TextViewCompat
.
setPrecomputedText
(
tv
,
pre
);
}
}
}
@BindingAdapter
(
"markdownText"
)
@BindingAdapter
(
"markdownText"
)
fun
setMarkdownText
(
tv
:
TextView
,
text
:
CharSequence
)
{
fun
setMarkdownText
(
tv
:
TextView
,
text
:
CharSequence
)
{
val
markwon
=
get
<
Markwon
>()
tv
.
coroutineScope
.
launch
(
Dispatchers
.
IO
)
{
markwon
.
setMarkdown
(
tv
,
text
.
toString
())
val
markwon
=
get
<
Markwon
>()
markwon
.
setMarkdown
(
tv
,
text
.
toString
())
}
}
}
app/src/main/java/com/topjohnwu/magisk/di/NetworkingModule.kt
View file @
ac2a9da4
...
@@ -5,15 +5,14 @@ import android.os.Build
...
@@ -5,15 +5,14 @@ import android.os.Build
import
com.squareup.moshi.Moshi
import
com.squareup.moshi.Moshi
import
com.topjohnwu.magisk.core.Const
import
com.topjohnwu.magisk.core.Const
import
com.topjohnwu.magisk.core.Info
import
com.topjohnwu.magisk.core.Info
import
com.topjohnwu.magisk.core.utils.MarkwonImagePlugin
import
com.topjohnwu.magisk.data.network.GithubApiServices
import
com.topjohnwu.magisk.data.network.GithubApiServices
import
com.topjohnwu.magisk.data.network.GithubRawServices
import
com.topjohnwu.magisk.data.network.GithubRawServices
import
com.topjohnwu.magisk.ktx.precomputedText
import
com.topjohnwu.magisk.net.Networking
import
com.topjohnwu.magisk.net.Networking
import
com.topjohnwu.magisk.net.NoSSLv3SocketFactory
import
com.topjohnwu.magisk.net.NoSSLv3SocketFactory
import
com.topjohnwu.magisk.view.PrecomputedTextSetter
import
io.noties.markwon.Markwon
import
io.noties.markwon.Markwon
import
io.noties.markwon.html.HtmlPlugin
import
io.noties.markwon.html.HtmlPlugin
import
io.noties.markwon.image.ImagesPlugin
import
io.noties.markwon.image.network.OkHttpNetworkSchemeHandler
import
okhttp3.Dns
import
okhttp3.Dns
import
okhttp3.HttpUrl
import
okhttp3.HttpUrl
import
okhttp3.OkHttpClient
import
okhttp3.OkHttpClient
...
@@ -96,10 +95,11 @@ inline fun <reified T> createApiService(retrofitBuilder: Retrofit.Builder, baseU
...
@@ -96,10 +95,11 @@ inline fun <reified T> createApiService(retrofitBuilder: Retrofit.Builder, baseU
fun
createMarkwon
(
context
:
Context
,
okHttpClient
:
OkHttpClient
):
Markwon
{
fun
createMarkwon
(
context
:
Context
,
okHttpClient
:
OkHttpClient
):
Markwon
{
return
Markwon
.
builder
(
context
)
return
Markwon
.
builder
(
context
)
.
textSetter
(
PrecomputedTextSetter
())
.
textSetter
{
textView
,
spanned
,
_
,
onComplete
->
textView
.
tag
=
onComplete
textView
.
precomputedText
=
spanned
}
.
usePlugin
(
HtmlPlugin
.
create
())
.
usePlugin
(
HtmlPlugin
.
create
())
.
usePlugin
(
ImagesPlugin
.
create
{
.
usePlugin
(
MarkwonImagePlugin
(
okHttpClient
))
it
.
addSchemeHandler
(
OkHttpNetworkSchemeHandler
.
create
(
okHttpClient
))
})
.
build
()
.
build
()
}
}
app/src/main/java/com/topjohnwu/magisk/ktx/XAndroid.kt
View file @
ac2a9da4
...
@@ -22,8 +22,12 @@ import android.net.Uri
...
@@ -22,8 +22,12 @@ import android.net.Uri
import
android.os.Build
import
android.os.Build
import
android.os.Build.VERSION.SDK_INT
import
android.os.Build.VERSION.SDK_INT
import
android.provider.OpenableColumns
import
android.provider.OpenableColumns
import
android.text.PrecomputedText
import
android.view.View
import
android.view.View
import
android.view.ViewGroup
import
android.view.ViewTreeObserver
import
android.view.inputmethod.InputMethodManager
import
android.view.inputmethod.InputMethodManager
import
android.widget.TextView
import
androidx.annotation.ColorRes
import
androidx.annotation.ColorRes
import
androidx.annotation.DrawableRes
import
androidx.annotation.DrawableRes
import
androidx.appcompat.content.res.AppCompatResources
import
androidx.appcompat.content.res.AppCompatResources
...
@@ -31,13 +35,26 @@ import androidx.core.content.ContextCompat
...
@@ -31,13 +35,26 @@ import androidx.core.content.ContextCompat
import
androidx.core.content.getSystemService
import
androidx.core.content.getSystemService
import
androidx.core.net.toFile
import
androidx.core.net.toFile
import
androidx.core.net.toUri
import
androidx.core.net.toUri
import
androidx.core.text.PrecomputedTextCompat
import
androidx.core.view.isGone
import
androidx.core.widget.TextViewCompat
import
androidx.databinding.BindingAdapter
import
androidx.fragment.app.Fragment
import
androidx.fragment.app.Fragment
import
androidx.interpolator.view.animation.FastOutSlowInInterpolator
import
androidx.transition.AutoTransition
import
androidx.transition.TransitionManager
import
com.topjohnwu.magisk.FileProvider
import
com.topjohnwu.magisk.FileProvider
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.core.Const
import
com.topjohnwu.magisk.core.Const
import
com.topjohnwu.magisk.core.ResMgr
import
com.topjohnwu.magisk.core.utils.Utils
import
com.topjohnwu.magisk.core.utils.Utils
import
com.topjohnwu.magisk.core.utils.currentLocale
import
com.topjohnwu.magisk.core.utils.currentLocale
import
com.topjohnwu.magisk.utils.DynamicClassLoader
import
com.topjohnwu.magisk.utils.DynamicClassLoader
import
com.topjohnwu.superuser.Shell
import
com.topjohnwu.superuser.Shell
import
kotlinx.coroutines.CoroutineScope
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.launch
import
java.io.File
import
java.io.File
import
java.io.FileNotFoundException
import
java.io.FileNotFoundException
import
java.lang.reflect.Array
as
JArray
import
java.lang.reflect.Array
as
JArray
...
@@ -328,3 +345,78 @@ fun Activity.hideKeyboard() {
...
@@ -328,3 +345,78 @@ fun Activity.hideKeyboard() {
fun
Fragment
.
hideKeyboard
()
{
fun
Fragment
.
hideKeyboard
()
{
activity
?.
hideKeyboard
()
activity
?.
hideKeyboard
()
}
}
fun
View
.
setOnViewReadyListener
(
callback
:
()
->
Unit
)
=
addOnGlobalLayoutListener
(
true
,
callback
)
fun
View
.
addOnGlobalLayoutListener
(
oneShot
:
Boolean
=
false
,
callback
:
()
->
Unit
)
=
viewTreeObserver
.
addOnGlobalLayoutListener
(
object
:
ViewTreeObserver
.
OnGlobalLayoutListener
{
override
fun
onGlobalLayout
()
{
if
(
oneShot
)
viewTreeObserver
.
removeOnGlobalLayoutListener
(
this
)
callback
()
}
})
fun
ViewGroup
.
startAnimations
()
{
val
transition
=
AutoTransition
()
.
setInterpolator
(
FastOutSlowInInterpolator
()).
setDuration
(
400
)
TransitionManager
.
beginDelayedTransition
(
this
,
transition
)
}
var
View
.
coroutineScope
:
CoroutineScope
get
()
=
getTag
(
R
.
id
.
coroutineScope
)
as
?
CoroutineScope
?:
GlobalScope
set
(
value
)
=
setTag
(
R
.
id
.
coroutineScope
,
value
)
@set
:
BindingAdapter
(
"precomputedText"
)
var
TextView
.
precomputedText
:
CharSequence
get
()
=
text
set
(
value
)
{
val
callback
=
tag
as
?
Runnable
// Don't even bother pre 21
if
(
SDK_INT
<
21
)
{
post
{
text
=
value
isGone
=
false
callback
?.
run
()
}
return
}
coroutineScope
.
launch
(
Dispatchers
.
IO
)
{
if
(
SDK_INT
>=
29
)
{
// Internally PrecomputedTextCompat will platform API on API 29+
// Due to some stupid crap OEM (Samsung) implementation, this can actually
// crash our app. Directly use platform APIs with some workarounds
val
pre
=
PrecomputedText
.
create
(
value
,
textMetricsParams
)
post
{
try
{
text
=
pre
}
catch
(
e
:
IllegalArgumentException
)
{
// Override to computed params to workaround crashes
textMetricsParams
=
pre
.
params
text
=
pre
}
isGone
=
false
callback
?.
run
()
}
}
else
{
val
tv
=
this
@precomputedText
val
params
=
TextViewCompat
.
getTextMetricsParams
(
tv
)
val
pre
=
PrecomputedTextCompat
.
create
(
value
,
params
)
post
{
TextViewCompat
.
setPrecomputedText
(
tv
,
pre
)
isGone
=
false
callback
?.
run
()
}
}
}
}
fun
Int
.
dpInPx
():
Int
{
val
scale
=
ResMgr
.
resource
.
displayMetrics
.
density
return
(
this
*
scale
+
0.5
).
toInt
()
}
app/src/main/java/com/topjohnwu/magisk/ktx/XView.kt
deleted
100644 → 0
View file @
d8b1d798
package
com.topjohnwu.magisk.ktx
import
android.view.View
import
android.view.ViewGroup
import
android.view.ViewTreeObserver
import
androidx.interpolator.view.animation.FastOutSlowInInterpolator
import
androidx.transition.AutoTransition
import
androidx.transition.TransitionManager
fun
View
.
setOnViewReadyListener
(
callback
:
()
->
Unit
)
=
addOnGlobalLayoutListener
(
true
,
callback
)
fun
View
.
addOnGlobalLayoutListener
(
oneShot
:
Boolean
=
false
,
callback
:
()
->
Unit
)
=
viewTreeObserver
.
addOnGlobalLayoutListener
(
object
:
ViewTreeObserver
.
OnGlobalLayoutListener
{
override
fun
onGlobalLayout
()
{
if
(
oneShot
)
viewTreeObserver
.
removeOnGlobalLayoutListener
(
this
)
callback
()
}
})
fun
ViewGroup
.
startAnimations
()
{
val
transition
=
AutoTransition
().
setInterpolator
(
FastOutSlowInInterpolator
()).
setDuration
(
400
)
TransitionManager
.
beginDelayedTransition
(
this
,
transition
)
}
app/src/main/java/com/topjohnwu/magisk/view/MarkDownWindow.kt
View file @
ac2a9da4
package
com.topjohnwu.magisk.view
package
com.topjohnwu.magisk.view
import
android.content.Context
import
android.content.Context
import
android.text.Spanned
import
android.view.LayoutInflater
import
android.view.LayoutInflater
import
android.widget.TextView
import
android.widget.TextView
import
androidx.core.text.PrecomputedTextCompat
import
androidx.core.widget.TextViewCompat
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.data.repository.StringRepository
import
com.topjohnwu.magisk.data.repository.StringRepository
import
com.topjohnwu.magisk.ktx.coroutineScope
import
io.noties.markwon.Markwon
import
io.noties.markwon.Markwon
import
kotlinx.coroutines.*
import
kotlinx.coroutines.CancellationException
import
kotlinx.coroutines.CoroutineScope
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.withContext
import
org.koin.core.KoinComponent
import
org.koin.core.KoinComponent
import
org.koin.core.inject
import
org.koin.core.inject
import
timber.log.Timber
import
timber.log.Timber
import
kotlin.coroutines.coroutineContext
import
kotlin.coroutines.coroutineContext
class
PrecomputedTextSetter
:
Markwon
.
TextSetter
{
override
fun
setText
(
tv
:
TextView
,
text
:
Spanned
,
bufferType
:
TextView
.
BufferType
,
onComplete
:
Runnable
)
{
val
scope
=
tv
.
tag
as
?
CoroutineScope
?:
GlobalScope
scope
.
launch
(
Dispatchers
.
Default
)
{
val
pre
=
PrecomputedTextCompat
.
create
(
text
,
TextViewCompat
.
getTextMetricsParams
(
tv
))
tv
.
post
{
TextViewCompat
.
setPrecomputedText
(
tv
,
pre
)
onComplete
.
run
()
}
}
}
}
object
MarkDownWindow
:
KoinComponent
{
object
MarkDownWindow
:
KoinComponent
{
private
val
repo
:
StringRepository
by
inject
()
private
val
repo
:
StringRepository
by
inject
()
...
@@ -41,26 +28,27 @@ object MarkDownWindow : KoinComponent {
...
@@ -41,26 +28,27 @@ object MarkDownWindow : KoinComponent {
}
}
suspend
fun
show
(
activity
:
Context
,
title
:
String
?,
input
:
suspend
()
->
String
)
{
suspend
fun
show
(
activity
:
Context
,
title
:
String
?,
input
:
suspend
()
->
String
)
{
val
mdRes
=
R
.
layout
.
markdown_window_md2
val
view
=
LayoutInflater
.
from
(
activity
).
inflate
(
R
.
layout
.
markdown_window_md2
,
null
)
val
mv
=
LayoutInflater
.
from
(
activity
).
inflate
(
mdRes
,
null
)
val
tv
=
mv
.
findViewById
<
TextView
>(
R
.
id
.
md_txt
)
tv
.
tag
=
CoroutineScope
(
coroutineContext
)
try
{
markwon
.
setMarkdown
(
tv
,
input
())
}
catch
(
e
:
Exception
)
{
if
(
e
is
CancellationException
)
throw
e
Timber
.
e
(
e
)
tv
.
setText
(
R
.
string
.
download_file_error
)
}
MagiskDialog
(
activity
)
MagiskDialog
(
activity
)
.
applyTitle
(
title
?:
""
)
.
applyTitle
(
title
?:
""
)
.
applyView
(
mv
)
.
applyView
(
view
)
.
applyButton
(
MagiskDialog
.
ButtonType
.
NEGATIVE
)
{
.
applyButton
(
MagiskDialog
.
ButtonType
.
NEGATIVE
)
{
titleRes
=
android
.
R
.
string
.
cancel
titleRes
=
android
.
R
.
string
.
cancel
}
}
.
reveal
()
.
reveal
()
val
tv
=
view
.
findViewById
<
TextView
>(
R
.
id
.
md_txt
)
tv
.
coroutineScope
=
CoroutineScope
(
coroutineContext
)
withContext
(
Dispatchers
.
IO
)
{
try
{
markwon
.
setMarkdown
(
tv
,
input
())
}
catch
(
e
:
Exception
)
{
if
(
e
is
CancellationException
)
throw
e
Timber
.
e
(
e
)
tv
.
setText
(
R
.
string
.
download_file_error
)
}
}
}
}
}
}
app/src/main/res/layout/fragment_install_md2.xml
View file @
ac2a9da4
...
@@ -233,6 +233,7 @@
...
@@ -233,6 +233,7 @@
android:layout_height=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_margin=
"15dp"
android:layout_margin=
"15dp"
android:textAppearance=
"@style/AppearanceFoundation.Caption"
android:textAppearance=
"@style/AppearanceFoundation.Caption"
android:visibility=
"gone"
markdownText=
"@{viewModel.notes}"
/>
markdownText=
"@{viewModel.notes}"
/>
</com.google.android.material.card.MaterialCardView>
</com.google.android.material.card.MaterialCardView>
...
...
app/src/main/res/values/ids.xml
View file @
ac2a9da4
...
@@ -2,5 +2,6 @@
...
@@ -2,5 +2,6 @@
<resources>
<resources>
<item
name=
"recyclerScrollListener"
type=
"id"
/>
<item
name=
"recyclerScrollListener"
type=
"id"
/>
<item
name=
"coroutineScope"
type=
"id"
/>
</resources>
</resources>
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment