├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── libs │ ├── arm64-v8a │ │ └── libtun2socks.so │ ├── armeabi-v7a │ │ └── libtun2socks.so │ ├── x86 │ │ └── libtun2socks.so │ └── x86_64 │ │ └── libtun2socks.so ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── agn │ │ └── v2ray │ │ └── ApplicationTest.java │ ├── dev │ └── res │ │ └── values │ │ └── strings.xml │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── custom_routing_block │ │ ├── custom_routing_direct │ │ ├── custom_routing_proxy │ │ ├── proxy_packagename.txt │ │ └── v2ray_config.json │ ├── java │ │ └── com │ │ │ └── agn │ │ │ └── v2ray │ │ │ ├── ThemeSettings.java │ │ │ └── helper │ │ │ ├── ItemTouchHelperAdapter.java │ │ │ ├── ItemTouchHelperViewHolder.java │ │ │ ├── OnStartDragListener.java │ │ │ └── SimpleItemTouchHelperCallback.java │ ├── kotlin │ │ └── com │ │ │ └── agn │ │ │ └── v2ray │ │ │ ├── AngApplication.kt │ │ │ ├── AppConfig.kt │ │ │ ├── SplashActivity.kt │ │ │ ├── dto │ │ │ ├── AngConfig.kt │ │ │ ├── AppInfo.kt │ │ │ ├── EConfigType.kt │ │ │ ├── ERoutingMode.kt │ │ │ ├── ServerAffiliationInfo.kt │ │ │ ├── ServerConfig.kt │ │ │ ├── ServersCache.kt │ │ │ ├── SubscriptionItem.kt │ │ │ ├── V2rayConfig.kt │ │ │ └── VmessQRCode.kt │ │ │ ├── extension │ │ │ └── _Ext.kt │ │ │ ├── receiver │ │ │ ├── TaskerReceiver.kt │ │ │ └── WidgetProvider.kt │ │ │ ├── service │ │ │ ├── QSTileService.kt │ │ │ ├── ServiceControl.kt │ │ │ ├── SubscriptionUpdater.kt │ │ │ ├── V2RayProxyOnlyService.kt │ │ │ ├── V2RayServiceManager.kt │ │ │ ├── V2RayTestService.kt │ │ │ └── V2RayVpnService.kt │ │ │ ├── ui │ │ │ ├── AboutActivity.kt │ │ │ ├── BaseActivity.kt │ │ │ ├── FragmentAdapter.kt │ │ │ ├── LogcatActivity.kt │ │ │ ├── MainActivity.kt │ │ │ ├── MainRecyclerAdapter.kt │ │ │ ├── PerAppProxyActivity.kt │ │ │ ├── PerAppProxyAdapter.kt │ │ │ ├── RoutingSettingsActivity.kt │ │ │ ├── RoutingSettingsFragment.kt │ │ │ ├── ScScannerActivity.kt │ │ │ ├── ScSwitchActivity.kt │ │ │ ├── ScannerActivity.kt │ │ │ ├── ServerActivity.kt │ │ │ ├── ServerCustomConfigActivity.kt │ │ │ ├── SettingsActivity.kt │ │ │ ├── SubEditActivity.kt │ │ │ ├── SubSettingActivity.kt │ │ │ ├── SubSettingRecyclerAdapter.kt │ │ │ ├── TaskerActivity.kt │ │ │ ├── UrlSchemeActivity.kt │ │ │ └── UserAssetActivity.kt │ │ │ ├── util │ │ │ ├── AngConfigManager.kt │ │ │ ├── AppManagerUtil.kt │ │ │ ├── LanguageHelper.kt │ │ │ ├── LanguagePrefs.kt │ │ │ ├── MessageUtil.kt │ │ │ ├── MmkvManager.kt │ │ │ ├── MyContextWrapper.kt │ │ │ ├── QRCodeDecoder.kt │ │ │ ├── SpeedtestUtil.kt │ │ │ ├── Utils.kt │ │ │ └── V2rayConfigUtil.kt │ │ │ └── viewmodel │ │ │ ├── MainViewModel.kt │ │ │ └── SettingsViewModel.kt │ └── res │ │ ├── anim │ │ ├── fade_in.xml │ │ └── fade_out.xml │ │ ├── color │ │ └── color_highlight_material.xml │ │ ├── drawable-hdpi │ │ ├── ic_stat_direct.png │ │ ├── ic_stat_name.png │ │ └── ic_stat_proxy.png │ │ ├── drawable-ldrtl │ │ └── bg_nav.xml │ │ ├── drawable-mdpi │ │ ├── ic_stat_direct.png │ │ ├── ic_stat_name.png │ │ └── ic_stat_proxy.png │ │ ├── drawable-xhdpi │ │ ├── ic_stat_direct.png │ │ ├── ic_stat_name.png │ │ └── ic_stat_proxy.png │ │ ├── drawable-xxhdpi │ │ ├── donate.png │ │ ├── ic_stat_direct.png │ │ ├── ic_stat_name.png │ │ ├── ic_stat_proxy.png │ │ └── side_nav_bar.xml │ │ ├── drawable-xxxhdpi │ │ ├── ic_stat_direct.png │ │ ├── ic_stat_name.png │ │ └── ic_stat_proxy.png │ │ ├── drawable │ │ ├── autorenew.xml │ │ ├── background_test_button.xml │ │ ├── baseline_privacy_tip_24.xml │ │ ├── bg_nav.xml │ │ ├── ic_action_done.xml │ │ ├── ic_action_done_white.xml │ │ ├── ic_add_black_24dp.xml │ │ ├── ic_add_white_24dp.xml │ │ ├── ic_attach_money_black_24dp.xml │ │ ├── ic_attach_money_white_24dp.xml │ │ ├── ic_autorenew_black_24dp.png │ │ ├── ic_baseline_add_24.xml │ │ ├── ic_baseline_arrow_back_24.xml │ │ ├── ic_baseline_filter_alt_24.xml │ │ ├── ic_close_grey_800_24dp.xml │ │ ├── ic_cloud_download_white_24dp.xml │ │ ├── ic_copy_white.xml │ │ ├── ic_delete_black_24dp.xml │ │ ├── ic_delete_white_24dp.xml │ │ ├── ic_description_black_24dp.xml │ │ ├── ic_description_white_24dp.xml │ │ ├── ic_edit_black_24dp.xml │ │ ├── ic_fab_check.xml │ │ ├── ic_fab_uncheck.png │ │ ├── ic_feedback_white_24dp.xml │ │ ├── ic_file_white_24dp.xml │ │ ├── ic_image_photo.xml │ │ ├── ic_info.xml │ │ ├── ic_info_black_24dp.xml │ │ ├── ic_logcat_white_24dp.xml │ │ ├── ic_outline_filter_alt_white_24.xml │ │ ├── ic_person.xml │ │ ├── ic_plane.xml │ │ ├── ic_qu_scan_black_24dp.xml │ │ ├── ic_qu_settings_black_24dp.xml │ │ ├── ic_qu_switch_black_24dp.xml │ │ ├── ic_rounded_corner_grey.xml │ │ ├── ic_rounded_corner_theme.xml │ │ ├── ic_save_white_24dp.xml │ │ ├── ic_scan_black_24dp.xml │ │ ├── ic_select_all_white_24dp.xml │ │ ├── ic_settings_white_24dp.xml │ │ ├── ic_share_black_24dp.xml │ │ ├── ic_share_white_24dp.xml │ │ ├── ic_shortcut_background.xml │ │ ├── ic_start_busy.xml │ │ ├── ic_start_connected.xml │ │ ├── ic_start_connected_black.xml │ │ ├── ic_start_idle.xml │ │ ├── ic_stat_direct.png │ │ ├── ic_stat_proxy.png │ │ ├── ic_subscriptions_black_24dp.xml │ │ ├── ic_subscriptions_white_24dp.xml │ │ ├── ic_v.png │ │ ├── ic_v_connected_black.xml │ │ ├── ic_v_idle.png │ │ ├── ic_whatshot_black_24dp.xml │ │ ├── ic_whatshot_white_24dp.xml │ │ ├── ic_youtube.png │ │ ├── icon.png │ │ └── nav_header_bg.png │ │ ├── font │ │ └── poppins_medium.ttf │ │ ├── layout │ │ ├── .activity_main.xml.kate-swp │ │ ├── activity_about.xml │ │ ├── activity_bypass_list.xml │ │ ├── activity_logcat.xml │ │ ├── activity_main.xml │ │ ├── activity_none.xml │ │ ├── activity_routing_settings.xml │ │ ├── activity_server_custom_config.xml │ │ ├── activity_server_shadowsocks.xml │ │ ├── activity_server_socks.xml │ │ ├── activity_server_trojan.xml │ │ ├── activity_server_vless.xml │ │ ├── activity_server_vmess.xml │ │ ├── activity_settings.xml │ │ ├── activity_splash.xml │ │ ├── activity_sub_edit.xml │ │ ├── activity_sub_setting.xml │ │ ├── activity_tasker.xml │ │ ├── daynightswitch.xml │ │ ├── dialog_config_filter.xml │ │ ├── fragment_routing_settings.xml │ │ ├── item_qrcode.xml │ │ ├── item_recycler_bypass_list.xml │ │ ├── item_recycler_footer.xml │ │ ├── item_recycler_main.xml │ │ ├── item_recycler_sub_setting.xml │ │ ├── item_recycler_user_asset.xml │ │ ├── nav_header.xml │ │ ├── nav_toolbar.xml │ │ ├── nav_view.xml │ │ ├── preference_with_help_link.xml │ │ ├── tls_layout.xml │ │ └── widget_switch.xml │ │ ├── menu │ │ ├── action_server.xml │ │ ├── action_sub_setting.xml │ │ ├── menu_asset.xml │ │ ├── menu_bypass_list.xml │ │ ├── menu_drawer.xml │ │ ├── menu_logcat.xml │ │ ├── menu_main.xml │ │ ├── menu_routing.xml │ │ └── menu_scanner.xml │ │ ├── raw │ │ └── licenses.xml │ │ ├── values-ar │ │ └── strings.xml │ │ ├── values-de │ │ └── strings.xml │ │ ├── values-es │ │ └── strings.xml │ │ ├── values-fa │ │ └── strings.xml │ │ ├── values-fr │ │ └── strings.xml │ │ ├── values-lt │ │ └── strings.xml │ │ ├── values-night │ │ └── colors.xml │ │ ├── values-pt │ │ └── strings.xml │ │ ├── values-ru │ │ └── strings.xml │ │ ├── values-sw360dp-v13 │ │ └── values-preference.xml │ │ ├── values-th │ │ └── strings.xml │ │ ├── values-vi │ │ └── strings.xml │ │ ├── values-zh-rCN │ │ └── strings.xml │ │ ├── values-zh-rTW │ │ └── strings.xml │ │ ├── values │ │ ├── arrays.xml │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ ├── app_widget_provider.xml │ │ ├── pref_settings.xml │ │ └── shortcuts.xml │ └── test │ ├── java │ └── com │ │ └── agn │ │ └── v2ray │ │ └── ExampleUnitTest.java │ └── kotlin │ └── com │ └── agn │ └── v2ray │ └── ExampleUnitTest.kt ├── build.gradle ├── daynightswitch ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── agn │ │ └── daynightswitch │ │ ├── DayNightSwitch.java │ │ ├── DayNightSwitchAnimListener.java │ │ └── DayNightSwitchListener.java │ └── res │ ├── drawable-hdpi │ ├── dark_background.png │ ├── img_clouds.png │ ├── img_moon.png │ └── img_sun.png │ ├── drawable-ldpi │ ├── dark_background.png │ ├── img_clouds.png │ ├── img_moon.png │ └── img_sun.png │ ├── drawable-mdpi │ ├── dark_background.png │ ├── img_clouds.png │ ├── img_moon.png │ └── img_sun.png │ ├── drawable-xhdpi │ ├── dark_background.png │ ├── img_clouds.png │ ├── img_moon.png │ └── img_sun.png │ ├── drawable-xxhdpi │ ├── dark_background.png │ ├── img_clouds.png │ ├── img_moon.png │ └── img_sun.png │ ├── drawable-xxxhdpi │ ├── dark_background.png │ ├── img_clouds.png │ ├── img_moon.png │ └── img_sun.png │ └── values │ ├── colors.xml │ └── strings.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | *.apk 9 | signing.properties 10 | *.aar 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # V2rayAGN - Enhanced V2Ray Client for Android 2 | 3 | Static Badge 4 | 5 | #### Introduction 6 | 7 | Welcome to V2rayAGN, an advanced and user-friendly V2Ray client for Android devices, based on the popular [v2rayNG](https://github.com/2dust/v2rayNG) codebase. Our app is designed to provide a seamless and secure browsing experience, now with added features and improvements for an even better user experience. 8 | 9 | #### Features 10 | 11 | - Dark Mode: A sleek and eye-friendly interface for nighttime browsing. 12 | - Improved Language Selection: Enhanced multilingual support for a global user base. 13 | - Beautiful UI: A revamped, modern, and intuitive user interface. 14 | - Core V2Ray Functionality: All the robust features from v2rayNG, including VMess, Shadowsocks, and more. 15 | - Play Store Availability: Easy to download and update through the Google Play Store. 16 | 17 | 18 | # Getting Started 19 | 20 | #### Prerequisites 21 | 22 | - Android 5.0 or higher. 23 | - Active internet connection. 24 | 25 | #### Installation 26 | 27 | Download the app directly from the Google Play Store: 28 | 29 |

30 | 31 |

32 | 33 | #### Usage 34 | 35 | - Open V2rayAGN. 36 | - Configure your server settings or import from a pre-existing configuration. 37 | - Connect and enjoy secure and private browsing. 38 | 39 | # Feedback and Support 40 | 41 | For support or bug reporting, please open an issue in the GitHub repository. 42 | 43 | 44 | # License 45 | 46 | This project is licensed under [GPL-3.0] - see the [LICENSE](https://github.com/khaledagn/V2rayAGN/blob/main/LICENSE) file for details. 47 | 48 | 49 | # Acknowledgments 50 | 51 | Thanks to the creators of [v2rayNG](https://github.com/2dust/v2rayNG) for their foundational work. 52 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /google-services.json 3 | -------------------------------------------------------------------------------- /app/libs/arm64-v8a/libtun2socks.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/libs/arm64-v8a/libtun2socks.so -------------------------------------------------------------------------------- /app/libs/armeabi-v7a/libtun2socks.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/libs/armeabi-v7a/libtun2socks.so -------------------------------------------------------------------------------- /app/libs/x86/libtun2socks.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/libs/x86/libtun2socks.so -------------------------------------------------------------------------------- /app/libs/x86_64/libtun2socks.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/libs/x86_64/libtun2socks.so -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/proguard-rules.pro -------------------------------------------------------------------------------- /app/src/androidTest/java/com/agn/v2ray/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/dev/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | V2rayAGN 4 | -------------------------------------------------------------------------------- /app/src/main/assets/custom_routing_block: -------------------------------------------------------------------------------- 1 | geosite:category-ads-all, -------------------------------------------------------------------------------- /app/src/main/assets/custom_routing_direct: -------------------------------------------------------------------------------- 1 | domain:12306.com, 2 | domain:51ym.me, 3 | domain:52pojie.cn, 4 | domain:8686c.com, 5 | domain:abercrombie.com, 6 | domain:adobesc.com, 7 | domain:air-matters.com, 8 | domain:air-matters.io, 9 | domain:airtable.com, 10 | domain:akadns.net, 11 | domain:apache.org, 12 | domain:api.crisp.chat, 13 | domain:api.termius.com, 14 | domain:appshike.com, 15 | domain:appstore.com, 16 | domain:aweme.snssdk.com, 17 | domain:bababian.com, 18 | domain:battle.net, 19 | domain:beatsbydre.com, 20 | domain:bet365.com, 21 | domain:bilibili.cn, 22 | domain:ccgslb.com, 23 | domain:ccgslb.net, 24 | domain:chunbo.com, 25 | domain:chunboimg.com, 26 | domain:clashroyaleapp.com, 27 | domain:cloudsigma.com, 28 | domain:cloudxns.net, 29 | domain:cmfu.com, 30 | domain:culturedcode.com, 31 | domain:dct-cloud.com, 32 | domain:didialift.com, 33 | domain:douyutv.com, 34 | domain:duokan.com, 35 | domain:dytt8.net, 36 | domain:easou.com, 37 | domain:ecitic.net, 38 | domain:eclipse.org, 39 | domain:eudic.net, 40 | domain:ewqcxz.com, 41 | domain:fir.im, 42 | domain:frdic.com, 43 | domain:fresh-ideas.cc, 44 | domain:godic.net, 45 | domain:goodread.com, 46 | domain:haibian.com, 47 | domain:hdslb.net, 48 | domain:hollisterco.com, 49 | domain:hongxiu.com, 50 | domain:hxcdn.net, 51 | domain:images.unsplash.com, 52 | domain:img4me.com, 53 | domain:ipify.org, 54 | domain:ixdzs.com, 55 | domain:jd.hk, 56 | domain:jianshuapi.com, 57 | domain:jomodns.com, 58 | domain:jsboxbbs.com, 59 | domain:knewone.com, 60 | domain:kuaidi100.com, 61 | domain:lemicp.com, 62 | domain:letvcloud.com, 63 | domain:lizhi.io, 64 | domain:localizecdn.com, 65 | domain:lucifr.com, 66 | domain:luoo.net, 67 | domain:mai.tn, 68 | domain:maven.org, 69 | domain:miwifi.com, 70 | domain:moji.com, 71 | domain:moke.com, 72 | domain:mtalk.google.com, 73 | domain:mxhichina.com, 74 | domain:myqcloud.com, 75 | domain:myunlu.com, 76 | domain:netease.com, 77 | domain:nfoservers.com, 78 | domain:nssurge.com, 79 | domain:nuomi.com, 80 | domain:ourdvs.com, 81 | domain:overcast.fm, 82 | domain:paypal.com, 83 | domain:paypalobjects.com, 84 | domain:pgyer.com, 85 | domain:qdaily.com, 86 | domain:qdmm.com, 87 | domain:qin.io, 88 | domain:qingmang.me, 89 | domain:qingmang.mobi, 90 | domain:qqurl.com, 91 | domain:rarbg.to, 92 | domain:rrmj.tv, 93 | domain:ruguoapp.com, 94 | domain:sm.ms, 95 | domain:snwx.com, 96 | domain:soku.com, 97 | domain:startssl.com, 98 | domain:store.steampowered.com, 99 | domain:symcd.com, 100 | domain:teamviewer.com, 101 | domain:tmzvps.com, 102 | domain:trello.com, 103 | domain:trellocdn.com, 104 | domain:ttmeiju.com, 105 | domain:udache.com, 106 | domain:uxengine.net, 107 | domain:weather.bjango.com, 108 | domain:weather.com, 109 | domain:webqxs.com, 110 | domain:weico.cc, 111 | domain:wenku8.net, 112 | domain:werewolf.53site.com, 113 | domain:windowsupdate.com, 114 | domain:wkcdn.com, 115 | domain:workflowy.com, 116 | domain:xdrig.com, 117 | domain:xiaojukeji.com, 118 | domain:xiaomi.net, 119 | domain:xiaomicp.com, 120 | domain:ximalaya.com, 121 | domain:xitek.com, 122 | domain:xmcdn.com, 123 | domain:xslb.net, 124 | domain:xteko.com, 125 | domain:yach.me, 126 | domain:yixia.com, 127 | domain:yunjiasu-cdn.net, 128 | domain:zealer.com, 129 | domain:zgslb.net, 130 | domain:zimuzu.tv, 131 | domain:zmz002.com, 132 | domain:samsungdm.com, -------------------------------------------------------------------------------- /app/src/main/assets/custom_routing_proxy: -------------------------------------------------------------------------------- 1 | geosite:google, 2 | geosite:github, 3 | geosite:netflix, 4 | geosite:steam, 5 | geosite:telegram, 6 | geosite:tumblr, 7 | geosite:speedtest, 8 | geosite:bbc, 9 | domain:gvt1.com, 10 | domain:textnow.com, 11 | domain:twitch.tv, 12 | domain:wikileaks.org, 13 | domain:naver.com, 14 | 91.108.4.0/22, 15 | 91.108.8.0/22, 16 | 91.108.12.0/22, 17 | 91.108.20.0/22, 18 | 91.108.36.0/23, 19 | 91.108.38.0/23, 20 | 91.108.56.0/22, 21 | 149.154.160.0/20, 22 | 149.154.164.0/22, 23 | 149.154.172.0/22, 24 | 74.125.0.0/16, 25 | 173.194.0.0/16, 26 | 172.217.0.0/16, 27 | 216.58.200.0/24, 28 | 216.58.220.0/24, 29 | 91.108.56.116, 30 | 91.108.56.0/24, 31 | 109.239.140.0/24, 32 | 149.154.167.0/24, 33 | 149.154.175.0/24, -------------------------------------------------------------------------------- /app/src/main/assets/v2ray_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "stats":{}, 3 | "log": { 4 | "loglevel": "warning" 5 | }, 6 | "policy":{ 7 | "levels": { 8 | "8": { 9 | "handshake": 4, 10 | "connIdle": 300, 11 | "uplinkOnly": 1, 12 | "downlinkOnly": 1 13 | } 14 | }, 15 | "system": { 16 | "statsOutboundUplink": true, 17 | "statsOutboundDownlink": true 18 | } 19 | }, 20 | "inbounds": [{ 21 | "tag": "socks", 22 | "port": 10808, 23 | "protocol": "socks", 24 | "settings": { 25 | "auth": "noauth", 26 | "udp": true, 27 | "userLevel": 8 28 | }, 29 | "sniffing": { 30 | "enabled": true, 31 | "destOverride": [ 32 | "http", 33 | "tls" 34 | ] 35 | } 36 | }, 37 | { 38 | "tag": "http", 39 | "port": 10809, 40 | "protocol": "http", 41 | "settings": { 42 | "userLevel": 8 43 | } 44 | } 45 | ], 46 | "outbounds": [{ 47 | "tag": "proxy", 48 | "protocol": "vmess", 49 | "settings": { 50 | "vnext": [ 51 | { 52 | "address": "v2ray.cool", 53 | "port": 10086, 54 | "users": [ 55 | { 56 | "id": "a3482e88-686a-4a58-8126-99c9df64b7bf", 57 | "alterId": 0, 58 | "security": "auto", 59 | "level": 8 60 | } 61 | ] 62 | } 63 | ], 64 | "servers": [ 65 | { 66 | "address": "v2ray.cool", 67 | "method": "chacha20", 68 | "ota": false, 69 | "password": "123456", 70 | "port": 10086, 71 | "level": 8 72 | } 73 | ] 74 | }, 75 | "streamSettings": { 76 | "network": "tcp" 77 | }, 78 | "mux": { 79 | "enabled": false 80 | } 81 | }, 82 | { 83 | "protocol": "freedom", 84 | "settings": {}, 85 | "tag": "direct" 86 | }, 87 | { 88 | "protocol": "blackhole", 89 | "tag": "block", 90 | "settings": { 91 | "response": { 92 | "type": "http" 93 | } 94 | } 95 | } 96 | ], 97 | "routing": { 98 | "domainStrategy": "IPIfNonMatch", 99 | "rules": [] 100 | }, 101 | "dns": { 102 | "hosts": {}, 103 | "servers": [] 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /app/src/main/java/com/agn/v2ray/ThemeSettings.java: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import androidx.appcompat.app.AppCompatDelegate; 6 | 7 | 8 | public class ThemeSettings { 9 | 10 | private static ThemeSettings instance; 11 | 12 | public static ThemeSettings getInstance(Context context) { 13 | if (instance == null) instance = new ThemeSettings(context); 14 | return instance; 15 | } 16 | 17 | private static class Key { 18 | private static final String NIGHT_MODE = "nightMode"; 19 | } 20 | 21 | public boolean nightMode; 22 | 23 | private ThemeSettings(Context context) { 24 | SharedPreferences prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE); 25 | nightMode = prefs.getBoolean(Key.NIGHT_MODE, false); 26 | } 27 | 28 | public void save(Context context) { 29 | SharedPreferences.Editor editor = context.getSharedPreferences("settings", Context.MODE_PRIVATE).edit(); 30 | editor.putBoolean(Key.NIGHT_MODE, nightMode); 31 | editor.apply(); 32 | } 33 | 34 | public void refreshTheme() { 35 | AppCompatDelegate.setDefaultNightMode(nightMode ? 36 | AppCompatDelegate.MODE_NIGHT_YES : 37 | AppCompatDelegate.MODE_NIGHT_NO); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/agn/v2ray/helper/ItemTouchHelperAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Paul Burke 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.agn.v2ray.helper; 18 | 19 | import androidx.recyclerview.widget.RecyclerView; 20 | import androidx.recyclerview.widget.ItemTouchHelper; 21 | 22 | /** 23 | * Interface to listen for a move or dismissal event from a {@link ItemTouchHelper.Callback}. 24 | * 25 | * @author Paul Burke (ipaulpro) 26 | */ 27 | public interface ItemTouchHelperAdapter { 28 | 29 | /** 30 | * Called when an item has been dragged far enough to trigger a move. This is called every time 31 | * an item is shifted, and not at the end of a "drop" event.
32 | *
33 | * Implementations should call {@link RecyclerView.Adapter#notifyItemMoved(int, int)} after 34 | * adjusting the underlying data to reflect this move. 35 | * 36 | * @param fromPosition The start position of the moved item. 37 | * @param toPosition Then resolved position of the moved item. 38 | * @return True if the item was moved to the new adapter position. 39 | * 40 | * @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder) 41 | * @see RecyclerView.ViewHolder#getAdapterPosition() 42 | */ 43 | boolean onItemMove(int fromPosition, int toPosition); 44 | 45 | 46 | void onItemMoveCompleted(); 47 | 48 | /** 49 | * Called when an item has been dismissed by a swipe.
50 | *
51 | * Implementations should call {@link RecyclerView.Adapter#notifyItemRemoved(int)} after 52 | * adjusting the underlying data to reflect this removal. 53 | * 54 | * @param position The position of the item dismissed. 55 | * 56 | * @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder) 57 | * @see RecyclerView.ViewHolder#getAdapterPosition() 58 | */ 59 | void onItemDismiss(int position); 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/agn/v2ray/helper/ItemTouchHelperViewHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Paul Burke 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.agn.v2ray.helper; 18 | 19 | import androidx.recyclerview.widget.ItemTouchHelper; 20 | 21 | /** 22 | * Interface to notify an item ViewHolder of relevant callbacks from {@link 23 | * ItemTouchHelper.Callback}. 24 | * 25 | * @author Paul Burke (ipaulpro) 26 | */ 27 | public interface ItemTouchHelperViewHolder { 28 | 29 | /** 30 | * Called when the {@link ItemTouchHelper} first registers an item as being moved or swiped. 31 | * Implementations should update the item view to indicate it's active state. 32 | */ 33 | void onItemSelected(); 34 | 35 | 36 | /** 37 | * Called when the {@link ItemTouchHelper} has completed the move or swipe, and the active item 38 | * state should be cleared. 39 | */ 40 | void onItemClear(); 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/agn/v2ray/helper/OnStartDragListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Paul Burke 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.agn.v2ray.helper; 18 | 19 | import androidx.recyclerview.widget.RecyclerView; 20 | 21 | /** 22 | * Listener for manual initiation of a drag. 23 | */ 24 | public interface OnStartDragListener { 25 | 26 | /** 27 | * Called when a view is requesting a start of a drag. 28 | * 29 | * @param viewHolder The holder of the view to drag. 30 | */ 31 | void onStartDrag(RecyclerView.ViewHolder viewHolder); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/AngApplication.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray 2 | 3 | import android.content.Context 4 | import androidx.multidex.MultiDexApplication 5 | import androidx.preference.PreferenceManager 6 | import androidx.work.Configuration 7 | import com.agn.v2ray.util.MyContextWrapper 8 | import com.tencent.mmkv.MMKV 9 | 10 | class AngApplication : MultiDexApplication(), Configuration.Provider { 11 | companion object { 12 | const val PREF_LAST_VERSION = "pref_last_version" 13 | lateinit var application: AngApplication 14 | } 15 | 16 | override fun attachBaseContext(newBase: Context) { 17 | val context = MyContextWrapper.wrap(newBase) 18 | application = this 19 | super.attachBaseContext(context) 20 | } 21 | 22 | 23 | var firstRun = false 24 | private set 25 | 26 | override fun onCreate() { 27 | super.onCreate() 28 | ThemeSettings.getInstance(this).refreshTheme(); 29 | 30 | val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) 31 | firstRun = defaultSharedPreferences.getInt(PREF_LAST_VERSION, 0) != BuildConfig.VERSION_CODE 32 | if (firstRun) 33 | defaultSharedPreferences.edit().putInt(PREF_LAST_VERSION, BuildConfig.VERSION_CODE) 34 | .apply() 35 | 36 | //Logger.init().logLevel(if (BuildConfig.DEBUG) LogLevel.FULL else LogLevel.NONE) 37 | MMKV.initialize(this) 38 | 39 | } 40 | 41 | override fun getWorkManagerConfiguration(): Configuration { 42 | return Configuration.Builder() 43 | .setDefaultProcessName("${BuildConfig.APPLICATION_ID}:bg") 44 | .build() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/SplashActivity.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Intent 5 | import android.content.SharedPreferences 6 | import android.os.Bundle 7 | import android.os.Handler 8 | import android.os.Looper 9 | import android.widget.TextView 10 | import androidx.appcompat.app.AppCompatActivity 11 | import com.agn.v2ray.databinding.ActivitySplashBinding 12 | import com.agn.v2ray.ui.MainActivity 13 | 14 | @SuppressLint("CustomSplashScreen") 15 | class SplashActivity : AppCompatActivity() { 16 | override fun onCreate(savedInstanceState: Bundle?) { 17 | super.onCreate(savedInstanceState) 18 | setContentView(R.layout.activity_splash) 19 | supportActionBar?.hide() 20 | Handler(Looper.getMainLooper()).postDelayed({ 21 | 22 | startActivity(Intent(this, MainActivity::class.java)) 23 | 24 | finish() 25 | }, 2000) 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/dto/AngConfig.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.dto 2 | 3 | data class AngConfig( 4 | var index: Int, 5 | var vmess: ArrayList, 6 | var subItem: ArrayList 7 | ) { 8 | data class VmessBean(var guid: String = "123456", 9 | var address: String = "v2ray.cool", 10 | var port: Int = 10086, 11 | var id: String = "a3482e88-686a-4a58-8126-99c9df64b7bf", 12 | var alterId: Int = 64, 13 | var security: String = "aes-128-cfb", 14 | var network: String = "tcp", 15 | var remarks: String = "def", 16 | var headerType: String = "", 17 | var requestHost: String = "", 18 | var path: String = "", 19 | var streamSecurity: String = "", 20 | var allowInsecure: String = "", 21 | var configType: Int = 1, 22 | var configVersion: Int = 1, 23 | var testResult: String = "", 24 | var subid: String = "", 25 | var flow: String = "", 26 | var sni: String = "") 27 | 28 | data class SubItemBean(var id: String = "", 29 | var remarks: String = "", 30 | var url: String = "", 31 | var enabled: Boolean = true) 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/dto/AppInfo.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.dto 2 | 3 | import android.graphics.drawable.Drawable 4 | 5 | data class AppInfo(val appName: String, 6 | val packageName: String, 7 | val appIcon: Drawable, 8 | val isSystemApp: Boolean, 9 | var isSelected: Int) -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/dto/EConfigType.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.dto 2 | 3 | enum class EConfigType(val value: Int, val protocolScheme: String) { 4 | VMESS(1, "vmess://"), 5 | CUSTOM(2, ""), 6 | SHADOWSOCKS(3, "ss://"), 7 | SOCKS(4, "socks://"), 8 | VLESS(5, "vless://"), 9 | TROJAN(6, "trojan://"), 10 | WIREGUARD(7, "wireguard://"); 11 | 12 | companion object { 13 | fun fromInt(value: Int) = values().firstOrNull { it.value == value } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/dto/ERoutingMode.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.dto 2 | 3 | enum class ERoutingMode(val value: String ) { 4 | GLOBAL_PROXY("0"), 5 | BYPASS_LAN("1"), 6 | BYPASS_MAINLAND("2"), 7 | BYPASS_LAN_MAINLAND("3"), 8 | GLOBAL_DIRECT("4"); 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/dto/ServerAffiliationInfo.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.dto 2 | 3 | data class ServerAffiliationInfo(var testDelayMillis: Long = 0L) { 4 | fun getTestDelayString(): String { 5 | if (testDelayMillis == 0L) { 6 | return "" 7 | } 8 | return testDelayMillis.toString() + "ms" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/dto/ServerConfig.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.dto 2 | 3 | import com.agn.v2ray.AppConfig.TAG_AGENT 4 | import com.agn.v2ray.AppConfig.TAG_BLOCKED 5 | import com.agn.v2ray.AppConfig.TAG_DIRECT 6 | import com.agn.v2ray.util.Utils 7 | 8 | data class ServerConfig( 9 | val configVersion: Int = 3, 10 | val configType: EConfigType, 11 | var subscriptionId: String = "", 12 | val addedTime: Long = System.currentTimeMillis(), 13 | var remarks: String = "", 14 | val outboundBean: V2rayConfig.OutboundBean? = null, 15 | var fullConfig: V2rayConfig? = null 16 | ) { 17 | companion object { 18 | fun create(configType: EConfigType): ServerConfig { 19 | when(configType) { 20 | EConfigType.VMESS, EConfigType.VLESS -> 21 | return ServerConfig( 22 | configType = configType, 23 | outboundBean = V2rayConfig.OutboundBean( 24 | protocol = configType.name.lowercase(), 25 | settings = V2rayConfig.OutboundBean.OutSettingsBean( 26 | vnext = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean( 27 | users = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean.UsersBean())))), 28 | streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean())) 29 | EConfigType.CUSTOM, EConfigType.WIREGUARD -> 30 | return ServerConfig(configType = configType) 31 | EConfigType.SHADOWSOCKS, EConfigType.SOCKS, EConfigType.TROJAN -> 32 | return ServerConfig( 33 | configType = configType, 34 | outboundBean = V2rayConfig.OutboundBean( 35 | protocol = configType.name.lowercase(), 36 | settings = V2rayConfig.OutboundBean.OutSettingsBean( 37 | servers = listOf(V2rayConfig.OutboundBean.OutSettingsBean.ServersBean())), 38 | streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean())) 39 | } 40 | } 41 | } 42 | 43 | fun getProxyOutbound(): V2rayConfig.OutboundBean? { 44 | if (configType != EConfigType.CUSTOM) { 45 | return outboundBean 46 | } 47 | return fullConfig?.getProxyOutbound() 48 | } 49 | 50 | fun getAllOutboundTags(): MutableList { 51 | if (configType != EConfigType.CUSTOM) { 52 | return mutableListOf(TAG_AGENT, TAG_DIRECT, TAG_BLOCKED) 53 | } 54 | fullConfig?.let { config -> 55 | return config.outbounds.map { it.tag }.toMutableList() 56 | } 57 | return mutableListOf() 58 | } 59 | 60 | fun getV2rayPointDomainAndPort(): String { 61 | val address = getProxyOutbound()?.getServerAddress().orEmpty() 62 | val port = getProxyOutbound()?.getServerPort() 63 | return if (Utils.isIpv6Address(address)) { 64 | String.format("[%s]:%s", address, port) 65 | } else { 66 | String.format("%s:%s", address, port) 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/dto/ServersCache.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.dto 2 | 3 | data class ServersCache(val guid: String, 4 | val config: ServerConfig) -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/dto/SubscriptionItem.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.dto 2 | 3 | data class SubscriptionItem( 4 | var remarks: String = "", 5 | var url: String = "", 6 | var enabled: Boolean = true, 7 | val addedTime: Long = System.currentTimeMillis(), 8 | var lastUpdated: Long = -1, 9 | var autoUpdate: Boolean = false, 10 | val updateInterval: Int? = null, 11 | ) -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/dto/VmessQRCode.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.dto 2 | 3 | data class VmessQRCode(var v: String = "", 4 | var ps: String = "", 5 | var add: String = "", 6 | var port: String = "", 7 | var id: String = "", 8 | var aid: String = "0", 9 | var scy: String = "", 10 | var net: String = "", 11 | var type: String = "", 12 | var host: String = "", 13 | var path: String = "", 14 | var tls: String = "", 15 | var sni: String = "", 16 | var alpn: String = "", 17 | var fp: String = "") -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/extension/_Ext.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.extension 2 | 3 | import android.content.Context 4 | import android.os.Build 5 | import android.widget.Toast 6 | import com.agn.v2ray.AngApplication 7 | import me.drakeet.support.toast.ToastCompat 8 | import org.json.JSONObject 9 | import java.net.URI 10 | import java.net.URLConnection 11 | 12 | /** 13 | * Some extensions 14 | */ 15 | 16 | val Context.v2RayApplication: AngApplication 17 | get() = applicationContext as AngApplication 18 | 19 | fun Context.toast(message: Int): Toast = ToastCompat 20 | .makeText(this, message, Toast.LENGTH_SHORT) 21 | .apply { 22 | show() 23 | } 24 | 25 | fun Context.toast(message: CharSequence): Toast = ToastCompat 26 | .makeText(this, message, Toast.LENGTH_SHORT) 27 | .apply { 28 | show() 29 | } 30 | 31 | fun JSONObject.putOpt(pair: Pair) = putOpt(pair.first, pair.second) 32 | fun JSONObject.putOpt(pairs: Map) = pairs.forEach { putOpt(it.key to it.value) } 33 | 34 | const val threshold = 1000 35 | const val divisor = 1024F 36 | 37 | fun Long.toSpeedString() = toTrafficString() + "/s" 38 | 39 | fun Long.toTrafficString(): String { 40 | if (this == 0L) 41 | return "\t\t\t0\t B" 42 | 43 | if (this < threshold) 44 | return "${this.toFloat().toShortString()}\t B" 45 | 46 | val kib = this / divisor 47 | if (kib < threshold) 48 | return "${kib.toShortString()}\t KB" 49 | 50 | val mib = kib / divisor 51 | if (mib < threshold) 52 | return "${mib.toShortString()}\t MB" 53 | 54 | val gib = mib / divisor 55 | if (gib < threshold) 56 | return "${gib.toShortString()}\t GB" 57 | 58 | val tib = gib / divisor 59 | if (tib < threshold) 60 | return "${tib.toShortString()}\t TB" 61 | 62 | val pib = tib / divisor 63 | if (pib < threshold) 64 | return "${pib.toShortString()}\t PB" 65 | 66 | return "∞" 67 | } 68 | 69 | private fun Float.toShortString(): String { 70 | val s = "%.2f".format(this) 71 | if (s.length <= 4) 72 | return s 73 | return s.substring(0, 4).removeSuffix(".") 74 | } 75 | 76 | val URLConnection.responseLength: Long 77 | get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) contentLengthLong else contentLength.toLong() 78 | 79 | val URI.idnHost: String 80 | get() = (host!!).replace("[", "").replace("]", "") -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/receiver/TaskerReceiver.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.receiver 2 | 3 | import android.content.BroadcastReceiver 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.text.TextUtils 7 | import com.google.zxing.WriterException 8 | import com.tencent.mmkv.MMKV 9 | import com.agn.v2ray.AppConfig 10 | import com.agn.v2ray.service.V2RayServiceManager 11 | import com.agn.v2ray.util.MmkvManager 12 | 13 | import com.agn.v2ray.util.Utils 14 | 15 | class TaskerReceiver : BroadcastReceiver() { 16 | private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) } 17 | 18 | override fun onReceive(context: Context, intent: Intent?) { 19 | 20 | try { 21 | val bundle = intent?.getBundleExtra(AppConfig.TASKER_EXTRA_BUNDLE) 22 | val switch = bundle?.getBoolean(AppConfig.TASKER_EXTRA_BUNDLE_SWITCH, false) 23 | val guid = bundle?.getString(AppConfig.TASKER_EXTRA_BUNDLE_GUID, "") 24 | 25 | if (switch == null || guid == null || TextUtils.isEmpty(guid)) { 26 | return 27 | } else if (switch) { 28 | if (guid == AppConfig.TASKER_DEFAULT_GUID) { 29 | Utils.startVServiceFromToggle(context) 30 | } else { 31 | mainStorage?.encode(MmkvManager.KEY_SELECTED_SERVER, guid) 32 | V2RayServiceManager.startV2Ray(context) 33 | } 34 | } else { 35 | Utils.stopVService(context) 36 | } 37 | } catch (e: Exception) { 38 | e.printStackTrace() 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/receiver/WidgetProvider.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.receiver 2 | 3 | import android.app.PendingIntent 4 | import android.appwidget.AppWidgetManager 5 | import android.appwidget.AppWidgetProvider 6 | import android.content.ComponentName 7 | import android.content.Context 8 | import android.content.Intent 9 | import android.os.Build 10 | import android.widget.RemoteViews 11 | import com.agn.v2ray.R 12 | import com.agn.v2ray.AppConfig 13 | import com.agn.v2ray.service.V2RayServiceManager 14 | import com.agn.v2ray.util.Utils 15 | 16 | class WidgetProvider : AppWidgetProvider() { 17 | /** 18 | * 每次窗口小部件被更新都调用一次该方法 19 | */ 20 | override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) { 21 | super.onUpdate(context, appWidgetManager, appWidgetIds) 22 | updateWidgetBackground(context, appWidgetManager, appWidgetIds, V2RayServiceManager.v2rayPoint.isRunning) 23 | } 24 | 25 | 26 | private fun updateWidgetBackground(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray, isRunning: Boolean) { 27 | val remoteViews = RemoteViews(context.packageName, R.layout.widget_switch) 28 | val intent = Intent(context, WidgetProvider::class.java) 29 | intent.action = AppConfig.BROADCAST_ACTION_WIDGET_CLICK 30 | val pendingIntent = PendingIntent.getBroadcast( 31 | context, 32 | R.id.layout_switch, 33 | intent, 34 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 35 | PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT 36 | } else { 37 | PendingIntent.FLAG_UPDATE_CURRENT 38 | }) 39 | remoteViews.setOnClickPendingIntent(R.id.layout_switch, pendingIntent) 40 | if (isRunning) { 41 | remoteViews.setInt( 42 | R.id.layout_switch, 43 | "setBackgroundResource", 44 | R.drawable.ic_rounded_corner_theme 45 | ) 46 | } else { 47 | remoteViews.setInt( 48 | R.id.layout_switch, 49 | "setBackgroundResource", 50 | R.drawable.ic_rounded_corner_grey 51 | ) 52 | } 53 | 54 | for (appWidgetId in appWidgetIds) { 55 | appWidgetManager.updateAppWidget(appWidgetId, remoteViews) 56 | } 57 | } 58 | 59 | /** 60 | * 接收窗口小部件发送的广播 61 | */ 62 | override fun onReceive(context: Context, intent: Intent) { 63 | super.onReceive(context, intent) 64 | if (AppConfig.BROADCAST_ACTION_WIDGET_CLICK == intent.action) { 65 | if (V2RayServiceManager.v2rayPoint.isRunning) { 66 | Utils.stopVService(context) 67 | } else { 68 | Utils.startVServiceFromToggle(context) 69 | } 70 | } else if (AppConfig.BROADCAST_ACTION_ACTIVITY == intent.action) { 71 | AppWidgetManager.getInstance(context)?.let { manager -> 72 | when (intent.getIntExtra("key", 0)) { 73 | AppConfig.MSG_STATE_RUNNING, AppConfig.MSG_STATE_START_SUCCESS -> { 74 | updateWidgetBackground(context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)), 75 | true) 76 | } 77 | AppConfig.MSG_STATE_NOT_RUNNING, AppConfig.MSG_STATE_START_FAILURE, AppConfig.MSG_STATE_STOP_SUCCESS -> { 78 | updateWidgetBackground(context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)), 79 | false) 80 | } 81 | } 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/service/QSTileService.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.service 2 | 3 | import android.annotation.TargetApi 4 | import android.content.BroadcastReceiver 5 | import android.content.Context 6 | import android.content.Intent 7 | import android.content.IntentFilter 8 | import android.graphics.drawable.Icon 9 | import android.os.Build 10 | import android.service.quicksettings.Tile 11 | import android.service.quicksettings.TileService 12 | import com.agn.v2ray.AppConfig 13 | import com.agn.v2ray.R 14 | import com.agn.v2ray.util.MessageUtil 15 | import com.agn.v2ray.util.Utils 16 | import java.lang.ref.SoftReference 17 | 18 | @TargetApi(Build.VERSION_CODES.N) 19 | class QSTileService : TileService() { 20 | 21 | fun setState(state: Int) { 22 | if (state == Tile.STATE_INACTIVE) { 23 | qsTile?.state = Tile.STATE_INACTIVE 24 | qsTile?.label = getString(R.string.app_name) 25 | qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_v) 26 | } else if (state == Tile.STATE_ACTIVE) { 27 | qsTile?.state = Tile.STATE_ACTIVE 28 | qsTile?.label = V2RayServiceManager.currentConfig?.remarks 29 | qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_v) 30 | } 31 | 32 | qsTile?.updateTile() 33 | } 34 | 35 | override fun onStartListening() { 36 | super.onStartListening() 37 | setState(Tile.STATE_INACTIVE) 38 | mMsgReceive = ReceiveMessageHandler(this) 39 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { 40 | registerReceiver(mMsgReceive, IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY), Context.RECEIVER_EXPORTED) 41 | } else { 42 | registerReceiver(mMsgReceive, IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY)) 43 | } 44 | 45 | MessageUtil.sendMsg2Service(this, AppConfig.MSG_REGISTER_CLIENT, "") 46 | } 47 | 48 | override fun onStopListening() { 49 | super.onStopListening() 50 | 51 | unregisterReceiver(mMsgReceive) 52 | mMsgReceive = null 53 | } 54 | 55 | override fun onClick() { 56 | super.onClick() 57 | when (qsTile.state) { 58 | Tile.STATE_INACTIVE -> { 59 | Utils.startVServiceFromToggle(this) 60 | } 61 | Tile.STATE_ACTIVE -> { 62 | Utils.stopVService(this) 63 | } 64 | } 65 | } 66 | 67 | private var mMsgReceive: BroadcastReceiver? = null 68 | 69 | private class ReceiveMessageHandler(context: QSTileService) : BroadcastReceiver() { 70 | internal var mReference: SoftReference = SoftReference(context) 71 | override fun onReceive(ctx: Context?, intent: Intent?) { 72 | val context = mReference.get() 73 | when (intent?.getIntExtra("key", 0)) { 74 | AppConfig.MSG_STATE_RUNNING -> { 75 | context?.setState(Tile.STATE_ACTIVE) 76 | } 77 | AppConfig.MSG_STATE_NOT_RUNNING -> { 78 | context?.setState(Tile.STATE_INACTIVE) 79 | } 80 | AppConfig.MSG_STATE_START_SUCCESS -> { 81 | context?.setState(Tile.STATE_ACTIVE) 82 | } 83 | AppConfig.MSG_STATE_START_FAILURE -> { 84 | context?.setState(Tile.STATE_INACTIVE) 85 | } 86 | AppConfig.MSG_STATE_STOP_SUCCESS -> { 87 | context?.setState(Tile.STATE_INACTIVE) 88 | } 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/service/ServiceControl.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.service 2 | 3 | import android.app.Service 4 | 5 | interface ServiceControl { 6 | fun getService(): Service 7 | 8 | fun startService() 9 | 10 | fun stopService() 11 | 12 | fun vpnProtect(socket: Int): Boolean 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/service/SubscriptionUpdater.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.service 2 | 3 | import android.annotation.SuppressLint 4 | import android.app.NotificationChannel 5 | import android.app.NotificationManager 6 | import android.content.Context 7 | import android.os.Build 8 | import android.util.Log 9 | import androidx.core.app.NotificationCompat 10 | import androidx.core.app.NotificationManagerCompat 11 | import androidx.work.CoroutineWorker 12 | import androidx.work.WorkerParameters 13 | import com.agn.v2ray.AppConfig 14 | import com.agn.v2ray.R 15 | import com.agn.v2ray.util.AngConfigManager 16 | import com.agn.v2ray.util.MmkvManager 17 | import com.agn.v2ray.util.Utils 18 | 19 | object SubscriptionUpdater { 20 | 21 | const val notificationChannel = "subscription_update_channel" 22 | 23 | class UpdateTask(context: Context, params: WorkerParameters) : 24 | CoroutineWorker(context, params) { 25 | 26 | private val notificationManager = NotificationManagerCompat.from(applicationContext) 27 | private val notification = 28 | NotificationCompat.Builder(applicationContext, notificationChannel) 29 | .setWhen(0) 30 | .setTicker("Update") 31 | .setContentTitle(context.getString(R.string.title_pref_auto_update_subscription)) 32 | .setSmallIcon(R.drawable.ic_v) 33 | .setCategory(NotificationCompat.CATEGORY_SERVICE) 34 | .setPriority(NotificationCompat.PRIORITY_DEFAULT) 35 | 36 | @SuppressLint("MissingPermission") 37 | override suspend fun doWork(): Result { 38 | Log.d(AppConfig.ANG_PACKAGE, "subscription automatic update starting") 39 | 40 | val subs = MmkvManager.decodeSubscriptions().filter { it.second.autoUpdate } 41 | 42 | for (i in subs) { 43 | val subscription = i.second 44 | 45 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 46 | notification.setChannelId(notificationChannel) 47 | val channel = 48 | NotificationChannel( 49 | notificationChannel, 50 | "Subscription Update Service", 51 | NotificationManager.IMPORTANCE_MIN 52 | ) 53 | notificationManager.createNotificationChannel(channel) 54 | } 55 | notificationManager.notify(3, notification.build()) 56 | Log.d( 57 | AppConfig.ANG_PACKAGE, 58 | "subscription automatic update: ---${subscription.remarks}" 59 | ) 60 | val configs = Utils.getUrlContentWithCustomUserAgent(subscription.url) 61 | importBatchConfig(configs, i.first) 62 | notification.setContentText("Updating ${subscription.remarks}") 63 | } 64 | notificationManager.cancel(3) 65 | return Result.success() 66 | } 67 | } 68 | 69 | fun importBatchConfig(server: String?, subid: String = "") { 70 | val append = subid.isEmpty() 71 | 72 | val count = AngConfigManager.importBatchConfig(server, subid, append) 73 | if (count <= 0) { 74 | AngConfigManager.importBatchConfig(Utils.decode(server!!), subid, append) 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/service/V2RayProxyOnlyService.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.service 2 | 3 | import android.app.Service 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.os.Build 7 | import android.os.IBinder 8 | import androidx.annotation.RequiresApi 9 | import com.agn.v2ray.util.MyContextWrapper 10 | import com.agn.v2ray.util.Utils 11 | import java.lang.ref.SoftReference 12 | 13 | class V2RayProxyOnlyService : Service(), ServiceControl { 14 | override fun onCreate() { 15 | super.onCreate() 16 | V2RayServiceManager.serviceControl = SoftReference(this) 17 | } 18 | 19 | override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { 20 | V2RayServiceManager.startV2rayPoint() 21 | return START_STICKY 22 | } 23 | 24 | override fun onDestroy() { 25 | super.onDestroy() 26 | V2RayServiceManager.stopV2rayPoint() 27 | } 28 | 29 | override fun getService(): Service { 30 | return this 31 | } 32 | 33 | override fun startService() { 34 | // do nothing 35 | } 36 | 37 | override fun stopService() { 38 | stopSelf() 39 | } 40 | 41 | override fun vpnProtect(socket: Int): Boolean { 42 | return true 43 | } 44 | 45 | override fun onBind(intent: Intent?): IBinder? { 46 | return null 47 | } 48 | 49 | @RequiresApi(Build.VERSION_CODES.N) 50 | override fun attachBaseContext(newBase: Context) { 51 | val context = MyContextWrapper.wrap(newBase) 52 | super.attachBaseContext(context) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/service/V2RayTestService.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.service 2 | 3 | import android.app.Service 4 | import android.content.Intent 5 | import android.os.IBinder 6 | import com.agn.v2ray.AppConfig.MSG_MEASURE_CONFIG 7 | import com.agn.v2ray.AppConfig.MSG_MEASURE_CONFIG_CANCEL 8 | import com.agn.v2ray.AppConfig.MSG_MEASURE_CONFIG_SUCCESS 9 | import com.agn.v2ray.util.MessageUtil 10 | import com.agn.v2ray.util.SpeedtestUtil 11 | import com.agn.v2ray.util.Utils 12 | import go.Seq 13 | import kotlinx.coroutines.* 14 | import libv2ray.Libv2ray 15 | import java.util.concurrent.Executors 16 | 17 | class V2RayTestService : Service() { 18 | private val realTestScope by lazy { CoroutineScope(Executors.newFixedThreadPool(10).asCoroutineDispatcher()) } 19 | 20 | override fun onCreate() { 21 | super.onCreate() 22 | Seq.setContext(this) 23 | Libv2ray.initV2Env(Utils.userAssetPath(this), Utils.getDeviceIdForXUDPBaseKey()) 24 | } 25 | 26 | override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { 27 | when (intent?.getIntExtra("key", 0)) { 28 | MSG_MEASURE_CONFIG -> { 29 | val contentPair = intent.getSerializableExtra("content") as Pair 30 | realTestScope.launch { 31 | val result = SpeedtestUtil.realPing(contentPair.second) 32 | MessageUtil.sendMsg2UI(this@V2RayTestService, MSG_MEASURE_CONFIG_SUCCESS, Pair(contentPair.first, result)) 33 | } 34 | } 35 | MSG_MEASURE_CONFIG_CANCEL -> { 36 | realTestScope.coroutineContext[Job]?.cancelChildren() 37 | } 38 | } 39 | return super.onStartCommand(intent, flags, startId) 40 | } 41 | 42 | override fun onBind(intent: Intent?): IBinder? { 43 | return null 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/ui/AboutActivity.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.ui 2 | 3 | import com.agn.v2ray.R 4 | import android.annotation.SuppressLint 5 | import android.app.AlertDialog 6 | import android.content.Intent 7 | import android.net.Uri 8 | import android.os.Bundle 9 | import android.text.Html 10 | import android.view.View 11 | import androidx.appcompat.app.AppCompatActivity 12 | 13 | 14 | class AboutActivity : AppCompatActivity() { 15 | 16 | 17 | override fun onCreate(savedInstanceState: Bundle?) { 18 | super.onCreate(savedInstanceState) 19 | setContentView(R.layout.activity_about) 20 | title = getString(R.string.about) 21 | 22 | } 23 | 24 | 25 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/ui/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.ui 2 | 3 | import android.content.Context 4 | import android.os.Build 5 | import android.os.Bundle 6 | import android.view.MenuItem 7 | import androidx.annotation.RequiresApi 8 | import androidx.appcompat.app.AppCompatActivity 9 | import androidx.core.view.WindowCompat 10 | import com.agn.v2ray.util.MyContextWrapper 11 | import com.agn.v2ray.util.Utils 12 | 13 | abstract class BaseActivity : AppCompatActivity() { 14 | override fun onCreate(savedInstanceState: Bundle?) { 15 | super.onCreate(savedInstanceState) 16 | 17 | if (!Utils.getDarkModeStatus(this)) { 18 | WindowCompat.getInsetsController(window, window.decorView).apply { 19 | isAppearanceLightStatusBars = true 20 | } 21 | } 22 | } 23 | override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { 24 | android.R.id.home -> { 25 | onBackPressed() 26 | true 27 | } 28 | else -> super.onOptionsItemSelected(item) 29 | } 30 | 31 | @RequiresApi(Build.VERSION_CODES.N) 32 | override fun attachBaseContext(newBase: Context) { 33 | val context = MyContextWrapper.wrap(newBase) 34 | super.attachBaseContext(context) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/ui/FragmentAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.ui 2 | 3 | import androidx.fragment.app.Fragment 4 | import androidx.fragment.app.FragmentActivity 5 | import androidx.viewpager2.adapter.FragmentStateAdapter 6 | 7 | class FragmentAdapter(fragmentActivity: FragmentActivity, private val mFragments: List) : 8 | FragmentStateAdapter(fragmentActivity) { 9 | 10 | override fun createFragment(position: Int): Fragment { 11 | return mFragments[position] 12 | } 13 | 14 | override fun getItemCount(): Int { 15 | return mFragments.size 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/ui/LogcatActivity.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.ui 2 | 3 | import android.os.Handler 4 | import android.os.Looper 5 | import android.os.Bundle 6 | import android.text.method.ScrollingMovementMethod 7 | import android.view.Menu 8 | import android.view.MenuItem 9 | import android.view.View 10 | import androidx.lifecycle.lifecycleScope 11 | import com.agn.v2ray.AppConfig.ANG_PACKAGE 12 | import com.agn.v2ray.R 13 | import com.agn.v2ray.databinding.ActivityLogcatBinding 14 | import com.agn.v2ray.extension.toast 15 | import com.agn.v2ray.util.Utils 16 | import kotlinx.coroutines.Dispatchers 17 | import kotlinx.coroutines.GlobalScope 18 | import kotlinx.coroutines.launch 19 | 20 | import java.io.IOException 21 | import java.util.LinkedHashSet 22 | 23 | class LogcatActivity : BaseActivity() { 24 | private lateinit var binding: ActivityLogcatBinding 25 | 26 | override fun onCreate(savedInstanceState: Bundle?) { 27 | super.onCreate(savedInstanceState) 28 | binding = ActivityLogcatBinding.inflate(layoutInflater) 29 | val view = binding.root 30 | setContentView(view) 31 | 32 | title = getString(R.string.title_logcat) 33 | 34 | logcat(false) 35 | } 36 | 37 | private fun logcat(shouldFlushLog: Boolean) { 38 | 39 | try { 40 | binding.pbWaiting.visibility = View.VISIBLE 41 | 42 | lifecycleScope.launch(Dispatchers.Default) { 43 | if (shouldFlushLog) { 44 | val lst = LinkedHashSet() 45 | lst.add("logcat") 46 | lst.add("-c") 47 | val process = Runtime.getRuntime().exec(lst.toTypedArray()) 48 | process.waitFor() 49 | } 50 | val lst = LinkedHashSet() 51 | lst.add("logcat") 52 | lst.add("-d") 53 | lst.add("-v") 54 | lst.add("time") 55 | lst.add("-s") 56 | lst.add("GoLog,tun2socks,${ANG_PACKAGE},AndroidRuntime,System.err") 57 | val process = Runtime.getRuntime().exec(lst.toTypedArray()) 58 | // val bufferedReader = BufferedReader( 59 | // InputStreamReader(process.inputStream)) 60 | // val allText = bufferedReader.use(BufferedReader::readText) 61 | val allText = process.inputStream.bufferedReader().use { it.readText() } 62 | launch(Dispatchers.Main) { 63 | binding.tvLogcat.text = allText 64 | binding.tvLogcat.movementMethod = ScrollingMovementMethod() 65 | binding.pbWaiting.visibility = View.GONE 66 | Handler(Looper.getMainLooper()).post { binding.svLogcat.fullScroll(View.FOCUS_DOWN) } 67 | } 68 | } 69 | } catch (e: IOException) { 70 | e.printStackTrace() 71 | } 72 | } 73 | 74 | override fun onCreateOptionsMenu(menu: Menu): Boolean { 75 | menuInflater.inflate(R.menu.menu_logcat, menu) 76 | return super.onCreateOptionsMenu(menu) 77 | } 78 | 79 | override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { 80 | R.id.copy_all -> { 81 | Utils.setClipboard(this, binding.tvLogcat.text.toString()) 82 | toast(R.string.toast_success) 83 | true 84 | } 85 | R.id.clear_all -> { 86 | logcat(true) 87 | true 88 | } 89 | else -> super.onOptionsItemSelected(item) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/ui/PerAppProxyAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.ui 2 | 3 | import android.view.LayoutInflater 4 | import androidx.recyclerview.widget.RecyclerView 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import com.agn.v2ray.R 8 | import com.agn.v2ray.databinding.ItemRecyclerBypassListBinding 9 | import com.agn.v2ray.dto.AppInfo 10 | import java.util.* 11 | 12 | class PerAppProxyAdapter(val activity: BaseActivity, val apps: List, blacklist: MutableSet?) : 13 | RecyclerView.Adapter() { 14 | 15 | companion object { 16 | private const val VIEW_TYPE_HEADER = 0 17 | private const val VIEW_TYPE_ITEM = 1 18 | } 19 | 20 | val blacklist = if (blacklist == null) HashSet() else HashSet(blacklist) 21 | 22 | override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { 23 | if (holder is AppViewHolder) { 24 | val appInfo = apps[position - 1] 25 | holder.bind(appInfo) 26 | } 27 | } 28 | 29 | override fun getItemCount() = apps.size + 1 30 | 31 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder { 32 | val ctx = parent.context 33 | 34 | return when (viewType) { 35 | VIEW_TYPE_HEADER -> { 36 | val view = View(ctx) 37 | view.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 38 | ctx.resources.getDimensionPixelSize(R.dimen.bypass_list_header_height) * 0) 39 | BaseViewHolder(view) 40 | } 41 | // VIEW_TYPE_ITEM -> AppViewHolder(ctx.layoutInflater 42 | // .inflate(R.layout.item_recycler_bypass_list, parent, false)) 43 | 44 | else -> AppViewHolder(ItemRecyclerBypassListBinding.inflate(LayoutInflater.from(ctx), parent, false)) 45 | 46 | } 47 | } 48 | 49 | override fun getItemViewType(position: Int) = if (position == 0) VIEW_TYPE_HEADER else VIEW_TYPE_ITEM 50 | 51 | open class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) 52 | 53 | inner class AppViewHolder(private val itemBypassBinding: ItemRecyclerBypassListBinding) : BaseViewHolder(itemBypassBinding.root), 54 | View.OnClickListener { 55 | private val inBlacklist: Boolean get() = blacklist.contains(appInfo.packageName) 56 | private lateinit var appInfo: AppInfo 57 | 58 | fun bind(appInfo: AppInfo) { 59 | this.appInfo = appInfo 60 | 61 | itemBypassBinding.icon.setImageDrawable(appInfo.appIcon) 62 | // name.text = appInfo.appName 63 | 64 | itemBypassBinding.checkBox.isChecked = inBlacklist 65 | itemBypassBinding.packageName.text = appInfo.packageName 66 | if (appInfo.isSystemApp) { 67 | itemBypassBinding.name.text = String.format("** %1s", appInfo.appName) 68 | //name.textColor = Color.RED 69 | } else { 70 | itemBypassBinding.name.text = appInfo.appName 71 | //name.textColor = Color.DKGRAY 72 | } 73 | 74 | itemView.setOnClickListener(this) 75 | } 76 | 77 | override fun onClick(v: View?) { 78 | if (inBlacklist) { 79 | blacklist.remove(appInfo.packageName) 80 | itemBypassBinding.checkBox.isChecked = false 81 | } else { 82 | blacklist.add(appInfo.packageName) 83 | itemBypassBinding.checkBox.isChecked = true 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/ui/RoutingSettingsActivity.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.ui 2 | 3 | import android.os.Bundle 4 | import com.agn.v2ray.R 5 | import androidx.fragment.app.Fragment 6 | import com.google.android.material.tabs.TabLayoutMediator 7 | import com.agn.v2ray.AppConfig 8 | import com.agn.v2ray.databinding.ActivityRoutingSettingsBinding 9 | 10 | class RoutingSettingsActivity : BaseActivity() { 11 | private lateinit var binding: ActivityRoutingSettingsBinding 12 | 13 | private val titles: Array by lazy { 14 | resources.getStringArray(R.array.routing_tag) 15 | } 16 | 17 | override fun onCreate(savedInstanceState: Bundle?) { 18 | super.onCreate(savedInstanceState) 19 | binding = ActivityRoutingSettingsBinding.inflate(layoutInflater) 20 | val view = binding.root 21 | setContentView(view) 22 | 23 | title = getString(R.string.title_pref_routing_custom) 24 | 25 | 26 | val fragments = ArrayList() 27 | fragments.add(RoutingSettingsFragment().newInstance(AppConfig.PREF_V2RAY_ROUTING_AGENT)) 28 | fragments.add(RoutingSettingsFragment().newInstance(AppConfig.PREF_V2RAY_ROUTING_DIRECT)) 29 | fragments.add(RoutingSettingsFragment().newInstance(AppConfig.PREF_V2RAY_ROUTING_BLOCKED)) 30 | 31 | val adapter = FragmentAdapter(this, fragments) 32 | binding.viewpager.adapter = adapter 33 | //tablayout.setTabTextColors(Color.BLACK, Color.RED) 34 | TabLayoutMediator(binding.tablayout, binding.viewpager) { tab, position -> 35 | tab.text = titles[position] 36 | }.attach() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/ui/ScScannerActivity.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.ui 2 | 3 | import android.Manifest 4 | import android.content.* 5 | import com.tbruyelle.rxpermissions.RxPermissions 6 | import com.agn.v2ray.R 7 | import com.agn.v2ray.util.AngConfigManager 8 | import android.os.Bundle 9 | import androidx.activity.result.contract.ActivityResultContracts 10 | import com.agn.v2ray.extension.toast 11 | 12 | class ScScannerActivity : BaseActivity() { 13 | 14 | override fun onCreate(savedInstanceState: Bundle?) { 15 | super.onCreate(savedInstanceState) 16 | setContentView(R.layout.activity_none) 17 | importQRcode() 18 | } 19 | 20 | fun importQRcode(): Boolean { 21 | RxPermissions(this) 22 | .request(Manifest.permission.CAMERA) 23 | .subscribe { 24 | if (it) 25 | scanQRCode.launch(Intent(this, ScannerActivity::class.java)) 26 | else 27 | toast(R.string.toast_permission_denied) 28 | } 29 | 30 | return true 31 | } 32 | 33 | private val scanQRCode = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { 34 | if (it.resultCode == RESULT_OK) { 35 | val count = AngConfigManager.importBatchConfig(it.data?.getStringExtra("SCAN_RESULT"), "", false) 36 | if (count > 0) { 37 | toast(R.string.toast_success) 38 | } else { 39 | toast(R.string.toast_failure) 40 | } 41 | startActivity(Intent(this, MainActivity::class.java)) 42 | } 43 | finish() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/ui/ScSwitchActivity.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.ui 2 | 3 | import com.agn.v2ray.R 4 | import com.agn.v2ray.util.Utils 5 | import android.os.Bundle 6 | import com.agn.v2ray.service.V2RayServiceManager 7 | 8 | class ScSwitchActivity : BaseActivity() { 9 | override fun onCreate(savedInstanceState: Bundle?) { 10 | super.onCreate(savedInstanceState) 11 | moveTaskToBack(true) 12 | 13 | setContentView(R.layout.activity_none) 14 | 15 | if (V2RayServiceManager.v2rayPoint.isRunning) { 16 | Utils.stopVService(this) 17 | } else { 18 | Utils.startVServiceFromToggle(this) 19 | } 20 | finish() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/ui/ScannerActivity.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.ui 2 | 3 | import android.Manifest 4 | import android.app.Activity 5 | import android.os.Bundle 6 | import android.content.Intent 7 | import android.graphics.BitmapFactory 8 | import android.os.Build 9 | import android.view.Menu 10 | import android.view.MenuItem 11 | import androidx.activity.result.contract.ActivityResultContracts 12 | import com.tbruyelle.rxpermissions.RxPermissions 13 | import com.tencent.mmkv.MMKV 14 | import com.agn.v2ray.AppConfig 15 | import com.agn.v2ray.R 16 | import com.agn.v2ray.extension.toast 17 | import com.agn.v2ray.util.MmkvManager 18 | import com.agn.v2ray.util.QRCodeDecoder 19 | import io.github.g00fy2.quickie.QRResult 20 | import io.github.g00fy2.quickie.ScanCustomCode 21 | import io.github.g00fy2.quickie.config.ScannerConfig 22 | 23 | class ScannerActivity : BaseActivity(){ 24 | 25 | private val scanQrCode = registerForActivityResult(ScanCustomCode(), ::handleResult) 26 | private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) } 27 | 28 | public override fun onCreate(savedInstanceState: Bundle?) { 29 | super.onCreate(savedInstanceState) 30 | 31 | if (settingsStorage?.decodeBool(AppConfig.PREF_START_SCAN_IMMEDIATE) == true) { 32 | launchScan() 33 | } 34 | supportActionBar?.setTitle("Scan QrCode") 35 | 36 | } 37 | 38 | 39 | 40 | private fun launchScan(){ 41 | scanQrCode.launch( 42 | ScannerConfig.build { 43 | setHapticSuccessFeedback(true) // enable (default) or disable haptic feedback when a barcode was detected 44 | setShowTorchToggle(true) // show or hide (default) torch/flashlight toggle button 45 | setShowCloseButton(true) // show or hide (default) close button 46 | } 47 | ) 48 | } 49 | 50 | private fun handleResult(result: QRResult) { 51 | if (result is QRResult.QRSuccess ) { 52 | finished(result.content.rawValue) 53 | } else { 54 | finish() 55 | } 56 | } 57 | 58 | private fun finished(text: String) { 59 | val intent = Intent() 60 | intent.putExtra("SCAN_RESULT", text) 61 | setResult(Activity.RESULT_OK, intent) 62 | finish() 63 | } 64 | 65 | override fun onCreateOptionsMenu(menu: Menu): Boolean { 66 | menuInflater.inflate(R.menu.menu_scanner, menu) 67 | return true 68 | } 69 | 70 | override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { 71 | R.id.scan_code -> { 72 | launchScan() 73 | true 74 | } 75 | R.id.select_photo -> { 76 | val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { 77 | Manifest.permission.READ_MEDIA_IMAGES 78 | } else { 79 | Manifest.permission.READ_EXTERNAL_STORAGE 80 | } 81 | RxPermissions(this) 82 | .request(permission) 83 | .subscribe { 84 | if (it) { 85 | try { 86 | showFileChooser() 87 | } catch (e: Exception) { 88 | e.printStackTrace() 89 | } 90 | } else 91 | toast(R.string.toast_permission_denied) 92 | } 93 | true 94 | } 95 | else -> super.onOptionsItemSelected(item) 96 | } 97 | 98 | private fun showFileChooser() { 99 | val intent = Intent(Intent.ACTION_GET_CONTENT) 100 | intent.type = "image/*" 101 | intent.addCategory(Intent.CATEGORY_OPENABLE) 102 | //intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) 103 | 104 | try { 105 | chooseFile.launch(Intent.createChooser(intent, getString(R.string.title_file_chooser))) 106 | } catch (ex: android.content.ActivityNotFoundException) { 107 | toast(R.string.toast_require_file_manager) 108 | } 109 | } 110 | 111 | private val chooseFile = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { 112 | val uri = it.data?.data 113 | if (it.resultCode == RESULT_OK && uri != null) { 114 | try { 115 | val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri)) 116 | val text = QRCodeDecoder.syncDecodeQRCode(bitmap) 117 | finished(text!!) 118 | } catch (e: Exception) { 119 | e.printStackTrace() 120 | toast(e.message.toString()) 121 | } 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/ui/SubEditActivity.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.ui 2 | 3 | import android.os.Bundle 4 | import android.text.TextUtils 5 | import android.view.Menu 6 | import android.view.MenuItem 7 | import androidx.appcompat.app.AlertDialog 8 | import com.google.gson.Gson 9 | import com.tencent.mmkv.MMKV 10 | import com.agn.v2ray.R 11 | import com.agn.v2ray.databinding.ActivitySubEditBinding 12 | import com.agn.v2ray.dto.SubscriptionItem 13 | import com.agn.v2ray.extension.toast 14 | import com.agn.v2ray.util.MmkvManager 15 | import com.agn.v2ray.util.Utils 16 | 17 | class SubEditActivity : BaseActivity() { 18 | private lateinit var binding: ActivitySubEditBinding 19 | 20 | var del_config: MenuItem? = null 21 | var save_config: MenuItem? = null 22 | 23 | private val subStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SUB, MMKV.MULTI_PROCESS_MODE) } 24 | private val editSubId by lazy { intent.getStringExtra("subId").orEmpty() } 25 | 26 | override fun onCreate(savedInstanceState: Bundle?) { 27 | super.onCreate(savedInstanceState) 28 | binding = ActivitySubEditBinding.inflate(layoutInflater) 29 | val view = binding.root 30 | setContentView(view) 31 | title = getString(R.string.title_sub_setting) 32 | 33 | val json = subStorage?.decodeString(editSubId) 34 | if (!json.isNullOrBlank()) { 35 | bindingServer(Gson().fromJson(json, SubscriptionItem::class.java)) 36 | } else { 37 | clearServer() 38 | } 39 | 40 | } 41 | 42 | /** 43 | * bingding seleced server config 44 | */ 45 | private fun bindingServer(subItem: SubscriptionItem): Boolean { 46 | binding.etRemarks.text = Utils.getEditable(subItem.remarks) 47 | binding.etUrl.text = Utils.getEditable(subItem.url) 48 | binding.chkEnable.isChecked = subItem.enabled 49 | return true 50 | } 51 | 52 | /** 53 | * clear or init server config 54 | */ 55 | private fun clearServer(): Boolean { 56 | binding.etRemarks.text = null 57 | binding.etUrl.text = null 58 | binding.chkEnable.isChecked = true 59 | return true 60 | } 61 | 62 | /** 63 | * save server config 64 | */ 65 | private fun saveServer(): Boolean { 66 | val subItem: SubscriptionItem 67 | val json = subStorage?.decodeString(editSubId) 68 | var subId = editSubId 69 | if (!json.isNullOrBlank()) { 70 | subItem = Gson().fromJson(json, SubscriptionItem::class.java) 71 | } else { 72 | subId = Utils.getUuid() 73 | subItem = SubscriptionItem() 74 | } 75 | 76 | subItem.remarks = binding.etRemarks.text.toString() 77 | subItem.url = binding.etUrl.text.toString() 78 | subItem.enabled = binding.chkEnable.isChecked 79 | 80 | if (TextUtils.isEmpty(subItem.remarks)) { 81 | toast(R.string.sub_setting_remarks) 82 | return false 83 | } 84 | // if (TextUtils.isEmpty(subItem.url)) { 85 | // toast(R.string.sub_setting_url) 86 | // return false 87 | // } 88 | 89 | subStorage?.encode(subId, Gson().toJson(subItem)) 90 | toast(R.string.toast_success) 91 | finish() 92 | return true 93 | } 94 | 95 | /** 96 | * save server config 97 | */ 98 | private fun deleteServer(): Boolean { 99 | if (editSubId.isNotEmpty()) { 100 | AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) 101 | .setPositiveButton(android.R.string.ok) { _, _ -> 102 | MmkvManager.removeSubscription(editSubId) 103 | finish() 104 | } 105 | .show() 106 | } 107 | return true 108 | } 109 | 110 | override fun onCreateOptionsMenu(menu: Menu): Boolean { 111 | menuInflater.inflate(R.menu.action_server, menu) 112 | del_config = menu.findItem(R.id.del_config) 113 | save_config = menu.findItem(R.id.save_config) 114 | 115 | if (editSubId.isEmpty()) { 116 | del_config?.isVisible = false 117 | } 118 | 119 | return super.onCreateOptionsMenu(menu) 120 | } 121 | 122 | override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { 123 | R.id.del_config -> { 124 | deleteServer() 125 | true 126 | } 127 | R.id.save_config -> { 128 | saveServer() 129 | true 130 | } 131 | else -> super.onOptionsItemSelected(item) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/ui/SubSettingActivity.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.ui 2 | 3 | import android.content.Intent 4 | import androidx.recyclerview.widget.LinearLayoutManager 5 | import android.view.Menu 6 | import android.view.MenuItem 7 | import com.agn.v2ray.R 8 | import android.os.Bundle 9 | import com.agn.v2ray.databinding.ActivitySubSettingBinding 10 | import com.agn.v2ray.dto.SubscriptionItem 11 | import com.agn.v2ray.util.MmkvManager 12 | 13 | class SubSettingActivity : BaseActivity() { 14 | private lateinit var binding: ActivitySubSettingBinding 15 | 16 | var subscriptions:List> = listOf() 17 | private val adapter by lazy { SubSettingRecyclerAdapter(this) } 18 | 19 | override fun onCreate(savedInstanceState: Bundle?) { 20 | super.onCreate(savedInstanceState) 21 | binding = ActivitySubSettingBinding.inflate(layoutInflater) 22 | val view = binding.root 23 | setContentView(view) 24 | 25 | title = getString(R.string.title_sub_setting) 26 | 27 | binding.recyclerView.setHasFixedSize(true) 28 | binding.recyclerView.layoutManager = LinearLayoutManager(this) 29 | binding.recyclerView.adapter = adapter 30 | 31 | 32 | } 33 | 34 | override fun onResume() { 35 | super.onResume() 36 | subscriptions = MmkvManager.decodeSubscriptions() 37 | adapter.notifyDataSetChanged() 38 | } 39 | 40 | override fun onCreateOptionsMenu(menu: Menu): Boolean { 41 | menuInflater.inflate(R.menu.action_sub_setting, menu) 42 | menu.findItem(R.id.del_config)?.isVisible = false 43 | menu.findItem(R.id.save_config)?.isVisible = false 44 | 45 | return super.onCreateOptionsMenu(menu) 46 | } 47 | 48 | override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { 49 | R.id.add_config -> { 50 | startActivity(Intent(this, SubEditActivity::class.java)) 51 | true 52 | } 53 | else -> super.onOptionsItemSelected(item) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/ui/SubSettingRecyclerAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.ui 2 | 3 | import android.content.Intent 4 | import android.graphics.Color 5 | import android.text.TextUtils 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import androidx.recyclerview.widget.RecyclerView 9 | import android.view.ViewGroup 10 | import androidx.appcompat.app.AlertDialog 11 | import com.google.gson.Gson 12 | import com.tencent.mmkv.MMKV 13 | import com.agn.v2ray.R 14 | import com.agn.v2ray.databinding.ItemQrcodeBinding 15 | import com.agn.v2ray.databinding.ItemRecyclerSubSettingBinding 16 | import com.agn.v2ray.dto.EConfigType 17 | import com.agn.v2ray.extension.toast 18 | import com.agn.v2ray.util.AngConfigManager 19 | import com.agn.v2ray.util.MmkvManager 20 | import com.agn.v2ray.util.QRCodeDecoder 21 | import com.agn.v2ray.util.Utils 22 | 23 | class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : 24 | RecyclerView.Adapter() { 25 | 26 | private var mActivity: SubSettingActivity = activity 27 | private val subStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SUB, MMKV.MULTI_PROCESS_MODE) } 28 | 29 | private val share_method: Array by lazy { 30 | mActivity.resources.getStringArray(R.array.share_sub_method) 31 | } 32 | 33 | override fun getItemCount() = mActivity.subscriptions.size 34 | 35 | override fun onBindViewHolder(holder: MainViewHolder, position: Int) { 36 | val subId = mActivity.subscriptions[position].first 37 | val subItem = mActivity.subscriptions[position].second 38 | holder.itemSubSettingBinding.tvName.text = subItem.remarks 39 | holder.itemSubSettingBinding.tvUrl.text = subItem.url 40 | if (subItem.enabled) { 41 | holder.itemSubSettingBinding.chkEnable.setBackgroundResource(R.color.colorSelected) 42 | } else { 43 | holder.itemSubSettingBinding.chkEnable.setBackgroundResource(R.color.colorUnselected) 44 | } 45 | holder.itemView.setBackgroundColor(Color.TRANSPARENT) 46 | 47 | holder.itemSubSettingBinding.layoutEdit.setOnClickListener { 48 | mActivity.startActivity( 49 | Intent(mActivity, SubEditActivity::class.java) 50 | .putExtra("subId", subId) 51 | ) 52 | } 53 | holder.itemSubSettingBinding.infoContainer.setOnClickListener { 54 | subItem.enabled = !subItem.enabled 55 | subStorage?.encode(subId, Gson().toJson(subItem)) 56 | notifyItemChanged(position) 57 | } 58 | 59 | if (TextUtils.isEmpty(subItem.url)) { 60 | holder.itemSubSettingBinding.layoutShare.visibility = View.INVISIBLE 61 | } else { 62 | holder.itemSubSettingBinding.layoutShare.setOnClickListener { 63 | AlertDialog.Builder(mActivity) 64 | .setItems(share_method.asList().toTypedArray()) { _, i -> 65 | try { 66 | when (i) { 67 | 0 -> { 68 | val ivBinding = 69 | ItemQrcodeBinding.inflate(LayoutInflater.from(mActivity)) 70 | ivBinding.ivQcode.setImageBitmap( 71 | QRCodeDecoder.createQRCode( 72 | subItem.url 73 | 74 | ) 75 | ) 76 | AlertDialog.Builder(mActivity).setView(ivBinding.root).show() 77 | } 78 | 79 | 1 -> { 80 | Utils.setClipboard(mActivity, subItem.url) 81 | } 82 | 83 | else -> mActivity.toast("else") 84 | } 85 | } catch (e: Exception) { 86 | e.printStackTrace() 87 | } 88 | }.show() 89 | } 90 | } 91 | } 92 | 93 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder { 94 | return MainViewHolder( 95 | ItemRecyclerSubSettingBinding.inflate( 96 | LayoutInflater.from(parent.context), 97 | parent, 98 | false 99 | ) 100 | ) 101 | } 102 | 103 | class MainViewHolder(val itemSubSettingBinding: ItemRecyclerSubSettingBinding) : 104 | RecyclerView.ViewHolder(itemSubSettingBinding.root) 105 | } 106 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/ui/TaskerActivity.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.ui 2 | 3 | import android.app.Activity 4 | import android.os.Bundle 5 | import android.view.View 6 | import android.widget.ArrayAdapter 7 | import android.widget.ListView 8 | import java.util.ArrayList 9 | import com.agn.v2ray.R 10 | import android.content.Intent 11 | import android.text.TextUtils 12 | import android.view.Menu 13 | import android.view.MenuItem 14 | import com.google.zxing.WriterException 15 | import com.tencent.mmkv.MMKV 16 | import com.agn.v2ray.AppConfig 17 | import com.agn.v2ray.databinding.ActivityTaskerBinding 18 | import com.agn.v2ray.util.MmkvManager 19 | 20 | class TaskerActivity : BaseActivity() { 21 | private lateinit var binding: ActivityTaskerBinding 22 | 23 | private var listview: ListView? = null 24 | private var lstData: ArrayList = ArrayList() 25 | private var lstGuid: ArrayList = ArrayList() 26 | 27 | private val serverStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SERVER_CONFIG, MMKV.MULTI_PROCESS_MODE) } 28 | 29 | override fun onCreate(savedInstanceState: Bundle?) { 30 | super.onCreate(savedInstanceState) 31 | binding = ActivityTaskerBinding.inflate(layoutInflater) 32 | val view = binding.root 33 | setContentView(view) 34 | 35 | //add def value 36 | lstData.add("Default") 37 | lstGuid.add(AppConfig.TASKER_DEFAULT_GUID) 38 | 39 | serverStorage?.allKeys()?.forEach { key -> 40 | MmkvManager.decodeServerConfig(key)?.let { config -> 41 | lstData.add(config.remarks) 42 | lstGuid.add(key) 43 | } 44 | } 45 | val adapter = ArrayAdapter(this, 46 | android.R.layout.simple_list_item_single_choice, lstData) 47 | listview = findViewById(R.id.listview) as ListView 48 | listview!!.adapter = adapter 49 | 50 | init() 51 | } 52 | 53 | private fun init() { 54 | try { 55 | val bundle = intent?.getBundleExtra(AppConfig.TASKER_EXTRA_BUNDLE) 56 | val switch = bundle?.getBoolean(AppConfig.TASKER_EXTRA_BUNDLE_SWITCH, false) 57 | val guid = bundle?.getString(AppConfig.TASKER_EXTRA_BUNDLE_GUID, "") 58 | 59 | if (switch == null || TextUtils.isEmpty(guid)) { 60 | return 61 | } else { 62 | binding.switchStartService.isChecked = switch 63 | val pos = lstGuid.indexOf(guid.toString()) 64 | if (pos >= 0) { 65 | listview?.setItemChecked(pos, true) 66 | } 67 | } 68 | } catch (e: WriterException) { 69 | e.printStackTrace() 70 | 71 | } 72 | } 73 | 74 | private fun confirmFinish() { 75 | val position = listview?.checkedItemPosition 76 | if (position == null || position < 0) { 77 | return 78 | } 79 | 80 | val extraBundle = Bundle() 81 | extraBundle.putBoolean(AppConfig.TASKER_EXTRA_BUNDLE_SWITCH, binding.switchStartService.isChecked) 82 | extraBundle.putString(AppConfig.TASKER_EXTRA_BUNDLE_GUID, lstGuid[position]) 83 | val intent = Intent() 84 | 85 | val remarks = lstData[position] 86 | val blurb = if (binding.switchStartService.isChecked) { 87 | "Start $remarks" 88 | } else { 89 | "Stop $remarks" 90 | } 91 | 92 | intent.putExtra(AppConfig.TASKER_EXTRA_BUNDLE, extraBundle) 93 | intent.putExtra(AppConfig.TASKER_EXTRA_STRING_BLURB, blurb) 94 | setResult(Activity.RESULT_OK, intent) 95 | finish() 96 | } 97 | 98 | override fun onCreateOptionsMenu(menu: Menu): Boolean { 99 | menuInflater.inflate(R.menu.action_server, menu) 100 | val del_config = menu.findItem(R.id.del_config) 101 | del_config?.isVisible = false 102 | return super.onCreateOptionsMenu(menu) 103 | } 104 | 105 | override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { 106 | R.id.del_config -> { 107 | true 108 | } 109 | R.id.save_config -> { 110 | confirmFinish() 111 | true 112 | } 113 | else -> super.onOptionsItemSelected(item) 114 | } 115 | 116 | } 117 | 118 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/ui/UrlSchemeActivity.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.ui 2 | 3 | import android.content.Intent 4 | import android.net.Uri 5 | import android.os.Bundle 6 | import com.google.zxing.WriterException 7 | import com.agn.v2ray.R 8 | import com.agn.v2ray.databinding.ActivityLogcatBinding 9 | import com.agn.v2ray.extension.toast 10 | import com.agn.v2ray.util.AngConfigManager 11 | 12 | class UrlSchemeActivity : BaseActivity() { 13 | private lateinit var binding: ActivityLogcatBinding 14 | 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | binding = ActivityLogcatBinding.inflate(layoutInflater) 18 | val view = binding.root 19 | setContentView(view) 20 | 21 | var shareUrl: String = "" 22 | try { 23 | intent?.apply { 24 | when (action) { 25 | Intent.ACTION_SEND -> { 26 | if ("text/plain" == type) { 27 | intent.getStringExtra(Intent.EXTRA_TEXT)?.let { 28 | shareUrl = it 29 | } 30 | } 31 | } 32 | Intent.ACTION_VIEW -> { 33 | val uri: Uri? = intent.data 34 | shareUrl = uri?.getQueryParameter("url")!! 35 | } 36 | } 37 | } 38 | toast(shareUrl) 39 | val count = AngConfigManager.importBatchConfig(shareUrl, "", false) 40 | if (count > 0) { 41 | toast(R.string.toast_success) 42 | } else { 43 | toast(R.string.toast_failure) 44 | } 45 | startActivity(Intent(this, MainActivity::class.java)) 46 | finish() 47 | } catch (e: WriterException) { 48 | e.printStackTrace() 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/util/AppManagerUtil.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.util 2 | 3 | import android.Manifest 4 | import android.content.Context 5 | import android.content.pm.ApplicationInfo 6 | import android.content.pm.PackageInfo 7 | import android.content.pm.PackageManager 8 | import com.agn.v2ray.dto.AppInfo 9 | import rx.Observable 10 | import java.util.* 11 | 12 | object AppManagerUtil { 13 | fun loadNetworkAppList(ctx: Context): ArrayList { 14 | val packageManager = ctx.packageManager 15 | val packages = packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS) 16 | val apps = ArrayList() 17 | 18 | for (pkg in packages) { 19 | if (!pkg.hasInternetPermission && pkg.packageName != "android") continue 20 | 21 | val applicationInfo = pkg.applicationInfo 22 | 23 | val appName = applicationInfo.loadLabel(packageManager).toString() 24 | val appIcon = applicationInfo.loadIcon(packageManager) 25 | val isSystemApp = (applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) > 0 26 | 27 | val appInfo = AppInfo(appName, pkg.packageName, appIcon, isSystemApp, 0) 28 | apps.add(appInfo) 29 | } 30 | 31 | return apps 32 | } 33 | 34 | fun rxLoadNetworkAppList(ctx: Context): Observable> = Observable.unsafeCreate { 35 | it.onNext(loadNetworkAppList(ctx)) 36 | } 37 | 38 | val PackageInfo.hasInternetPermission: Boolean 39 | get() { 40 | val permissions = requestedPermissions 41 | return permissions?.any { it == Manifest.permission.INTERNET } ?: false 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/util/LanguageHelper.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.util 2 | 3 | import android.content.Context 4 | import android.content.res.Configuration 5 | import java.util.Locale 6 | 7 | object LanguageHelper { 8 | fun updateContextLocale(context: Context, language: String): Context { 9 | val locale = Locale(language) 10 | Locale.setDefault(locale) 11 | val config = Configuration(context.resources.configuration) 12 | config.setLocale(locale) 13 | return context.createConfigurationContext(config) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/util/LanguagePrefs.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.util 2 | 3 | import android.content.Context 4 | 5 | class LanguagePrefs(context: Context) { 6 | private val prefs = context.getSharedPreferences("MyAppPrefs", Context.MODE_PRIVATE) 7 | 8 | fun getSelectedLanguage(): String { 9 | return prefs.getString("selected_language", "auto") ?: "auto" 10 | } 11 | 12 | fun setSelectedLanguage(language: String) { 13 | prefs.edit().putString("selected_language", language).apply() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/util/MessageUtil.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.util 2 | 3 | import android.content.ComponentName 4 | import android.content.Context 5 | import android.content.Intent 6 | import com.agn.v2ray.AppConfig 7 | import com.agn.v2ray.service.V2RayTestService 8 | import java.io.Serializable 9 | 10 | 11 | object MessageUtil { 12 | 13 | fun sendMsg2Service(ctx: Context, what: Int, content: Serializable) { 14 | sendMsg(ctx, AppConfig.BROADCAST_ACTION_SERVICE, what, content) 15 | } 16 | 17 | fun sendMsg2UI(ctx: Context, what: Int, content: Serializable) { 18 | sendMsg(ctx, AppConfig.BROADCAST_ACTION_ACTIVITY, what, content) 19 | } 20 | 21 | fun sendMsg2TestService(ctx: Context, what: Int, content: Serializable) { 22 | try { 23 | val intent = Intent() 24 | intent.component = ComponentName(ctx, V2RayTestService::class.java) 25 | intent.putExtra("key", what) 26 | intent.putExtra("content", content) 27 | ctx.startService(intent) 28 | } catch (e: Exception) { 29 | e.printStackTrace() 30 | } 31 | } 32 | 33 | private fun sendMsg(ctx: Context, action: String, what: Int, content: Serializable) { 34 | try { 35 | val intent = Intent() 36 | intent.action = action 37 | intent.`package` = AppConfig.ANG_PACKAGE 38 | intent.putExtra("key", what) 39 | intent.putExtra("content", content) 40 | ctx.sendBroadcast(intent) 41 | } catch (e: Exception) { 42 | e.printStackTrace() 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/util/MyContextWrapper.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.util 2 | 3 | import android.content.Context 4 | import java.util.Locale 5 | 6 | object MyContextWrapper { 7 | fun wrap(context: Context): Context { 8 | val languagePrefs = LanguagePrefs(context) 9 | val language = languagePrefs.getSelectedLanguage() 10 | val locale = when (language) { 11 | "auto" -> Utils.getSysLocale() 12 | "en" -> Locale("en") 13 | "zh-rCN" -> Locale("zh", "CN") 14 | "zh-rTW" -> Locale("zh", "TW") 15 | "vi" -> Locale("vi") 16 | "ru" -> Locale("ru") 17 | "fa" -> Locale("fa") 18 | "ar" -> Locale("ar") 19 | "es" -> Locale("es") 20 | "de" -> Locale("de") 21 | "th" -> Locale("th") 22 | "fr" -> Locale("fr") 23 | "pt" -> Locale("pt") 24 | "lt" -> Locale("lt") 25 | 26 | else -> Utils.getSysLocale() 27 | 28 | } 29 | val configuration = context.resources.configuration 30 | configuration.setLocale(locale) 31 | return context.createConfigurationContext(configuration) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/util/QRCodeDecoder.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.util 2 | 3 | import android.graphics.Bitmap 4 | import android.graphics.BitmapFactory 5 | import com.google.zxing.* 6 | import com.google.zxing.common.GlobalHistogramBinarizer 7 | import com.google.zxing.common.HybridBinarizer 8 | import com.google.zxing.qrcode.QRCodeWriter 9 | import java.util.* 10 | 11 | /** 12 | * 描述:解析二维码图片 13 | */ 14 | object QRCodeDecoder { 15 | val HINTS: MutableMap = EnumMap(DecodeHintType::class.java) 16 | 17 | /** 18 | * create qrcode using zxing 19 | */ 20 | fun createQRCode(text: String, size: Int = 800): Bitmap? { 21 | try { 22 | val hints = HashMap() 23 | hints[EncodeHintType.CHARACTER_SET] = "utf-8" 24 | val bitMatrix = QRCodeWriter().encode(text, 25 | BarcodeFormat.QR_CODE, size, size, hints) 26 | val pixels = IntArray(size * size) 27 | for (y in 0 until size) { 28 | for (x in 0 until size) { 29 | if (bitMatrix.get(x, y)) { 30 | pixels[y * size + x] = 0xff000000.toInt() 31 | } else { 32 | pixels[y * size + x] = 0xffffffff.toInt() 33 | } 34 | 35 | } 36 | } 37 | val bitmap = Bitmap.createBitmap(size, size, 38 | Bitmap.Config.ARGB_8888) 39 | bitmap.setPixels(pixels, 0, size, 0, 0, size, size) 40 | return bitmap 41 | } catch (e: Exception) { 42 | e.printStackTrace() 43 | return null 44 | } 45 | } 46 | 47 | /** 48 | * 同步解析本地图片二维码。该方法是耗时操作,请在子线程中调用。 49 | * 50 | * @param picturePath 要解析的二维码图片本地路径 51 | * @return 返回二维码图片里的内容 或 null 52 | */ 53 | fun syncDecodeQRCode(picturePath: String): String? { 54 | return syncDecodeQRCode(getDecodeAbleBitmap(picturePath)) 55 | } 56 | 57 | /** 58 | * 同步解析bitmap二维码。该方法是耗时操作,请在子线程中调用。 59 | * 60 | * @param bitmap 要解析的二维码图片 61 | * @return 返回二维码图片里的内容 或 null 62 | */ 63 | fun syncDecodeQRCode(bitmap: Bitmap?): String? { 64 | var source: RGBLuminanceSource? = null 65 | try { 66 | val width = bitmap!!.width 67 | val height = bitmap.height 68 | val pixels = IntArray(width * height) 69 | bitmap.getPixels(pixels, 0, width, 0, 0, width, height) 70 | source = RGBLuminanceSource(width, height, pixels) 71 | return MultiFormatReader().decode(BinaryBitmap(HybridBinarizer(source)), HINTS).text 72 | } catch (e: Exception) { 73 | e.printStackTrace() 74 | } 75 | if (source != null) { 76 | try { 77 | return MultiFormatReader().decode(BinaryBitmap(GlobalHistogramBinarizer(source)), HINTS).text 78 | } catch (e2: Throwable) { 79 | e2.printStackTrace() 80 | } 81 | } 82 | return null 83 | } 84 | 85 | /** 86 | * 将本地图片文件转换成可解码二维码的 Bitmap。为了避免图片太大,这里对图片进行了压缩。感谢 https://github.com/devilsen 提的 PR 87 | * 88 | * @param picturePath 本地图片文件路径 89 | * @return 90 | */ 91 | private fun getDecodeAbleBitmap(picturePath: String): Bitmap? { 92 | return try { 93 | val options = BitmapFactory.Options() 94 | options.inJustDecodeBounds = true 95 | BitmapFactory.decodeFile(picturePath, options) 96 | var sampleSize = options.outHeight / 400 97 | if (sampleSize <= 0) { 98 | sampleSize = 1 99 | } 100 | options.inSampleSize = sampleSize 101 | options.inJustDecodeBounds = false 102 | BitmapFactory.decodeFile(picturePath, options) 103 | } catch (e: Exception) { 104 | null 105 | } 106 | } 107 | 108 | init { 109 | val allFormats: List = arrayListOf( 110 | BarcodeFormat.AZTEC 111 | ,BarcodeFormat.CODABAR 112 | ,BarcodeFormat.CODE_39 113 | ,BarcodeFormat.CODE_93 114 | ,BarcodeFormat.CODE_128 115 | ,BarcodeFormat.DATA_MATRIX 116 | ,BarcodeFormat.EAN_8 117 | ,BarcodeFormat.EAN_13 118 | ,BarcodeFormat.ITF 119 | ,BarcodeFormat.MAXICODE 120 | ,BarcodeFormat.PDF_417 121 | ,BarcodeFormat.QR_CODE 122 | ,BarcodeFormat.RSS_14 123 | ,BarcodeFormat.RSS_EXPANDED 124 | ,BarcodeFormat.UPC_A 125 | ,BarcodeFormat.UPC_E 126 | ,BarcodeFormat.UPC_EAN_EXTENSION) 127 | HINTS[DecodeHintType.TRY_HARDER] = BarcodeFormat.QR_CODE 128 | HINTS[DecodeHintType.POSSIBLE_FORMATS] = allFormats 129 | HINTS[DecodeHintType.CHARACTER_SET] = "utf-8" 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/agn/v2ray/viewmodel/SettingsViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.agn.v2ray.viewmodel 2 | 3 | import android.app.Application 4 | import android.content.SharedPreferences 5 | import android.util.Log 6 | import androidx.lifecycle.AndroidViewModel 7 | import androidx.preference.PreferenceManager 8 | import com.tencent.mmkv.MMKV 9 | import com.agn.v2ray.AppConfig 10 | import com.agn.v2ray.util.MmkvManager 11 | 12 | class SettingsViewModel(application: Application) : AndroidViewModel(application), SharedPreferences.OnSharedPreferenceChangeListener { 13 | 14 | private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) } 15 | 16 | fun startListenPreferenceChange() { 17 | PreferenceManager.getDefaultSharedPreferences(getApplication()).registerOnSharedPreferenceChangeListener(this) 18 | } 19 | 20 | override fun onCleared() { 21 | PreferenceManager.getDefaultSharedPreferences(getApplication()).unregisterOnSharedPreferenceChangeListener(this) 22 | Log.i(AppConfig.ANG_PACKAGE, "Settings ViewModel is cleared") 23 | super.onCleared() 24 | } 25 | 26 | override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) { 27 | Log.d(AppConfig.ANG_PACKAGE, "Observe settings changed: $key") 28 | when(key) { 29 | AppConfig.PREF_MODE, 30 | AppConfig.PREF_VPN_DNS, 31 | AppConfig.PREF_REMOTE_DNS, 32 | AppConfig.PREF_DOMESTIC_DNS, 33 | AppConfig.PREF_LOCAL_DNS_PORT, 34 | AppConfig.PREF_SOCKS_PORT, 35 | AppConfig.PREF_HTTP_PORT, 36 | AppConfig.PREF_LOGLEVEL, 37 | AppConfig.PREF_LANGUAGE, 38 | AppConfig.PREF_ROUTING_DOMAIN_STRATEGY, 39 | AppConfig.PREF_ROUTING_MODE, 40 | AppConfig.PREF_V2RAY_ROUTING_AGENT, 41 | AppConfig.PREF_V2RAY_ROUTING_BLOCKED, 42 | AppConfig.PREF_V2RAY_ROUTING_DIRECT, 43 | AppConfig.SUBSCRIPTION_AUTO_UPDATE_INTERVAL, 44 | AppConfig.PREF_MUX_XUDP_QUIC, -> { 45 | settingsStorage?.encode(key, sharedPreferences.getString(key, "")) 46 | } 47 | AppConfig.PREF_SPEED_ENABLED, 48 | AppConfig.PREF_PROXY_SHARING, 49 | AppConfig.PREF_LOCAL_DNS_ENABLED, 50 | AppConfig.PREF_FAKE_DNS_ENABLED, 51 | AppConfig.PREF_ALLOW_INSECURE, 52 | AppConfig.PREF_PREFER_IPV6, 53 | AppConfig.PREF_PER_APP_PROXY, 54 | AppConfig.PREF_BYPASS_APPS, 55 | AppConfig.PREF_CONFIRM_REMOVE, 56 | AppConfig.PREF_START_SCAN_IMMEDIATE, 57 | AppConfig.SUBSCRIPTION_AUTO_UPDATE, 58 | AppConfig.PREF_MUX_ENABLED, -> { 59 | settingsStorage?.encode(key, sharedPreferences.getBoolean(key, false)) 60 | } 61 | AppConfig.PREF_SNIFFING_ENABLED -> { 62 | settingsStorage?.encode(key, sharedPreferences.getBoolean(key, true)) 63 | } 64 | AppConfig.PREF_MUX_CONCURRENCY, 65 | AppConfig.PREF_MUX_XUDP_CONCURRENCY -> { 66 | settingsStorage?.encode(key, sharedPreferences.getString(key, "8")?.toIntOrNull() ?: 8) 67 | } 68 | AppConfig.PREF_PER_APP_PROXY_SET -> { 69 | settingsStorage?.encode(key, sharedPreferences.getStringSet(key, setOf())) 70 | } 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /app/src/main/res/anim/fade_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/anim/fade_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/color/color_highlight_material.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_stat_direct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable-hdpi/ic_stat_direct.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable-hdpi/ic_stat_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_stat_proxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable-hdpi/ic_stat_proxy.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-ldrtl/bg_nav.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_stat_direct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable-mdpi/ic_stat_direct.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable-mdpi/ic_stat_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_stat_proxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable-mdpi/ic_stat_proxy.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_stat_direct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable-xhdpi/ic_stat_direct.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable-xhdpi/ic_stat_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_stat_proxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable-xhdpi/ic_stat_proxy.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/donate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable-xxhdpi/donate.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_stat_direct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable-xxhdpi/ic_stat_direct.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable-xxhdpi/ic_stat_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_stat_proxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable-xxhdpi/ic_stat_proxy.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/side_nav_bar.xml: -------------------------------------------------------------------------------- 1 | 3 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_stat_direct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable-xxxhdpi/ic_stat_direct.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable-xxxhdpi/ic_stat_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_stat_proxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable-xxxhdpi/ic_stat_proxy.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/autorenew.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/background_test_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_privacy_tip_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_nav.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_done.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_done_white.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_add_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_add_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_attach_money_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_attach_money_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_autorenew_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable/ic_autorenew_black_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_add_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_arrow_back_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_filter_alt_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_close_grey_800_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_cloud_download_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_copy_white.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_delete_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_delete_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_description_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_description_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_edit_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_fab_check.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_fab_uncheck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable/ic_fab_uncheck.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_feedback_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_file_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 20 | 27 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_image_photo.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_info_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_logcat_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_outline_filter_alt_white_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_person.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_plane.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_qu_scan_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 14 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_qu_switch_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_rounded_corner_grey.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_rounded_corner_theme.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_save_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_scan_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 14 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_select_all_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_share_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_share_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_shortcut_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_start_busy.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_start_connected.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_start_connected_black.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_start_idle.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 15 | 18 | 21 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_stat_direct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable/ic_stat_direct.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_stat_proxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable/ic_stat_proxy.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_subscriptions_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_subscriptions_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_v.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable/ic_v.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_v_connected_black.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_v_idle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable/ic_v_idle.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_whatshot_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_whatshot_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_youtube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable/ic_youtube.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable/icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/nav_header_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/drawable/nav_header_bg.png -------------------------------------------------------------------------------- /app/src/main/res/font/poppins_medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/font/poppins_medium.ttf -------------------------------------------------------------------------------- /app/src/main/res/layout/.activity_main.xml.kate-swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khaledagn/V2rayAGN/f348cb956337c1f9cbcc553f4beb0cf0c9f9bed8/app/src/main/res/layout/.activity_main.xml.kate-swp -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_bypass_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 21 | 22 | 26 | 27 | 35 | 36 | 41 | 42 | 47 | 48 | 49 | 50 | 58 | 59 | 65 | 66 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 84 | 85 | 91 | 92 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_logcat.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 27 | 28 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_none.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_routing_settings.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_server_custom_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 18 | 19 | 25 | 26 | 27 | 32 | 33 | 38 | 39 | 40 | 45 | 46 | 50 | 51 | 56 | 57 | 58 | 59 | 63 | 64 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 19 | 20 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_sub_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 15 | 16 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tasker.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | 13 | 21 | 22 | 32 | 33 | 34 | 35 | 40 | 41 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/res/layout/daynightswitch.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_config_filter.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | 17 | 18 | 22 | 23 | 24 | 25 | 30 | 31 | 35 | 36 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_routing_settings.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 11 | 17 | 18 | 23 | 24 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_qrcode.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_recycler_bypass_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | 19 | 27 | 28 | 32 | 33 | 38 | 39 | 40 | 41 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_recycler_footer.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 23 | 24 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_recycler_sub_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 28 | 29 | 35 | 36 | 42 | 43 | 48 | 49 | 56 | 57 | 58 | 59 | 60 | 65 | 66 | 77 | 78 | 83 | 84 | 85 | 86 | 97 | 98 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_recycler_user_asset.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 20 | 21 | 26 | 27 | 33 | 34 | 41 | 42 | 43 | 51 | 52 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /app/src/main/res/layout/nav_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 19 | 20 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/nav_toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/nav_view.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/preference_with_help_link.xml: -------------------------------------------------------------------------------- 1 | 2 |