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"
|
android:authorities="${applicationId}.androidx-startup"
|
||||||
tools:node="remove" />
|
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
|
<service
|
||||||
android:name="androidx.room.MultiInstanceInvalidationService"
|
android:name="androidx.room.MultiInstanceInvalidationService"
|
||||||
android:process=":bg" />
|
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_PUBLIC = "configuration.db"
|
||||||
const val DB_PROFILE = "sager_net.db"
|
const val DB_PROFILE = "sager_net.db"
|
||||||
|
|
||||||
|
const val PERSIST_ACROSS_REBOOT = "isAutoConnect"
|
||||||
|
|
||||||
const val APP_EXPERT = "isExpert"
|
const val APP_EXPERT = "isExpert"
|
||||||
const val APP_THEME = "appTheme"
|
const val APP_THEME = "appTheme"
|
||||||
const val NIGHT_THEME = "nightTheme"
|
const val NIGHT_THEME = "nightTheme"
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import android.content.IntentFilter
|
|||||||
import android.os.*
|
import android.os.*
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import io.nekohasekai.sagernet.Action
|
import io.nekohasekai.sagernet.Action
|
||||||
|
import io.nekohasekai.sagernet.BootReceiver
|
||||||
import io.nekohasekai.sagernet.R
|
import io.nekohasekai.sagernet.R
|
||||||
import io.nekohasekai.sagernet.SagerNet
|
import io.nekohasekai.sagernet.SagerNet
|
||||||
import io.nekohasekai.sagernet.aidl.ISagerNetService
|
import io.nekohasekai.sagernet.aidl.ISagerNetService
|
||||||
@ -314,6 +315,7 @@ class BaseService {
|
|||||||
|
|
||||||
val proxy = ProxyInstance(profile, this)
|
val proxy = ProxyInstance(profile, this)
|
||||||
data.proxy = proxy
|
data.proxy = proxy
|
||||||
|
BootReceiver.enabled = DataStore.persistAcrossReboot
|
||||||
if (!data.closeReceiverRegistered) {
|
if (!data.closeReceiverRegistered) {
|
||||||
registerReceiver(data.receiver, IntentFilter().apply {
|
registerReceiver(data.receiver, IntentFilter().apply {
|
||||||
addAction(Action.RELOAD)
|
addAction(Action.RELOAD)
|
||||||
|
|||||||
@ -60,7 +60,7 @@ class TileService : BaseTileService(), SagerConnection.Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick() {
|
override fun onClick() {
|
||||||
toggle()
|
if (isLocked) unlockAndRun(this::toggle) else toggle()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateTile(serviceState: BaseService.State, profileName: () -> String?) {
|
private fun updateTile(serviceState: BaseService.State, profileName: () -> String?) {
|
||||||
@ -72,15 +72,18 @@ class TileService : BaseTileService(), SagerConnection.Callback {
|
|||||||
icon = iconBusy
|
icon = iconBusy
|
||||||
state = Tile.STATE_ACTIVE
|
state = Tile.STATE_ACTIVE
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseService.State.Connected -> {
|
BaseService.State.Connected -> {
|
||||||
icon = iconConnected
|
icon = iconConnected
|
||||||
label = profileName()
|
label = profileName()
|
||||||
state = Tile.STATE_ACTIVE
|
state = Tile.STATE_ACTIVE
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseService.State.Stopping -> {
|
BaseService.State.Stopping -> {
|
||||||
icon = iconBusy
|
icon = iconBusy
|
||||||
state = Tile.STATE_UNAVAILABLE
|
state = Tile.STATE_UNAVAILABLE
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseService.State.Stopped -> {
|
BaseService.State.Stopped -> {
|
||||||
icon = iconIdle
|
icon = iconIdle
|
||||||
state = Tile.STATE_INACTIVE
|
state = Tile.STATE_INACTIVE
|
||||||
|
|||||||
@ -151,6 +151,8 @@ object DataStore : OnPreferenceDataStoreChangeListener {
|
|||||||
var individual by configurationStore.string(Key.INDIVIDUAL)
|
var individual by configurationStore.string(Key.INDIVIDUAL)
|
||||||
var showDirectSpeed by configurationStore.boolean(Key.SHOW_DIRECT_SPEED) { true }
|
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 appendHttpProxy by configurationStore.boolean(Key.APPEND_HTTP_PROXY)
|
||||||
var requireTransproxy by configurationStore.boolean(Key.REQUIRE_TRANSPROXY)
|
var requireTransproxy by configurationStore.boolean(Key.REQUIRE_TRANSPROXY)
|
||||||
var transproxyMode by configurationStore.stringToInt(Key.TRANSPROXY_MODE)
|
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) {
|
override fun onNewIntent(intent: Intent) {
|
||||||
@ -347,17 +355,6 @@ class MainActivity : ThemedActivity(),
|
|||||||
binding.fab.changeState(state, DataStore.serviceState, animate)
|
binding.fab.changeState(state, DataStore.serviceState, animate)
|
||||||
binding.stats.changeState(state)
|
binding.stats.changeState(state)
|
||||||
if (msg != null) snackbar(getString(R.string.vpn_error, msg)).show()
|
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 {
|
override fun snackbarInternal(text: CharSequence): Snackbar {
|
||||||
|
|||||||
@ -189,6 +189,11 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() {
|
|||||||
val resolveDestination = findPreference<SwitchPreference>(Key.RESOLVE_DESTINATION)!!
|
val resolveDestination = findPreference<SwitchPreference>(Key.RESOLVE_DESTINATION)!!
|
||||||
val acquireWakeLock = findPreference<SwitchPreference>(Key.ACQUIRE_WAKE_LOCK)!!
|
val acquireWakeLock = findPreference<SwitchPreference>(Key.ACQUIRE_WAKE_LOCK)!!
|
||||||
val enableClashAPI = findPreference<SwitchPreference>(Key.ENABLE_CLASH_API)!!
|
val enableClashAPI = findPreference<SwitchPreference>(Key.ENABLE_CLASH_API)!!
|
||||||
|
enableClashAPI.setOnPreferenceChangeListener { _, newValue ->
|
||||||
|
(activity as MainActivity?)?.refreshNavMenu(newValue as Boolean)
|
||||||
|
needReload()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
serviceMode.onPreferenceChangeListener = reloadListener
|
serviceMode.onPreferenceChangeListener = reloadListener
|
||||||
mixedPort.onPreferenceChangeListener = reloadListener
|
mixedPort.onPreferenceChangeListener = reloadListener
|
||||||
@ -218,7 +223,6 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() {
|
|||||||
resolveDestination.onPreferenceChangeListener = reloadListener
|
resolveDestination.onPreferenceChangeListener = reloadListener
|
||||||
tunImplementation.onPreferenceChangeListener = reloadListener
|
tunImplementation.onPreferenceChangeListener = reloadListener
|
||||||
acquireWakeLock.onPreferenceChangeListener = reloadListener
|
acquireWakeLock.onPreferenceChangeListener = reloadListener
|
||||||
enableClashAPI.onPreferenceChangeListener = reloadListener
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,12 @@
|
|||||||
app:icon="@drawable/ic_baseline_android_24"
|
app:icon="@drawable/ic_baseline_android_24"
|
||||||
app:key="nekoPlugins"
|
app:key="nekoPlugins"
|
||||||
app:title="@string/neko_plugin" />
|
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
|
<moe.matsuri.nb4a.ui.ColorPickerPreference
|
||||||
android:title="@string/theme"
|
android:title="@string/theme"
|
||||||
app:icon="@drawable/ic_baseline_color_lens_24"
|
app:icon="@drawable/ic_baseline_color_lens_24"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user