Skip to content

Commit

Permalink
Watch client count and none state
Browse files Browse the repository at this point in the history
  • Loading branch information
pyamsoft committed Jul 10, 2024
1 parent e0a0ef9 commit 26174b0
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 125 deletions.
208 changes: 100 additions & 108 deletions app/src/main/java/com/pyamsoft/tetherfi/main/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,121 +46,113 @@ import javax.inject.Inject

class MainActivity : AppCompatActivity() {

@Inject
@JvmField
internal var viewModel: ThemeViewModeler? = null
@Inject @JvmField internal var viewModel: ThemeViewModeler? = null

@Inject
@JvmField
internal var launcher: ServiceLauncher? = null
@Inject @JvmField internal var launcher: ServiceLauncher? = null

private var pydroid: PYDroidActivityDelegate? = null
private var pydroid: PYDroidActivityDelegate? = null

private fun initializePYDroid() {
pydroid =
installPYDroid(
provider =
private fun initializePYDroid() {
pydroid =
installPYDroid(
provider =
object : ChangeLogProvider {

override val applicationIcon = R.mipmap.ic_launcher

override val changelog = buildChangeLog {
bugfix("When stopping the hotspot, stop operation happens faster.")
change(
"Wake Locks have been made ON by default. The option to toggle them will be removed in the next version."
)
change(
"The \"Enable Idle Timeout\" tweak will be made a default in the next version"
)
feature("New Shortcut to directly start the hotspot")
}
},
)
}

private fun handleShowInAppRating() {
pydroid?.loadInAppRating()
}

private fun setupActivity() {
// Setup PYDroid first
initializePYDroid()

// Create and initialize the ObjectGraph
val component = ObjectGraph.ApplicationScope.retrieve(this).plusMain().create()
component.inject(this)
ObjectGraph.ActivityScope.install(this, component)

// Then register for any permissions
PermissionManager.createAndRegister(this, component)
}
override val applicationIcon = R.mipmap.ic_launcher

private fun safeOpenSettingsIntent(action: String) {

// Try specific first, may fail on some devices
try {
val intent = Intent(action, "package:${packageName}".toUri())
startActivity(intent)
} catch (e: Throwable) {
Timber.e(e) { "Failed specific intent for $action" }
val intent = Intent(action)
startActivity(intent)
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupActivity()

val vm = viewModel.requireNotNull()
val appName = getString(R.string.app_name)

setContent {
val theme by vm.mode.collectAsStateWithLifecycle()
val isMaterialYou by vm.isMaterialYou.collectAsStateWithLifecycle()

SaveStateDisposableEffect(vm)

TetherFiTheme(
theme = theme,
isMaterialYou = isMaterialYou,
) {
SystemBars(
isDarkMode = theme.getSystemDarkMode(),
)
InstallPYDroidExtras(
modifier = Modifier
.fillUpToPortraitSize()
.widthIn(max = LANDSCAPE_MAX_WIDTH),
appName = appName,
)
MainEntry(
modifier = Modifier.fillMaxSize(),
appName = appName,
onShowInAppRating = { handleShowInAppRating() },
onUpdateTile = { ProxyTileService.updateTile(this) },
onLaunchIntent = { safeOpenSettingsIntent(it) },
)
}
}

vm.init(this)
}

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
override val changelog = buildChangeLog {
bugfix("When stopping the hotspot, stop operation happens faster.")
change(
"Wake Locks have been made ON by default. The option to toggle them will be removed in the next version.")
change(
"The \"Enable Idle Timeout\" tweak will be made a default in the next version")
feature("New Shortcut to directly start the hotspot")
}
},
)
}

private fun handleShowInAppRating() {
pydroid?.loadInAppRating()
}

private fun setupActivity() {
// Setup PYDroid first
initializePYDroid()

// Create and initialize the ObjectGraph
val component = ObjectGraph.ApplicationScope.retrieve(this).plusMain().create()
component.inject(this)
ObjectGraph.ActivityScope.install(this, component)

// Then register for any permissions
PermissionManager.createAndRegister(this, component)
}

private fun safeOpenSettingsIntent(action: String) {

// Try specific first, may fail on some devices
try {
val intent = Intent(action, "package:${packageName}".toUri())
startActivity(intent)
} catch (e: Throwable) {
Timber.e(e) { "Failed specific intent for $action" }
val intent = Intent(action)
startActivity(intent)
}

override fun onResume() {
super.onResume()
reportFullyDrawn()
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupActivity()

val vm = viewModel.requireNotNull()
val appName = getString(R.string.app_name)

setContent {
val theme by vm.mode.collectAsStateWithLifecycle()
val isMaterialYou by vm.isMaterialYou.collectAsStateWithLifecycle()

SaveStateDisposableEffect(vm)

TetherFiTheme(
theme = theme,
isMaterialYou = isMaterialYou,
) {
SystemBars(
isDarkMode = theme.getSystemDarkMode(),
)
InstallPYDroidExtras(
modifier = Modifier.fillUpToPortraitSize().widthIn(max = LANDSCAPE_MAX_WIDTH),
appName = appName,
)
MainEntry(
modifier = Modifier.fillMaxSize(),
appName = appName,
onShowInAppRating = { handleShowInAppRating() },
onUpdateTile = { ProxyTileService.updateTile(this) },
onLaunchIntent = { safeOpenSettingsIntent(it) },
)
}
}

override fun onDestroy() {
super.onDestroy()
pydroid = null
viewModel = null
launcher = null
}
vm.init(this)
}

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
}

override fun onResume() {
super.onResume()
reportFullyDrawn()
}

override fun onDestroy() {
super.onDestroy()
pydroid = null
viewModel = null
launcher = null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,9 @@ internal constructor(
is HostNameClient -> client.copy(totalBytes = client.mergeReport(report))
}

private fun CoroutineScope.onNewClientSeen(client: TetherClient) {
private fun onNewClientSeen(client: TetherClient) {
Timber.d { "First time seeing client: $client" }
inAppRatingPreferences.markDeviceConnected()

watchForOldClients()
}

@Suppress("UnusedReceiverParameter")
Expand Down Expand Up @@ -151,9 +149,10 @@ internal constructor(
private fun CoroutineScope.watchForOldClients() {
val scope = this

jobs.update {
it +
jobs.update { j ->
j +
scope.launch(context = Dispatchers.Default) {
Timber.d { "Watch client count and purge old clients" }
onTimerElapsed(OLD_CLIENT_TIMER_PERIOD) { purgeOldClients(it) }
}
}
Expand All @@ -167,20 +166,19 @@ internal constructor(
private suspend fun CoroutineScope.watchForNoClients() {
val scope = this

// Remember when we started watching
//
// We do this weird check because in development I noticed when starting the server
// once a random ShutdownWithNoClients was published during startup, which caused
// the app to break since Broadcast started but Proxy never started
val startedAt = LocalDateTime.now(clock)

// Start new if needed
if (isShutdownWithNoClientsEnabled()) {
Timber.d { "Watch client count and shutdown if none" }

jobs.update {
it +
// Remember when we started watching
//
// We do this weird check because in development I noticed when starting the server
// once a random ShutdownWithNoClients was published during startup, which caused
// the app to break since Broadcast started but Proxy never started
val startedAt = LocalDateTime.now(clock)

jobs.update { j ->
j +
scope.launch(context = Dispatchers.Default) {
Timber.d { "Watch client count and shutdown if none" }
onTimerElapsed(NO_CLIENTS_TIMER_PERIOD) { cutoff ->
if (startedAt >= cutoff) {
Timber.w { "Shutdown check received but client started AFTER cutoff - invalid" }
Expand Down Expand Up @@ -235,7 +233,10 @@ internal constructor(
}

override suspend fun started() =
withContext(context = Dispatchers.Default) { watchForNoClients() }
withContext(context = Dispatchers.Default) {
watchForOldClients()
watchForNoClients()
}

override fun block(client: TetherClient) {
blockedClients.update { set ->
Expand Down

0 comments on commit 26174b0

Please sign in to comment.