├── hidden ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── android │ └── os │ └── SystemProperties.java ├── app ├── .gitignore ├── src │ ├── main │ │ ├── cpp │ │ │ ├── .gitignore │ │ │ ├── unique_fd.cpp │ │ │ ├── test.sh │ │ │ ├── netlink_test.cpp │ │ │ ├── netlink.h │ │ │ ├── CMakeLists.txt │ │ │ ├── unique_fd.h │ │ │ ├── wadb-jni.cpp │ │ │ └── netlink.cpp │ │ ├── res │ │ │ ├── values-enm │ │ │ │ └── strings.xml │ │ │ ├── values-lb │ │ │ │ └── strings.xml │ │ │ ├── values-th │ │ │ │ └── strings.xml │ │ │ ├── values-night │ │ │ │ ├── styles.xml │ │ │ │ └── themes_overlay.xml │ │ │ ├── values-v30 │ │ │ │ └── bools.xml │ │ │ ├── values │ │ │ │ ├── bools.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── attrs.xml │ │ │ │ ├── themes_overlay.xml │ │ │ │ ├── styles.xml │ │ │ │ ├── themes.xml │ │ │ │ ├── themes_override.xml │ │ │ │ └── strings.xml │ │ │ ├── xml │ │ │ │ ├── full_backup_content.xml │ │ │ │ ├── data_extraction_rules.xml │ │ │ │ └── preferences.xml │ │ │ ├── values-v21 │ │ │ │ └── themes_override.xml │ │ │ ├── drawable │ │ │ │ ├── ic_close_white_24dp.xml │ │ │ │ ├── ic_outline_notifications_24.xml │ │ │ │ ├── ic_outline_wb_sunny_24.xml │ │ │ │ ├── ic_qs_network_adb_off.xml │ │ │ │ ├── ic_task_icon_black.xml │ │ │ │ ├── ic_task_icon_white.xml │ │ │ │ ├── ic_qs_network_adb_on.xml │ │ │ │ └── ic_wadb_24.xml │ │ │ ├── values-sr │ │ │ │ └── strings.xml │ │ │ ├── layout │ │ │ │ ├── appbar_activity.xml │ │ │ │ ├── appbar_fragment_activity.xml │ │ │ │ ├── appbar.xml │ │ │ │ └── preference_recyclerview.xml │ │ │ ├── menu │ │ │ │ └── home.xml │ │ │ ├── animator │ │ │ │ └── appbar_animator.xml │ │ │ ├── drawable-nodpi │ │ │ │ └── ic_launcher.xml │ │ │ ├── values-ms │ │ │ │ └── strings.xml │ │ │ ├── values-zh-rCN │ │ │ │ └── strings.xml │ │ │ ├── values-zh-rTW │ │ │ │ └── strings.xml │ │ │ ├── values-ko │ │ │ │ └── strings.xml │ │ │ ├── drawable-nodpi-v26 │ │ │ │ └── ic_launcher.xml │ │ │ ├── values-ja │ │ │ │ └── strings.xml │ │ │ ├── values-pl │ │ │ │ └── strings.xml │ │ │ ├── values-ar │ │ │ │ └── strings.xml │ │ │ ├── values-uk │ │ │ │ └── strings.xml │ │ │ ├── values-pt │ │ │ │ └── strings.xml │ │ │ ├── values-it │ │ │ │ └── strings.xml │ │ │ ├── values-vi │ │ │ │ └── strings.xml │ │ │ ├── values-lv │ │ │ │ └── strings.xml │ │ │ ├── values-tr │ │ │ │ └── strings.xml │ │ │ ├── values-id │ │ │ │ └── strings.xml │ │ │ ├── values-ru │ │ │ │ └── strings.xml │ │ │ ├── values-de │ │ │ │ └── strings.xml │ │ │ ├── values-pt-rBR │ │ │ │ └── strings.xml │ │ │ └── values-es │ │ │ │ └── strings.xml │ │ ├── java │ │ │ └── moe │ │ │ │ └── haruue │ │ │ │ └── wadb │ │ │ │ ├── events │ │ │ │ ├── Event.java │ │ │ │ ├── Function.java │ │ │ │ ├── WadbFailureEvent.java │ │ │ │ ├── WadbStateChangedEvent.java │ │ │ │ ├── Events.java │ │ │ │ └── GlobalRequestHandler.java │ │ │ │ ├── ui │ │ │ │ └── service │ │ │ │ │ └── WadbTileService.java │ │ │ │ ├── receiver │ │ │ │ ├── TurnOffReceiver.java │ │ │ │ ├── SecretCodeReceiver.java │ │ │ │ └── BootCompletedReceiver.kt │ │ │ │ ├── WadbPreferences.java │ │ │ │ ├── util │ │ │ │ ├── ScreenKeeper.java │ │ │ │ ├── ThemeHelper.java │ │ │ │ ├── NetworksUtils.java │ │ │ │ ├── LibWADB.kt │ │ │ │ ├── SuShell.java │ │ │ │ └── NotificationHelper.kt │ │ │ │ ├── app │ │ │ │ ├── AppBarActivity.kt │ │ │ │ └── AppActivity.kt │ │ │ │ ├── service │ │ │ │ └── WadbTileService.java │ │ │ │ ├── WadbApplication.kt │ │ │ │ └── component │ │ │ │ ├── HomeActivity.kt │ │ │ │ └── HomeFragment.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── moe │ │ │ └── haruue │ │ │ └── wadb │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── moe │ │ └── haruue │ │ └── wadb │ │ └── ExampleInstrumentationTest.java ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── readme.res ├── 01a.png ├── 01b.png └── 02.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── manifest.gradle ├── .gitignore ├── README.md ├── gradle.properties ├── gradlew.bat ├── gradlew └── license.txt /hidden/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /release 3 | /.cxx -------------------------------------------------------------------------------- /app/src/main/cpp/.gitignore: -------------------------------------------------------------------------------- 1 | /netlink_test 2 | -------------------------------------------------------------------------------- /app/src/main/cpp/unique_fd.cpp: -------------------------------------------------------------------------------- 1 | #include "unique_fd.h" 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | include ':hidden' 3 | -------------------------------------------------------------------------------- /readme.res/01a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RikkaApps/WADB/HEAD/readme.res/01a.png -------------------------------------------------------------------------------- /readme.res/01b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RikkaApps/WADB/HEAD/readme.res/01b.png -------------------------------------------------------------------------------- /readme.res/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RikkaApps/WADB/HEAD/readme.res/02.png -------------------------------------------------------------------------------- /app/src/main/res/values-enm/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/values-lb/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/values-th/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RikkaApps/WADB/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /manifest.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | kotlin_version = '1.6.20' 3 | 4 | min_sdk = 23 5 | target_sdk = 31 6 | } 7 | -------------------------------------------------------------------------------- /app/src/main/java/moe/haruue/wadb/events/Event.java: -------------------------------------------------------------------------------- 1 | package moe.haruue.wadb.events; 2 | 3 | public interface Event { 4 | } 5 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_close_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/cpp/netlink_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "netlink.h" 6 | 7 | int main(int argc, char *argv[]) { 8 | std::list ips{}; 9 | auto ret = wadb::netlink::get_interface_ips(false, ips); 10 | 11 | for (const auto &info : ips) { 12 | std::cout << info.idx << ": [" << info.interface << "] " << info.ip << std::endl; 13 | } 14 | 15 | return ret; 16 | } 17 | -------------------------------------------------------------------------------- /app/src/test/java/moe/haruue/wadb/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package moe.haruue.wadb; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/moe/haruue/wadb/receiver/TurnOffReceiver.java: -------------------------------------------------------------------------------- 1 | package moe.haruue.wadb.receiver; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | import moe.haruue.wadb.events.GlobalRequestHandler; 8 | 9 | public class TurnOffReceiver extends BroadcastReceiver { 10 | 11 | public TurnOffReceiver() { 12 | } 13 | 14 | @Override 15 | public void onReceive(Context context, Intent intent) { 16 | GlobalRequestHandler.stopWadb(); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_outline_notifications_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/java/moe/haruue/wadb/receiver/SecretCodeReceiver.java: -------------------------------------------------------------------------------- 1 | package moe.haruue.wadb.receiver; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.provider.Telephony; 7 | 8 | import moe.haruue.wadb.component.HomeActivity; 9 | 10 | public class SecretCodeReceiver extends BroadcastReceiver { 11 | 12 | @Override 13 | public void onReceive(Context context, Intent intent) { 14 | if (Telephony.Sms.Intents.SECRET_CODE_ACTION.equals(intent.getAction())) { 15 | context.startActivity(new Intent(context, HomeActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/res/values-sr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Ovaj uređaj nije rutovan ili je zahtjev odbijen od root manager-a. 4 | Ovaj softver je open source ( 5 | \n%1$s) ispod %2$s. 6 | O tome 7 | Bežični adb 8 | Ovoj tranzlaciji doprinosi 9 | \n%1$s. 10 | -------------------------------------------------------------------------------- /app/src/main/cpp/netlink.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace wadb::netlink { 8 | struct InterfaceIPPair { 9 | InterfaceIPPair() = default; 10 | InterfaceIPPair(uint idx, uint8_t family, std::string interface, std::string ip) : 11 | idx{idx}, 12 | family{family}, 13 | interface{std::move(interface)}, 14 | ip{std::move(ip)} { 15 | } 16 | 17 | uint idx{0}; 18 | uint8_t family{AF_INET}; 19 | std::string interface{}; 20 | std::string ip{}; 21 | }; 22 | 23 | int get_interface_ips(bool include_ipv6, std::list &ips); 24 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/appbar_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_outline_wb_sunny_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/menu/home.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 12 | 13 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/moe/haruue/wadb/receiver/BootCompletedReceiver.kt: -------------------------------------------------------------------------------- 1 | package moe.haruue.wadb.receiver 2 | 3 | import android.content.BroadcastReceiver 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.util.Log 7 | import moe.haruue.wadb.WadbApplication 8 | import moe.haruue.wadb.events.GlobalRequestHandler 9 | 10 | class BootCompletedReceiver : BroadcastReceiver() { 11 | 12 | override fun onReceive(context: Context, intent: Intent) { 13 | if (intent.action != Intent.ACTION_BOOT_COMPLETED 14 | && intent.action != Intent.ACTION_LOCKED_BOOT_COMPLETED) { 15 | return 16 | } 17 | 18 | Log.d("WABD", "onReceive: ${intent.action}") 19 | 20 | GlobalRequestHandler.startWadb(WadbApplication.wadbPort) 21 | } 22 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WADB - A simple switch for wireless ADB 2 | 3 | ## Feature 4 | 5 | * Optimized to respond as quickly as possible 6 | * Support quick settings tile (requires Android 7.0+) 7 | * Option to start on boot 8 | * And more… 9 | 10 | Note, to enable wireless ADB directly on from the device, **root is a must**. 11 | 12 | ## Download 13 | 14 | [Google Play](https://play.google.com/store/apps/details?id=moe.haruue.wadb) 15 | 16 | [GitHub release](https://github.com/RikkaApps/WADB/releases/latest) 17 | 18 | ## Contribute translation 19 | 20 | or pull request. 21 | 22 | ## Screenshots 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
-------------------------------------------------------------------------------- /app/src/main/res/layout/appbar_fragment_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | ## Project-wide Gradle settings. 2 | # 3 | # For more details on how to configure your build environment visit 4 | # http://www.gradle.org/docs/current/userguide/build_environment.html 5 | # 6 | # Specifies the JVM arguments used for the daemon process. 7 | # The setting is particularly useful for tweaking memory settings. 8 | # Default value: -Xmx1024m -XX:MaxPermSize=256m 9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 10 | # 11 | # When configured, Gradle will run in incubating parallel mode. 12 | # This option should only be used with decoupled projects. More details, visit 13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 14 | # org.gradle.parallel=true 15 | #Fri Oct 21 14:57:45 CST 2016 16 | android.useAndroidX=true 17 | android.debug.obsoleteApi=true 18 | android.enableResourceOptimizations=false 19 | -------------------------------------------------------------------------------- /app/src/androidTest/java/moe/haruue/wadb/ExampleInstrumentationTest.java: -------------------------------------------------------------------------------- 1 | package moe.haruue.wadb; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.filters.MediumTest; 6 | import android.support.test.runner.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | 12 | import static org.junit.Assert.*; 13 | 14 | /** 15 | * Instrumentation test, which will execute on an Android device. 16 | * 17 | * @see Testing documentation 18 | */ 19 | @MediumTest 20 | @RunWith(AndroidJUnit4.class) 21 | public class ExampleInstrumentationTest { 22 | @Test 23 | public void useAppContext() throws Exception { 24 | // Context of the app under test. 25 | Context appContext = InstrumentationRegistry.getTargetContext(); 26 | 27 | assertEquals("moe.haruue.wadb", appContext.getPackageName()); 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/appbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/preference_recyclerview.xml: -------------------------------------------------------------------------------- 1 | 2 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/moe/haruue/wadb/WadbPreferences.java: -------------------------------------------------------------------------------- 1 | package moe.haruue.wadb; 2 | 3 | import moe.haruue.wadb.util.ThemeHelper; 4 | 5 | public class WadbPreferences { 6 | 7 | public static final String KEY_WAKE_IP = "pref_key_wadb_ip"; 8 | public static final String KEY_WAKE_PORT = "pref_key_wadb_port"; 9 | public static final String KEY_WAKE_LOCK = "pref_key_wake_lock"; 10 | public static final String KEY_SCREEN_LOCK_SWITCH = "pref_key_screen_lock_switch"; 11 | public static final String KEY_NOTIFICATION = "pref_key_notification"; 12 | public static final String KEY_NOTIFICATION_LOW_PRIORITY = "pref_key_notification_low_priority"; 13 | public static final String KEY_NOTIFICATION_SETTINGS = "pref_key_notification_settings"; 14 | public static final String KEY_LAUNCHER_ICONS = "pref_key_hide_launcher_icon"; 15 | public static final String KEY_WADB_SWITCH = "pref_key_wadb_switch"; 16 | public static final String KEY_LIGHT_THEME = ThemeHelper.KEY_LIGHT_THEME; 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 16 | 17 | 10 | 11 | 16 | 17 | 21 | 22 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 18 | 23 | 24 | 28 | 29 | 33 | 34 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/cpp/unique_fd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace hx::tux { 6 | struct unique_fd { 7 | unique_fd() = default; 8 | 9 | explicit unique_fd(int fd) { 10 | reset(fd); 11 | } 12 | 13 | unique_fd(const unique_fd& copy) = delete; 14 | unique_fd(unique_fd&& move) noexcept { 15 | *this = std::move(move); 16 | } 17 | 18 | ~unique_fd() { 19 | reset(); 20 | } 21 | 22 | unique_fd& operator=(const unique_fd& copy) = delete; 23 | unique_fd& operator=(unique_fd&& move) noexcept { 24 | if (this == &move) { 25 | return *this; 26 | } 27 | 28 | reset(); 29 | 30 | if (move.fd_ != -1) { 31 | fd_ = move.fd_; 32 | move.fd_ = -1; 33 | } 34 | 35 | return *this; 36 | } 37 | 38 | bool operator==(int fd) { 39 | return fd_ == fd; 40 | } 41 | 42 | bool operator!=(int fd) { 43 | return !(*this == fd); 44 | } 45 | 46 | int get() { return fd_; } 47 | 48 | [[nodiscard]] int release() { 49 | if (fd_ == -1) { 50 | return -1; 51 | } 52 | 53 | int fd = fd_; 54 | fd_ = -1; 55 | 56 | return fd; 57 | } 58 | 59 | void reset(int new_fd = -1) { 60 | if (fd_ != -1) { 61 | close(fd_); 62 | fd_ = -1; 63 | } 64 | 65 | if (new_fd != -1) { 66 | fd_ = new_fd; 67 | } 68 | } 69 | 70 | private: 71 | int fd_{-1}; 72 | }; 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/moe/haruue/wadb/app/AppBarActivity.kt: -------------------------------------------------------------------------------- 1 | package moe.haruue.wadb.app 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.FrameLayout 7 | import androidx.annotation.LayoutRes 8 | import androidx.appcompat.widget.Toolbar 9 | import com.google.android.material.appbar.AppBarLayout 10 | import moe.haruue.wadb.R 11 | 12 | abstract class AppBarActivity : AppActivity() { 13 | 14 | private val rootView: ViewGroup by lazy { 15 | findViewById(R.id.root) 16 | } 17 | 18 | private val toolbarContainer: AppBarLayout by lazy { 19 | findViewById(R.id.toolbar_container) 20 | } 21 | 22 | private val toolbar: Toolbar by lazy { 23 | findViewById(R.id.toolbar) 24 | } 25 | 26 | override fun onCreate(savedInstanceState: Bundle?) { 27 | super.onCreate(savedInstanceState) 28 | super.setContentView(getLayoutId()) 29 | 30 | setSupportActionBar(toolbar) 31 | } 32 | 33 | @LayoutRes 34 | open fun getLayoutId(): Int { 35 | return R.layout.appbar_activity 36 | } 37 | 38 | override fun setContentView(layoutResID: Int) { 39 | layoutInflater.inflate(layoutResID, rootView, true) 40 | rootView.bringChildToFront(toolbarContainer) 41 | } 42 | 43 | override fun setContentView(view: View?) { 44 | setContentView( 45 | view, 46 | FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) 47 | ) 48 | } 49 | 50 | override fun setContentView(view: View?, params: ViewGroup.LayoutParams?) { 51 | rootView.addView(view, 0, params) 52 | } 53 | } 54 | 55 | abstract class AppBarFragmentActivity : AppBarActivity() { 56 | 57 | override fun getLayoutId(): Int { 58 | return R.layout.appbar_fragment_activity 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/moe/haruue/wadb/util/NetworksUtils.java: -------------------------------------------------------------------------------- 1 | package moe.haruue.wadb.util; 2 | 3 | import android.content.Context; 4 | import android.net.wifi.WifiInfo; 5 | import android.net.wifi.WifiManager; 6 | import android.system.OsConstants; 7 | import android.util.Log; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | public class NetworksUtils { 13 | private static final String TAG = NetworksUtils.class.getSimpleName(); 14 | 15 | public static String getLocalIPAddress(Context context) { 16 | List ipInfoList = getLocalIPInfo(context); 17 | if (ipInfoList.isEmpty()) { 18 | return ""; 19 | } 20 | return ipInfoList.get(0).getIp(); 21 | } 22 | 23 | public static List getLocalIPInfo(Context context) { 24 | List result = null; 25 | try { 26 | result = LibWADB.getInterfaceIps(false); 27 | } catch (Exception e) { 28 | Log.e(TAG, "getLocalIPInfo: LibWADB.getInterfaceIps() failed", e); 29 | } 30 | 31 | if (result != null) { 32 | return result; 33 | } 34 | 35 | // fallback to the legacy way 36 | result = new ArrayList<>(); 37 | WifiManager wifiManger = context.getApplicationContext().getSystemService(WifiManager.class); 38 | if (wifiManger == null) { 39 | return result; 40 | } 41 | WifiInfo wifiInfo = wifiManger.getConnectionInfo(); 42 | if (wifiInfo.getIpAddress() != 0) { 43 | LibWADB.InterfaceIPPair info = new LibWADB.InterfaceIPPair(0, (byte) OsConstants.AF_INET, "wlan0", intToIp(wifiInfo.getIpAddress())); 44 | result.add(info); 45 | } 46 | return result; 47 | } 48 | 49 | 50 | private static String intToIp(int i) { 51 | return (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + ((i >> 16) & 0xFF) + "." + (i >> 24 & 0xFF); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/res/values-ms/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Keep the screen on when \"%s\" is enabled 4 | Show notification 5 | Disabled 6 | %s is enabled 7 | Wireless adb 8 | This software is open source (%1$s) under %2$s. 9 | Port 10 | Keep screen on 11 | Note, starting from Android 10, the system may enforce all apps to be shown on the home screen. 12 | Hide this app from the home screen 13 | Hide from home screen 14 | Allow switching from quick settings when the screen is locked 15 | Show a notification when \"%s\" is enabled 16 | Allow switching when locked 17 | Enabled 18 | This device is not rooted or the request was denied by the root manager. 19 | 20 | IP address 21 | The translation is contributed by %1$s. 22 | Contribute translation 23 | translators 24 | -------------------------------------------------------------------------------- /app/src/main/res/values-zh-rCN/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 协助翻译 4 | 翻译由 %s 贡献。 5 | 无线 adb 6 | 关于 7 | 10 | 此设备没有 root 或是请求被 root 管理器拒绝。 11 | %s 已启用 12 | 禁用 %s 13 | 已禁用 14 | 已启用 15 | 显示通知 16 | 启用“%s”时显示一条通知 17 | 锁定时允许切换 18 | 允许屏幕锁定时从快速设置切换 19 | 从主屏幕隐藏 20 | 从主屏幕隐藏此应用 21 | 请注意,从 Android 10 开始,系统可能会强制所有应用显示在主屏幕上。 22 | 保持屏幕开启 23 | 启用“%s”时保持屏幕开启 24 | 端口 25 | 端口号只能在 1025–65535 之间 26 | 使用低优先级通知 27 | 通知将显示在其他通知的底部,通知图标将从状态栏中隐藏 28 | 主题 29 | 默认 30 | 粉色 31 | 开机启动 32 | 设备启动时启用“%s” 33 | 运行状态 34 | 通知设置 35 | 从系统更改通知设置 36 | 关闭 37 | 绿色 38 | 启用无线 adb 39 | IP 地址 40 | -------------------------------------------------------------------------------- /app/src/main/res/values-zh-rTW/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 協助翻譯 4 | 翻譯由 %s 貢獻。 5 | 無線 adb 6 | 關於 7 | 10 | 此裝置沒有 root 或是請求被 root 管理器拒絕。 11 | %s 已啟用 12 | 禁用 %s 13 | 已禁用 14 | 已啟用 15 | 顯示通知 16 | 啟用「%s」時顯示一條通知 17 | 鎖定時允許切換 18 | 允許螢幕鎖定時從快速設定切換 19 | 從主螢幕隱藏 20 | 從主螢幕隱藏此應用程式 21 | 請注意,從 Android 10 開始,系統可能會強制所有應用程式顯示在主螢幕上。 22 | 保持螢幕開啟 23 | 啟用「%s」時保持螢幕開啟 24 | 通訊埠 25 | 通訊埠只能在 1025–65535 之間 26 | 使用低優先順序通知 27 | 通知將顯示在其他通知的底部,通知圖示將從狀態列中隱藏 28 | 主題 29 | 預設 30 | 粉色 31 | 開機啟動 32 | 裝置啟動時啟用“%s” 33 | 執行狀態 34 | 通知設定 35 | 從系統更改通知設定 36 | 關閉 37 | 綠色 38 | 啟用無線 adb 39 | IP 位址 40 | -------------------------------------------------------------------------------- /app/src/main/res/values-ko/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 시스템에서 알림 설정 변경 4 | 알림 설정 5 | 실행 상태 6 | 장치 시작 시 \"%s\" 활성화 7 | 부팅 시 시작 8 | 핑크 9 | 기본값 10 | 테마 11 | 알림은 다른 알림의 하단에 표시되고 알림 아이콘은 상태 표시줄에서 숨겨집니다. 12 | 낮은 우선 순위 알림 사용 13 | 포트 번호는 1025–65535 사이여야 합니다 14 | 포트 15 | \"%s\"이(가) 활성화된 경우 화면을 켜진 상태로 유지 16 | 화면 켜기 17 | Android 10부터 시스템은 모든 앱이 홈 화면에 표시되도록 강제할 수 있습니다. 18 | 홈 화면에서 이 앱 숨기기 19 | 홈 화면에서 숨기기 20 | 화면이 잠겨 있을 때 빠른 설정에서 전환 허용 21 | 잠겨 있을 때 전환 허용 22 | \"%s\" 가 활성화되면 알림 표시 23 | 알림 표시 24 | 비활성화됨 25 | 활성화됨 26 | %s 비활성화 27 | %s이(가) 활성화되었습니다 28 | 이 장치는 루팅되지 않았거나 루트 관리자가 요청을 거부했습니다. 29 | 이 소프트웨어는 %2$s 에 따라 오픈 소스 (%1$s) 입니다. 30 | 정보 31 | 무선 adb 32 | <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\" id=\"translator_names\">이 번역은 %s 에 의해 기여 되었습니다. 33 | 번역 기여 34 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 18 | 20 | 21 | 22 | 23 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 41 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/res/values-ja/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 通知設定 4 | 実行状態 5 | テーマ 6 | ポート番号は 1025〜65535 の範囲で指定する必要があります 7 | ポート 8 | 画面を点灯したままにする 9 | ロック中の切り替えを許可 10 | 無効 11 | 有効 12 | このアプリについて 13 | 翻訳に協力する 14 | システムで通知設定を変更します 15 | デバイスの起動時に「%s」を有効にします 16 | 自動起動 17 | ピンク 18 | デフォルト 19 | 通知は他の通知より下に表示され、通知アイコンはステータスバーから非表示になります 20 | 重要度の低い通知を使用 21 | 「%s」が有効になっている間、画面を点灯したままにします 22 | 注意: Android 10 以降では、システムがすべてのアプリをホーム画面に表示させる可能性があります。 23 | ランチャーのアイコンを隠す 24 | このアプリをホーム画面から非表示にします 25 | デバイスがロックされているときにクイック設定から切り替えられるようにします 26 | 「%s」が有効になっている間、通知を表示します 27 | 通知を表示 28 | %s が有効です 29 | %s を無効化 30 | このデバイスは root 化されていないか、または root マネージャーによってリクエストが拒否されました。 31 | このアプリは、%2$s のもとでオープンソースで公開されています。(%1$s) 32 | 無線ADB 33 | この翻訳は %1$s によって提供されました。 34 | オフ 35 | 36 | IP アドレス 37 | 無線ADBを有効にする 38 | -------------------------------------------------------------------------------- /app/src/main/res/values-pl/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Ważne, od Androida 10, system może wymusić pokazywanie na ekranie startowym. 4 | W tłumaczeniu pomogli: %s. 5 | Zmień ustawienia powiadomień z poziomu systemu 6 | Ustawienia powiadomień 7 | Status działania 8 | Włącz \"%s\" gdy urządzenie startuje 9 | Uruchom przy starcie systemu 10 | Różowy 11 | Domyślny 12 | Motyw 13 | Powiadomienie będzie ukryte na dole i ikona zostanie ukryta z paska powiadomień 14 | Użyj powiadomienia z niskim priorytetem 15 | Numer portu mysi być między 1025 a 65535 16 | Port 17 | Utrzymaj ekran włączony gdy \"%s\" jest włączone 18 | Utrzymaj ekran włączony 19 | Ukryj na ekranu głównym 20 | Ukryj tę aplikację na ekranie startowym 21 | Dopuść przełączanie z szybkich ustawień gdy ekran jest zablokowany 22 | Dopuść przełączanie gdy zablokowano 23 | Pokaż powiadomienie gdy \"%s\" jest włączone 24 | Pokaż powiadomienie 25 | Wyłączone 26 | Włączone 27 | Wyłącz %s 28 | %s jest włączone 29 | Urządzenie nie jest zrootowane, bądź żądanie zostało odrzucone przez manager roota. 30 | To oprogramowanie jest otwartoźródłowe (%1$s) pod %2$s. 31 | O aplikacji 32 | Bezprzewodowe ADB 33 | Pomóż w tłumaczeniu 34 | Gabriel Markowski 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/java/moe/haruue/wadb/app/AppActivity.kt: -------------------------------------------------------------------------------- 1 | package moe.haruue.wadb.app 2 | 3 | import android.app.ActivityManager 4 | import android.content.res.Resources 5 | import android.graphics.Bitmap 6 | import android.graphics.Canvas 7 | import android.os.Build 8 | import android.os.Bundle 9 | import android.view.MenuItem 10 | import androidx.core.content.ContextCompat 11 | import moe.haruue.wadb.R 12 | import moe.haruue.wadb.util.ThemeHelper 13 | import rikka.core.res.resolveColor 14 | import rikka.material.app.MaterialActivity 15 | 16 | abstract class AppActivity : MaterialActivity() { 17 | 18 | override fun onCreate(savedInstanceState: Bundle?) { 19 | super.onCreate(savedInstanceState) 20 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { 21 | updateTaskDescription() 22 | } 23 | } 24 | 25 | override fun computeUserThemeKey(): String? { 26 | return ThemeHelper.getTheme() 27 | } 28 | 29 | override fun onApplyUserThemeResource(theme: Resources.Theme, isDecorView: Boolean) { 30 | theme.applyStyle(ThemeHelper.getThemeStyleRes(), true) 31 | theme.applyStyle(R.style.ThemeOverlay_Rikka_Material3_Preference, true) 32 | } 33 | 34 | private fun updateTaskDescription() { 35 | val color: Int = theme.resolveColor(R.attr.appBarColor) 36 | val icon: Int = when (ThemeHelper.getTheme()) { 37 | ThemeHelper.THEME_GREEN -> { 38 | R.drawable.ic_task_icon_black 39 | } 40 | ThemeHelper.THEME_PINK -> { 41 | R.drawable.ic_task_icon_black 42 | } 43 | ThemeHelper.THEME_DEFAULT -> { 44 | R.drawable.ic_task_icon_white 45 | } 46 | else -> { 47 | R.drawable.ic_task_icon_white 48 | } 49 | } 50 | 51 | if (Build.VERSION.SDK_INT >= 28) { 52 | setTaskDescription(ActivityManager.TaskDescription(null, icon, color)) 53 | } else { 54 | val drawable = ContextCompat.getDrawable(this, icon) 55 | 56 | val bitmap = 57 | Bitmap.createBitmap(drawable!!.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888) 58 | val canvas = Canvas(bitmap) 59 | drawable.setBounds(0, 0, canvas.width, canvas.height) 60 | drawable.draw(canvas) 61 | 62 | @Suppress("DEPRECATION") 63 | setTaskDescription(ActivityManager.TaskDescription(null, bitmap, color)) 64 | } 65 | } 66 | 67 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 68 | return when (item.itemId) { 69 | android.R.id.home -> { 70 | onBackPressed() 71 | true 72 | } 73 | else -> super.onOptionsItemSelected(item) 74 | } 75 | } 76 | 77 | override fun shouldApplyTranslucentSystemBars(): Boolean { 78 | return true 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/moe/haruue/wadb/util/LibWADB.kt: -------------------------------------------------------------------------------- 1 | package moe.haruue.wadb.util 2 | 3 | import androidx.annotation.Keep 4 | import java.lang.IllegalStateException 5 | import java.lang.reflect.Method 6 | 7 | object LibWADB { 8 | init { 9 | System.loadLibrary("wadb") 10 | 11 | val method_ipsListAdd = let { 12 | for (method in LibWADB::class.java.declaredMethods) { 13 | val jnc = method.getAnnotation(JNC::class.java) 14 | if (jnc != null && jnc.id == JNC.ID_ipsListAdd) { 15 | return@let method 16 | } 17 | } 18 | return@let null 19 | } ?: throw IllegalStateException("method ipsListAdd() not found") 20 | 21 | initializeNative(method_ipsListAdd) 22 | } 23 | 24 | private external fun initializeNative(method_ipsListAdd: Method) 25 | private external fun nativeGetInterfaceIps(includeIPv6: Boolean, outList: MutableList): Int 26 | 27 | @JvmStatic 28 | fun getInterfaceIps(includeIPv6: Boolean): List { 29 | val list = mutableListOf() 30 | val ret = nativeGetInterfaceIps(includeIPv6, list) 31 | if (ret != 0) { 32 | throw IllegalStateException("failed to use netlink socket for interface ips: $ret") 33 | } 34 | list.removeAll { it.interfaceName == "lo" || it.interfaceName.contains("rmnet") } 35 | list.sort() 36 | return list 37 | } 38 | 39 | @JvmStatic 40 | @Keep 41 | @JNC(JNC.ID_ipsListAdd) 42 | fun ipsListAdd(outList: MutableList, idx: Int, family: Byte, interfaceName: String, ip: String) { 43 | outList.add(InterfaceIPPair(idx, family.toInt(), interfaceName, ip)) 44 | } 45 | 46 | data class InterfaceIPPair( 47 | val idx: Int, 48 | val family: Int, 49 | val interfaceName: String, 50 | val ip: String 51 | ) : Comparable { 52 | private val interfaceNameOrder: Int = when { 53 | interfaceName.startsWith("wlan") -> 0 54 | interfaceName.startsWith("eth") -> 1 55 | else -> 99 56 | } 57 | 58 | override fun compareTo(other: InterfaceIPPair): Int { 59 | if (family != other.family) { 60 | return family.compareTo(other.family) 61 | } 62 | if (interfaceNameOrder != other.interfaceNameOrder) { 63 | return interfaceNameOrder.compareTo(other.interfaceNameOrder) 64 | } 65 | if (interfaceName != other.interfaceName) { 66 | return interfaceName.compareTo(interfaceName) 67 | } 68 | return ip.compareTo(other.ip) 69 | } 70 | } 71 | 72 | @Target(AnnotationTarget.FUNCTION) 73 | @Retention(AnnotationRetention.RUNTIME) 74 | private annotation class JNC( 75 | val id: Int, 76 | ) { 77 | companion object { 78 | const val ID_ipsListAdd = 1 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /app/src/main/res/values-ar/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | عنوان IP 4 | تفعيل ADB اللاسلكي 5 | إيقاف 6 | تغيير إعدادات الإخطار من النظام 7 | إعدادات الإشعار 8 | حالة التشغيل 9 | قم بتمكين \"%s\" عند بدء تشغيل الجهاز 10 | ابدأ في التمهيد 11 | زهري 12 | ثيم أخضر 13 | ثيم افتراضي 14 | ثـِيم 15 | سيظهر الإشعار في الجزء السفلي من الآخرين وسيتم إخفاء رمز الإشعار من شريط الحالة 16 | استخدم إشعارًا ذا أولوية منخفضة 17 | يجب أن يكون رقم المنفذ 1025-65535 18 | بوابة الدخول 19 | حافظ على الشاشة قيد التشغيل \"%s\" عند تشغيل 20 | حافظ على الشاشة قيد التشغيل 21 | ملاحظة ، بدءًا من Android 10 ، قد يفرض النظام عرض جميع التطبيقات على الشاشة الرئيسية. 22 | إخفاء هذا التطبيق من الشاشة الرئيسية 23 | إخفاء من الشاشة الرئيسية 24 | اسمح بالتبديل من الإعدادات السريعة عندما تكون الشاشة مقفلة 25 | السماح بالتبديل عند القفل 26 | إظهار إشعار \"%s\" عند تشغيل 27 | إظهار الإشعارات 28 | إيقاف 29 | تشغيل 30 | تعطيل %s 31 | تم تمكين %s 32 | لم يتم عمل روت لهذا الجهاز أو تم رفض الطلب من قبل مدير الجذر. 33 | هذا البرنامج مفتوح المصدر (%1$s) under %2$s. 34 | حول 35 | ADB اللاسلكي 36 | ساهم في الترجمة %1$s. 37 | المساهمة في الترجمة 38 | -------------------------------------------------------------------------------- /app/src/main/res/values-uk/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | IP адреса 4 | Увімкнути бездротовий adb 5 | Вимкнено 6 | Змінити налаштування сповіщень у самій системі 7 | Налаштування сповіщення 8 | Поточний статус 9 | Вмикати \"%s\" при запуску системи 10 | Автозапуск 11 | Зелена 12 | Рожева 13 | За замовчуванням 14 | Тема 15 | Повідомлення буде показуватися знизу, окремо від інших, а іконка повідомлення буде захована від рядка стану 16 | Використовувати повідомлення з низьки пріоритетом 17 | Номер порту має бути від 1025 до 65535 18 | Порт 19 | Не вимикати екран поки \"%s\" увімкнено 20 | Не вимикати екран 21 | Увага, починаючи з Android 10, система може вимушено відображати всі програми на домашньому екрані. 22 | Прибрати цю програму з головного екрану 23 | Заховати з домашнього екрану 24 | Дозволити перемикання із швидких налаштувань при заблокованому екрані 25 | Дозволити перемикання при заблокованому екрані 26 | Показати сповіщення коли \"%s\" увімкнено 27 | Показати сповіщення 28 | Вимкнено 29 | Увімкнено 30 | Вимкнути %s 31 | %s ввімкнено 32 | Цей пристрій не рутований або запит відхилено менеджером root. 33 | Ця програма з відкритим кодом (%1$s) по ліцензії %2$s. 34 | Про додаток 35 | Бездротовий adb 36 | Переклад надано %1$s. 37 | Внести свій переклад 38 | -------------------------------------------------------------------------------- /app/src/main/res/values-pt/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Essa tradução teve a contribuição de %1$s. 4 | Endereço IP 5 | Ative o adb pela rede 6 | Desativado 7 | Alterar as configurações de notificação do sistema 8 | Conduções de notificação 9 | Status de execução 10 | Ative \"%s\" quando o dispositivo iniciar 11 | Iniciar ao ligar 12 | Rosa 13 | Verde 14 | Padrão 15 | Tema 16 | A notificação será mostrada na parte inferior dos outros e o ícone de notificação ficará oculto na barra de status 17 | Usar notificação de baixa prioridade 18 | O número deve ser 1025–65535 19 | Port 20 | Mantenha a tela ligada quando \"%s\" estiver ativado 21 | Mantenha a tela ligada 22 | Observe que, a partir do Android 10, o sistema pode forçar a exibição de todos os aplicativos na tela inicial. 23 | Ocultar este aplicativo da tela inicial 24 | Ocultar da tela inicial 25 | Permitir alternar a partir das configurações rápidas quando a tela estiver bloqueada 26 | Permitir a troca quando bloqueado 27 | Mostrar uma notificação quando \"%s\" estiver ativado 28 | Mostrar notificação 29 | Desativado 30 | Ativado 31 | Desative %s 32 | %s está ativado 33 | O disposição não é roteado, ou o acesso foi negado pelo root. 34 | Este software é de código aberto (%1$s) Debaixo%2$s. 35 | Sobre 36 | Adb por rede 37 | Contribua com a tradução 38 | -------------------------------------------------------------------------------- /app/src/main/res/values-it/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Impostazioni notifica 4 | Stato esecuzione 5 | Il numero della porta deve essere un valore compreso tra 1025 e 65535 6 | Porta 7 | Lascia lo schermo acceso 8 | Permetti il cambio quando è bloccato 9 | Disabilitato 10 | Abilitato 11 | Informazioni 12 | Mostra notifica 13 | Permetti il passaggio dalle impostazioni rapide quando lo schermo è bloccato 14 | Nota, a partire da Android 10, il sistema potrebbe imporre la visualizzazione di tutte le app nella schermata iniziale. 15 | Disattiva %s 16 | Contribuisci alla traduzione 17 | ADB Wireless 18 | La traduzione è fornita da %1$s. 19 | Abilita \"%s\" all\'avvio del dispositivo 20 | Abilita all\'avvio 21 | %s è abilitato 22 | Lascia lo schermo acceso quando \"%s\" è abilitato 23 | Rosa 24 | Verde 25 | Predefinito 26 | Indirizzo IP 27 | Abilita ADB wireless 28 | Tema 29 | Mostra una notifica quando \"%s\" è abilitato 30 | Nascondi quest\'app dalla schermata principale 31 | Questo dispositivo è senza root o la richiesta è stata negata dal gestore root. 32 | Nascondi dalla schermata iniziale 33 | Questo software è open source (%1$s) sotto %2$s. 34 | Disattivato 35 | Cambia i settaggi di notifica dal sistema 36 | La notifica verrà mostrata in fondo alle altre e la sua icona verrà nascosta dalla barra di stato 37 | Usa notifiche a bassa priorità 38 | -------------------------------------------------------------------------------- /app/src/main/res/values-vi/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Thay đổi cài đặt thông báo từ hệ thống 4 | Cài đặt thông báo 5 | Trạng thái đang chạy 6 | Bật \"%s\" khi thiết bị khởi động 7 | Bắt đầu khi khởi động 8 | Hồng 9 | Mặc định 10 | Chủ đề 11 | Thông báo sẽ được hiển thị ở dưới cùng với những thông báo khác và biểu tượng thông báo sẽ bị ẩn khỏi thanh trạng thái 12 | Sử dụng thông báo có mức độ ưu tiên thấp 13 | Số cổng phải là 1025–65535 14 | Cổng 15 | Giữ màn hình bật khi \"%s\" được bật 16 | Giữ màn hình bật 17 | Lưu ý, bắt đầu từ Android 10, hệ thống có thể buộc tất cả các ứng dụng hiển thị trên màn hình chính. 18 | Ẩn ứng dụng này khỏi màn hình chính 19 | Ẩn khỏi màn hình chính 20 | Cho phép chuyển từ cài đặt nhanh khi màn hình bị khóa 21 | Cho phép chuyển đổi khi bị khóa 22 | Hiện một thông báo khi \"%s\" được bật 23 | Hiện thông báo 24 | Đã vô hiệu hoá 25 | Đã bật 26 | Vô hiệu hóa %s 27 | %s đã bật 28 | Thiết bị này chưa được root hoặc yêu cầu bị từ chối bởi trình quản lí root. 29 | Phần mềm này là mã nguồn mở (%1$s) dưới %2$s. 30 | Về 31 | ADB không dây 32 | Bản dịch được đóng góp bởi %1$s. 33 | Đóng góp bản dịch 34 | The Primal Pea 35 | Địa chỉ IP 36 | Bật adb không dây 37 | Tắt 38 | Xanh lá cây 39 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Translators 5 | Contribute translation 6 | The translation is contributed by %1$s. 7 | WADB 8 | Wireless adb 9 | IP address 10 | This software is open source (%1$s) under %2$s. 11 | This device is not rooted or the request was denied by the root manager. 12 | %s is enabled 13 | Disable %s 14 | Enabled 15 | Disabled 16 | Show notification 17 | Show a notification when \"%s\" is enabled 18 | Allow switching when locked 19 | Allow switching from quick settings when the screen is locked 20 | Hide from home screen 21 | Hide this app from the home screen 22 | Note, starting from Android 10, the system may enforce all apps to be shown on the home screen. 23 | Keep screen on 24 | Keep the screen on when \"%s\" is enabled 25 | Port 26 | Port number must be 1025–65535 27 | Use low priority notification 28 | The notification will be shown at the bottom from others and the notification icon will be hidden from the status bar 29 | Theme 30 | Default 31 | Green 32 | Pink 33 | Start on boot 34 | Enable \"%s\" when the device starts 35 | Running status 36 | Notification settings 37 | Change notification settings from the system 38 | Off 39 | Enable wireless adb 40 | IP address 41 | -------------------------------------------------------------------------------- /app/src/main/res/values-lv/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Mainīt paziņojuma iestatījumus pašā sistēmā 4 | Iespējot \"%s\", kad ierīce sāknējas 5 | Paziņojums tiks parādīts apakšā, atsevišķi no pārējiem, un paziņojuma ikona tiks paslēpta statusa joslā 6 | Izmantot zemas prioritātes paziņojumu 7 | Lūdzu, ņemiet vērā, ka, sākot ar operētājsistēmu Android 10, sistēma var likt parādīt visas lietotnes sākuma ekrānā. 8 | Noņemt šo lietotni no sākuma ekrāna 9 | Ļaut pārslēgt darbošanās stāvokli no ātro iestatījumu izvēlnes, kad ekrāns ir bloķēts 10 | Atļaut pārslēgt, kad ekrāns ir bloķēts 11 | Šai ierīcei nav root piekļuves tiesības vai arī root pārvaldnieks noraidīja piekļuvi. 12 | IP adrese 13 | Iespējot bezvadu adb 14 | Izslēgts 15 | Paziņojuma iestatījumi 16 | Pašreizējais stāvoklis 17 | Palaist pie iekārtas sāknēšanas 18 | Rozavais 19 | Zaļais 20 | Noklusējuma 21 | Motīvs 22 | Porta numuram jābūt 1025–65535 diapazonā 23 | Ports 24 | Atstāt ekrānu ieslēgtu, kad \"%s\" ir iespējots 25 | Atstāt ekrānu ieslēgtu 26 | Paslēpt no sākuma ekrāna 27 | Rādīt paziņojumu, kad \"%s\" ir iespējots 28 | Rādīt paziņojumu 29 | Atspējots 30 | Iespējots 31 | Atspējot %s 32 | %s iespējots 33 | Šī lietotne ir atvērtā koda programmatūra (%1$s) izlaista zem %2$s licences. 34 | IP adrese 35 | Bezvadu adb 36 | Tulkojumu veica %1$s. 37 | Palīdzēt iztulkot 38 | Tulkotāji 39 | -------------------------------------------------------------------------------- /app/src/main/res/values-tr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Bildirim ayarlarını sistem üzerinden değiştir 4 | Bildirim ayarları 5 | Çalışma durumu 6 | \"%s\"’i cihaz başladığında aktif et 7 | Cihazın açılışında başlat 8 | Pembe 9 | Varsayılan 10 | Tema 11 | Bildirim diğerlerinden altta gösterilecek ve bildirim simgesi durum çubuğundan gizlenecektir 12 | Düşük öncelikli bildirim kullan 13 | Port numarası 1025 ile 65535 arasında olmalıdır 14 | Port 15 | \"%s\" açık iken ekranı açık tut 16 | Ekranı açık tut 17 | Not, Android 10 ve sonrasında, sistem tüm uygulamaların ana ekranda gösterilmesini zorunlu tutabilir. 18 | Bu uygulamayı ana ekrandan gizle 19 | Ana ekrandan gizle 20 | Ekran kilitliyken hızlı ayarlar kullanarak değiştirmeye izin ver 21 | Kilitli iken değiştirmeye izin ver 22 | \"%s\" aktif olduğunda bildirim göster 23 | Bildirimi göster 24 | Devre Dışı 25 | Aktif 26 | %s’i devre dışı bırak 27 | %s aktif 28 | Bu cihazda kök kullanıcı (root) izni yok veya izin kök kullanıcı yönetimi uygulaması tarafından engellendi. 29 | Bu yazılım (%1$s) altında açık kaynak %2$s lisanslıdır. 30 | Hakkında 31 | Kablosuz adb 32 | Bu çeviri %1$s tarafından yapılmıştır. 33 | Çeviriye katkıda bulun 34 | Çevirmen isimleri 35 | IP adresi 36 | Kablosuz adb’yi etkinleştir 37 | Kapat 38 | Yeşil 39 | -------------------------------------------------------------------------------- /app/src/main/res/values-id/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Pengaturan notifikasi 4 | Status aktivitas 5 | Tema 6 | Nomor port hanya boleh 1025–65535 7 | Port 8 | Jaga layar tetap menyala 9 | Izinkan beralih ketika layar terkunci 10 | Dinonaktifkan 11 | Diaktifkan 12 | Tentang 13 | The translation is contributed by %1$s. 14 | Bantu penerjemahan 15 | Ubah pengaturan notifikasi dari sistem 16 | Aktifkan \"%s\" saat perangkat dimulai 17 | Mulai saat boot 18 | Merah muda 19 | Bawaan 20 | Pemberitahuan akan ditampilkan di bagian bawah dari yang lain dan ikon pemberitahuan akan disembunyikan dari bilah status 21 | Gunakan pemberitahuan prioritas rendah 22 | Tetap aktifkan layar saat \"%s\" diaktifkan 23 | Catatan, mulai dari Android 10, sistem dapat memberlakukan semua aplikasi untuk ditampilkan di layar beranda. 24 | Sembunyikan aplikasi ini dari layar beranda 25 | Sembunyikan dari layar beranda 26 | Izinkan beralih dari pengaturan cepat saat layar terkunci 27 | Tampilkan notifikasi saat \"%s\" diaktifkan 28 | Tampilkan notifikasi 29 | Nonaktifkan %s 30 | %s diaktifkan 31 | Perangkat ini tidak di-root atau permintaan ditolak oleh pengelola root. 32 | This software is open source (%1$s) under %2$s. 33 | Adb nirkabel 34 | Ian 35 | Alamat IP 36 | Aktifkan adb wireless 37 | Mati 38 | Hijau 39 | -------------------------------------------------------------------------------- /app/src/main/res/values-ru/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Перевод выполнен: %1$s. 4 | Отключено 5 | Включено 6 | Изменение настроек уведомления в самой системе 7 | Настройки уведомления 8 | Текущий статус 9 | Включать «%s» по окончанию загрузки системы 10 | Автозагрузка 11 | Розовая 12 | По умолчанию 13 | Тема 14 | Уведомление будет показываться внизу, отдельно от остальных, а значок уведомления будет скрыт из строки состояния 15 | Исп. уведомления с низким приоритетом 16 | Номер порта должен быть от 1025 до 65535 17 | Порт 18 | Не отключать экран, пока работает «%s» 19 | Не отключать экран 20 | Убрать это приложение с главного экрана 21 | Обратите внимание, что, начиная с Android 10, система может принудительно отображать все приложения на главном экране. 22 | Скрыть с рабочего стола 23 | Позволять переключать состояние из быстрых настроек на экране блокировки 24 | Разрешить переключение из-под блокировки 25 | Выводить уведомление в шторке, когда «%s» включен 26 | Показывать уведомление 27 | Отключить %s 28 | %s включен 29 | На этом устройстве заблокирован root или запрос был отклонен root-менеджером. 30 | Это приложение с открытым исходным кодом (%1$s) по лицензии %2$s. 31 | IP-адрес 32 | Беспроводной adb 33 | Помочь с переводом 34 | Переводчики 35 | IP-адрес 36 | Включить беспроводную отладку 37 | Отключено 38 | Зеленая 39 | -------------------------------------------------------------------------------- /app/src/main/res/values-de/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Benachrichtigungseinstellungen 4 | Aktivitätsstatus 5 | Port kann nur 1025–65535 sein 6 | Port 7 | Lasse den Bildschirm an 8 | Erlaube umschalten, wenn der Bildschirm gesperrt ist 9 | Deaktiviert 10 | Aktiviert 11 | Über 12 | Drahtloses ADB 13 | Auf dem Startbildschirm ausblenden 14 | Bildschirm eingeschaltet lassen, wenn \"%s\"aktiviert ist 15 | Standard 16 | Zeige Benachrichtigungen 17 | Diese Software ist Open Source (%1$s) unter %2$s. 18 | Diese App auf dem Startbildschirm ausblenden 19 | Benachrichtigungseinstellungen vom System aus ändern 20 | Zeige Benachrichtigungen, wenn \"%s\" aktiviert ist 21 | Beim Booten starten 22 | Benachrichtigung mit niedriger Priorität verwenden 23 | Die Übersetzung wird bereitgestellt von %1$s. 24 | Dieses Gerät ist nicht gerootet oder die Anfrage wurde vom Root-Manager abgelehnt. 25 | %s ist aktiviert 26 | Deaktiviere %s 27 | Umschalten aus den Schnelleinstellungen erlauben, wenn der Bildschirm gesperrt ist 28 | Aktiviere \"%s\", wenn das Gerät startet 29 | IP-Adresse 30 | Wireless ADB aktivieren 31 | Aus 32 | Pink 33 | Grün 34 | Theme 35 | Die Benachrichtigung wird am unteren Rand angezeigt und das Benachrichtigungssymbol in der Statusleiste ausgeblendet 36 | Beachten Sie, dass das System ab Android 10 erzwingen kann, dass alle Apps auf dem Startbildschirm angezeigt werden. 37 | Mitwirken bei der Übersetzung 38 | -------------------------------------------------------------------------------- /app/src/main/res/values-pt-rBR/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Altere as configurações de notificação deste app no sistema 4 | Configurações de notificação 5 | Status de execução 6 | Ative \"%s\" quando o dispositivo for iniciado 7 | Iniciar app no boot 8 | Rosa 9 | Padrão 10 | Tema 11 | A notificação será mostrada na parte inferior de outras pessoas e o ícone de notificação ficará oculto na barra de status 12 | Usar notificação de baixa prioridade 13 | O número da porta deve ser de 1025 até 65535 14 | Porta 15 | Mantenha a tela ligada quando \"%s\" estiver ativado 16 | Manter a tela ligada 17 | Observe que a partir do Android 10, o sistema pode forçar a exibição de todos os apps na tela inicial. 18 | Ocultar o ícone deste app da tela inicial 19 | Ocultar ícone da tela inicial 20 | Permite a alternar entre configurações rápidas quando a tela estiver bloqueada 21 | Permitir troca quando bloqueado 22 | Mostre uma notificação quando \"%s\" estiver ativado 23 | Mostrar notificação 24 | Desativado 25 | Ativado 26 | Desativar %s 27 | %s está ativado 28 | Este dispositivo não está rooteado ou a solicitação foi negada pelo gerenciador root. 29 | Este software é de código aberto (%1$s) sob %2$s. 30 | Endereço IP 31 | ADB sem fio 32 | A tradução é uma contribuição de %1$s. 33 | Contribua com a tradução 34 | Augusto Peres e Igor 35 | Endereço IP 36 | Ativar ADB sem fio 37 | Desligado 38 | Verde 39 | -------------------------------------------------------------------------------- /app/src/main/res/xml/preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 16 | 17 | 24 | 25 | 32 | 33 | 40 | 41 | 46 | 47 | 50 | 53 | 54 | 55 | 56 | 62 | 63 | 69 | 70 | 75 | 76 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /app/src/main/res/values-es/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Este dispositivo no está rooteado o el administrador root rechazó la solicitud. 4 | Este software es de código abierto (%1$s) bajo %2$s. 5 | Acerca de 6 | La traducción es aportada por %1$s. 7 | Contribuir con la traducción 8 | —Peter Cuevas 9 | Mostrar notificación cuando \"%s\" es activado 10 | Ocultar esta app en la pantalla de inicio 11 | Dirección IP 12 | Activar adb wifi 13 | Apagado 14 | Cambiar ajustes de las notificaciones desde el sistema 15 | Configuración de las notificaciones 16 | Estado de funcionamiento 17 | Activar \"%s\" cuando el dispositivo se inicie 18 | Iniciar al arrancar 19 | Rosa 20 | Verde 21 | Por defecto 22 | Tema 23 | La notificación se mostrará en la parte inferior de los demás y el icono de notificación se ocultará de la barra de estado 24 | Usar prioridad baja en las notificaciónes 25 | El número de puerto debe ser 1025-65535 26 | Puerto 27 | Mantener la pantalla encendida cuando \"%s\" este activado 28 | Mantener la pantalla encendida 29 | Nota, a partir de Android 10, el sistema puede obligar a que todas las aplicaciones se muestren en la pantalla de inicio. 30 | Ocultar desde la pantalla de inicio 31 | Aceptar el cambio cuando esté bloqueado 32 | Permitir el cambio desde los ajustes rápidos cuando la pantalla está bloqueada 33 | Mostrar notificación 34 | Desactivado 35 | Activado 36 | Desactivar %s 37 | %s está activado 38 | ADB Inalámbrico 39 | -------------------------------------------------------------------------------- /app/src/main/cpp/wadb-jni.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "netlink.h" 9 | 10 | namespace wadb { 11 | struct JniGlobal { 12 | JavaVM *vm; 13 | 14 | jclass c_LibWADB; 15 | jmethodID m_ipsListAdd; 16 | }; 17 | 18 | JniGlobal &jni_global() { 19 | static auto *p = new JniGlobal{}; 20 | return *p; 21 | } 22 | 23 | JNIEnv *ensure_jni_env_for_current_thread() { 24 | auto vm = jni_global().vm; 25 | assert(vm != nullptr); 26 | JNIEnv *env{}; 27 | vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); 28 | if (env == nullptr) { 29 | auto ret = vm->AttachCurrentThread(&env, nullptr); 30 | assert(ret == 0); 31 | } 32 | return env; 33 | } 34 | 35 | namespace jni_methods { 36 | void initializeNative(JNIEnv *env, jobject, 37 | jobject method_ipsListAdd) { 38 | jni_global().m_ipsListAdd = env->FromReflectedMethod(method_ipsListAdd); 39 | } 40 | 41 | jint getInterfaceIps(JNIEnv *env, jobject, 42 | jboolean j_include_ipv6, jobject outList) { 43 | auto c_LibWADB = jni_global().c_LibWADB; 44 | auto m_ipsListAdd = jni_global().m_ipsListAdd; 45 | assert(m_ipsListAdd != nullptr); 46 | 47 | auto append_to_java_list = [env, c_LibWADB, m_ipsListAdd, outList] (const auto &info) { 48 | auto js_if_name = env->NewStringUTF(info.interface.c_str()); 49 | auto js_ip = env->NewStringUTF(info.ip.c_str()); 50 | env->CallStaticVoidMethod(c_LibWADB, m_ipsListAdd, outList, info.idx, info.family, js_if_name, js_ip); 51 | }; 52 | 53 | std::list ips{}; 54 | auto ret = netlink::get_interface_ips(j_include_ipv6 == JNI_TRUE, ips); 55 | 56 | for (const auto &ip : ips) { 57 | append_to_java_list(ip); 58 | } 59 | 60 | return ret; 61 | } 62 | } 63 | 64 | namespace { 65 | jint jni_on_load(JavaVM *vm, void *reserved) { 66 | jni_global().vm = vm; 67 | auto env = ensure_jni_env_for_current_thread(); 68 | jni_global().c_LibWADB = (jclass) env->NewGlobalRef(env->FindClass("moe/haruue/wadb/util/LibWADB")); 69 | assert(jni_global().c_LibWADB != nullptr); 70 | std::vector methods{}; 71 | /*initializeNative*/ { 72 | auto &m = methods.emplace_back(); 73 | m.name = "initializeNative"; 74 | m.signature = "(Ljava/lang/reflect/Method;)V"; 75 | m.fnPtr = reinterpret_cast(jni_methods::initializeNative); 76 | } 77 | /*getInterfaceIps*/ { 78 | auto &m = methods.emplace_back(); 79 | m.name = "nativeGetInterfaceIps"; 80 | m.signature = "(ZLjava/util/List;)I"; 81 | m.fnPtr = reinterpret_cast(jni_methods::getInterfaceIps); 82 | } 83 | auto ret = env->RegisterNatives(jni_global().c_LibWADB, methods.data(), methods.size()); 84 | if (ret < 0) { 85 | return JNI_ERR; 86 | } 87 | return JNI_VERSION_1_6; 88 | } 89 | } 90 | } 91 | 92 | JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { 93 | return wadb::jni_on_load(vm, reserved); 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/java/moe/haruue/wadb/events/Events.java: -------------------------------------------------------------------------------- 1 | package moe.haruue.wadb.events; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import java.util.Arrays; 9 | import java.util.Collection; 10 | import java.util.Objects; 11 | import java.util.Set; 12 | import java.util.concurrent.CopyOnWriteArraySet; 13 | 14 | public class Events { 15 | 16 | private static class Receiver { 17 | 18 | public T event; 19 | public Object[] tags; 20 | 21 | private Receiver(T event, Object[] tags) { 22 | this.event = event; 23 | this.tags = tags; 24 | } 25 | 26 | @Override 27 | public boolean equals(Object o) { 28 | if (this == o) return true; 29 | if (o == null || getClass() != o.getClass()) return false; 30 | Receiver receiver1 = (Receiver) o; 31 | return event.equals(receiver1.event) && 32 | Arrays.equals(tags, receiver1.tags); 33 | } 34 | 35 | @Override 36 | public int hashCode() { 37 | int result = Objects.hash(event); 38 | result = 31 * result + Arrays.hashCode(tags); 39 | return result; 40 | } 41 | } 42 | 43 | private static Handler mainThreadHandler = new Handler(Looper.getMainLooper()); 44 | 45 | private static void post(Function consumer, Collection> receivers, Object... tags) { 46 | for (Receiver receiver : receivers) { 47 | if (!Arrays.equals(tags, receiver.tags)) { 48 | continue; 49 | } 50 | 51 | /*if (receiver.event instanceof LifecycleOwner) { 52 | LifecycleOwner lifecycleOwner = (LifecycleOwner) receiver.event; 53 | lifecycleOwner.getLifecycle().getCurrentState(); 54 | }*/ 55 | 56 | if (Looper.myLooper() != Looper.getMainLooper()) { 57 | mainThreadHandler.post(() -> consumer.invoke(receiver.event)); 58 | } else { 59 | consumer.invoke(receiver.event); 60 | } 61 | } 62 | } 63 | 64 | private static Set> wadbStateChangedEventReceivers = new CopyOnWriteArraySet<>(); 65 | 66 | private static Set> wadbFailureEventReceivers = new CopyOnWriteArraySet<>(); 67 | 68 | public static void registerAll(@NonNull Event receiver, Object... tags) { 69 | if (receiver instanceof WadbStateChangedEvent) { 70 | wadbStateChangedEventReceivers.add(new Receiver<>((WadbStateChangedEvent) receiver, tags)); 71 | } 72 | if (receiver instanceof WadbFailureEvent) { 73 | wadbFailureEventReceivers.add(new Receiver<>((WadbFailureEvent) receiver, tags)); 74 | } 75 | } 76 | 77 | public static void unregisterAll(@NonNull Event receiver, Object... tags) { 78 | if (receiver instanceof WadbStateChangedEvent) { 79 | wadbStateChangedEventReceivers.remove(new Receiver<>((WadbStateChangedEvent) receiver, tags)); 80 | } 81 | if (receiver instanceof WadbFailureEvent) { 82 | wadbFailureEventReceivers.remove(new Receiver<>((WadbFailureEvent) receiver, tags)); 83 | } 84 | } 85 | 86 | public static void postWadbStateChangedEvent(Function consumer) { 87 | post(consumer, wadbStateChangedEventReceivers); 88 | } 89 | 90 | public static void postWadbFailureEvent(Function consumer) { 91 | post(consumer, wadbFailureEventReceivers); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /app/src/main/java/moe/haruue/wadb/util/SuShell.java: -------------------------------------------------------------------------------- 1 | package moe.haruue.wadb.util; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.topjohnwu.superuser.CallbackList; 7 | import com.topjohnwu.superuser.Shell; 8 | 9 | import java.io.IOException; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | import moe.haruue.wadb.BuildConfig; 14 | 15 | public class SuShell { 16 | 17 | static { 18 | Shell.enableVerboseLogging = BuildConfig.DEBUG; 19 | Shell.setDefaultBuilder(Shell.Builder.create() 20 | .setFlags(Shell.FLAG_REDIRECT_STDERR) 21 | .setTimeout(10)); 22 | } 23 | 24 | /** 25 | * Result class. 26 | */ 27 | public static class Result implements Parcelable { 28 | 29 | /** 30 | * Exit code of the last command 31 | */ 32 | public final int exitCode; 33 | 34 | /** 35 | * Output of command(s), might be null 36 | */ 37 | public final List output; 38 | 39 | public Result(int exitCode, List output) { 40 | this.exitCode = exitCode; 41 | this.output = output != null ? new ArrayList<>(output) : new ArrayList(); 42 | } 43 | 44 | public String getOutputString() { 45 | StringBuilder sb = new StringBuilder(); 46 | for (String s : output) { 47 | sb.append(s).append('\n'); 48 | } 49 | return sb.toString().trim(); 50 | } 51 | 52 | protected Result(Parcel in) { 53 | exitCode = in.readInt(); 54 | output = in.createStringArrayList(); 55 | } 56 | 57 | public static final Creator CREATOR = new Creator() { 58 | @Override 59 | public Result createFromParcel(Parcel in) { 60 | return new Result(in); 61 | } 62 | 63 | @Override 64 | public Result[] newArray(int size) { 65 | return new Result[size]; 66 | } 67 | }; 68 | 69 | @Override 70 | public int describeContents() { 71 | return 0; 72 | } 73 | 74 | @Override 75 | public void writeToParcel(Parcel dest, int flags) { 76 | dest.writeInt(exitCode); 77 | dest.writeStringList(output); 78 | } 79 | } 80 | 81 | public synchronized static boolean available() { 82 | if (!Shell.rootAccess()) { 83 | try { 84 | Shell.getShell().close(); 85 | } catch (IOException e) { 86 | e.printStackTrace(); 87 | } 88 | Shell.getShell(); 89 | Shell.su("echo test").exec(); 90 | } 91 | return Shell.rootAccess(); 92 | } 93 | 94 | public synchronized static void close() { 95 | Shell cached = Shell.getCachedShell(); 96 | if (cached != null) { 97 | try { 98 | cached.close(); 99 | } catch (IOException e) { 100 | e.printStackTrace(); 101 | } 102 | } 103 | } 104 | 105 | public static synchronized String version(boolean internal) { 106 | Result result = run(internal ? "su -V" : "su -v"); 107 | return result.output.size() > 0 ? result.output.get(0) : "unknown"; 108 | } 109 | 110 | public synchronized static Result run(String command) { 111 | return run(new String[]{command}); 112 | } 113 | 114 | public synchronized static Result run(List commands) { 115 | return run(commands.toArray(new String[0])); 116 | } 117 | 118 | public synchronized static Result run(String... commands) { 119 | Shell.Result result = Shell.su(commands).exec(); 120 | return new Result(result.getCode(), result.getOut()); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /app/src/main/java/moe/haruue/wadb/service/WadbTileService.java: -------------------------------------------------------------------------------- 1 | package moe.haruue.wadb.service; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Icon; 5 | import android.os.Build; 6 | import android.service.quicksettings.Tile; 7 | import android.service.quicksettings.TileService; 8 | import android.util.Log; 9 | 10 | import androidx.annotation.RequiresApi; 11 | 12 | import moe.haruue.wadb.R; 13 | import moe.haruue.wadb.WadbApplication; 14 | import moe.haruue.wadb.WadbPreferences; 15 | import moe.haruue.wadb.events.Events; 16 | import moe.haruue.wadb.events.GlobalRequestHandler; 17 | import moe.haruue.wadb.events.WadbStateChangedEvent; 18 | import moe.haruue.wadb.util.NetworksUtils; 19 | 20 | /** 21 | * @author Haruue Icymoon haruue@caoyue.com.cn 22 | */ 23 | 24 | @RequiresApi(api = Build.VERSION_CODES.N) 25 | public abstract class WadbTileService extends TileService implements WadbStateChangedEvent { 26 | 27 | private static final String TAG = "WadbTileService"; 28 | 29 | private final Runnable mStartWadbRunnable = () -> { 30 | GlobalRequestHandler.startWadb(WadbApplication.getWadbPort()); 31 | }; 32 | 33 | private static final Runnable STOP_WADB = GlobalRequestHandler::stopWadb; 34 | 35 | @Override 36 | public void onCreate() { 37 | super.onCreate(); 38 | Events.registerAll(this); 39 | } 40 | 41 | @Override 42 | public void onDestroy() { 43 | super.onDestroy(); 44 | Events.unregisterAll(this); 45 | } 46 | 47 | @Override 48 | public void onStartListening() { 49 | super.onStartListening(); 50 | 51 | int port; 52 | if ((port = GlobalRequestHandler.getWadbPort()) != -1) { 53 | showStateOn(NetworksUtils.getLocalIPAddress(this), port); 54 | } else { 55 | showStateOff(); 56 | } 57 | } 58 | 59 | @Override 60 | public void onClick() { 61 | super.onClick(); 62 | 63 | Log.d(TAG, "onClick"); 64 | boolean enableScreenLockSwitch = WadbApplication.getDefaultSharedPreferences().getBoolean(WadbPreferences.KEY_SCREEN_LOCK_SWITCH, false); 65 | if (getQsTile().getState() == Tile.STATE_ACTIVE) { 66 | if (enableScreenLockSwitch) { 67 | STOP_WADB.run(); 68 | } else { 69 | unlockAndRun(STOP_WADB); 70 | } 71 | } else { 72 | if (enableScreenLockSwitch) { 73 | mStartWadbRunnable.run(); 74 | } else { 75 | unlockAndRun(mStartWadbRunnable); 76 | } 77 | } 78 | } 79 | 80 | private void showStateOn(String ip, int port) { 81 | Log.d(TAG, "showStateOn"); 82 | 83 | final Tile tile = getQsTile(); 84 | final String address = ip + ":" + port; 85 | final Context context = this; 86 | 87 | tile.setState(Tile.STATE_ACTIVE); 88 | tile.setIcon(Icon.createWithResource(context, R.drawable.ic_qs_network_adb_on)); 89 | 90 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 91 | tile.setLabel(context.getString(R.string.wireless_adb)); 92 | tile.setSubtitle(address); 93 | } else { 94 | tile.setLabel(address); 95 | } 96 | 97 | tile.updateTile(); 98 | } 99 | 100 | private void showStateOff() { 101 | Log.d(TAG, "showStateOff"); 102 | 103 | final Tile tile = getQsTile(); 104 | final Context context = this; 105 | 106 | tile.setState(Tile.STATE_INACTIVE); 107 | tile.setIcon(Icon.createWithResource(context, R.drawable.ic_qs_network_adb_off)); 108 | 109 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 110 | tile.setLabel(context.getString(R.string.wireless_adb)); 111 | tile.setSubtitle(context.getString(R.string.tile_off)); 112 | } else { 113 | tile.setLabel(context.getString(R.string.wireless_adb)); 114 | } 115 | 116 | tile.updateTile(); 117 | } 118 | 119 | private void showStateUnavailable() { 120 | Tile tile = getQsTile(); 121 | tile.setState(Tile.STATE_UNAVAILABLE); 122 | tile.updateTile(); 123 | } 124 | 125 | @Override 126 | public void onWadbStarted(int port) { 127 | showStateOn(NetworksUtils.getLocalIPAddress(this), port); 128 | } 129 | 130 | @Override 131 | public void onWadbStopped() { 132 | showStateOff(); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 23 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 74 | 75 | 76 | 77 | 81 | 82 | 83 | 86 | 87 | 88 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /app/src/main/java/moe/haruue/wadb/util/NotificationHelper.kt: -------------------------------------------------------------------------------- 1 | package moe.haruue.wadb.util 2 | 3 | import android.app.Notification 4 | import android.app.NotificationChannel 5 | import android.app.NotificationManager 6 | import android.app.PendingIntent 7 | import android.content.ComponentName 8 | import android.content.Context 9 | import android.content.Intent 10 | import android.graphics.drawable.Icon 11 | import android.os.Build 12 | import androidx.annotation.RequiresApi 13 | import moe.haruue.wadb.R 14 | import moe.haruue.wadb.WadbApplication 15 | import moe.haruue.wadb.WadbPreferences 16 | import moe.haruue.wadb.component.HomeActivity 17 | import moe.haruue.wadb.receiver.TurnOffReceiver 18 | 19 | object NotificationHelper { 20 | 21 | private const val NOTIFICATION_ID = R.string.wireless_adb_short 22 | private const val NOTIFICATION_CHANNEL = "state" 23 | 24 | @JvmStatic 25 | fun showNotification(context: Context, ip: String, port: Int) { 26 | val notificationManager = context.getSystemService(NotificationManager::class.java) ?: return 27 | val contentPendingIntent = PendingIntent.getActivity(context, 0, Intent(context, HomeActivity::class.java), PendingIntent.FLAG_IMMUTABLE) 28 | val turnOffIntent = Intent("moe.haruue.wadb.action.TURN_OFF_WADB").apply { component = ComponentName(context, TurnOffReceiver::class.java) } 29 | 30 | val turnOffPendingIntent = PendingIntent.getBroadcast(context, 0, turnOffIntent, PendingIntent.FLAG_IMMUTABLE) 31 | val turnOffAction = Notification.Action.Builder(Icon.createWithResource(context, R.drawable.ic_close_white_24dp), context.getString(R.string.notification_wadb_enabled_button_disable, context.getString(R.string.wireless_adb)), turnOffPendingIntent).build() 32 | val visibility = if (WadbApplication.defaultSharedPreferences.getBoolean(WadbPreferences.KEY_SCREEN_LOCK_SWITCH, false)) Notification.VISIBILITY_PUBLIC else Notification.VISIBILITY_PRIVATE 33 | 34 | // Android Q supports dark status bar, but still uses color restriction algorithm of light background, 35 | // so we still have to use light color here 36 | val color: Int = when (ThemeHelper.getTheme()) { 37 | ThemeHelper.THEME_GREEN -> { 38 | context.getColor(R.color.md_theme_green_palette_primary_50) 39 | } 40 | ThemeHelper.THEME_PINK -> { 41 | context.getColor(R.color.md_theme_pink_palette_primary_50) 42 | } 43 | else -> { 44 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 45 | context.getColor(android.R.color.system_accent1_500) 46 | } else { 47 | context.getColor(R.color.md_theme_green_palette_primary_50) 48 | } 49 | } 50 | } 51 | 52 | // Notification 53 | val builder: Notification.Builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 54 | Notification.Builder(context, NOTIFICATION_CHANNEL) 55 | } else { 56 | @Suppress("DEPRECATION") 57 | Notification.Builder(context) 58 | } 59 | 60 | builder.setContentTitle(context.getString(R.string.notification_wadb_enabled_title, context.getString(R.string.wireless_adb))) 61 | .setContentText("$ip:$port") 62 | .setSmallIcon(R.drawable.ic_qs_network_adb_on) 63 | .setContentIntent(contentPendingIntent) 64 | .addAction(turnOffAction) 65 | .setVisibility(visibility) 66 | .setColor(color) 67 | .setOngoing(true) 68 | 69 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { 70 | @Suppress("DEPRECATION") 71 | builder.setPriority(if (WadbApplication.defaultSharedPreferences.getBoolean(WadbPreferences.KEY_NOTIFICATION_LOW_PRIORITY, true)) Notification.PRIORITY_MIN else Notification.PRIORITY_DEFAULT) 72 | } else { 73 | createNotificationChannel(context) 74 | } 75 | notificationManager.notify(NOTIFICATION_ID, builder.build()) 76 | } 77 | 78 | @JvmStatic 79 | fun cancelNotification(context: Context) { 80 | val notificationManager = context.getSystemService(NotificationManager::class.java) 81 | notificationManager?.cancel(NOTIFICATION_ID) 82 | } 83 | 84 | @RequiresApi(Build.VERSION_CODES.O) 85 | fun createNotificationChannel(context: Context) { 86 | val notificationManager = context.getSystemService(NotificationManager::class.java) 87 | if (notificationManager != null) { 88 | val channel = NotificationChannel(NOTIFICATION_CHANNEL, context.getString(R.string.notification_channel_state), NotificationManager.IMPORTANCE_DEFAULT) 89 | channel.setSound(null, null) 90 | channel.setShowBadge(false) 91 | channel.setBypassDnd(false) 92 | channel.enableLights(false) 93 | channel.enableVibration(false) 94 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 95 | channel.setAllowBubbles(false) 96 | } 97 | notificationManager.createNotificationChannel(channel) 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /app/src/main/java/moe/haruue/wadb/WadbApplication.kt: -------------------------------------------------------------------------------- 1 | package moe.haruue.wadb 2 | 3 | import android.app.Application 4 | import android.content.ComponentName 5 | import android.content.Context 6 | import android.content.SharedPreferences 7 | import android.content.pm.PackageManager 8 | import android.os.Build 9 | import android.os.Build.VERSION 10 | import moe.haruue.wadb.events.Events 11 | import moe.haruue.wadb.events.WadbFailureEvent 12 | import moe.haruue.wadb.events.WadbStateChangedEvent 13 | import moe.haruue.wadb.receiver.BootCompletedReceiver 14 | import moe.haruue.wadb.util.NetworksUtils 15 | import moe.haruue.wadb.util.NotificationHelper.cancelNotification 16 | import moe.haruue.wadb.util.NotificationHelper.showNotification 17 | import moe.haruue.wadb.util.ScreenKeeper 18 | import rikka.material.app.DayNightDelegate 19 | import rikka.sui.Sui 20 | 21 | lateinit var wadbApplication: WadbApplication 22 | 23 | class WadbApplication : Application(), WadbStateChangedEvent, WadbFailureEvent { 24 | 25 | companion object { 26 | 27 | val defaultSharedPreferenceName: String 28 | get() = BuildConfig.APPLICATION_ID + "_preferences" 29 | 30 | @JvmStatic 31 | val defaultSharedPreferences: SharedPreferences 32 | get() { 33 | var context: Context? = wadbApplication 34 | if (VERSION.SDK_INT >= Build.VERSION_CODES.N) { 35 | context = wadbApplication.createDeviceProtectedStorageContext() 36 | context.moveSharedPreferencesFrom(wadbApplication, defaultSharedPreferenceName) 37 | } 38 | return context!!.getSharedPreferences(defaultSharedPreferenceName, MODE_PRIVATE) 39 | } 40 | 41 | @JvmStatic 42 | val wadbPort: String 43 | get() { 44 | val port = defaultSharedPreferences.getString(WadbPreferences.KEY_WAKE_PORT, "5555") 45 | var p: Int 46 | try { 47 | p = port!!.toInt() 48 | if (p < 1025 || p > 65535) { 49 | p = 5555 50 | defaultSharedPreferences.edit().putString(WadbPreferences.KEY_WAKE_PORT, "5555").apply() 51 | } 52 | } catch (e: Throwable) { 53 | p = 5555 54 | defaultSharedPreferences.edit().putString(WadbPreferences.KEY_WAKE_PORT, "5555").apply() 55 | } 56 | return p.toString() 57 | } 58 | 59 | } 60 | 61 | override fun onCreate() { 62 | super.onCreate() 63 | Events.registerAll(this) 64 | DayNightDelegate.setApplicationContext(this) 65 | DayNightDelegate.setDefaultNightMode(DayNightDelegate.MODE_NIGHT_FOLLOW_SYSTEM) 66 | } 67 | 68 | override fun onWadbStarted(port: Int) { 69 | val preferences = defaultSharedPreferences 70 | preferences.edit().putString(WadbPreferences.KEY_WAKE_PORT, Integer.toString(port)).apply() 71 | val ip = NetworksUtils.getLocalIPAddress(this) 72 | if (preferences.getBoolean(WadbPreferences.KEY_NOTIFICATION, true)) { 73 | showNotification(this, ip, port) 74 | } 75 | if (preferences.getBoolean(WadbPreferences.KEY_WAKE_LOCK, false)) { 76 | ScreenKeeper.acquireWakeLock(this) 77 | } 78 | } 79 | 80 | override fun onWadbStopped() { 81 | cancelNotification(this) 82 | ScreenKeeper.releaseWakeLock() 83 | } 84 | 85 | override fun attachBaseContext(base: Context) { 86 | super.attachBaseContext(base) 87 | wadbApplication = this 88 | Sui.init(base.packageName) 89 | } 90 | 91 | private inline val launcherActivity get() = ComponentName.createRelative(packageName, ".ui.activity.LaunchActivity") 92 | 93 | fun isLauncherActivityEnabled(): Boolean { 94 | val state = wadbApplication.packageManager.getComponentEnabledSetting(launcherActivity) 95 | return state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT || state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED 96 | } 97 | 98 | fun disableLauncherActivity() { 99 | wadbApplication.packageManager.setComponentEnabledSetting( 100 | launcherActivity, 101 | PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 102 | PackageManager.DONT_KILL_APP 103 | ) 104 | } 105 | 106 | fun enableLauncherActivity() { 107 | wadbApplication.packageManager.setComponentEnabledSetting( 108 | launcherActivity, 109 | PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 110 | PackageManager.DONT_KILL_APP 111 | ) 112 | } 113 | 114 | private inline val bootCompletedReceiver get() = ComponentName.createRelative(packageName, BootCompletedReceiver::class.java.name) 115 | 116 | fun isBootCompletedReceiverEnabled(): Boolean { 117 | val state = wadbApplication.packageManager.getComponentEnabledSetting(bootCompletedReceiver) 118 | return state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED 119 | } 120 | 121 | fun disableBootCompletedReceiver() { 122 | wadbApplication.packageManager.setComponentEnabledSetting( 123 | bootCompletedReceiver, 124 | PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 125 | PackageManager.DONT_KILL_APP 126 | ) 127 | } 128 | 129 | fun enableBootCompletedReceiver() { 130 | wadbApplication.packageManager.setComponentEnabledSetting( 131 | bootCompletedReceiver, 132 | PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 133 | PackageManager.DONT_KILL_APP 134 | ) 135 | } 136 | } -------------------------------------------------------------------------------- /app/src/main/java/moe/haruue/wadb/component/HomeActivity.kt: -------------------------------------------------------------------------------- 1 | package moe.haruue.wadb.component 2 | 3 | import android.app.Dialog 4 | import android.content.Intent 5 | import android.content.pm.PackageManager 6 | import android.net.Uri 7 | import android.os.Build 8 | import android.os.Bundle 9 | import android.text.method.LinkMovementMethod 10 | import android.view.Menu 11 | import android.view.MenuItem 12 | import android.view.View 13 | import android.widget.ImageView 14 | import android.widget.TextView 15 | import androidx.core.view.isVisible 16 | import androidx.lifecycle.Lifecycle 17 | import com.google.android.material.dialog.MaterialAlertDialogBuilder 18 | import moe.haruue.wadb.BuildConfig 19 | import moe.haruue.wadb.R 20 | import moe.haruue.wadb.app.AppBarFragmentActivity 21 | import moe.haruue.wadb.util.ThemeHelper 22 | import rikka.core.ktx.unsafeLazy 23 | import rikka.html.text.HtmlCompat 24 | import rikka.html.text.toHtml 25 | 26 | class HomeActivity : AppBarFragmentActivity() { 27 | 28 | private val themes by unsafeLazy { 29 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) 30 | arrayOf( 31 | getString(R.string.theme_default), 32 | getString(R.string.theme_pink), 33 | ) else 34 | arrayOf( 35 | getString(R.string.theme_default), 36 | getString(R.string.theme_green), 37 | getString(R.string.theme_pink), 38 | ) 39 | } 40 | 41 | private val themesValue = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) 42 | arrayOf( 43 | ThemeHelper.THEME_DEFAULT, 44 | ThemeHelper.THEME_PINK 45 | ) else 46 | arrayOf( 47 | ThemeHelper.THEME_DEFAULT, 48 | ThemeHelper.THEME_GREEN, 49 | ThemeHelper.THEME_PINK 50 | ) 51 | 52 | private val themesId = themesValue.map { it.hashCode() } 53 | 54 | override fun onCreate(savedInstanceState: Bundle?) { 55 | super.onCreate(savedInstanceState) 56 | 57 | if (savedInstanceState == null) { 58 | val fragment = HomeFragment() 59 | supportFragmentManager.beginTransaction() 60 | .replace(R.id.fragment_container, fragment) 61 | .setMaxLifecycle(fragment, Lifecycle.State.RESUMED) 62 | .commit() 63 | } 64 | } 65 | 66 | override fun onCreateOptionsMenu(menu: Menu): Boolean { 67 | menuInflater.inflate(R.menu.home, menu) 68 | menu.findItem(R.id.menu_theme).subMenu.apply { 69 | val currentTheme = ThemeHelper.getTheme() 70 | for ((index, theme) in themes.withIndex()) { 71 | add(R.id.menu_theme_group, themesId[index].hashCode(), index, theme).apply { 72 | isCheckable = true 73 | isChecked = currentTheme == themesValue[index] 74 | } 75 | } 76 | setGroupCheckable(R.id.menu_theme_group, true, true) 77 | } 78 | return true 79 | } 80 | 81 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 82 | return when (item.itemId) { 83 | R.id.menu_about -> { 84 | val context = this 85 | val versionName: String 86 | try { 87 | versionName = context.packageManager.getPackageInfo(context.packageName, 0).versionName 88 | } catch (ignored: PackageManager.NameNotFoundException) { 89 | return true 90 | } 91 | val text = StringBuilder() 92 | text.append(versionName) 93 | .append("

") 94 | .append( 95 | getString( 96 | R.string.open_source_info, 97 | "GitHub", 98 | BuildConfig.LICENSE 99 | ) 100 | ) 101 | val translators = getString(R.string.translators) 102 | if (translators.isNotBlank()) { 103 | text.append("

").append(getString(R.string.translation_contributors, translators)) 104 | } 105 | text.append("

").append(BuildConfig.COPYRIGHT) 106 | 107 | val dialog: Dialog = MaterialAlertDialogBuilder(context) 108 | .setView(R.layout.dialog_about) 109 | .show() 110 | (dialog.findViewById(R.id.design_about_icon) as ImageView).setImageDrawable(context.getDrawable(R.drawable.ic_launcher)) 111 | (dialog.findViewById(R.id.design_about_title) as TextView).text = 112 | getString(R.string.wireless_adb_short) 113 | (dialog.findViewById(R.id.design_about_version) as TextView).apply { 114 | movementMethod = LinkMovementMethod.getInstance() 115 | this.text = text.toHtml(HtmlCompat.FROM_HTML_OPTION_TRIM_WHITESPACE) 116 | } 117 | (dialog.findViewById(R.id.design_about_info) as TextView).isVisible = false 118 | true 119 | } 120 | R.id.menu_translate -> { 121 | startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(BuildConfig.TRANSLATION_URL))) 122 | true 123 | } 124 | else -> { 125 | val index = themesId.indexOf(item.itemId) 126 | if (index == -1) return super.onOptionsItemSelected(item) 127 | 128 | if (ThemeHelper.getTheme() != themesValue[index]) { 129 | ThemeHelper.setLightTheme(themesValue[index]) 130 | recreate() 131 | } 132 | return true 133 | } 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /app/src/main/java/moe/haruue/wadb/events/GlobalRequestHandler.java: -------------------------------------------------------------------------------- 1 | package moe.haruue.wadb.events; 2 | 3 | import android.os.SystemProperties; 4 | import android.text.TextUtils; 5 | import android.util.Log; 6 | 7 | import java.util.Arrays; 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.Executors; 10 | 11 | import moe.haruue.wadb.BuildConfig; 12 | import moe.haruue.wadb.util.SuShell; 13 | import rikka.shizuku.Shizuku; 14 | import rikka.shizuku.ShizukuSystemProperties; 15 | import rikka.sui.Sui; 16 | 17 | import static android.content.pm.PackageManager.PERMISSION_GRANTED; 18 | 19 | public class GlobalRequestHandler { 20 | 21 | private static final String TAG = "GlobalRequestHandler"; 22 | private static final boolean DEBUG = BuildConfig.DEBUG; 23 | 24 | private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(1); 25 | 26 | private final static String[] STOP_WADB_COMMANDS; 27 | 28 | static { 29 | if (BuildConfig.DONOT_RESTART_ADBD && BuildConfig.DEBUG) { 30 | STOP_WADB_COMMANDS = new String[]{ 31 | "setprop service.adb.tcp.port -1" 32 | }; 33 | } else { 34 | STOP_WADB_COMMANDS = new String[]{ 35 | "setprop service.adb.tcp.port -1", 36 | "setprop ctl.restart adbd" 37 | }; 38 | } 39 | } 40 | 41 | private static String[] getStartWadbCommand(String port) { 42 | if (BuildConfig.DONOT_RESTART_ADBD && BuildConfig.DEBUG) { 43 | return new String[]{ 44 | "setprop service.adb.tcp.port " + port 45 | }; 46 | } else { 47 | return new String[]{ 48 | "setprop service.adb.tcp.port " + port, 49 | "setprop ctl.restart adbd" 50 | }; 51 | } 52 | } 53 | 54 | public static int getWadbPort() { 55 | String port = SystemProperties.get("service.adb.tcp.port"); 56 | if (!TextUtils.isEmpty(port)) { 57 | try { 58 | return Integer.parseInt(port); 59 | } catch (Throwable tr) { 60 | tr.printStackTrace(); 61 | } 62 | } 63 | return -1; 64 | } 65 | 66 | public static void checkWadbState() { 67 | Log.d(TAG, "checkWadbState"); 68 | 69 | int port; 70 | if ((port = getWadbPort()) != -1) { 71 | Events.postWadbStateChangedEvent(event -> event.onWadbStarted(port)); 72 | } else { 73 | Events.postWadbFailureEvent(WadbFailureEvent::onOperateFailure); 74 | } 75 | } 76 | 77 | private static int runCommands(String[] cmds) { 78 | Thread.currentThread().setPriority(Thread.MAX_PRIORITY); 79 | 80 | int exitCode = -1; 81 | long time = 0; 82 | if (DEBUG) { 83 | time = System.currentTimeMillis(); 84 | } 85 | 86 | if (!SuShell.available()) { 87 | Events.postWadbFailureEvent(WadbFailureEvent::onRootPermissionFailure); 88 | return exitCode; 89 | } 90 | SuShell.Result shellResult = SuShell.run(cmds); 91 | exitCode = shellResult.exitCode; 92 | if (DEBUG) { 93 | Log.d(TAG, "su " + Arrays.toString(cmds) + " takes " + (System.currentTimeMillis() - time) + "ms"); 94 | } 95 | return exitCode; 96 | } 97 | 98 | public static void startWadb(String port) { 99 | if (Sui.isSui()) { 100 | Runnable runnable = () -> { 101 | try { 102 | ShizukuSystemProperties.set("service.adb.tcp.port", port); 103 | if (!BuildConfig.DONOT_RESTART_ADBD || !BuildConfig.DEBUG) { 104 | ShizukuSystemProperties.set("ctl.restart", "adbd"); 105 | } 106 | Events.postWadbStateChangedEvent(event -> event.onWadbStarted(Integer.parseInt(port))); 107 | } catch (Throwable e) { 108 | e.printStackTrace(); 109 | Events.postWadbFailureEvent(WadbFailureEvent::onOperateFailure); 110 | } 111 | }; 112 | 113 | if (Shizuku.checkSelfPermission() == PERMISSION_GRANTED) { 114 | runnable.run(); 115 | } else if (Shizuku.shouldShowRequestPermissionRationale()) { 116 | Events.postWadbFailureEvent(WadbFailureEvent::onRootPermissionFailure); 117 | } else { 118 | Shizuku.addRequestPermissionResultListener(new Shizuku.OnRequestPermissionResultListener() { 119 | @Override 120 | public void onRequestPermissionResult(int requestCode, int grantResult) { 121 | if (requestCode != 1) return; 122 | 123 | Shizuku.removeRequestPermissionResultListener(this); 124 | if (grantResult == PERMISSION_GRANTED) { 125 | runnable.run(); 126 | } 127 | } 128 | }); 129 | Shizuku.requestPermission(1); 130 | } 131 | } else { 132 | EXECUTOR.submit(() -> { 133 | int exitCode = runCommands(getStartWadbCommand(port)); 134 | Log.d(TAG, "startWadb: " + exitCode); 135 | if (exitCode == 0) { 136 | Events.postWadbStateChangedEvent(event -> event.onWadbStarted(Integer.parseInt(port))); 137 | } else { 138 | Events.postWadbFailureEvent(WadbFailureEvent::onOperateFailure); 139 | } 140 | }); 141 | } 142 | } 143 | 144 | public static void stopWadb() { 145 | if (Sui.isSui()) { 146 | Runnable runnable = () -> { 147 | try { 148 | ShizukuSystemProperties.set("service.adb.tcp.port", "-1"); 149 | if (!BuildConfig.DONOT_RESTART_ADBD || !BuildConfig.DEBUG) { 150 | ShizukuSystemProperties.set("ctl.restart", "adbd"); 151 | } 152 | Events.postWadbStateChangedEvent(WadbStateChangedEvent::onWadbStopped); 153 | } catch (Throwable e) { 154 | e.printStackTrace(); 155 | Events.postWadbFailureEvent(WadbFailureEvent::onOperateFailure); 156 | } 157 | }; 158 | 159 | if (Shizuku.checkSelfPermission() == PERMISSION_GRANTED) { 160 | runnable.run(); 161 | } else if (Shizuku.shouldShowRequestPermissionRationale()) { 162 | Events.postWadbFailureEvent(WadbFailureEvent::onRootPermissionFailure); 163 | } else { 164 | Shizuku.addRequestPermissionResultListener(new Shizuku.OnRequestPermissionResultListener() { 165 | @Override 166 | public void onRequestPermissionResult(int requestCode, int grantResult) { 167 | if (requestCode != 2) return; 168 | 169 | Shizuku.removeRequestPermissionResultListener(this); 170 | if (grantResult == PERMISSION_GRANTED) { 171 | runnable.run(); 172 | } 173 | } 174 | }); 175 | Shizuku.requestPermission(2); 176 | } 177 | } else { 178 | EXECUTOR.submit(() -> { 179 | int exitCode = runCommands(STOP_WADB_COMMANDS); 180 | 181 | if (exitCode == 0) { 182 | Events.postWadbStateChangedEvent(WadbStateChangedEvent::onWadbStopped); 183 | } else { 184 | Events.postWadbFailureEvent(WadbFailureEvent::onOperateFailure); 185 | } 186 | }); 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | import java.nio.file.Paths 2 | 3 | plugins { 4 | id('com.android.application') 5 | id('kotlin-android') 6 | id('dev.rikka.tools.autoresconfig') 7 | id('dev.rikka.tools.materialthemebuilder') 8 | } 9 | 10 | def localFile = rootProject.file('local.properties') 11 | def localProps = new Properties() 12 | def dontRestartAdbd 13 | if (localFile.canRead()) { 14 | localProps.load(localFile.newDataInputStream()) 15 | dontRestartAdbd = localProps.get("debug.dontRestartAdbd", false) 16 | } 17 | 18 | def signFile = rootProject.file('signing.properties') 19 | def signProps = new Properties() 20 | def hasSignConfig = false 21 | if (signFile.canRead()) { 22 | signProps.load(new FileInputStream(signFile)) 23 | hasSignConfig = true 24 | } 25 | 26 | android { 27 | compileSdkVersion target_sdk 28 | ndkVersion "23.1.7779620" 29 | defaultConfig { 30 | applicationId "moe.haruue.wadb" 31 | minSdkVersion min_sdk 32 | targetSdkVersion target_sdk 33 | versionCode rootProject.ext.versionCode 34 | versionName rootProject.ext.versionName 35 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 36 | resConfigs 'de', 'id', 'it', 'ja', 'pt-rBR', 'ru', 'tr', 'zh-rCN', 'zh-rTW' 37 | buildConfigField "String", "GITHUB_URL", "\"https://github.com/RikkaApps/WADB\"" 38 | buildConfigField "String", "LICENSE", "\"Apache License 2.0\"" 39 | buildConfigField "String", "TRANSLATION_URL", "\"https://rikka.app/contribute_translation/\"" 40 | buildConfigField "String", "COPYRIGHT", "\"Copyright © Haruue Icymoon, PinkD, Rikka\"" 41 | buildConfigField "boolean", "DONOT_RESTART_ADBD", "${dontRestartAdbd}" // set it true to prevent restart adbd (for debugging) 42 | setProperty("archivesBaseName", "wadb-v${versionName}") 43 | externalNativeBuild { 44 | cmake { 45 | abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' 46 | arguments '-DANDROID_STL=none' 47 | } 48 | } 49 | } 50 | buildFeatures { 51 | prefab = true 52 | } 53 | signingConfigs { 54 | if (hasSignConfig) { 55 | sign { 56 | storeFile signProps.getProperty("KEYSTORE_FILE") != null ? file(signProps.getProperty("KEYSTORE_FILE")) : android.signingConfigs.debug.storeFile 57 | storePassword signProps.getProperty("KEYSTORE_PASSWORD", android.signingConfigs.debug.storePassword) 58 | keyAlias signProps.getProperty("KEYSTORE_ALIAS", android.signingConfigs.debug.keyAlias) 59 | keyPassword signProps.getProperty("KEYSTORE_ALIAS_PASSWORD", android.signingConfigs.debug.keyPassword) 60 | } 61 | } 62 | } 63 | compileOptions { 64 | sourceCompatibility JavaVersion.VERSION_1_8 65 | targetCompatibility JavaVersion.VERSION_1_8 66 | } 67 | kotlinOptions { 68 | jvmTarget = "1.8" 69 | } 70 | buildTypes { 71 | debug { 72 | if (hasSignConfig) { 73 | signingConfig signingConfigs.sign 74 | } 75 | } 76 | release { 77 | minifyEnabled true 78 | shrinkResources true 79 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 80 | if (hasSignConfig) { 81 | signingConfig signingConfigs.sign 82 | } 83 | } 84 | } 85 | externalNativeBuild { 86 | cmake { 87 | path "src/main/cpp/CMakeLists.txt" 88 | } 89 | } 90 | packagingOptions { 91 | jniLibs { 92 | excludes += ['/kotlin/**'] 93 | } 94 | resources { 95 | excludes += ['/META-INF/*.version', '/META-INF/*.version', '/META-INF/*.kotlin_module', '/kotlin/**'] 96 | } 97 | } 98 | dependenciesInfo.includeInApk false 99 | lint { 100 | checkReleaseBuilds false 101 | } 102 | } 103 | 104 | autoResConfig { 105 | generateClass = false 106 | generateRes = false 107 | } 108 | 109 | materialThemeBuilder { 110 | themes { 111 | wadb { 112 | primaryColor = "#88b984" 113 | dynamicColors = true 114 | lightThemeFormat = "Theme.Material3.Light.%s" 115 | lightThemeParent = "Theme.Material3.Light.Rikka" 116 | darkThemeFormat = "Theme.Material3.Dark.%s" 117 | darkThemeParent = "Theme.Material3.Dark.Rikka" 118 | } 119 | green { 120 | primaryColor = "#88b984" 121 | lightThemeFormat = "ThemeOverlay.%s.Light" 122 | lightThemeParent = "ThemeOverlay" 123 | darkThemeFormat = "ThemeOverlay.%s.Dark" 124 | darkThemeParent = "ThemeOverlay" 125 | } 126 | pink { 127 | primaryColor = "#F5A9B8" 128 | lightThemeFormat = "ThemeOverlay.%s.Light" 129 | lightThemeParent = "ThemeOverlay" 130 | darkThemeFormat = "ThemeOverlay.%s.Dark" 131 | darkThemeParent = "ThemeOverlay" 132 | } 133 | } 134 | generatePalette = true 135 | packageName = "moe.haruue.wadb" 136 | } 137 | 138 | def optimizeReleaseResources = task('optimizeReleaseResources').doLast { 139 | def aapt2 = Paths.get(project.android.sdkDirectory.path, 'build-tools', project.android.buildToolsVersion, 'aapt2') 140 | def zip = Paths.get(project.buildDir.path, 'intermediates', 141 | 'processed_res', 'release', 'out', "resources-release.ap_") 142 | def optimized = new File("${zip}.opt") 143 | def cmd = exec { 144 | commandLine aapt2, 'optimize', '--collapse-resource-names', 145 | '--shorten-resource-paths', 146 | '-o', optimized, zip 147 | ignoreExitValue false 148 | } 149 | if (cmd.exitValue == 0) { 150 | delete(zip) 151 | optimized.renameTo("$zip") 152 | } 153 | } 154 | 155 | tasks.whenTaskAdded { task -> 156 | if (task.name == 'processReleaseResources') { 157 | task.finalizedBy optimizeReleaseResources 158 | } 159 | } 160 | 161 | android.applicationVariants.all { variant -> 162 | variant.outputs.all { 163 | if (variant.getBuildType().isMinifyEnabled()) { 164 | variant.assembleProvider.get().doLast { 165 | copy { 166 | from variant.mappingFile 167 | into "release" 168 | rename { String fileName -> 169 | "mapping-${variant.versionCode}.txt" 170 | } 171 | } 172 | copy { 173 | from outputFile 174 | into "release" 175 | } 176 | } 177 | } 178 | } 179 | } 180 | 181 | repositories { 182 | maven { 183 | url 'https://jitpack.io' 184 | content { 185 | includeGroup("com.github.topjohnwu.libsu") 186 | } 187 | } 188 | } 189 | 190 | configurations.all { 191 | exclude group: 'androidx.appcompat', module: 'appcompat' 192 | } 193 | 194 | dependencies { 195 | compileOnly project(':hidden') 196 | 197 | implementation 'com.google.android.material:material:1.7.0-alpha02' 198 | implementation 'androidx.core:core-ktx:1.8.0' 199 | implementation 'androidx.fragment:fragment-ktx:1.4.1' 200 | implementation 'androidx.recyclerview:recyclerview:1.2.1' 201 | implementation 'androidx.preference:preference-ktx:1.2.0' 202 | 203 | implementation "dev.rikka.rikkax.appcompat:appcompat:1.4.1" 204 | implementation "dev.rikka.rikkax.core:core:1.4.0" 205 | implementation 'dev.rikka.rikkax.html:html-ktx:1.1.2' 206 | implementation 'dev.rikka.rikkax.material:material:2.4.0' 207 | implementation 'dev.rikka.rikkax.material:material-preference:1.1.0' 208 | implementation 'dev.rikka.rikkax.recyclerview:recyclerview-ktx:1.3.1' 209 | implementation 'dev.rikka.rikkax.widget:borderview:1.1.0' 210 | implementation 'dev.rikka.rikkax.widget:mainswitchbar:1.1.0' 211 | implementation 'dev.rikka.ndk.thirdparty:cxx:1.2.0' 212 | 213 | def libsuVersion = '4.0.3' 214 | implementation "com.github.topjohnwu.libsu:core:${libsuVersion}" 215 | 216 | def shizuku_version = '12.0.0' 217 | implementation "dev.rikka.shizuku:api:${shizuku_version}" 218 | } 219 | -------------------------------------------------------------------------------- /app/src/main/cpp/netlink.cpp: -------------------------------------------------------------------------------- 1 | #include "netlink.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "unique_fd.h" 20 | 21 | #ifdef ANDROID 22 | #include 23 | #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, "wadb_netlink", __VA_ARGS__) 24 | #else 25 | #include 26 | #define ALOGE(...) fprintf(stderr, "\n[ERROR] wadb_netlink: " __VA_ARGS__) 27 | #endif 28 | 29 | namespace wadb::netlink { 30 | namespace { 31 | ssize_t send_get_link_msg(int fd) { 32 | struct Request { 33 | nlmsghdr nh; 34 | ifinfomsg ifi; 35 | }; 36 | 37 | Request req{}; 38 | req.nh.nlmsg_len = sizeof(req); 39 | req.nh.nlmsg_type = RTM_GETLINK; 40 | req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 41 | req.nh.nlmsg_seq = 0; 42 | req.ifi.ifi_family = AF_INET; 43 | req.ifi.ifi_type = ARPHRD_NETROM; 44 | 45 | sockaddr_nl sa{}; 46 | sa.nl_family = AF_NETLINK; 47 | iovec iov{}; 48 | iov.iov_base = &req; 49 | iov.iov_len = sizeof(req); 50 | 51 | msghdr msg{}; 52 | msg.msg_name = &sa; 53 | msg.msg_namelen = sizeof(sa); 54 | msg.msg_iov = &iov; 55 | msg.msg_iovlen = 1; 56 | 57 | return sendmsg(fd, &msg, 0); 58 | } 59 | 60 | ssize_t send_get_addr_msg(int fd) { 61 | struct Request { 62 | nlmsghdr nh; 63 | ifaddrmsg ifa; 64 | }; 65 | 66 | Request req{}; 67 | req.nh.nlmsg_len = sizeof(req); 68 | req.nh.nlmsg_type = RTM_GETADDR; 69 | req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 70 | req.nh.nlmsg_seq = 1; 71 | 72 | sockaddr_nl sa{}; 73 | sa.nl_family = AF_NETLINK; 74 | iovec iov{}; 75 | iov.iov_base = &req; 76 | iov.iov_len = sizeof(req); 77 | 78 | msghdr msg{}; 79 | msg.msg_name = &sa; 80 | msg.msg_namelen = sizeof(sa); 81 | msg.msg_iov = &iov; 82 | msg.msg_iovlen = 1; 83 | 84 | return sendmsg(fd, &msg, 0); 85 | } 86 | 87 | ssize_t recv_netlink_msg(int fd, void *rcvbuf, size_t rcvbuf_len) { 88 | sockaddr_nl sa{}; 89 | sa.nl_family = AF_NETLINK; 90 | iovec iov{}; 91 | iov.iov_base = rcvbuf; 92 | iov.iov_len = rcvbuf_len; 93 | 94 | msghdr msg{}; 95 | msg.msg_name = &sa; 96 | msg.msg_namelen = sizeof(sa); 97 | msg.msg_iov = &iov; 98 | msg.msg_iovlen = 1; 99 | 100 | return recvmsg(fd, &msg, 0); 101 | } 102 | 103 | int parse_netlink_getlink_response_for_interfaces(const void *buf, size_t len, 104 | std::map &out_if_idx_name_map) { 105 | uint16_t last_nlmsg_type = NLMSG_DONE; 106 | 107 | for (auto *nh = reinterpret_cast(buf); 108 | NLMSG_OK(nh, len); 109 | nh = NLMSG_NEXT(nh, len)) { 110 | last_nlmsg_type = nh->nlmsg_type; 111 | 112 | if (nh->nlmsg_type == NLMSG_ERROR) { 113 | return -1; 114 | } else if (nh->nlmsg_type == RTM_NEWLINK) { 115 | auto ifi = reinterpret_cast(NLMSG_DATA(nh)); 116 | auto ifi_len = IFLA_PAYLOAD(nh); 117 | 118 | for (auto rta = reinterpret_cast(IFLA_RTA(ifi)); 119 | RTA_OK(rta, ifi_len); 120 | rta = RTA_NEXT(rta, ifi_len)) { 121 | if (rta->rta_type == IFLA_IFNAME) { 122 | std::string_view rta_if_name{reinterpret_cast(RTA_DATA(rta))}; 123 | out_if_idx_name_map.emplace(ifi->ifi_index, rta_if_name); 124 | } 125 | } 126 | 127 | } 128 | } 129 | 130 | return last_nlmsg_type; 131 | } 132 | 133 | int parse_netlink_getaddr_response_for_ips(const void *buf, size_t len, 134 | bool include_ipv6, 135 | const std::map &out_if_idx_name_map, 136 | std::list &ips) { 137 | uint16_t last_nlmsg_type = NLMSG_DONE; 138 | 139 | for (auto *nh = reinterpret_cast(buf); 140 | NLMSG_OK(nh, len); 141 | nh = NLMSG_NEXT(nh, len)) { 142 | last_nlmsg_type = nh->nlmsg_type; 143 | 144 | if (nh->nlmsg_type == NLMSG_ERROR) { 145 | return NLMSG_ERROR; 146 | } else if (nh->nlmsg_type == RTM_NEWADDR) { 147 | auto ifa = reinterpret_cast(NLMSG_DATA(nh)); 148 | auto ifa_len = IFA_PAYLOAD(nh); 149 | 150 | if (ifa->ifa_family != AF_INET) { 151 | if (!include_ipv6 || ifa->ifa_family != AF_INET6) { 152 | continue; 153 | } 154 | } 155 | 156 | if (auto it = out_if_idx_name_map.find(ifa->ifa_index); 157 | it != out_if_idx_name_map.cend()) { 158 | for (auto rta = reinterpret_cast(IFA_RTA(ifa)); 159 | RTA_OK(rta, ifa_len); 160 | rta = RTA_NEXT(rta, ifa_len)) { 161 | if (rta->rta_type == IFA_ADDRESS) { 162 | std::string ip_str_buf(64, '\0'); 163 | inet_ntop(ifa->ifa_family, RTA_DATA(rta), ip_str_buf.data(), ip_str_buf.size()); 164 | ips.emplace_back(ifa->ifa_index, ifa->ifa_family, it->second, ip_str_buf.data()); 165 | } 166 | } 167 | } 168 | } 169 | } 170 | 171 | return last_nlmsg_type; 172 | } 173 | } 174 | 175 | int get_interface_ips(bool include_ipv6, std::list &ips) { 176 | hx::tux::unique_fd fd{socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE)}; 177 | if (fd == -1) { 178 | ALOGE("socket(AF_NETLINK) failed with errno=%d", errno); 179 | } 180 | constexpr uint sndbuf_size{/*32 KB*/ 32 * (1u << 10)}; 181 | if (auto ret = setsockopt(fd.get(), SOL_SOCKET, SO_SNDBUF, &sndbuf_size, sizeof(sndbuf_size)); 182 | ret < 0) { 183 | ALOGE("setsockopt(SO_SNDBUF) failed with ret=%d, errno=%d", ret, errno); 184 | return ret; 185 | } 186 | constexpr uint rcvbuf_size{/*32 KB*/ 32 * (1u << 10)}; 187 | if (auto ret = setsockopt(fd.get(), SOL_SOCKET, SO_RCVBUF, &rcvbuf_size, sizeof(rcvbuf_size)); 188 | ret < 0) { 189 | ALOGE("setsockopt(SO_RCVBUF) failed with ret=%d, errno=%d", ret, errno); 190 | return ret; 191 | } 192 | std::vector rcvbuf(static_cast(rcvbuf_size)); 193 | 194 | std::map if_idx_name_map{}; 195 | 196 | // both if_indextoname(3) and if_nametoindex(3) won't work on android (permission denied), 197 | // and if_nameindex(3) requires SDK >= 24 (we are 23 now), 198 | // so we send a RTM_GETLINK request and parse the mapping between if_index and if_name by ourselves. 199 | if (auto ret = send_get_link_msg(fd.get()); 200 | ret < 0) { 201 | ALOGE("send_get_link_msg failed with ret=%zd, errno=%d", ret, errno); 202 | return ret; 203 | } 204 | 205 | while (true) { 206 | auto len = recv_netlink_msg(fd.get(), rcvbuf.data(), rcvbuf_size); 207 | 208 | if (len < 0) { 209 | ALOGE("recv_netlink_msg failed with ret=%zd, errno=%d", len, errno); 210 | break; 211 | } 212 | 213 | if (len == 0) { 214 | // eof 215 | break; 216 | } 217 | 218 | auto last_netlink_msg_type = parse_netlink_getlink_response_for_interfaces(rcvbuf.data(), len, if_idx_name_map); 219 | 220 | if (last_netlink_msg_type == NLMSG_DONE) { 221 | break; 222 | } 223 | 224 | if (last_netlink_msg_type == NLMSG_ERROR) { 225 | ALOGE("parse_netlink_getlink_response_for_interfaces: NLMSG_ERROR"); 226 | return -1; 227 | } 228 | } 229 | 230 | if (if_idx_name_map.empty()) { 231 | ALOGE("no if_idx found"); 232 | return -1; 233 | } 234 | 235 | if (auto ret = send_get_addr_msg(fd.get()); 236 | ret < 0) { 237 | ALOGE("send_get_addr_msg failed with ret=%zd, errno=%d", ret, errno); 238 | return ret; 239 | } 240 | 241 | while (true) { 242 | auto len = recv_netlink_msg(fd.get(), rcvbuf.data(), rcvbuf_size); 243 | 244 | if (len < 0) { 245 | ALOGE("recv_netlink_msg failed with ret=%zd, errno=%d", len, errno); 246 | break; 247 | } 248 | 249 | if (len == 0) { 250 | // eof 251 | break; 252 | } 253 | 254 | auto last_netlink_msg_type = parse_netlink_getaddr_response_for_ips(rcvbuf.data(), len, include_ipv6, if_idx_name_map, ips); 255 | 256 | if (last_netlink_msg_type == NLMSG_DONE) { 257 | break; 258 | } 259 | 260 | if (last_netlink_msg_type == NLMSG_ERROR) { 261 | ALOGE("parse_netlink_getaddr_response_for_ips: NLMSG_ERROR"); 262 | return -1; 263 | } 264 | } 265 | 266 | return 0; 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /app/src/main/java/moe/haruue/wadb/component/HomeFragment.kt: -------------------------------------------------------------------------------- 1 | package moe.haruue.wadb.component 2 | 3 | import android.content.SharedPreferences 4 | import android.os.Build 5 | import android.os.Bundle 6 | import android.text.InputType 7 | import android.view.LayoutInflater 8 | import android.view.ViewGroup 9 | import android.widget.FrameLayout 10 | import android.widget.Toast 11 | import androidx.preference.* 12 | import androidx.recyclerview.widget.RecyclerView 13 | import com.google.android.material.dialog.MaterialAlertDialogBuilder 14 | import moe.haruue.wadb.R 15 | import moe.haruue.wadb.WadbApplication 16 | import moe.haruue.wadb.WadbPreferences.* 17 | import moe.haruue.wadb.events.Events 18 | import moe.haruue.wadb.events.GlobalRequestHandler 19 | import moe.haruue.wadb.events.WadbFailureEvent 20 | import moe.haruue.wadb.events.WadbStateChangedEvent 21 | import moe.haruue.wadb.util.NetworksUtils 22 | import moe.haruue.wadb.util.NotificationHelper 23 | import moe.haruue.wadb.util.ScreenKeeper 24 | import moe.haruue.wadb.wadbApplication 25 | import rikka.preference.MainSwitchPreference 26 | import rikka.recyclerview.fixEdgeEffect 27 | import rikka.widget.borderview.BorderRecyclerView 28 | 29 | class HomeFragment : PreferenceFragmentCompat(), WadbStateChangedEvent, WadbFailureEvent, 30 | SharedPreferences.OnSharedPreferenceChangeListener { 31 | 32 | private lateinit var togglePreference: MainSwitchPreference 33 | private lateinit var ipPreference: Preference 34 | private lateinit var portPreference: EditTextPreference 35 | 36 | override fun onCreate(savedInstanceState: Bundle?) { 37 | super.onCreate(savedInstanceState) 38 | Events.registerAll(this) 39 | GlobalRequestHandler.checkWadbState() 40 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 41 | NotificationHelper.createNotificationChannel(requireContext()) 42 | } 43 | } 44 | 45 | override fun onDestroy() { 46 | super.onDestroy() 47 | Events.unregisterAll(this) 48 | } 49 | 50 | override fun onCreateRecyclerView( 51 | inflater: LayoutInflater, 52 | parent: ViewGroup, 53 | savedInstanceState: Bundle? 54 | ): RecyclerView { 55 | val recyclerView = super.onCreateRecyclerView(inflater, parent, savedInstanceState) as BorderRecyclerView 56 | 57 | recyclerView.fixEdgeEffect() 58 | 59 | val lp = recyclerView.layoutParams 60 | if (lp is FrameLayout.LayoutParams) { 61 | lp.rightMargin = recyclerView.context.resources.getDimension(R.dimen.rd_activity_horizontal_margin).toInt() 62 | lp.leftMargin = lp.rightMargin 63 | } 64 | 65 | return recyclerView 66 | } 67 | 68 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 69 | preferenceManager.setStorageDeviceProtected() 70 | addPreferencesFromResource(R.xml.preferences) 71 | preferenceManager.sharedPreferencesName = WadbApplication.defaultSharedPreferenceName 72 | 73 | val context = requireContext() 74 | togglePreference = findPreference(KEY_WADB_SWITCH)!! 75 | ipPreference = findPreference(KEY_WAKE_IP)!! 76 | portPreference = findPreference(KEY_WAKE_PORT)!! 77 | 78 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 79 | val notificationLowPriorityPreference: TwoStatePreference = findPreference(KEY_NOTIFICATION_LOW_PRIORITY)!! 80 | notificationLowPriorityPreference.isVisible = false 81 | } 82 | 83 | portPreference.setOnBindEditTextListener { 84 | it.inputType = InputType.TYPE_CLASS_NUMBER 85 | } 86 | portPreference.setOnPreferenceChangeListener { _, newValue -> 87 | val port: String = newValue as String 88 | val p = try { 89 | Integer.parseInt(port) 90 | } catch (e: Throwable) { 91 | -1 92 | } 93 | if (p < 1025 || p > 65535) { 94 | Toast.makeText(context, R.string.toast_bad_port_number, Toast.LENGTH_SHORT).show() 95 | return@setOnPreferenceChangeListener false 96 | } 97 | 98 | if (togglePreference.isChecked) { 99 | GlobalRequestHandler.startWadb(port) 100 | } 101 | true 102 | } 103 | 104 | togglePreference.addOnSwitchChangeListener { switchView, isChecked -> 105 | togglePreference.isEnabled = false 106 | portPreference.isEnabled = false 107 | if (isChecked) { 108 | GlobalRequestHandler.startWadb(WadbApplication.wadbPort) 109 | } else { 110 | GlobalRequestHandler.stopWadb() 111 | } 112 | } 113 | 114 | val wadbActive = GlobalRequestHandler.getWadbPort() != -1 115 | togglePreference.isChecked = wadbActive 116 | ipPreference.isVisible = wadbActive 117 | 118 | findPreference(KEY_NOTIFICATION_SETTINGS)!!.isVisible = 119 | Build.VERSION.SDK_INT >= Build.VERSION_CODES.O 120 | findPreference(KEY_SCREEN_LOCK_SWITCH)!!.isVisible = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N 121 | 122 | val launcherIconPreference: TwoStatePreference = findPreference(KEY_LAUNCHER_ICONS)!! 123 | val launcherActivityEnabled = wadbApplication.isLauncherActivityEnabled() 124 | 125 | launcherIconPreference.isChecked = !launcherActivityEnabled 126 | launcherIconPreference.setOnPreferenceChangeListener { _, newValue -> 127 | if (newValue as Boolean) { 128 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 129 | MaterialAlertDialogBuilder(requireContext()) 130 | .setMessage(R.string.dialog_hide_icon_message_q) 131 | .setNegativeButton(android.R.string.cancel, null) 132 | .setPositiveButton(android.R.string.ok) { _, _ -> 133 | wadbApplication.disableLauncherActivity() 134 | launcherIconPreference.isChecked = true 135 | } 136 | .show() 137 | false 138 | } else { 139 | wadbApplication.disableLauncherActivity() 140 | true 141 | } 142 | } else { 143 | wadbApplication.enableLauncherActivity() 144 | true 145 | } 146 | } 147 | 148 | val bootCompletedReceiverPreference: TwoStatePreference = findPreference("start_on_boot")!! 149 | bootCompletedReceiverPreference.summary = 150 | getString(R.string.settings_start_on_boot_summary, getString(R.string.wireless_adb)) 151 | bootCompletedReceiverPreference.isChecked = wadbApplication.isBootCompletedReceiverEnabled() 152 | bootCompletedReceiverPreference.setOnPreferenceChangeListener { _, newValue -> 153 | if (newValue as Boolean) { 154 | wadbApplication.enableBootCompletedReceiver() 155 | } else { 156 | wadbApplication.disableBootCompletedReceiver() 157 | } 158 | true 159 | } 160 | 161 | findPreference(KEY_NOTIFICATION)!!.summary = 162 | getString(R.string.settings_show_notification_summary, getString(R.string.wireless_adb)) 163 | findPreference(KEY_WAKE_LOCK)!!.summary = 164 | getString(R.string.settings_keep_screen_on_summary, getString(R.string.wireless_adb)) 165 | } 166 | 167 | override fun onPause() { 168 | super.onPause() 169 | preferenceScreen.sharedPreferences!!.unregisterOnSharedPreferenceChangeListener(this) 170 | } 171 | 172 | override fun onResume() { 173 | super.onResume() 174 | preferenceScreen.sharedPreferences!!.registerOnSharedPreferenceChangeListener(this) 175 | } 176 | 177 | override fun onWadbStarted(port: Int) { 178 | val context = context ?: return 179 | 180 | val ipInfoList = NetworksUtils.getLocalIPInfo(context) 181 | 182 | // refresh switch 183 | togglePreference.isChecked = true 184 | when { 185 | ipInfoList.isEmpty() -> { 186 | ipPreference.summary = "" 187 | } 188 | ipInfoList.size == 1 -> { 189 | ipPreference.summary = "${ipInfoList[0].ip}:$port" 190 | } 191 | else -> { 192 | ipPreference.summary = ipInfoList.joinToString(separator = "\n") { 193 | val uiInterfaceName = when (it.interfaceName) { 194 | "wlan0" -> "WLAN" 195 | "wlan1" -> "\t\tAP\t\t" 196 | else -> it.interfaceName 197 | } 198 | "[${uiInterfaceName}]\t${it.ip}:$port" 199 | } 200 | } 201 | } 202 | // refresh port 203 | portPreference.text = port.toString() 204 | togglePreference.isEnabled = true 205 | portPreference.isEnabled = true 206 | ipPreference.isVisible = true 207 | } 208 | 209 | override fun onWadbStopped() { 210 | // refresh switch 211 | togglePreference.isChecked = false 212 | ipPreference.isVisible = false 213 | 214 | togglePreference.isEnabled = true 215 | portPreference.isEnabled = true 216 | } 217 | 218 | override fun onRootPermissionFailure() { 219 | val activity = activity 220 | if (activity == null || activity.isFinishing) { 221 | return 222 | } 223 | 224 | onWadbStopped() 225 | 226 | MaterialAlertDialogBuilder(activity) 227 | .setMessage(activity.getString(R.string.dialog_not_rooted_message)) 228 | .setPositiveButton(android.R.string.ok, null) 229 | .create() 230 | .show() 231 | } 232 | 233 | override fun onStart() { 234 | super.onStart() 235 | GlobalRequestHandler.checkWadbState() 236 | } 237 | 238 | override fun onSharedPreferenceChanged(preferences: SharedPreferences, key: String) { 239 | val context = requireContext() 240 | when (key) { 241 | // refresh notification when notification preferences are changed 242 | KEY_NOTIFICATION, KEY_NOTIFICATION_LOW_PRIORITY -> if (togglePreference.isChecked) { 243 | if (preferences.getBoolean(KEY_NOTIFICATION, true)) { 244 | GlobalRequestHandler.checkWadbState() 245 | } 246 | } else { 247 | NotificationHelper.cancelNotification(context) 248 | } 249 | KEY_WAKE_LOCK -> if (preferences.getBoolean(key, false) && togglePreference.isChecked) { 250 | ScreenKeeper.acquireWakeLock(context) 251 | } else { 252 | ScreenKeeper.releaseWakeLock() 253 | } 254 | } 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | --------------------------------------------------------------------------------