mirror of
https://github.com/MatsuriDayo/NekoBoxForAndroid.git
synced 2025-12-19 14:40:06 +08:00
feat: boot receiver
This commit is contained in:
parent
4ea5cc3d40
commit
c56f9b1d9f
@ -297,6 +297,18 @@
|
||||
android:authorities="${applicationId}.androidx-startup"
|
||||
tools:node="remove" />
|
||||
|
||||
<receiver
|
||||
android:name="io.nekohasekai.sagernet.BootReceiver"
|
||||
android:enabled="false"
|
||||
android:exported="true"
|
||||
android:process=":bg">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
android:name="androidx.room.MultiInstanceInvalidationService"
|
||||
android:process=":bg" />
|
||||
|
||||
42
app/src/main/java/io/nekohasekai/sagernet/BootReceiver.kt
Normal file
42
app/src/main/java/io/nekohasekai/sagernet/BootReceiver.kt
Normal file
@ -0,0 +1,42 @@
|
||||
package io.nekohasekai.sagernet
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import io.nekohasekai.sagernet.bg.SubscriptionUpdater
|
||||
import io.nekohasekai.sagernet.database.DataStore
|
||||
import io.nekohasekai.sagernet.ktx.app
|
||||
import io.nekohasekai.sagernet.ktx.runOnDefaultDispatcher
|
||||
|
||||
class BootReceiver : BroadcastReceiver() {
|
||||
companion object {
|
||||
private val componentName by lazy { ComponentName(app, BootReceiver::class.java) }
|
||||
var enabled: Boolean
|
||||
get() = app.packageManager.getComponentEnabledSetting(componentName) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
|
||||
set(value) = app.packageManager.setComponentEnabledSetting(
|
||||
componentName, if (value) PackageManager.COMPONENT_ENABLED_STATE_ENABLED
|
||||
else PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP
|
||||
)
|
||||
}
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
runOnDefaultDispatcher {
|
||||
SubscriptionUpdater.reconfigureUpdater()
|
||||
}
|
||||
|
||||
if (!DataStore.persistAcrossReboot) { // sanity check
|
||||
enabled = false
|
||||
return
|
||||
}
|
||||
|
||||
val doStart = when (intent.action) {
|
||||
Intent.ACTION_LOCKED_BOOT_COMPLETED -> false // DataStore.directBootAware
|
||||
else -> Build.VERSION.SDK_INT < 24 || SagerNet.user.isUserUnlocked
|
||||
} && DataStore.selectedProxy > 0
|
||||
|
||||
if (doStart) SagerNet.startService()
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,8 @@ object Key {
|
||||
const val DB_PUBLIC = "configuration.db"
|
||||
const val DB_PROFILE = "sager_net.db"
|
||||
|
||||
const val PERSIST_ACROSS_REBOOT = "isAutoConnect"
|
||||
|
||||
const val APP_EXPERT = "isExpert"
|
||||
const val APP_THEME = "appTheme"
|
||||
const val NIGHT_THEME = "nightTheme"
|
||||
|
||||
@ -7,6 +7,7 @@ import android.content.IntentFilter
|
||||
import android.os.*
|
||||
import android.widget.Toast
|
||||
import io.nekohasekai.sagernet.Action
|
||||
import io.nekohasekai.sagernet.BootReceiver
|
||||
import io.nekohasekai.sagernet.R
|
||||
import io.nekohasekai.sagernet.SagerNet
|
||||
import io.nekohasekai.sagernet.aidl.ISagerNetService
|
||||
@ -314,6 +315,7 @@ class BaseService {
|
||||
|
||||
val proxy = ProxyInstance(profile, this)
|
||||
data.proxy = proxy
|
||||
BootReceiver.enabled = DataStore.persistAcrossReboot
|
||||
if (!data.closeReceiverRegistered) {
|
||||
registerReceiver(data.receiver, IntentFilter().apply {
|
||||
addAction(Action.RELOAD)
|
||||
|
||||
@ -60,7 +60,7 @@ class TileService : BaseTileService(), SagerConnection.Callback {
|
||||
}
|
||||
|
||||
override fun onClick() {
|
||||
toggle()
|
||||
if (isLocked) unlockAndRun(this::toggle) else toggle()
|
||||
}
|
||||
|
||||
private fun updateTile(serviceState: BaseService.State, profileName: () -> String?) {
|
||||
@ -72,15 +72,18 @@ class TileService : BaseTileService(), SagerConnection.Callback {
|
||||
icon = iconBusy
|
||||
state = Tile.STATE_ACTIVE
|
||||
}
|
||||
|
||||
BaseService.State.Connected -> {
|
||||
icon = iconConnected
|
||||
label = profileName()
|
||||
state = Tile.STATE_ACTIVE
|
||||
}
|
||||
|
||||
BaseService.State.Stopping -> {
|
||||
icon = iconBusy
|
||||
state = Tile.STATE_UNAVAILABLE
|
||||
}
|
||||
|
||||
BaseService.State.Stopped -> {
|
||||
icon = iconIdle
|
||||
state = Tile.STATE_INACTIVE
|
||||
|
||||
@ -151,6 +151,8 @@ object DataStore : OnPreferenceDataStoreChangeListener {
|
||||
var individual by configurationStore.string(Key.INDIVIDUAL)
|
||||
var showDirectSpeed by configurationStore.boolean(Key.SHOW_DIRECT_SPEED) { true }
|
||||
|
||||
val persistAcrossReboot by configurationStore.boolean(Key.PERSIST_ACROSS_REBOOT) { false }
|
||||
|
||||
var appendHttpProxy by configurationStore.boolean(Key.APPEND_HTTP_PROXY)
|
||||
var requireTransproxy by configurationStore.boolean(Key.REQUIRE_TRANSPROXY)
|
||||
var transproxyMode by configurationStore.stringToInt(Key.TRANSPROXY_MODE)
|
||||
|
||||
@ -98,6 +98,14 @@ class MainActivity : ThemedActivity(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
refreshNavMenu(DataStore.enableClashAPI)
|
||||
}
|
||||
|
||||
fun refreshNavMenu(clashApi: Boolean) {
|
||||
if (::navigation.isInitialized) {
|
||||
navigation.menu.findItem(R.id.nav_traffic)?.isVisible = clashApi
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
@ -347,17 +355,6 @@ class MainActivity : ThemedActivity(),
|
||||
binding.fab.changeState(state, DataStore.serviceState, animate)
|
||||
binding.stats.changeState(state)
|
||||
if (msg != null) snackbar(getString(R.string.vpn_error, msg)).show()
|
||||
|
||||
when (state) {
|
||||
BaseService.State.Stopped -> {
|
||||
runOnDefaultDispatcher {
|
||||
// refresh view
|
||||
ProfileManager.postUpdate(DataStore.currentProfile)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun snackbarInternal(text: CharSequence): Snackbar {
|
||||
|
||||
@ -189,6 +189,11 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() {
|
||||
val resolveDestination = findPreference<SwitchPreference>(Key.RESOLVE_DESTINATION)!!
|
||||
val acquireWakeLock = findPreference<SwitchPreference>(Key.ACQUIRE_WAKE_LOCK)!!
|
||||
val enableClashAPI = findPreference<SwitchPreference>(Key.ENABLE_CLASH_API)!!
|
||||
enableClashAPI.setOnPreferenceChangeListener { _, newValue ->
|
||||
(activity as MainActivity?)?.refreshNavMenu(newValue as Boolean)
|
||||
needReload()
|
||||
true
|
||||
}
|
||||
|
||||
serviceMode.onPreferenceChangeListener = reloadListener
|
||||
mixedPort.onPreferenceChangeListener = reloadListener
|
||||
@ -218,7 +223,6 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() {
|
||||
resolveDestination.onPreferenceChangeListener = reloadListener
|
||||
tunImplementation.onPreferenceChangeListener = reloadListener
|
||||
acquireWakeLock.onPreferenceChangeListener = reloadListener
|
||||
enableClashAPI.onPreferenceChangeListener = reloadListener
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -6,6 +6,12 @@
|
||||
app:icon="@drawable/ic_baseline_android_24"
|
||||
app:key="nekoPlugins"
|
||||
app:title="@string/neko_plugin" />
|
||||
<SwitchPreference
|
||||
app:defaultValue="false"
|
||||
app:icon="@drawable/ic_communication_phonelink_ring"
|
||||
app:key="isAutoConnect"
|
||||
app:summary="@string/auto_connect_summary"
|
||||
app:title="@string/auto_connect" />
|
||||
<moe.matsuri.nb4a.ui.ColorPickerPreference
|
||||
android:title="@string/theme"
|
||||
app:icon="@drawable/ic_baseline_color_lens_24"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user