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
abc54571
Commit
abc54571
authored
Aug 21, 2020
by
topjohnwu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Cleanup DownloadService
parent
4b238a9c
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
264 additions
and
222 deletions
+264
-222
BaseDownloadService.kt
...com/topjohnwu/magisk/core/download/BaseDownloadService.kt
+201
-0
DownloadService.kt
...ava/com/topjohnwu/magisk/core/download/DownloadService.kt
+29
-84
ManagerUpgrade.kt
...java/com/topjohnwu/magisk/core/download/ManagerUpgrade.kt
+8
-6
NotificationService.kt
...com/topjohnwu/magisk/core/download/NotificationService.kt
+0
-96
ProgressInputStream.kt
...va/com/topjohnwu/magisk/core/utils/ProgressInputStream.kt
+1
-2
HomeFragment.kt
...rc/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt
+2
-0
HomeViewModel.kt
...c/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt
+7
-9
InstallFragment.kt
...n/java/com/topjohnwu/magisk/ui/install/InstallFragment.kt
+2
-0
InstallViewModel.kt
.../java/com/topjohnwu/magisk/ui/install/InstallViewModel.kt
+10
-12
ModuleFragment.kt
...ain/java/com/topjohnwu/magisk/ui/module/ModuleFragment.kt
+2
-0
ModuleViewModel.kt
...in/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt
+2
-13
No files found.
app/src/main/java/com/topjohnwu/magisk/core/download/
RemoteFile
Service.kt
→
app/src/main/java/com/topjohnwu/magisk/core/download/
BaseDownload
Service.kt
View file @
abc54571
...
...
@@ -2,17 +2,20 @@ package com.topjohnwu.magisk.core.download
import
android.app.Notification
import
android.content.Intent
import
androidx.lifecycle.LiveData
import
android.os.IBinder
import
androidx.lifecycle.LifecycleOwner
import
androidx.lifecycle.MutableLiveData
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.core.ForegroundTracker
import
com.topjohnwu.magisk.core.download.DownloadSubject.Magisk
import
com.topjohnwu.magisk.core.download.DownloadSubject.Module
import
com.topjohnwu.magisk.core.base.BaseService
import
com.topjohnwu.magisk.core.utils.ProgressInputStream
import
com.topjohnwu.magisk.data.network.GithubRawServices
import
com.topjohnwu.magisk.ktx.checkSum
import
com.topjohnwu.magisk.ktx.writeTo
import
com.topjohnwu.magisk.view.Notifications
import
kotlinx.coroutines.CoroutineScope
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.cancel
import
kotlinx.coroutines.launch
import
okhttp3.ResponseBody
import
org.koin.android.ext.android.inject
...
...
@@ -20,81 +23,105 @@ import org.koin.core.KoinComponent
import
timber.log.Timber
import
java.io.IOException
import
java.io.InputStream
import
java.util.*
import
kotlin.collections.HashMap
import
kotlin.random.Random.Default.nextInt
abstract
class
RemoteFileService
:
NotificationService
()
{
abstract
class
BaseDownloadService
:
BaseService
(),
KoinComponent
{
private
val
hasNotifications
get
()
=
notifications
.
isNotEmpty
()
private
val
notifications
=
Collections
.
synchronizedMap
(
HashMap
<
Int
,
Notification
.
Builder
>())
private
val
coroutineScope
=
CoroutineScope
(
Dispatchers
.
IO
)
val
service
:
GithubRawServices
by
inject
()
// -- Service overrides
override
fun
onBind
(
intent
:
Intent
?):
IBinder
?
=
null
override
fun
onStartCommand
(
intent
:
Intent
?,
flags
:
Int
,
startId
:
Int
):
Int
{
intent
?.
getParcelableExtra
<
DownloadSubject
>(
ARG_URL
)
?.
let
{
update
(
it
.
hashCode
())
intent
?.
getParcelableExtra
<
DownloadSubject
>(
ARG_URL
)
?.
let
{
subject
->
update
(
subject
.
notifyID
())
coroutineScope
.
launch
{
try
{
s
tart
(
it
)
s
ubject
.
startDownload
(
)
}
catch
(
e
:
IOException
)
{
Timber
.
e
(
e
)
failNotify
(
i
t
)
notifyFail
(
subjec
t
)
}
}
}
return
START_REDELIVER_INTENT
}
override
fun
createNotification
()
=
Notifications
.
progress
(
this
,
""
)
override
fun
onTaskRemoved
(
rootIntent
:
Intent
?)
{
super
.
onTaskRemoved
(
rootIntent
)
notifications
.
forEach
{
cancel
(
it
.
key
)
}
notifications
.
clear
()
}
// ---
override
fun
onDestroy
()
{
super
.
onDestroy
()
coroutineScope
.
cancel
()
}
// -- Download logic
private
suspend
fun
start
(
subject
:
DownloadSubject
)
{
if
(
subject
!
is
Magisk
||
!
subject
.
file
.
exists
()
||
!
subject
.
file
.
checkSum
(
"MD5"
,
subject
.
magisk
.
md5
))
{
val
stream
=
service
.
fetchFile
(
subject
.
url
).
toProgressStream
(
subject
)
when
(
subject
)
{
is
Module
->
stream
.
toModule
(
subject
.
file
,
service
.
fetchInstaller
().
byteStream
())
private
suspend
fun
DownloadSubject
.
startDownload
()
{
val
skip
=
this
is
DownloadSubject
.
Magisk
&&
file
.
exists
()
&&
file
.
checkSum
(
"MD5"
,
magisk
.
md5
)
if
(!
skip
)
{
val
stream
=
service
.
fetchFile
(
url
).
toProgressStream
(
this
)
when
(
this
)
{
is
DownloadSubject
.
Module
->
// Download and process on-the-fly
stream
.
toModule
(
file
,
service
.
fetchInstaller
().
byteStream
())
else
->
stream
.
writeTo
(
subject
.
file
)
stream
.
writeTo
(
file
)
}
}
val
newId
=
finishNotify
(
subject
)
if
(
ForegroundTracker
.
hasForeground
)
{
onFinished
(
subject
,
newId
)
}
val
newId
=
notifyFinish
(
this
)
if
(
ForegroundTracker
.
hasForeground
)
onFinish
(
this
,
newId
)
if
(!
hasNotifications
)
stopSelf
()
}
private
fun
ResponseBody
.
toProgressStream
(
subject
:
DownloadSubject
):
InputStream
{
val
max
Raw
=
contentLength
()
val
max
=
maxRaw
/
1
_000_000f
val
id
=
subject
.
hashCode
()
val
max
=
contentLength
()
val
total
=
max
.
toFloat
()
/
1048576
val
id
=
subject
.
notifyID
()
update
(
id
)
{
it
.
setContentTitle
(
subject
.
title
)
}
return
ProgressInputStream
(
byteStream
())
{
val
progress
=
it
/
1
_000_000f
val
progress
=
it
.
toFloat
()
/
1048576
update
(
id
)
{
notification
->
if
(
max
Raw
>
0
)
{
send
(
progress
/
max
,
subject
)
if
(
max
>
0
)
{
broadcast
(
progress
/
total
,
subject
)
notification
.
setProgress
(
max
Raw
.
toInt
(),
it
.
toInt
(),
false
)
.
setContentText
(
"%.2f / %.2f MB"
.
format
(
progress
,
max
))
.
setProgress
(
max
.
toInt
(),
it
.
toInt
(),
false
)
.
setContentText
(
"%.2f / %.2f MB"
.
format
(
progress
,
total
))
}
else
{
send
(-
1f
,
subject
)
broadcast
(-
1f
,
subject
)
notification
.
setContentText
(
"%.2f MB / ??"
.
format
(
progress
))
}
}
}
}
private
fun
failNotify
(
subject
:
DownloadSubject
)
=
lastNotify
(
subject
.
hashCode
())
{
send
(
0f
,
subject
)
// --- Notification managements
fun
DownloadSubject
.
notifyID
()
=
hashCode
()
private
fun
notifyFail
(
subject
:
DownloadSubject
)
=
lastNotify
(
subject
.
notifyID
())
{
broadcast
(-
1f
,
subject
)
it
.
setContentText
(
getString
(
R
.
string
.
download_file_error
))
.
setSmallIcon
(
android
.
R
.
drawable
.
stat_notify_error
)
.
setOngoing
(
false
)
}
private
fun
finishNotify
(
subject
:
DownloadSubject
)
=
lastNotify
(
subject
.
hashCode
())
{
send
(
1f
,
subject
)
it
.
addActions
(
subject
)
private
fun
notifyFinish
(
subject
:
DownloadSubject
)
=
lastNotify
(
subject
.
notifyID
())
{
broadcast
(
1f
,
subject
)
it
.
setIntent
(
subject
)
.
setContentText
(
getString
(
R
.
string
.
download_complete
))
.
setSmallIcon
(
android
.
R
.
drawable
.
stat_sys_download_done
)
.
setProgress
(
0
,
0
,
false
)
...
...
@@ -102,27 +129,73 @@ abstract class RemoteFileService : NotificationService() {
.
setAutoCancel
(
true
)
}
// ---
private
fun
create
()
=
Notifications
.
progress
(
this
,
""
)
fun
update
(
id
:
Int
,
editor
:
(
Notification
.
Builder
)
->
Unit
=
{})
{
val
wasEmpty
=
!
hasNotifications
val
notification
=
notifications
.
getOrPut
(
id
,
::
create
).
also
(
editor
)
if
(
wasEmpty
)
updateForeground
()
else
notify
(
id
,
notification
.
build
())
}
private
fun
lastNotify
(
id
:
Int
,
editor
:
(
Notification
.
Builder
)
->
Notification
.
Builder
?
=
{
null
}
)
:
Int
{
val
notification
=
remove
(
id
)
?.
run
(
editor
)
?:
return
-
1
val
newId
:
Int
=
nextInt
()
notify
(
newId
,
notification
.
build
())
return
newId
}
private
fun
remove
(
id
:
Int
)
=
notifications
.
remove
(
id
)
?.
also
{
updateForeground
()
cancel
(
id
)
}
private
fun
notify
(
id
:
Int
,
notification
:
Notification
)
{
Notifications
.
mgr
.
notify
(
id
,
notification
)
}
protected
fun
cancel
(
id
:
Int
)
{
Notifications
.
mgr
.
cancel
(
id
)
}
private
fun
updateForeground
()
{
if
(
hasNotifications
)
{
val
(
id
,
notification
)
=
notifications
.
entries
.
first
()
startForeground
(
id
,
notification
.
build
())
}
else
{
stopForeground
(
false
)
}
}
protected
abstract
suspend
fun
onFinished
(
subject
:
DownloadSubject
,
id
:
Int
)
// --- Implement custom logic
protected
abstract
fun
Notification
.
Builder
.
addActions
(
subject
:
DownloadSubject
)
protected
abstract
suspend
fun
onFinish
(
subject
:
DownloadSubject
,
id
:
Int
)
protected
abstract
fun
Notification
.
Builder
.
setIntent
(
subject
:
DownloadSubject
)
:
Notification
.
Builder
// ---
companion
object
:
KoinComponent
{
const
val
ARG_URL
=
"arg_url"
private
val
internalProgressBroadcast
=
MutableLiveData
<
Pair
<
Float
,
DownloadSubject
>>()
val
progressBroadcast
:
LiveData
<
Pair
<
Float
,
DownloadSubject
>>
get
()
=
internalProgressBroadcast
private
val
progressBroadcast
=
MutableLiveData
<
Pair
<
Float
,
DownloadSubject
>>()
fun
send
(
progress
:
Float
,
subject
:
DownloadSubject
)
{
internalProgressBroadcast
.
postValue
(
progress
to
subject
)
fun
observeProgress
(
owner
:
LifecycleOwner
,
callback
:
(
Float
,
DownloadSubject
)
->
Unit
)
{
progressBroadcast
.
value
=
null
progressBroadcast
.
observe
(
owner
)
{
val
(
progress
,
subject
)
=
it
?:
return
@observe
callback
(
progress
,
subject
)
}
}
fun
reset
(
)
{
internalProgressBroadcast
.
value
=
null
private
fun
broadcast
(
progress
:
Float
,
subject
:
DownloadSubject
)
{
progressBroadcast
.
postValue
(
progress
to
subject
)
}
}
}
app/src/main/java/com/topjohnwu/magisk/core/download/DownloadService.kt
View file @
abc54571
...
...
@@ -6,139 +6,84 @@ import android.app.PendingIntent
import
android.content.Context
import
android.content.Intent
import
android.os.Build
import
android.webkit.MimeTypeMap
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.core.download.Configuration.*
import
com.topjohnwu.magisk.core.download.Configuration.Flash.Secondary
import
com.topjohnwu.magisk.core.download.DownloadSubject.*
import
com.topjohnwu.magisk.core.intent
import
com.topjohnwu.magisk.core.tasks.EnvFixTask
import
com.topjohnwu.magisk.ktx.chooser
import
com.topjohnwu.magisk.ktx.exists
import
com.topjohnwu.magisk.ktx.provide
import
com.topjohnwu.magisk.ui.flash.FlashFragment
import
com.topjohnwu.magisk.utils.APKInstall
import
org.koin.core.get
import
java.io.File
import
kotlin.random.Random.Default.nextInt
/* More of a facade for [RemoteFileService], but whatever... */
@SuppressLint
(
"Registered"
)
open
class
DownloadService
:
RemoteFile
Service
()
{
open
class
DownloadService
:
BaseDownload
Service
()
{
private
val
context
get
()
=
this
private
val
File
.
type
get
()
=
MimeTypeMap
.
getSingleton
()
.
getMimeTypeFromExtension
(
extension
)
?:
"resource/folder"
override
suspend
fun
onFinished
(
subject
:
DownloadSubject
,
id
:
Int
)
=
when
(
subject
)
{
is
Magisk
->
onFinished
(
subject
,
id
)
is
Module
->
onFinished
(
subject
,
id
)
is
Manager
->
onFinished
(
subject
,
id
)
override
suspend
fun
onFinish
(
subject
:
DownloadSubject
,
id
:
Int
)
=
when
(
subject
)
{
is
Magisk
->
subject
.
onFinish
(
id
)
is
Module
->
subject
.
onFinish
(
id
)
is
Manager
->
subject
.
onFinish
(
id
)
}
private
suspend
fun
onFinished
(
subject
:
Magisk
,
id
:
Int
)
=
when
(
val
conf
=
subject
.
configuration
)
{
Uninstall
->
FlashFragment
.
uninstall
(
subject
.
file
,
id
)
private
suspend
fun
Magisk
.
onFinish
(
id
:
Int
)
=
when
(
val
conf
=
configuration
)
{
Uninstall
->
FlashFragment
.
uninstall
(
file
,
id
)
EnvFix
->
{
remove
(
id
)
EnvFixTask
(
subject
.
file
).
exec
()
cancel
(
id
)
EnvFixTask
(
file
).
exec
()
Unit
}
is
Patch
->
FlashFragment
.
patch
(
subject
.
file
,
conf
.
fileUri
,
id
)
is
Flash
->
FlashFragment
.
flash
(
subject
.
file
,
conf
is
Secondary
,
id
)
is
Patch
->
FlashFragment
.
patch
(
file
,
conf
.
fileUri
,
id
)
is
Flash
->
FlashFragment
.
flash
(
file
,
conf
is
Secondary
,
id
)
else
->
Unit
}
private
fun
onFinished
(
subject
:
Module
,
id
:
Int
)
=
when
(
subject
.
configuration
)
{
is
Flash
->
FlashFragment
.
install
(
subject
.
file
,
id
)
private
fun
Module
.
onFinish
(
id
:
Int
)
=
when
(
configuration
)
{
is
Flash
->
FlashFragment
.
install
(
file
,
id
)
else
->
Unit
}
private
suspend
fun
onFinished
(
subject
:
Manager
,
id
:
Int
)
{
handleAPK
(
subject
)
remove
(
id
)
when
(
subject
.
configuration
)
{
is
APK
.
Upgrade
->
APKInstall
.
install
(
this
,
subject
.
file
)
is
APK
.
Restore
->
Unit
}
private
suspend
fun
Manager
.
onFinish
(
id
:
Int
)
{
handleAPK
(
this
)
cancel
(
id
)
}
// ---
// ---
Customize finish notification
override
fun
Notification
.
Builder
.
addActions
(
subject
:
DownloadSubject
)
override
fun
Notification
.
Builder
.
setIntent
(
subject
:
DownloadSubject
)
=
when
(
subject
)
{
is
Magisk
->
addActions
(
subject
)
is
Module
->
addActions
(
subject
)
is
Manager
->
addActions
(
subject
)
is
Magisk
->
setIntent
(
subject
)
is
Module
->
setIntent
(
subject
)
is
Manager
->
setIntent
(
subject
)
}
private
fun
Notification
.
Builder
.
addActions
(
subject
:
Magisk
)
private
fun
Notification
.
Builder
.
setIntent
(
subject
:
Magisk
)
=
when
(
val
conf
=
subject
.
configuration
)
{
Download
->
apply
{
fileIntent
(
subject
.
file
.
parentFile
!!
)
.
takeIf
{
it
.
exists
(
get
())
}
?.
let
{
addAction
(
0
,
R
.
string
.
download_open_parent
,
it
.
chooser
())
}
fileIntent
(
subject
.
file
)
.
takeIf
{
it
.
exists
(
get
())
}
?.
let
{
addAction
(
0
,
R
.
string
.
download_open_self
,
it
.
chooser
())
}
}
Uninstall
->
setContentIntent
(
FlashFragment
.
uninstallIntent
(
context
,
subject
.
file
))
is
Flash
->
setContentIntent
(
FlashFragment
.
flashIntent
(
context
,
subject
.
file
,
conf
is
Secondary
))
is
Patch
->
setContentIntent
(
FlashFragment
.
patchIntent
(
context
,
subject
.
file
,
conf
.
fileUri
))
else
->
this
else
->
setContentIntent
(
Intent
())
}
private
fun
Notification
.
Builder
.
addActions
(
subject
:
Module
)
private
fun
Notification
.
Builder
.
setIntent
(
subject
:
Module
)
=
when
(
subject
.
configuration
)
{
Download
->
this
.
apply
{
fileIntent
(
subject
.
file
.
parentFile
!!
)
.
takeIf
{
it
.
exists
(
get
())
}
?.
let
{
addAction
(
0
,
R
.
string
.
download_open_parent
,
it
.
chooser
())
}
fileIntent
(
subject
.
file
)
.
takeIf
{
it
.
exists
(
get
())
}
?.
let
{
addAction
(
0
,
R
.
string
.
download_open_self
,
it
.
chooser
())
}
}
is
Flash
->
setContentIntent
(
FlashFragment
.
installIntent
(
context
,
subject
.
file
))
else
->
this
else
->
setContentIntent
(
Intent
())
}
private
fun
Notification
.
Builder
.
addActions
(
subject
:
Manager
)
private
fun
Notification
.
Builder
.
setIntent
(
subject
:
Manager
)
=
when
(
subject
.
configuration
)
{
APK
.
Upgrade
->
setContentIntent
(
APKInstall
.
installIntent
(
context
,
subject
.
file
))
else
->
this
else
->
setContentIntent
(
Intent
())
}
@Suppress
(
"ReplaceSingleLineLet"
)
private
fun
Notification
.
Builder
.
setContentIntent
(
intent
:
Intent
)
=
setContentIntent
(
PendingIntent
.
getActivity
(
context
,
nextInt
(),
intent
,
PendingIntent
.
FLAG_ONE_SHOT
)
)
@Suppress
(
"ReplaceSingleLineLet"
)
private
fun
Notification
.
Builder
.
addAction
(
icon
:
Int
,
title
:
Int
,
intent
:
Intent
)
=
addAction
(
icon
,
getString
(
title
),
PendingIntent
.
getActivity
(
context
,
nextInt
(),
intent
,
PendingIntent
.
FLAG_ONE_SHOT
)
)
// ---
private
fun
fileIntent
(
file
:
File
):
Intent
{
return
Intent
(
Intent
.
ACTION_VIEW
)
.
setDataAndType
(
file
.
provide
(
this
),
file
.
type
)
.
addFlags
(
Intent
.
FLAG_ACTIVITY_NEW_TASK
)
.
addFlags
(
Intent
.
FLAG_GRANT_READ_URI_PERMISSION
)
}
class
Builder
{
lateinit
var
subject
:
DownloadSubject
}
...
...
@@ -150,7 +95,7 @@ open class DownloadService : RemoteFileService() {
val
builder
=
Builder
().
apply
(
argBuilder
)
val
intent
=
app
.
intent
<
DownloadService
>().
putExtra
(
ARG_URL
,
builder
.
subject
)
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
O
)
{
if
(
Build
.
VERSION
.
SDK_INT
>=
26
)
{
app
.
startForegroundService
(
intent
)
}
else
{
app
.
startService
(
intent
)
...
...
app/src/main/java/com/topjohnwu/magisk/core/download/ManagerUpgrade.kt
View file @
abc54571
...
...
@@ -12,10 +12,11 @@ import com.topjohnwu.magisk.core.intent
import
com.topjohnwu.magisk.core.isRunningAsStub
import
com.topjohnwu.magisk.core.utils.PatchAPK
import
com.topjohnwu.magisk.ktx.writeTo
import
com.topjohnwu.magisk.utils.APKInstall
import
com.topjohnwu.superuser.Shell
import
java.io.File
private
fun
RemoteFile
Service
.
patch
(
apk
:
File
,
id
:
Int
)
{
private
fun
Download
Service
.
patch
(
apk
:
File
,
id
:
Int
)
{
if
(
packageName
==
BuildConfig
.
APPLICATION_ID
)
return
...
...
@@ -31,7 +32,7 @@ private fun RemoteFileService.patch(apk: File, id: Int) {
patched
.
renameTo
(
apk
)
}
private
suspend
fun
RemoteFile
Service
.
upgrade
(
apk
:
File
,
id
:
Int
)
{
private
suspend
fun
Download
Service
.
upgrade
(
apk
:
File
,
id
:
Int
)
{
if
(
isRunningAsStub
)
{
// Move to upgrade location
apk
.
copyTo
(
DynAPK
.
update
(
this
),
overwrite
=
true
)
...
...
@@ -49,9 +50,10 @@ private suspend fun RemoteFileService.upgrade(apk: File, id: Int) {
}
else
{
patch
(
apk
,
id
)
}
APKInstall
.
install
(
this
,
apk
)
}
private
fun
RemoteFile
Service
.
restore
(
apk
:
File
,
id
:
Int
)
{
private
fun
Download
Service
.
restore
(
apk
:
File
,
id
:
Int
)
{
update
(
id
)
{
it
.
setProgress
(
0
,
0
,
true
)
.
setProgress
(
0
,
0
,
true
)
...
...
@@ -64,8 +66,8 @@ private fun RemoteFileService.restore(apk: File, id: Int) {
Shell
.
su
(
"pm install $apk && pm uninstall $packageName"
).
exec
()
}
suspend
fun
RemoteFile
Service
.
handleAPK
(
subject
:
DownloadSubject
.
Manager
)
=
suspend
fun
Download
Service
.
handleAPK
(
subject
:
DownloadSubject
.
Manager
)
=
when
(
subject
.
configuration
)
{
is
Upgrade
->
upgrade
(
subject
.
file
,
subject
.
hashCode
())
is
Restore
->
restore
(
subject
.
file
,
subject
.
hashCode
())
is
Upgrade
->
upgrade
(
subject
.
file
,
subject
.
notifyID
())
is
Restore
->
restore
(
subject
.
file
,
subject
.
notifyID
())
}
app/src/main/java/com/topjohnwu/magisk/core/download/NotificationService.kt
deleted
100644 → 0
View file @
4b238a9c
package
com.topjohnwu.magisk.core.download
import
android.app.Notification
import
android.content.Intent
import
android.os.IBinder
import
com.topjohnwu.magisk.core.base.BaseService
import
com.topjohnwu.magisk.view.Notifications
import
kotlinx.coroutines.CoroutineScope
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.cancel
import
org.koin.core.KoinComponent
import
java.util.*
import
kotlin.collections.HashMap
import
kotlin.random.Random.Default.nextInt
abstract
class
NotificationService
:
BaseService
(),
KoinComponent
{
private
val
hasNotifications
get
()
=
notifications
.
isNotEmpty
()
private
val
notifications
=
Collections
.
synchronizedMap
(
HashMap
<
Int
,
Notification
.
Builder
>())
val
coroutineScope
=
CoroutineScope
(
Dispatchers
.
IO
)
override
fun
onTaskRemoved
(
rootIntent
:
Intent
?)
{
super
.
onTaskRemoved
(
rootIntent
)
notifications
.
forEach
{
cancel
(
it
.
key
)
}
notifications
.
clear
()
}
override
fun
onDestroy
()
{
super
.
onDestroy
()
coroutineScope
.
cancel
()
}
abstract
fun
createNotification
():
Notification
.
Builder
// --
fun
update
(
id
:
Int
,
body
:
(
Notification
.
Builder
)
->
Unit
=
{}
)
{
val
wasEmpty
=
notifications
.
isEmpty
()
val
notification
=
notifications
.
getOrPut
(
id
,
::
createNotification
).
also
(
body
)
if
(
wasEmpty
)
updateForeground
()
else
notify
(
id
,
notification
.
build
())
}
protected
fun
lastNotify
(
id
:
Int
,
editBody
:
(
Notification
.
Builder
)
->
Notification
.
Builder
?
=
{
null
}
)
:
Int
{
val
currentNotification
=
remove
(
id
)
?.
run
(
editBody
)
var
newId
=
-
1
currentNotification
?.
let
{
newId
=
nextInt
(
Int
.
MAX_VALUE
)
notify
(
newId
,
it
.
build
())
}
if
(!
hasNotifications
)
{
stopSelf
()
}
return
newId
}
protected
fun
remove
(
id
:
Int
)
=
notifications
.
remove
(
id
).
also
{
cancel
(
id
)
updateForeground
()
}
// ---
private
fun
notify
(
id
:
Int
,
notification
:
Notification
)
{
Notifications
.
mgr
.
notify
(
id
,
notification
)
}
private
fun
cancel
(
id
:
Int
)
{
Notifications
.
mgr
.
cancel
(
id
)
}
private
fun
updateForeground
()
{
if
(
hasNotifications
)
{
val
first
=
notifications
.
entries
.
first
()
startForeground
(
first
.
key
,
first
.
value
.
build
())
}
else
{
stopForeground
(
true
)
}
}
// --
override
fun
onBind
(
p0
:
Intent
?):
IBinder
?
=
null
}
app/src/main/java/com/topjohnwu/magisk/core/utils/ProgressInputStream.kt
View file @
abc54571
package
com.topjohnwu.magisk.core.utils
import
com.topjohnwu.superuser.internal.UiThreadHandler
import
java.io.FilterInputStream
import
java.io.InputStream
...
...
@@ -16,7 +15,7 @@ class ProgressInputStream(
val
cur
=
System
.
currentTimeMillis
()
if
(
cur
-
lastUpdate
>
1000
)
{
lastUpdate
=
cur
UiThreadHandler
.
run
{
progressEmitter
(
bytesRead
)
}
progressEmitter
(
bytesRead
)
}
}
...
...
app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt
View file @
abc54571
...
...
@@ -4,6 +4,7 @@ import android.os.Bundle
import
android.view.*
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.arch.BaseUIFragment
import
com.topjohnwu.magisk.core.download.BaseDownloadService
import
com.topjohnwu.magisk.databinding.FragmentHomeMd2Binding
import
com.topjohnwu.magisk.events.RebootEvent
import
com.topjohnwu.superuser.Shell
...
...
@@ -18,6 +19,7 @@ class HomeFragment : BaseUIFragment<HomeViewModel, FragmentHomeMd2Binding>() {
super
.
onStart
()
activity
.
title
=
resources
.
getString
(
R
.
string
.
section_home
)
setHasOptionsMenu
(
true
)
BaseDownloadService
.
observeProgress
(
this
,
viewModel
::
onProgressUpdate
)
}
override
fun
onCreateView
(
...
...
app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt
View file @
abc54571
...
...
@@ -8,8 +8,8 @@ import com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.arch.*
import
com.topjohnwu.magisk.core.Config
import
com.topjohnwu.magisk.core.Info
import
com.topjohnwu.magisk.core.download.DownloadSubject
import
com.topjohnwu.magisk.core.download.DownloadSubject.Manager
import
com.topjohnwu.magisk.core.download.RemoteFileService
import
com.topjohnwu.magisk.core.model.MagiskJson
import
com.topjohnwu.magisk.core.model.ManagerJson
import
com.topjohnwu.magisk.data.repository.MagiskRepository
...
...
@@ -77,14 +77,6 @@ class HomeViewModel(
private
var
shownDialog
=
false
init
{
RemoteFileService
.
progressBroadcast
.
observeForever
{
when
(
it
?.
second
)
{
is
Manager
->
stateManagerProgress
=
it
.
first
.
times
(
100f
).
roundToInt
()
}
}
}
override
fun
refresh
()
=
viewModelScope
.
launch
{
notifyPropertyChanged
(
BR
.
showUninstall
)
repoMagisk
.
fetchUpdate
()
?.
apply
{
...
...
@@ -119,6 +111,12 @@ class HomeViewModel(
}
}.
publish
()
fun
onProgressUpdate
(
progress
:
Float
,
subject
:
DownloadSubject
)
{
when
(
subject
)
{
is
Manager
->
stateManagerProgress
=
progress
.
times
(
100f
).
roundToInt
()
}
}
fun
onLinkPressed
(
link
:
String
)
=
OpenInappLinkEvent
(
link
).
publish
()
fun
onDeletePressed
()
=
UninstallDialog
().
publish
()
...
...
app/src/main/java/com/topjohnwu/magisk/ui/install/InstallFragment.kt
View file @
abc54571
...
...
@@ -4,6 +4,7 @@ import android.content.Intent
import
androidx.lifecycle.viewModelScope
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.arch.BaseUIFragment
import
com.topjohnwu.magisk.core.download.BaseDownloadService
import
com.topjohnwu.magisk.databinding.FragmentInstallMd2Binding
import
com.topjohnwu.magisk.events.RequestFileEvent
import
org.koin.androidx.viewmodel.ext.android.viewModel
...
...
@@ -24,6 +25,7 @@ class InstallFragment : BaseUIFragment<InstallViewModel, FragmentInstallMd2Bindi
// Allow markwon to run in viewmodel scope
binding
.
releaseNotes
.
tag
=
viewModel
.
viewModelScope
BaseDownloadService
.
observeProgress
(
this
,
viewModel
::
onProgressUpdate
)
}
}
app/src/main/java/com/topjohnwu/magisk/ui/install/InstallViewModel.kt
View file @
abc54571
...
...
@@ -11,7 +11,6 @@ import com.topjohnwu.magisk.core.Info
import
com.topjohnwu.magisk.core.download.Configuration
import
com.topjohnwu.magisk.core.download.DownloadService
import
com.topjohnwu.magisk.core.download.DownloadSubject
import
com.topjohnwu.magisk.core.download.RemoteFileService
import
com.topjohnwu.magisk.data.repository.StringRepository
import
com.topjohnwu.magisk.events.RequestFileEvent
import
com.topjohnwu.magisk.events.dialog.SecondSlotWarningDialog
...
...
@@ -60,22 +59,21 @@ class InstallViewModel(
set
(
value
)
=
set
(
value
,
field
,
{
field
=
it
},
BR
.
notes
)
init
{
RemoteFileService
.
reset
()
RemoteFileService
.
progressBroadcast
.
observeForever
{
val
(
progress
,
subject
)
=
it
?:
return
@observeForever
if
(
subject
!
is
DownloadSubject
.
Magisk
)
{
return
@observeForever
}
this
.
progress
=
progress
.
times
(
100
).
roundToInt
()
if
(
this
.
progress
>=
100
)
{
state
=
State
.
LOADED
}
}
viewModelScope
.
launch
{
notes
=
stringRepo
.
getString
(
Info
.
remote
.
magisk
.
note
)
}
}
fun
onProgressUpdate
(
progress
:
Float
,
subject
:
DownloadSubject
)
{
if
(
subject
!
is
DownloadSubject
.
Magisk
)
{
return
}
this
.
progress
=
progress
.
times
(
100
).
roundToInt
()
if
(
this
.
progress
>=
100
)
{
state
=
State
.
LOADED
}
}
fun
step
(
nextStep
:
Int
)
{
step
=
nextStep
}
...
...
app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleFragment.kt
View file @
abc54571
...
...
@@ -12,6 +12,7 @@ import com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.arch.BaseUIFragment
import
com.topjohnwu.magisk.arch.ReselectionTarget
import
com.topjohnwu.magisk.arch.ViewEvent
import
com.topjohnwu.magisk.core.download.BaseDownloadService
import
com.topjohnwu.magisk.databinding.FragmentModuleMd2Binding
import
com.topjohnwu.magisk.events.InstallExternalModuleEvent
import
com.topjohnwu.magisk.ktx.hideKeyboard
...
...
@@ -50,6 +51,7 @@ class ModuleFragment : BaseUIFragment<ModuleViewModel, FragmentModuleMd2Binding>
super
.
onStart
()
setHasOptionsMenu
(
true
)
activity
.
title
=
resources
.
getString
(
R
.
string
.
modules
)
BaseDownloadService
.
observeProgress
(
this
,
viewModel
::
onProgressUpdate
)
}
override
fun
onViewCreated
(
view
:
View
,
savedInstanceState
:
Bundle
?)
{
...
...
app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt
View file @
abc54571
...
...
@@ -2,14 +2,12 @@ package com.topjohnwu.magisk.ui.module
import
androidx.databinding.Bindable
import
androidx.databinding.ObservableArrayList
import
androidx.lifecycle.Observer
import
androidx.lifecycle.viewModelScope
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.arch.*
import
com.topjohnwu.magisk.core.Config
import
com.topjohnwu.magisk.core.download.DownloadSubject
import
com.topjohnwu.magisk.core.download.RemoteFileService
import
com.topjohnwu.magisk.core.model.module.Module
import
com.topjohnwu.magisk.core.tasks.RepoUpdater
import
com.topjohnwu.magisk.data.database.RepoByNameDao
...
...
@@ -47,7 +45,7 @@ class ModuleViewModel(
private
val
repoName
:
RepoByNameDao
,
private
val
repoUpdated
:
RepoByUpdatedDao
,
private
val
repoUpdater
:
RepoUpdater
)
:
BaseViewModel
(),
Queryable
,
Observer
<
Pair
<
Float
,
DownloadSubject
>>
{
)
:
BaseViewModel
(),
Queryable
{
override
val
queryDelay
=
1000L
private
var
queryJob
:
Job
?
=
null
...
...
@@ -125,9 +123,6 @@ class ModuleViewModel(
// ---
init
{
RemoteFileService
.
reset
()
RemoteFileService
.
progressBroadcast
.
observeForever
(
this
)
itemsInstalled
.
addOnListChangedCallback
(
onItemRangeInserted
=
{
_
,
_
,
_
->
if
(
installSectionList
.
isEmpty
())
...
...
@@ -152,13 +147,7 @@ class ModuleViewModel(
// ---
override
fun
onCleared
()
{
super
.
onCleared
()
RemoteFileService
.
progressBroadcast
.
removeObserver
(
this
)
}
override
fun
onChanged
(
it
:
Pair
<
Float
,
DownloadSubject
>?)
{
val
(
progress
,
subject
)
=
it
?:
return
fun
onProgressUpdate
(
progress
:
Float
,
subject
:
DownloadSubject
)
{
if
(
subject
!
is
DownloadSubject
.
Module
)
return
...
...
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