Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 78 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,78 @@
<div align="center">
<img src="media/app_icon.png" width="160" height="160" style="display: block; margin: 0 auto"/>
<h1>Network Switch</h1>
<p>Modern Android app for 4G/5G network mode switching</p>
<p>Modern Android app for network mode switching</p>

[![Build Status](https://img.shields.io/github/actions/workflow/status/aunchagaonkar/NetworkSwitch/build.yml)](https://github.com/aunchagaonkar/NetworkSwitch/actions)
[![License](https://img.shields.io/github/license/aunchagaonkar/NetworkSwitch)](https://github.com/aunchagaonkar/NetworkSwitch/blob/main/LICENSE)
[![Downloads](https://img.shields.io/github/downloads/aunchagaonkar/NetworkSwitch/total)](https://github.com/aunchagaonkar/NetworkSwitch/releases)
[![Release](https://img.shields.io/github/v/release/aunchagaonkar/NetworkSwitch)](https://github.com/aunchagaonkar/NetworkSwitch/releases/latest)
[![Awesome](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/timschneeb/awesome-shizuku)

</div>

---

A modern Android application that enables users to toggle between 4G and 5G network modes with dual control methods: Root access for rooted devices and Shizuku for non-rooted devices. Built using Jetpack Compose and Material Design 3.
A modern Android application that enables users to toggle between network modes, supporting 34 different network configurations including 2G, 3G, 4G, and 5G combinations. Features dual control methods: Root access for rooted devices and Shizuku for non-rooted devices. Built using Jetpack Compose and Material Design 3.

## Purpose

Network Switch allows Android users to manually control their device's network preference between pure 4G (LTE-only) and 5G (NR-only) modes. This is useful for:
Network Switch provides control over your device's network modes, supporting 34 different configurations from basic 2G-only to advanced 5G combinations. This includes:

- Quick network switching through Quick Settings tile
- Optimizing battery life by forcing 4G mode
- Maximizing speed by forcing 5G mode where available
- Managing data usage and connectivity preferences
- **Basic Modes**: GSM (2G), WCDMA (3G), LTE (4G), NR (5G) only
- **Combined Modes**: Various 2G/3G/4G/5G combinations
- **Regional Modes**: CDMA support for US carriers, TD-SCDMA for Chinese networks
- **Global Modes**: Network support for international usage

### Use Cases
- Quick network switching through customizable Quick Settings tile
- Battery optimization by selecting power-efficient modes
- Speed optimization by forcing high-performance modes
- Coverage optimization in areas with poor signal quality
- Data usage management with specific network restrictions
- Testing network compatibility and performance

The app provides two methods of operation:
- **Root Method**: Direct system access for rooted devices
- **Shizuku Method**: System access through Shizuku service for non-rooted devices

## Features

- Pure network mode switching (LTE-only for 4G, NR-only for 5G)
- Quick Settings tile for instant access
- Dual control methods (Root and Shizuku)
- Modern Material Design 3 interface
- Clean architecture implementation
- Privacy-focused (no internet permissions)
- Intelligent device compatibility detection
- Automatic fallback for unsupported devices
- **Network Control**: 34 different network modes including pure and combined configurations
- **Privacy-Focused**: No internet permissions, all operations are local
- **Configurable Toggle Modes**: Choose any two network modes for quick switching
- **Quick Settings Tile**: Shows current and next modes with customizable configuration
- **Dual Control Methods**: Root and Shizuku support for compatibility
- **Modern Material Design 3 Interface**: Clean, intuitive UI with extensive configuration options

## Configuration

### Network Mode Selection
The app supports 34 different network modes:

**Basic Modes:**
- GSM Only (2G)
- WCDMA Only (3G)
- LTE Only (4G)
- NR Only (5G)

**Combined Modes:**
- 2G/3G combinations with preference settings
- 3G/4G combinations (LTE/WCDMA)
- 4G/5G combinations (NR/LTE)
- Multi-generation support (2G/3G/4G/5G)

**Regional Modes:**
- CDMA support for US carriers (Verizon)
- TD-SCDMA support for Chinese networks
- Global modes for international roaming

### Toggle Configuration
1. Open the app and tap the configuration icon
2. Select **Mode A** and **Mode B** from the dropdown menus
3. Preview your configuration
4. Save the configuration
5. Use the main toggle or Quick Settings tile to switch between modes

## Screenshots

Expand All @@ -62,41 +98,46 @@ The app provides two methods of operation:
[<img src="media/get_it_github.png" alt="Get it on GitHub" height="80" align="center">](https://github.com/aunchagaonkar/NetworkSwitch/releases)
[<img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" alt="Get it at IzzyOnDroid" height="80" align="center">](https://apt.izzysoft.de/packages/com.supernova.networkswitch)
[<img src="https://raw.githubusercontent.com/ImranR98/Obtainium/main/assets/graphics/badge_obtainium.png" alt="Get it on Obtainium" height="55" align="center">](https://apps.obtainium.imranr.dev/redirect?r=obtainium://add/https://github.com/aunchagaonkar/NetworkSwitch/)

[<img src="https://www.openapk.net/images/openapk-badge.png" height="80" align="center">](https://www.openapk.net/network-switch/com.supernova.networkswitch/)
</div>

1. Download the latest APK from the Releases page
2. Install on your Android device
3. Choose your preferred control method:
- **Root**: Grant root permissions when prompted
- **Shizuku**: Install Shizuku app and grant permission
4. Add the "4G/5G Toggle" tile to Quick Settings
4. Configure your preferred network modes in the app
5. Add the "Network Switch" tile to Quick Settings for instant access

## Project Structure

```
app/
├── src/main/java/com/supernova/networkswitch/
│ ├── presentation/ # UI Layer
│ │ ├── ui/ # Compose UI components
│ │ ├── viewmodel/ # ViewModels
│ │ └── theme/ # Material Design theme
│ │ ├── ui/
│ │ │ ├── activity/ # Activities
│ │ │ ├── composable/ # Reusable Compose components
│ │ │ └── components/ # Shared UI components
│ │ ├── viewmodel/ # ViewModels with state management
│ │ └── theme/ # Material Design 3 theme
│ ├── domain/ # Business Logic Layer
│ │ ├── model/ # Domain models
│ │ ├── repository/ # Repository interfaces
│ │ └── usecase/ # Business use cases
│ ├── data/ # Data Layer
│ │ ├── repository/ # Repository implementations
│ │ ├── source/ # Data sources (Root, Shizuku)
│ │ └── preferences/ # Settings storage
│ │ └── source/ # Data sources
│ ├── service/ # System Services
│ │ ├── tile/ # Quick Settings tile
│ │ └── network/ # Network management
│ └── di/ # Dependency Injection modules
│ │ ├── NetworkTileService # Smart Quick Settings tile
│ │ ├── RootNetworkControllerService # Root-based network control
│ │ └── ShizukuControllerService # Shizuku-based network control
│ └── di/ # Hilt dependency injection modules
├── src/main/aidl/ # AIDL interfaces for IPC
├── build.gradle.kts # App build configuration
└── proguard-rules.pro # ProGuard rules

hiddenapi/ # Android Hidden API access
hiddenapi/ # Android Hidden API access module
├── src/main/aidl/ # AIDL interfaces
└── build.gradle.kts # Hidden API build config

Expand Down Expand Up @@ -129,13 +170,12 @@ cd NetworkSwitch

## TODO
- [x] Add unit tests for all core components
- [ ] Add network speed monitoring
- [ ] Implement network statistics tracking
- [ ] Add support for 3G fallback modes
- [x] Add customizable network mode support (34 modes)
- [ ] Implement adaptive app icon, with icon-pack launcher compatibility
- [ ] Implement scheduled/automatic network switching
- [ ] Add network speed monitoring and performance metrics
- [ ] Add multi-language support
- [ ] Implement network signal strength indicators
- [ ] Add scheduled network switching
- [ ] Add network performance benchmarking
- [ ] Add network performance benchmarking tools

## Contributing

Expand Down Expand Up @@ -166,10 +206,14 @@ To run the unit tests, execute the following command from the root of the projec
./gradlew test
```

**Testing Guidelines:**
- Add unit tests for new functionality
- Test on both rooted and non-rooted devices
- Verify compatibility across different Android versions
- Test network switching functionality thoroughly
- Verify compatibility across different Android versions (10+)
- Test network mode switching with various configurations
- Validate AIDL interface implementations
- Test Quick Settings tile functionality
- Verify configuration persistence and restoration

### Reporting Issues
- Use the GitHub issue tracker
Expand Down
31 changes: 1 addition & 30 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,7 @@ android {
useSupportLibrary = true
}

// Resource optimization
resourceConfigurations += listOf("en", "xxhdpi")

// Disable unnecessary features
vectorDrawables {
useSupportLibrary = true
}
}

buildTypes {
Expand All @@ -42,8 +36,6 @@ android {
)
signingConfig = signingConfigs.getByName("debug")
isDebuggable = false

// Additional optimizations
isJniDebuggable = false
isPseudoLocalesEnabled = false
}
Expand Down Expand Up @@ -94,31 +86,12 @@ android {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
excludes += "/META-INF/*.kotlin_module"
excludes += "**/*.txt"
excludes += "**/*.properties"
excludes += "/META-INF/DEPENDENCIES"
excludes += "/META-INF/LICENSE"
excludes += "/META-INF/LICENSE.txt"
excludes += "/META-INF/license.txt"
excludes += "/META-INF/NOTICE"
excludes += "/META-INF/NOTICE.txt"
excludes += "/META-INF/notice.txt"
excludes += "/META-INF/ASL2.0"
excludes += "/META-INF/maven/**"
excludes += "/META-INF/proguard/**"
excludes += "/META-INF/versions/**"
excludes += "**/*.kotlin_metadata"
excludes += "kotlin/**"
excludes += "META-INF/com.android.tools/**"
excludes += "/androidsupportmultidexversion.txt"
}

// JNI libraries optimization
jniLibs {
useLegacyPackaging = false
// Exclude native libraries from packaging
excludes += "**/libandroidx.graphics.path.so"
excludes += "**/libdatastore_shared_counter.so"
}
}

Expand Down Expand Up @@ -156,10 +129,8 @@ dependencies {
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
debugImplementation(libs.androidx.ui.tooling.preview) // Move to debug only
implementation(libs.androidx.material3)
implementation("androidx.compose.runtime:runtime")
implementation("androidx.compose.material:material-icons-extended")
implementation(libs.androidx.material.icons.extended)
implementation(libs.androidx.lifecycle.viewmodel.compose)

// Dependency Injection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ import org.junit.runner.RunWith
import org.junit.Assert.*
import org.junit.Rule

/**
* Instrumented tests for the Network Switch application.
*/
@RunWith(AndroidJUnit4::class)
class NetworkSwitchInstrumentedTest {

Expand Down
7 changes: 7 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@
android:parentActivityName=".presentation.ui.activity.MainActivity"
android:theme="@style/Theme.NetworkSwitch" />

<activity
android:name=".presentation.ui.activity.NetworkModeConfigActivity"
android:exported="false"
android:label="@string/network_mode_config"
android:parentActivityName=".presentation.ui.activity.MainActivity"
android:theme="@style/Theme.NetworkSwitch" />

<service
android:name=".service.NetworkTileService"
android:exported="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ package com.supernova.networkswitch;

interface IRootController {
boolean compatibilityCheck(int subId);
boolean getNetworkState(int subId);
void setNetworkState(int subId, boolean enabled);
int getCurrentNetworkMode(int subId);
void setNetworkMode(int subId, int networkMode);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.supernova.networkswitch;

interface IShizukuController {
boolean compatibilityCheck(int subId);
boolean getNetworkState(int subId);
void setNetworkState(int subId, boolean enabled);
int getCurrentNetworkMode(int subId);
void setNetworkMode(int subId, int networkMode);
void destroy();
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ import com.supernova.networkswitch.data.source.RootNetworkControlDataSource
import com.supernova.networkswitch.data.source.ShizukuNetworkControlDataSource
import com.supernova.networkswitch.domain.model.CompatibilityState
import com.supernova.networkswitch.domain.model.ControlMethod
import com.supernova.networkswitch.domain.model.NetworkMode
import com.supernova.networkswitch.domain.repository.NetworkControlRepository
import com.supernova.networkswitch.domain.repository.PreferencesRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import javax.inject.Inject
import javax.inject.Singleton

Expand All @@ -23,41 +21,39 @@ class NetworkControlRepositoryImpl @Inject constructor(
private val preferencesRepository: PreferencesRepository
) : NetworkControlRepository {

private val _connectionState = MutableStateFlow(false)

override suspend fun checkCompatibility(method: ControlMethod): CompatibilityState {
val dataSource = getDataSource(method)
val subId = android.telephony.SubscriptionManager.getDefaultDataSubscriptionId()
return dataSource.checkCompatibility(subId)
}

override suspend fun getNetworkState(subId: Int): Boolean {
val method = preferencesRepository.getControlMethod()
val dataSource = getDataSource(method)
return dataSource.getNetworkState(subId)
override suspend fun getCurrentNetworkMode(subId: Int): NetworkMode? {
return try {
val method = preferencesRepository.getControlMethod()
val dataSource = getDataSource(method)
dataSource.getCurrentNetworkMode(subId)
} catch (e: Exception) {
null
}
}

override suspend fun setNetworkState(subId: Int, enabled: Boolean): Result<Unit> {
override suspend fun setNetworkMode(subId: Int, mode: NetworkMode): Result<Unit> {
return try {
val method = preferencesRepository.getControlMethod()
val dataSource = getDataSource(method)
dataSource.setNetworkState(subId, enabled)
_connectionState.value = dataSource.isConnected()
dataSource.setNetworkMode(subId, mode)
Result.success(Unit)
} catch (e: Exception) {
Result.failure(e)
}
}

override fun observeConnectionState(): Flow<Boolean> = _connectionState.asStateFlow()

/**
* Reset connections for both data sources - useful when switching control methods
*/
override suspend fun resetConnections() {
rootDataSource.resetConnection()
shizukuDataSource.resetConnection()
_connectionState.value = false
}

private fun getDataSource(method: ControlMethod): NetworkControlDataSource {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.supernova.networkswitch.data.repository

import com.supernova.networkswitch.data.source.PreferencesDataSource
import com.supernova.networkswitch.domain.model.ControlMethod
import com.supernova.networkswitch.domain.model.ToggleModeConfig
import com.supernova.networkswitch.domain.repository.PreferencesRepository
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
Expand All @@ -26,4 +27,16 @@ class PreferencesRepositoryImpl @Inject constructor(
override fun observeControlMethod(): Flow<ControlMethod> {
return preferencesDataSource.observeControlMethod()
}

override suspend fun getToggleModeConfig(): ToggleModeConfig {
return preferencesDataSource.getToggleModeConfig()
}

override suspend fun setToggleModeConfig(config: ToggleModeConfig) {
preferencesDataSource.setToggleModeConfig(config)
}

override fun observeToggleModeConfig(): Flow<ToggleModeConfig> {
return preferencesDataSource.observeToggleModeConfig()
}
}
Loading