Skip to content

Commit 282593f

Browse files
committed
fix app
1 parent d3c36e7 commit 282593f

File tree

17 files changed

+419
-131
lines changed

17 files changed

+419
-131
lines changed

.vscode/settings.json

Lines changed: 0 additions & 3 deletions
This file was deleted.

app/build.gradle.kts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ android {
1717
applicationId = "com.pira.gnetp"
1818
minSdk = 23
1919
targetSdk = 36
20-
versionCode = 3
21-
versionName = "1.0.2"
20+
versionCode = 4
21+
versionName = "1.0.3"
2222

2323
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2424
}
@@ -98,6 +98,7 @@ dependencies {
9898
implementation(libs.androidx.navigation.compose)
9999
implementation(libs.hilt.android)
100100
implementation(libs.androidx.hilt.navigation.compose)
101+
implementation(libs.coil.compose)
101102
kapt(libs.hilt.android.compiler)
102103
testImplementation(libs.junit)
103104
androidTestImplementation(libs.androidx.junit)

app/src/main/java/com/pira/gnetp/MainActivity.kt

Lines changed: 71 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import androidx.compose.runtime.mutableStateOf
2525
import androidx.compose.runtime.remember
2626
import androidx.compose.runtime.setValue
2727
import androidx.compose.ui.Modifier
28+
import androidx.compose.ui.res.stringResource
2829
import androidx.compose.ui.tooling.preview.Preview
2930
import androidx.hilt.navigation.compose.hiltViewModel
3031
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -47,6 +48,8 @@ import com.pira.gnetp.ui.theme.ThemeManager
4748
import com.pira.gnetp.ui.theme.ThemeSettings
4849
import dagger.hilt.android.AndroidEntryPoint
4950
import javax.inject.Inject
51+
import com.pira.gnetp.R
52+
import com.pira.gnetp.ui.about.AboutScreen
5053

5154
@AndroidEntryPoint
5255
class MainActivity : ComponentActivity() {
@@ -81,18 +84,11 @@ fun MainApp(logRepository: LogRepository) {
8184
modifier = Modifier.fillMaxSize(),
8285
color = MaterialTheme.colorScheme.background
8386
) {
84-
Scaffold(
85-
bottomBar = {
86-
BottomNavigationBar(navController)
87-
}
88-
) { innerPadding ->
89-
MainNavHost(
90-
navController = navController,
91-
logRepository = logRepository,
92-
modifier = Modifier.padding(innerPadding),
93-
onThemeSettingsChanged = ::updateThemeSettings
94-
)
95-
}
87+
MainNavHost(
88+
navController = navController,
89+
logRepository = logRepository,
90+
onThemeSettingsChanged = ::updateThemeSettings
91+
)
9692
}
9793
}
9894
}
@@ -101,50 +97,67 @@ fun MainApp(logRepository: LogRepository) {
10197
fun MainNavHost(
10298
navController: NavHostController,
10399
logRepository: LogRepository,
104-
modifier: Modifier = Modifier,
105100
onThemeSettingsChanged: (ThemeSettings) -> Unit = {}
106101
) {
107-
// Create a single instance of the HomeViewModel to be shared between HomeScreen and HotspotScreen
108102
val homeViewModel = hiltViewModel<HomeViewModel>()
109103
val homeUiState by homeViewModel.uiState.collectAsStateWithLifecycle()
104+
val navBackStackEntry by navController.currentBackStackEntryAsState()
105+
val currentDestination = navBackStackEntry?.destination
106+
val isAboutScreen = currentDestination?.route == Screen.About.route
110107

111-
NavHost(
112-
navController = navController,
113-
startDestination = Screen.Home.route,
114-
modifier = modifier
115-
) {
116-
composable(Screen.Home.route) {
117-
HomeScreen(
118-
uiState = homeUiState,
119-
onStartProxy = { homeViewModel.startProxy() },
120-
onStopProxy = { homeViewModel.stopProxy() },
121-
onNavigateToSettings = { navController.navigate(Screen.Settings.route) },
122-
onNavigateToHotspot = { navController.navigate(Screen.Hotspot.route) },
123-
onNavigateToLogs = { navController.navigate(Screen.Logs.route) },
124-
onVpnPermissionRequest = { },
125-
onSelectIpAddress = { ip -> homeViewModel.selectIpAddress(ip) }
126-
)
127-
}
128-
129-
composable(Screen.Settings.route) {
130-
SettingsScreen(
131-
onNavigateBack = { navController.popBackStack() },
132-
onThemeSettingsChanged = onThemeSettingsChanged
133-
)
134-
}
135-
136-
composable(Screen.Hotspot.route) {
137-
HotspotScreen(
138-
uiState = homeUiState,
139-
onNavigateBack = { navController.popBackStack() }
140-
)
108+
Scaffold(
109+
topBar = {},
110+
bottomBar = {
111+
if (!isAboutScreen) {
112+
BottomNavigationBar(navController)
113+
}
141114
}
142-
143-
composable(Screen.Logs.route) {
144-
LogsScreen(
145-
onNavigateBack = { navController.popBackStack() },
146-
logRepository = logRepository
147-
)
115+
) { innerPadding ->
116+
NavHost(
117+
navController = navController,
118+
startDestination = Screen.Home.route,
119+
modifier = Modifier.padding(innerPadding)
120+
) {
121+
composable(Screen.Home.route) {
122+
HomeScreen(
123+
uiState = homeUiState,
124+
onStartProxy = { homeViewModel.startProxy() },
125+
onStopProxy = { homeViewModel.stopProxy() },
126+
onNavigateToSettings = { navController.navigate(Screen.Settings.route) },
127+
onNavigateToHotspot = { navController.navigate(Screen.Hotspot.route) },
128+
onNavigateToLogs = { navController.navigate(Screen.Logs.route) },
129+
onVpnPermissionRequest = { },
130+
onSelectIpAddress = { ip -> homeViewModel.selectIpAddress(ip) }
131+
)
132+
}
133+
134+
composable(Screen.Settings.route) {
135+
SettingsScreen(
136+
onNavigateBack = { navController.popBackStack() },
137+
onNavigateToAbout = { navController.navigate(Screen.About.route) },
138+
onThemeSettingsChanged = onThemeSettingsChanged
139+
)
140+
}
141+
142+
composable(Screen.Hotspot.route) {
143+
HotspotScreen(
144+
uiState = homeUiState,
145+
onNavigateBack = { navController.popBackStack() }
146+
)
147+
}
148+
149+
composable(Screen.Logs.route) {
150+
LogsScreen(
151+
onNavigateBack = { navController.popBackStack() },
152+
logRepository = logRepository
153+
)
154+
}
155+
156+
composable(Screen.About.route) {
157+
AboutScreen(
158+
onNavigateBack = { navController.popBackStack() }
159+
)
160+
}
148161
}
149162
}
150163
}
@@ -171,14 +184,14 @@ fun BottomNavigationBar(navController: NavHostController) {
171184
Screen.Settings -> Icons.Default.Settings
172185
Screen.Hotspot -> Icons.Default.Info
173186
Screen.Logs -> Icons.AutoMirrored.Filled.List
187+
Screen.About -> Icons.Default.Info
174188
},
175189
contentDescription = null
176190
)
177191
},
178192
label = { Text(getScreenTitle(screen)) },
179193
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
180194
onClick = {
181-
// Special handling for Home screen to ensure proper navigation
182195
if (screen == Screen.Home) {
183196
navController.navigate(screen.route) {
184197
popUpTo(navController.graph.findStartDestination().id) {
@@ -201,12 +214,15 @@ fun BottomNavigationBar(navController: NavHostController) {
201214
}
202215
}
203216

217+
@Composable
204218
fun getScreenTitle(screen: Screen): String {
205219
return when (screen) {
206-
Screen.Home -> "Home"
207-
Screen.Settings -> "Settings"
208-
Screen.Hotspot -> "Hotspot"
209-
Screen.Logs -> "Logs"
220+
Screen.Home -> stringResource(R.string.home)
221+
Screen.Settings -> stringResource(R.string.settings)
222+
Screen.Hotspot -> stringResource(R.string.hotspot)
223+
Screen.Logs -> stringResource(R.string.logs)
224+
Screen.About -> stringResource(R.string.about)
225+
else -> ""
210226
}
211227
}
212228

app/src/main/java/com/pira/gnetp/navigation/Screen.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ sealed class Screen(val route: String, @StringRes val resourceId: Int) {
88
object Hotspot : Screen("hotspot", R.string.hotspot)
99
object Logs : Screen("logs", R.string.logs)
1010
object Settings : Screen("settings", R.string.settings)
11+
object About : Screen("about", R.string.about)
1112
}

app/src/main/java/com/pira/gnetp/proxy/ProxyServerService.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import android.app.NotificationChannel
55
import android.app.NotificationManager
66
import android.app.PendingIntent
77
import android.app.Service
8+
import android.content.Context
89
import android.content.Intent
910
import android.os.Build
1011
import android.os.IBinder
@@ -592,7 +593,7 @@ class ProxyServerService : Service() {
592593
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
593594
val channel = NotificationChannel(
594595
CHANNEL_ID,
595-
"Proxy Server Service",
596+
getString(R.string.proxy_server_channel_name),
596597
NotificationManager.IMPORTANCE_LOW
597598
)
598599
val manager = getSystemService(NotificationManager::class.java)
@@ -607,8 +608,8 @@ class ProxyServerService : Service() {
607608
)
608609

609610
return NotificationCompat.Builder(this, CHANNEL_ID)
610-
.setContentTitle("GNet Proxy")
611-
.setContentText("Proxy server is running")
611+
.setContentTitle(getString(R.string.app_name))
612+
.setContentText(getString(R.string.proxy_server_running))
612613
.setSmallIcon(R.drawable.ic_launcher_foreground)
613614
.setContentIntent(pendingIntent)
614615
.setOngoing(true)
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package com.pira.gnetp.ui.about
2+
3+
import androidx.compose.foundation.layout.Arrangement
4+
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.Row
6+
import androidx.compose.foundation.layout.Spacer
7+
import androidx.compose.foundation.layout.fillMaxSize
8+
import androidx.compose.foundation.layout.fillMaxWidth
9+
import androidx.compose.foundation.layout.height
10+
import androidx.compose.foundation.layout.padding
11+
import androidx.compose.foundation.layout.size
12+
import androidx.compose.foundation.rememberScrollState
13+
import androidx.compose.foundation.verticalScroll
14+
import androidx.compose.material.icons.Icons
15+
import androidx.compose.material.icons.filled.ArrowBack
16+
import androidx.compose.material3.Icon
17+
import androidx.compose.material3.IconButton
18+
import androidx.compose.material3.MaterialTheme
19+
import androidx.compose.material3.Scaffold
20+
import androidx.compose.material3.Text
21+
import androidx.compose.runtime.Composable
22+
import androidx.compose.ui.Alignment
23+
import androidx.compose.ui.Modifier
24+
import androidx.compose.ui.platform.LocalContext
25+
import androidx.compose.ui.res.stringResource
26+
import androidx.compose.ui.text.font.FontWeight
27+
import androidx.compose.ui.text.style.TextAlign
28+
import androidx.compose.ui.unit.dp
29+
import coil.compose.AsyncImage
30+
import coil.request.ImageRequest
31+
import com.pira.gnetp.R
32+
33+
@Composable
34+
fun AboutScreen(
35+
onNavigateBack: () -> Unit
36+
) {
37+
val scrollState = rememberScrollState()
38+
39+
Scaffold(
40+
topBar = {
41+
Row(
42+
modifier = Modifier
43+
.fillMaxWidth()
44+
.padding(16.dp),
45+
horizontalArrangement = Arrangement.SpaceBetween,
46+
verticalAlignment = Alignment.CenterVertically
47+
) {
48+
IconButton(onClick = onNavigateBack) {
49+
Icon(
50+
imageVector = Icons.Default.ArrowBack,
51+
contentDescription = stringResource(R.string.back)
52+
)
53+
}
54+
55+
Text(
56+
text = stringResource(R.string.about),
57+
style = MaterialTheme.typography.headlineMedium,
58+
fontWeight = FontWeight.Bold,
59+
modifier = Modifier.weight(1f)
60+
)
61+
62+
Spacer(modifier = Modifier.size(48.dp))
63+
}
64+
}
65+
) { paddingValues ->
66+
Column(
67+
modifier = Modifier
68+
.fillMaxSize()
69+
.padding(paddingValues)
70+
.padding(16.dp)
71+
.verticalScroll(scrollState),
72+
horizontalAlignment = Alignment.CenterHorizontally,
73+
verticalArrangement = Arrangement.Top
74+
) {
75+
Spacer(modifier = Modifier.height(24.dp))
76+
77+
AsyncImage(
78+
model = ImageRequest.Builder(LocalContext.current)
79+
.data(R.drawable.logo)
80+
.crossfade(true)
81+
.build(),
82+
contentDescription = "App Logo",
83+
modifier = Modifier.size(120.dp)
84+
)
85+
86+
Spacer(modifier = Modifier.height(24.dp))
87+
88+
Text(
89+
text = stringResource(R.string.app_name),
90+
style = MaterialTheme.typography.headlineMedium,
91+
fontWeight = FontWeight.Bold,
92+
textAlign = TextAlign.Center
93+
)
94+
95+
Spacer(modifier = Modifier.height(16.dp))
96+
97+
Text(
98+
text = "Version 1.0.3",
99+
style = MaterialTheme.typography.bodyLarge,
100+
color = MaterialTheme.colorScheme.onSurfaceVariant
101+
)
102+
103+
Spacer(modifier = Modifier.height(24.dp))
104+
105+
Text(
106+
text = "GNet Proxy is a lightweight proxy application that allows you to share your internet connection with other devices through a secure proxy tunnel. It supports both HTTP and SOCKS5 proxy protocols.",
107+
style = MaterialTheme.typography.bodyMedium,
108+
textAlign = TextAlign.Center,
109+
modifier = Modifier.padding(horizontal = 16.dp)
110+
)
111+
112+
Spacer(modifier = Modifier.height(16.dp))
113+
114+
Text(
115+
text = "Developed with ❤️ by Hossein Pira",
116+
style = MaterialTheme.typography.bodyMedium,
117+
textAlign = TextAlign.Center,
118+
modifier = Modifier.padding(horizontal = 16.dp)
119+
)
120+
121+
Spacer(modifier = Modifier.height(24.dp))
122+
123+
Text(
124+
text = "© 2025 Hossein Pira. All rights reserved.",
125+
style = MaterialTheme.typography.bodySmall,
126+
color = MaterialTheme.colorScheme.onSurfaceVariant,
127+
textAlign = TextAlign.Center
128+
)
129+
}
130+
}
131+
}

0 commit comments

Comments
 (0)