├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── compileOnly │ └── api-82.jar ├── libs │ ├── RootShell.aar │ └── RootTools.aar ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── huanchengfly │ │ └── icebridge │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── bridge.json │ │ ├── litepal.xml │ │ └── xposed_init │ ├── ic_launcher-web.png │ ├── java │ │ └── com │ │ │ └── huanchengfly │ │ │ ├── about │ │ │ ├── AboutPage.java │ │ │ ├── ViewHolder.java │ │ │ ├── adapter │ │ │ │ ├── AboutPageAdapter.java │ │ │ │ └── BaseAdapter.java │ │ │ └── utils │ │ │ │ └── DisplayUtil.java │ │ │ └── icebridge │ │ │ ├── activities │ │ │ ├── AboutActivity.java │ │ │ ├── BaseActivity.java │ │ │ ├── BaseIntroActivity.java │ │ │ ├── BridgeActivity.java │ │ │ ├── BridgePreferencesActivity.java │ │ │ ├── EnhanceModeActivity.java │ │ │ ├── IntroActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── ShareBridgeActivity.java │ │ │ └── XposedBridgeActivity.java │ │ │ ├── adapters │ │ │ ├── ChooseAppAdapter.java │ │ │ ├── GridChooseAppAdapter.java │ │ │ ├── GridChooseResolveInfoAdapter.java │ │ │ ├── SingleChooseAdapter.java │ │ │ └── ViewPagerAdapter.java │ │ │ ├── api │ │ │ ├── CommonAPICallback.java │ │ │ ├── HttpConstant.java │ │ │ └── MyAPI.java │ │ │ ├── base │ │ │ └── MyApplication.java │ │ │ ├── beans │ │ │ ├── BridgeInfo.java │ │ │ └── UpdateInfo.java │ │ │ ├── databases │ │ │ └── AppConfig.java │ │ │ ├── dividers │ │ │ └── SpacesItemDecoration.java │ │ │ ├── engines │ │ │ ├── BaseEngine.java │ │ │ ├── EngineManager.java │ │ │ ├── IceBoxEngine.java │ │ │ └── RootEngine.java │ │ │ ├── fragments │ │ │ ├── BaseFragment.java │ │ │ ├── BridgePreferencesFragment.java │ │ │ ├── EnhanceModeFragment.java │ │ │ ├── MainFragment.java │ │ │ ├── PreferencesFragment.java │ │ │ └── intro │ │ │ │ ├── BaseIntroFragment.java │ │ │ │ ├── WelcomeFragment.java │ │ │ │ └── WorkModeFragment.java │ │ │ ├── modules │ │ │ ├── BridgeModule.java │ │ │ └── FixCoolapkShareModule.java │ │ │ ├── providers │ │ │ └── ConfigProvider.java │ │ │ ├── utils │ │ │ ├── AssetUtil.java │ │ │ ├── BridgeUtil.java │ │ │ ├── GsonUtil.java │ │ │ ├── MyViewHolder.java │ │ │ ├── OSUtils.java │ │ │ ├── PackageUtil.java │ │ │ └── Util.java │ │ │ └── widgets │ │ │ ├── MyViewPager.java │ │ │ ├── ShadowLayout.java │ │ │ └── SingleChooseView.java │ └── res │ │ ├── animator │ │ └── appbar_elevation.xml │ │ ├── drawable │ │ ├── avatar_huanchengfly.webp │ │ ├── avatar_icebox.webp │ │ ├── bg_placeholder_circle.xml │ │ ├── bg_radius_16dp.xml │ │ ├── bg_radius_4dp.xml │ │ ├── bg_round_top.xml │ │ ├── ic_alipay.xml │ │ ├── ic_archive.xml │ │ ├── ic_logo_github.xml │ │ ├── ic_round_arrow_back.xml │ │ ├── ic_round_check.xml │ │ ├── ic_round_check_circle.xml │ │ ├── ic_round_cloud_upload.xml │ │ ├── ic_round_error.xml │ │ ├── ic_round_info.xml │ │ ├── ic_round_local_mall.xml │ │ ├── ic_round_offline_bolt.xml │ │ ├── ic_round_warning.xml │ │ ├── ic_settings_applications.xml │ │ ├── text_color_secondary_selector.xml │ │ └── text_color_selector.xml │ │ ├── layout │ │ ├── about_page.xml │ │ ├── activity_about.xml │ │ ├── activity_bridge.xml │ │ ├── activity_bridge_preferences.xml │ │ ├── activity_enhance_mode.xml │ │ ├── activity_intro.xml │ │ ├── activity_main.xml │ │ ├── fragment_intro.xml │ │ ├── fragment_main.xml │ │ ├── item_about.xml │ │ ├── item_grid_choose.xml │ │ ├── item_single_choose.xml │ │ ├── layout_appbar.xml │ │ ├── layout_preference.xml │ │ ├── layout_preference_icon.xml │ │ ├── layout_single_choose.xml │ │ └── layout_work_mode.xml │ │ ├── menu │ │ └── menu_main_nav.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── values-v23 │ │ └── styles.xml │ │ ├── values-v27 │ │ └── styles.xml │ │ ├── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ ├── bridge_preferences.xml │ │ └── preferences.xml │ └── test │ └── java │ └── com │ └── huanchengfly │ └── icebridge │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | app/release/ 5 | 6 | # Files for the ART/Dalvik VM 7 | *.dex 8 | 9 | # Java class files 10 | *.class 11 | 12 | # Generated files 13 | bin/ 14 | gen/ 15 | out/ 16 | output.json 17 | 18 | # Gradle files 19 | .gradle/ 20 | build/ 21 | 22 | # Local configuration file (sdk path, etc) 23 | local.properties 24 | 25 | # Proguard folder generated by Eclipse 26 | proguard/ 27 | app/lint.xml 28 | app/proguard-project.txt 29 | 30 | # Log Files 31 | *.log 32 | 33 | # Android Studio Navigation editor temp files 34 | .navigation/ 35 | 36 | # Android Studio captures folder 37 | captures/ 38 | 39 | # IntelliJ 40 | *.iml 41 | .idea/ 42 | 43 | # Keystore files 44 | # Uncomment the following line if you do not want to check your keystore files in. 45 | *.jks 46 | 47 | # External native build folder generated in Android Studio 2.2 and later 48 | .externalNativeBuild 49 | 50 | # Google Services (e.g. APIs or Firebase) 51 | google-services.json 52 | 53 | # Freeline 54 | freeline.py 55 | freeline/ 56 | freeline_project_description.json 57 | 58 | # fastlane 59 | fastlane/report.xml 60 | fastlane/Preview.html 61 | fastlane/screenshots 62 | fastlane/test_output 63 | fastlane/readme.md 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #

Ice Bridge

2 | 3 | Ice Bridge 是一款冻结类应用的辅助应用,可以帮助您在其他 App 调用被冻结的 App 时自动解冻此 App。 -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'io.michaelrocks.paranoid' 3 | 4 | buildscript { 5 | repositories { 6 | jcenter() 7 | google() 8 | } 9 | dependencies { 10 | classpath 'io.michaelrocks:paranoid-gradle-plugin:0.2.5' 11 | } 12 | } 13 | 14 | android { 15 | repositories { 16 | flatDir { 17 | dirs 'libs' 18 | } 19 | } 20 | compileSdkVersion 28 21 | defaultConfig { 22 | applicationId "com.huanchengfly.icebridge" 23 | minSdkVersion 21 24 | targetSdkVersion 28 25 | versionCode 7 26 | versionName "0.0.7" 27 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 28 | } 29 | signingConfigs { 30 | release { 31 | keyAlias "huajikey" 32 | keyPassword "2g6-=armrm" 33 | storeFile file("C:\\Users\\xppxu\\huanchengfly.jks") 34 | storePassword "2g6-=armrm" 35 | v2SigningEnabled true 36 | } 37 | } 38 | buildTypes { 39 | release { 40 | minifyEnabled false 41 | shrinkResources false 42 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 43 | debuggable false 44 | jniDebuggable false 45 | signingConfig signingConfigs.release 46 | zipAlignEnabled true 47 | multiDexEnabled true 48 | } 49 | } 50 | compileOptions { 51 | sourceCompatibility = '1.8' 52 | targetCompatibility = '1.8' 53 | } 54 | } 55 | 56 | repositories { 57 | maven { url 'https://dl.bintray.com/heruoxin/icebox' } 58 | google() 59 | jcenter() 60 | } 61 | 62 | dependencies { 63 | implementation 'com.android.support:support-annotations:28.0.0' 64 | annotationProcessor "com.android.support:support-annotations:28.0.0" 65 | implementation 'com.github.bumptech.glide:glide:4.11.0' 66 | annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' 67 | implementation 'com.github.bumptech.glide:okhttp3-integration:4.11.0' 68 | implementation fileTree(dir: 'libs', include: ['*.jar']) 69 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 70 | compileOnly fileTree(dir: 'compileOnly', include: ['*.jar']) 71 | implementation 'androidx.appcompat:appcompat:1.1.0' 72 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 73 | implementation 'com.google.android.material:material:1.3.0-alpha02' 74 | implementation 'androidx.preference:preference:1.1.1' 75 | testImplementation 'junit:junit:4.13' 76 | androidTestImplementation 'androidx.test:runner:1.2.0' 77 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 78 | implementation 'com.catchingnow.icebox:SDK:1.0.5' 79 | implementation 'com.yanzhenjie:permission:2.0.0-rc12' 80 | implementation 'com.squareup.okhttp3:okhttp:4.5.0' 81 | implementation 'com.google.code.gson:gson:2.8.6' 82 | implementation 'org.litepal.android:java:3.0.0' 83 | implementation 'com.tsy:myokhttp:1.1.4' 84 | api(name: 'RootTools', ext: 'aar') 85 | api(name: 'RootShell', ext: 'aar') 86 | } 87 | -------------------------------------------------------------------------------- /app/compileOnly/api-82.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/compileOnly/api-82.jar -------------------------------------------------------------------------------- /app/libs/RootShell.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/libs/RootShell.aar -------------------------------------------------------------------------------- /app/libs/RootTools.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/libs/RootTools.aar -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/huanchengfly/icebridge/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge; 2 | 3 | import android.content.Context; 4 | import androidx.test.InstrumentationRegistry; 5 | import androidx.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.huanchengfly.icebridge", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/assets/bridge.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 11, 3 | "hooks": [ 4 | { 5 | "unfreeze_packages": [ 6 | "com.tencent.mm" 7 | ], 8 | "target_packages": [ 9 | "*" 10 | ], 11 | "target_methods": [ 12 | { 13 | "class_name": "com.tencent.mm.opensdk.openapi.BaseWXApiImplV10", 14 | "method_name": "sendReq", 15 | "params_classes": [ 16 | "com.tencent.mm.opensdk.modelbase.BaseReq" 17 | ], 18 | "return_value": { 19 | "type": "boolean", 20 | "value": "true" 21 | } 22 | } 23 | ] 24 | }, 25 | { 26 | "unfreeze_packages": [ 27 | "com.tencent.mm" 28 | ], 29 | "target_packages": [ 30 | "tv.danmaku.bili", 31 | "com.kuaikan.comic" 32 | ], 33 | "target_methods": [ 34 | { 35 | "class_name": "com.tencent.mm.opensdk.openapi.WXApiImplV10", 36 | "method_name": "sendReq", 37 | "params_classes": [ 38 | "com.tencent.mm.opensdk.modelbase.BaseReq" 39 | ], 40 | "return_value": { 41 | "type": "boolean", 42 | "value": "true" 43 | } 44 | } 45 | ] 46 | }, 47 | { 48 | "unfreeze_packages": [ 49 | "com.tencent.mobileqq", 50 | "com.tencent.tim" 51 | ], 52 | "target_packages": [ 53 | "*" 54 | ], 55 | "target_methods": [ 56 | { 57 | "class_name": "com.tencent.tauth.Tencent", 58 | "method_name": "login", 59 | "params_classes": [ 60 | "Activity", 61 | "String", 62 | "com.tencent.tauth.IUiListener" 63 | ], 64 | "return_value": { 65 | "type": "int", 66 | "value": "0" 67 | } 68 | }, 69 | { 70 | "class_name": "com.tencent.tauth.Tencent", 71 | "method_name": "login", 72 | "params_classes": [ 73 | "Activity", 74 | "String", 75 | "com.tencent.tauth.IUiListener", 76 | "boolean" 77 | ], 78 | "return_value": { 79 | "type": "int", 80 | "value": "0" 81 | } 82 | }, 83 | { 84 | "class_name": "com.tencent.tauth.Tencent", 85 | "method_name": "login", 86 | "params_classes": [ 87 | "androidx.fragment.app.Fragment", 88 | "String", 89 | "com.tencent.tauth.IUiListener" 90 | ], 91 | "return_value": { 92 | "type": "int", 93 | "value": "0" 94 | } 95 | }, 96 | { 97 | "class_name": "com.tencent.tauth.Tencent", 98 | "method_name": "login", 99 | "params_classes": [ 100 | "androidx.fragment.app.Fragment", 101 | "String", 102 | "com.tencent.tauth.IUiListener", 103 | "boolean" 104 | ], 105 | "return_value": { 106 | "type": "int", 107 | "value": "0" 108 | } 109 | } 110 | ] 111 | } 112 | ] 113 | } -------------------------------------------------------------------------------- /app/src/main/assets/litepal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/assets/xposed_init: -------------------------------------------------------------------------------- 1 | com.huanchengfly.icebridge.modules.BridgeModule 2 | com.huanchengfly.icebridge.modules.FixCoolapkShareModule -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/about/AboutPage.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.about; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.ActivityNotFoundException; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | 11 | import androidx.annotation.ColorInt; 12 | import androidx.annotation.DrawableRes; 13 | import androidx.annotation.LayoutRes; 14 | import androidx.annotation.Nullable; 15 | import androidx.annotation.StringRes; 16 | import androidx.recyclerview.widget.LinearLayoutManager; 17 | import androidx.recyclerview.widget.RecyclerView; 18 | 19 | import com.huanchengfly.about.adapter.AboutPageAdapter; 20 | import com.huanchengfly.icebridge.R; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | import static com.huanchengfly.about.AboutPage.Icon.NO_TINT; 26 | 27 | public class AboutPage { 28 | private final Context mContext; 29 | private final LayoutInflater mInflater; 30 | private final View mView; 31 | private final RecyclerView mRecyclerView; 32 | private final List itemList; 33 | private View mHeaderView; 34 | private AboutPageAdapter aboutPageAdapter; 35 | 36 | @SuppressLint("InflateParams") 37 | public AboutPage(Context context) { 38 | mContext = context; 39 | mInflater = LayoutInflater.from(context); 40 | mView = mInflater.inflate(R.layout.about_page, null); 41 | mRecyclerView = mView.findViewById(R.id.about_recycler_view); 42 | aboutPageAdapter = new AboutPageAdapter(mContext); 43 | aboutPageAdapter.setHasStableIds(true); 44 | mRecyclerView.setLayoutManager(new LinearLayoutManager(mContext)); 45 | mRecyclerView.setAdapter(aboutPageAdapter); 46 | itemList = new ArrayList<>(); 47 | } 48 | 49 | public View getHeaderView() { 50 | return mHeaderView; 51 | } 52 | 53 | public AboutPage setHeaderView(View view) { 54 | this.mHeaderView = view; 55 | return this; 56 | } 57 | 58 | public AboutPage setHeaderView(@LayoutRes int layoutId) { 59 | return this.setHeaderView(View.inflate(mContext, layoutId, null)); 60 | } 61 | 62 | public View into(ViewGroup viewGroup) { 63 | aboutPageAdapter.setItemList(itemList); 64 | aboutPageAdapter.setHeaderView(mHeaderView); 65 | viewGroup.addView(mView); 66 | return mView; 67 | } 68 | 69 | public AboutPage addTitle(CharSequence title) { 70 | itemList.add(new Item(title, true)); 71 | return this; 72 | } 73 | 74 | public AboutPage addTitle(CharSequence title, @ColorInt int color) { 75 | itemList.add(new Item(title, true).setTitleTextColor(color)); 76 | return this; 77 | } 78 | 79 | public AboutPage addTitle(@StringRes int resId) { 80 | return addTitle(mContext.getString(resId)); 81 | } 82 | 83 | public AboutPage addItem(Item item) { 84 | itemList.add(item); 85 | return this; 86 | } 87 | 88 | public static class Item { 89 | public static final int TYPE_TITLE = 10; 90 | public static final int TYPE_ITEM = 11; 91 | 92 | private int type; 93 | private CharSequence title; 94 | private CharSequence subtitle; 95 | private int titleTextColor; 96 | private int subtitleTextColor; 97 | private Icon icon; 98 | private View.OnClickListener onClickListener; 99 | 100 | public Item() { 101 | setTitleTextColor(-1); 102 | setSubtitleTextColor(-1); 103 | } 104 | 105 | public Item(CharSequence title) { 106 | this(title, false); 107 | } 108 | 109 | public Item(CharSequence title, boolean isTitle) { 110 | this(); 111 | setTitle(title); 112 | setSubtitle(null); 113 | setIcon((Icon) null); 114 | setType(isTitle ? TYPE_TITLE : TYPE_ITEM); 115 | if (isTitle) { 116 | setTitleTextColor(0xFF4477E0); 117 | } 118 | } 119 | 120 | public Item(CharSequence title, CharSequence subtitle) { 121 | this(); 122 | setTitle(title); 123 | setSubtitle(subtitle); 124 | setIcon((Icon) null); 125 | setType(TYPE_ITEM); 126 | } 127 | 128 | public Item(CharSequence title, CharSequence subtitle, @DrawableRes int drawableId) { 129 | this(title, subtitle, new Icon().setIconDrawable(drawableId, true)); 130 | } 131 | 132 | public Item(CharSequence title, CharSequence subtitle, @DrawableRes int drawableId, @ColorInt int tint) { 133 | this(title, subtitle, new Icon().setIconDrawable(drawableId, true).setIconTint(tint)); 134 | } 135 | 136 | public Item(CharSequence title, CharSequence subtitle, Icon icon) { 137 | this(); 138 | setTitle(title); 139 | setSubtitle(subtitle); 140 | setIcon(icon); 141 | setType(TYPE_ITEM); 142 | } 143 | 144 | public int getTitleTextColor() { 145 | return titleTextColor; 146 | } 147 | 148 | public Item setTitleTextColor(@ColorInt int titleTextColor) { 149 | this.titleTextColor = titleTextColor; 150 | return this; 151 | } 152 | 153 | public int getSubtitleTextColor() { 154 | return subtitleTextColor; 155 | } 156 | 157 | public Item setSubtitleTextColor(@ColorInt int subtitleTextColor) { 158 | this.subtitleTextColor = subtitleTextColor; 159 | return this; 160 | } 161 | 162 | public int getType() { 163 | return type; 164 | } 165 | 166 | public Item setType(int type) { 167 | this.type = type; 168 | return this; 169 | } 170 | 171 | public Item setIntent(Intent intent) { 172 | setOnClickListener(v -> { 173 | try { 174 | v.getContext().startActivity(intent); 175 | } catch (ActivityNotFoundException e) { 176 | e.printStackTrace(); 177 | } 178 | }); 179 | return this; 180 | } 181 | 182 | public View.OnClickListener getOnClickListener() { 183 | return onClickListener; 184 | } 185 | 186 | public Item setOnClickListener(View.OnClickListener onClickListener) { 187 | this.onClickListener = onClickListener; 188 | return this; 189 | } 190 | 191 | public CharSequence getTitle() { 192 | return title; 193 | } 194 | 195 | public Item setTitle(CharSequence title) { 196 | this.title = title; 197 | return this; 198 | } 199 | 200 | public CharSequence getSubtitle() { 201 | return subtitle; 202 | } 203 | 204 | public Item setSubtitle(CharSequence subtitle) { 205 | this.subtitle = subtitle; 206 | return this; 207 | } 208 | 209 | public Icon getIcon() { 210 | return icon; 211 | } 212 | 213 | public Item setIcon(int drawable) { 214 | return setIcon(drawable, true); 215 | } 216 | 217 | public Item setIcon(@Nullable Icon icon) { 218 | this.icon = icon; 219 | return this; 220 | } 221 | 222 | public Item setIcon(int drawable, boolean isIcon) { 223 | return setIcon(drawable, isIcon, NO_TINT); 224 | } 225 | 226 | public Item setIcon(int drawable, @ColorInt int tint) { 227 | return setIcon(drawable, true, tint); 228 | } 229 | 230 | public Item setIcon(int drawable, boolean isIcon, @ColorInt int tint) { 231 | return setIcon(new Icon().setIconDrawable(drawable, isIcon).setIconTint(tint)); 232 | } 233 | } 234 | 235 | public static class Icon { 236 | public static final int TYPE_ICON = 0; 237 | public static final int TYPE_IMAGE = 1; 238 | 239 | public static final int NO_TINT = -1; 240 | 241 | private int type; 242 | private int drawable; 243 | private int tint; 244 | 245 | public Icon() { 246 | setIconTint(NO_TINT); 247 | } 248 | 249 | public int getType() { 250 | return type; 251 | } 252 | 253 | public int getDrawable() { 254 | return drawable; 255 | } 256 | 257 | public int getIconTint() { 258 | return tint; 259 | } 260 | 261 | public Icon setIconTint(@ColorInt int color) { 262 | this.tint = color; 263 | return this; 264 | } 265 | 266 | public Icon setIconDrawable(@DrawableRes int drawable, boolean isIcon) { 267 | this.drawable = drawable; 268 | this.type = isIcon ? TYPE_ICON : TYPE_IMAGE; 269 | return this; 270 | } 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/about/ViewHolder.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.about; 2 | 3 | import android.content.Context; 4 | import android.util.SparseArray; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.TextView; 9 | 10 | import androidx.annotation.IdRes; 11 | import androidx.annotation.LayoutRes; 12 | import androidx.annotation.NonNull; 13 | import androidx.annotation.Nullable; 14 | import androidx.recyclerview.widget.RecyclerView; 15 | 16 | public class ViewHolder extends RecyclerView.ViewHolder { 17 | private SparseArray mViews; 18 | private View itemView; 19 | 20 | private ViewHolder(@NonNull View itemView) { 21 | super(itemView); 22 | this.itemView = itemView; 23 | this.mViews = new SparseArray(); 24 | } 25 | 26 | public static ViewHolder create(Context context, @LayoutRes int layoutId) { 27 | return create(context, layoutId, null); 28 | } 29 | 30 | public static ViewHolder create(Context context, @LayoutRes int layoutId, @Nullable ViewGroup parent) { 31 | return create(context, layoutId, parent, parent != null); 32 | } 33 | 34 | public static ViewHolder create(Context context, @LayoutRes int layoutId, @Nullable ViewGroup parent, boolean attachToRoot) { 35 | return new ViewHolder(LayoutInflater.from(context).inflate(layoutId, parent, attachToRoot)); 36 | } 37 | 38 | public static ViewHolder create(View view) { 39 | return new ViewHolder(view); 40 | } 41 | 42 | public T getView(@IdRes int id) { 43 | View view = this.mViews.get(id); 44 | if (view == null) { 45 | view = this.itemView.findViewById(id); 46 | this.mViews.put(id, view); 47 | } 48 | return (T) view; 49 | } 50 | 51 | public void setText(int viewId, CharSequence text) { 52 | TextView textView = this.getView(viewId); 53 | textView.setText(text); 54 | } 55 | 56 | public void setText(int viewId, int textId) { 57 | TextView textView = this.getView(viewId); 58 | textView.setText(textId); 59 | } 60 | 61 | public void setTextColor(int viewId, int colorId) { 62 | TextView textView = this.getView(viewId); 63 | textView.setTextColor(colorId); 64 | } 65 | 66 | public void setTextSize(int viewId, int size) { 67 | TextView textView = this.getView(viewId); 68 | textView.setTextSize(size); 69 | } 70 | 71 | public void setOnClickListener(int viewId, View.OnClickListener clickListener) { 72 | View view = this.getView(viewId); 73 | view.setOnClickListener(clickListener); 74 | } 75 | 76 | public void setVisibility(int viewId, int visibility) { 77 | View view = this.getView(viewId); 78 | view.setVisibility(visibility); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/about/adapter/AboutPageAdapter.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.about.adapter; 2 | 3 | import android.content.Context; 4 | import android.content.res.ColorStateList; 5 | import android.view.View; 6 | import android.widget.ImageView; 7 | import android.widget.RelativeLayout; 8 | 9 | import com.bumptech.glide.Glide; 10 | import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; 11 | import com.bumptech.glide.request.RequestOptions; 12 | import com.huanchengfly.about.AboutPage; 13 | import com.huanchengfly.about.ViewHolder; 14 | import com.huanchengfly.about.utils.DisplayUtil; 15 | import com.huanchengfly.icebridge.R; 16 | 17 | public class AboutPageAdapter extends BaseAdapter { 18 | public AboutPageAdapter(Context context) { 19 | super(context); 20 | } 21 | 22 | public long getItemId(int position) { 23 | return position; 24 | } 25 | 26 | @Override 27 | protected int getItemLayoutId() { 28 | return R.layout.item_about; 29 | } 30 | 31 | @Override 32 | protected void convert(ViewHolder viewHolder, AboutPage.Item item, int position) { 33 | int textColor = mContext.getResources().getColor(R.color.text_color_primary); 34 | int secondaryTextColor = mContext.getResources().getColor(R.color.text_color_secondary); 35 | viewHolder.setOnClickListener(R.id.item_about_root, item.getOnClickListener()); 36 | viewHolder.setVisibility(R.id.item_about_icon_holder, item.getIcon() == null ? View.GONE : View.VISIBLE); 37 | viewHolder.setVisibility(R.id.item_about_divider, item.getType() == AboutPage.Item.TYPE_TITLE && position > 0 ? View.VISIBLE : View.GONE); 38 | viewHolder.setVisibility(R.id.item_about_subtitle, item.getSubtitle() == null ? View.GONE : View.VISIBLE); 39 | viewHolder.setText(R.id.item_about_title, item.getTitle()); 40 | viewHolder.setText(R.id.item_about_subtitle, item.getSubtitle()); 41 | viewHolder.setTextColor(R.id.item_about_title, item.getTitleTextColor() != -1 ? item.getTitleTextColor() : textColor); 42 | viewHolder.setTextColor(R.id.item_about_subtitle, item.getSubtitleTextColor() != -1 ? item.getSubtitleTextColor() : secondaryTextColor); 43 | if (item.getType() == AboutPage.Item.TYPE_ITEM && item.getIcon() != null) { 44 | switch (item.getIcon().getType()) { 45 | case AboutPage.Icon.TYPE_ICON: 46 | ImageView iconView = viewHolder.getView(R.id.item_about_icon); 47 | iconView.setImageResource(item.getIcon().getDrawable()); 48 | if (item.getIcon().getIconTint() != AboutPage.Icon.NO_TINT) iconView.setImageTintList(ColorStateList.valueOf(item.getIcon().getIconTint())); 49 | RelativeLayout.LayoutParams iconLayoutParams = (RelativeLayout.LayoutParams) iconView.getLayoutParams(); 50 | iconLayoutParams.width = iconLayoutParams.height = DisplayUtil.dp2px(mContext, 24); 51 | iconView.setLayoutParams(iconLayoutParams); 52 | break; 53 | case AboutPage.Icon.TYPE_IMAGE: 54 | ImageView imageView = viewHolder.getView(R.id.item_about_icon); 55 | RelativeLayout.LayoutParams imageLayoutParams = (RelativeLayout.LayoutParams) imageView.getLayoutParams(); 56 | imageLayoutParams.width = imageLayoutParams.height = DisplayUtil.dp2px(mContext, 40); 57 | imageView.setLayoutParams(imageLayoutParams); 58 | Glide.with(mContext) 59 | .load(item.getIcon().getDrawable()) 60 | .apply(RequestOptions.circleCropTransform() 61 | .placeholder(R.drawable.bg_placeholder_circle)) 62 | .transition(DrawableTransitionOptions.withCrossFade()) 63 | .into(imageView); 64 | break; 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/about/adapter/BaseAdapter.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.about.adapter; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | 7 | import androidx.annotation.LayoutRes; 8 | import androidx.annotation.NonNull; 9 | import androidx.recyclerview.widget.RecyclerView; 10 | 11 | import com.huanchengfly.about.ViewHolder; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | public abstract class BaseAdapter extends RecyclerView.Adapter { 17 | private static final int TYPE_HEADER = 10; 18 | private static final int TYPE_COMMON = 11; 19 | protected Context mContext; 20 | private View mHeaderView; 21 | private List itemList; 22 | 23 | public BaseAdapter(Context context) { 24 | super(); 25 | mContext = context; 26 | itemList = new ArrayList<>(); 27 | mHeaderView = null; 28 | } 29 | 30 | public long getItemId(int position) { 31 | return position; 32 | } 33 | 34 | public List getItemList() { 35 | return itemList; 36 | } 37 | 38 | public BaseAdapter setItemList(List itemList) { 39 | this.itemList = itemList; 40 | notifyDataSetChanged(); 41 | return this; 42 | } 43 | 44 | public View getHeaderView() { 45 | return mHeaderView; 46 | } 47 | 48 | public void setHeaderView(View headerView) { 49 | mHeaderView = headerView; 50 | notifyItemInserted(0); 51 | } 52 | 53 | public void setHeaderView(@LayoutRes int layoutId) { 54 | mHeaderView = View.inflate(mContext, layoutId, null); 55 | notifyItemInserted(0); 56 | } 57 | 58 | @Override 59 | public final int getItemViewType(int position) { 60 | if (position == 0 && mHeaderView != null) { 61 | return TYPE_HEADER; 62 | } 63 | return TYPE_COMMON; 64 | } 65 | 66 | @NonNull 67 | @Override 68 | public final ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 69 | if (viewType == TYPE_HEADER && mHeaderView != null) { 70 | return ViewHolder.create(mHeaderView); 71 | } else { 72 | return ViewHolder.create(mContext, getItemLayoutId(), parent, false); 73 | } 74 | } 75 | 76 | @Override 77 | public final void onBindViewHolder(@NonNull ViewHolder holder, int position) { 78 | if (position == 0 && mHeaderView != null) { 79 | return; 80 | } 81 | if (mHeaderView != null) { 82 | position -= 1; 83 | } 84 | convert(holder, itemList.get(position), position); 85 | } 86 | 87 | 88 | @Override 89 | public final int getItemCount() { 90 | return (mHeaderView == null ? 0 : 1) + itemList.size(); 91 | } 92 | 93 | protected abstract int getItemLayoutId(); 94 | 95 | protected abstract void convert(ViewHolder viewHolder, T item, int position); 96 | } 97 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/about/utils/DisplayUtil.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.about.utils; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.util.DisplayMetrics; 6 | 7 | public class DisplayUtil { 8 | /** 9 | * 将px值转换为dp值 10 | */ 11 | public static int px2dp(Context context, float pxValue) { 12 | final float scale = context.getResources().getDisplayMetrics().density; 13 | return (int) (pxValue / scale + 0.5f); 14 | } 15 | 16 | /** 17 | * 将dp值转换为px值 18 | */ 19 | public static int dp2px(Context context, float dpValue) { 20 | final float scale = context.getResources().getDisplayMetrics().density; 21 | return (int) (dpValue * scale + 0.5f); 22 | } 23 | 24 | /** 25 | * 将px值转换为sp值 26 | */ 27 | public static int px2sp(Context context, float pxValue) { 28 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; 29 | return (int) (pxValue / fontScale + 0.5f); 30 | } 31 | 32 | /** 33 | * 将sp值转换为px值 34 | */ 35 | public static int sp2px(Context context, float spValue) { 36 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; 37 | return (int) (spValue * fontScale + 0.5f); 38 | } 39 | 40 | /** 41 | * 获取屏幕宽度 42 | */ 43 | public static int getScreenWidthPixels(Activity context) { 44 | DisplayMetrics metric = new DisplayMetrics(); 45 | context.getWindowManager().getDefaultDisplay().getMetrics(metric); 46 | return metric.widthPixels; 47 | } 48 | 49 | /** 50 | * 获取屏幕高度 51 | */ 52 | public static int getScreenHeightPixels(Activity context) { 53 | DisplayMetrics metric = new DisplayMetrics(); 54 | context.getWindowManager().getDefaultDisplay().getMetrics(metric); 55 | return metric.heightPixels; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/activities/AboutActivity.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.activities; 2 | 3 | import android.content.Intent; 4 | import android.net.Uri; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | 8 | import androidx.annotation.Nullable; 9 | 10 | import com.google.android.material.appbar.MaterialToolbar; 11 | import com.huanchengfly.about.AboutPage; 12 | import com.huanchengfly.icebridge.R; 13 | import com.huanchengfly.icebridge.utils.PackageUtil; 14 | 15 | public class AboutActivity extends BaseActivity { 16 | @Override 17 | protected void onCreate(@Nullable Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | MaterialToolbar mToolbar = findViewById(R.id.toolbar); 20 | setSupportActionBar(mToolbar); 21 | if (getSupportActionBar() != null) { 22 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 23 | getSupportActionBar().setTitle(R.string.title_about); 24 | } 25 | int colorIcon = getResources().getColor(R.color.colorAccent); 26 | View view = new AboutPage(this) 27 | .addTitle(getString(R.string.title_developer), colorIcon) 28 | .addItem(new AboutPage.Item("@幻了个城fly", "开发者") 29 | .setIcon(R.drawable.avatar_huanchengfly, false) 30 | .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("coolmarket://u/603089")) 31 | .setPackage("com.coolapk.market"))) 32 | .addItem(new AboutPage.Item("@不看私信和at的年轻开发者", "图标绘制") 33 | .setIcon(R.drawable.avatar_icebox, false) 34 | .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("coolmarket://u/184454")) 35 | .setPackage("com.coolapk.market"))) 36 | .addTitle(getString(R.string.title_app_info), colorIcon) 37 | .addItem(new AboutPage.Item("当前版本", PackageUtil.getVersionName(this), R.drawable.ic_round_info, colorIcon)) 38 | .addItem(new AboutPage.Item("去酷安查看", "检查更新、评论或反馈", R.drawable.ic_round_local_mall, colorIcon) 39 | .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.huanchengfly.icebridge")) 40 | .setPackage("com.coolapk.market"))) 41 | .addItem(new AboutPage.Item("源代码", null) 42 | .setIcon(R.drawable.ic_logo_github, colorIcon) 43 | .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/HuanCheng65/IceBridge")))) 44 | .addTitle(getString(R.string.title_support_me), colorIcon) 45 | .addItem(new AboutPage.Item("支付宝捐赠") 46 | .setIcon(R.drawable.ic_alipay, colorIcon) 47 | .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("https://qr.alipay.com/FKX06385UK8W8T8X2MG827")))) 48 | .addItem(new AboutPage.Item("支付宝领红包") 49 | .setIcon(R.drawable.ic_archive, colorIcon) 50 | .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("https://qr.alipay.com/c1x06336wvvmfwjwlzbq4a5")))) 51 | .addTitle(getString(R.string.title_license), colorIcon) 52 | .addItem(new AboutPage.Item("heruoxin/IceBox-SDK") 53 | .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/heruoxin/IceBox-SDK")))) 54 | .addItem(new AboutPage.Item("square/okhttp", "An HTTP client for Android, Kotlin, and Java.") 55 | .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/square/okhttp")))) 56 | .addItem(new AboutPage.Item("google/gson", "A Java serialization/deserialization library to convert Java Objects into JSON and back") 57 | .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/google/gson")))) 58 | .addItem(new AboutPage.Item("bumptech/glide", "An image loading and caching library for Android focused on smooth scrolling") 59 | .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/bumptech/glide")))) 60 | .addItem(new AboutPage.Item("tsy12321/MyOkHttp", "对Okhttp3进行二次封装,对外提供了POST请求、GET请求、PATCH请求、PUT请求、DELETE请求、上传文件、下载文件、取消请求、Raw/Json/Gson返回、后台下载管理等功能") 61 | .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/tsy12321/MyOkHttp")))) 62 | .addItem(new AboutPage.Item("LitePalFramework/LitePal", "An Android library that makes developers use SQLite database extremely easy.") 63 | .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/LitePalFramework/LitePal")))) 64 | .addItem(new AboutPage.Item("yanzhenjie/AndPermission", "\uD83C\uDF53 Permissions manager for Android platform.") 65 | .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/yanzhenjie/AndPermission")))) 66 | .into(findViewById(R.id.main)); 67 | } 68 | 69 | @Override 70 | protected int getLayoutId() { 71 | return R.layout.activity_about; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/activities/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.activities; 2 | 3 | import android.os.Bundle; 4 | import android.view.MenuItem; 5 | 6 | import androidx.annotation.LayoutRes; 7 | import androidx.annotation.Nullable; 8 | import androidx.appcompat.app.AppCompatActivity; 9 | import androidx.core.content.ContextCompat; 10 | 11 | public abstract class BaseActivity extends AppCompatActivity { 12 | public static final int NO_LAYOUT = 0; 13 | public final static int REQUEST_CODE_PERMISSIONS = 0x233; 14 | 15 | @Override 16 | public int checkSelfPermission(String permission) { 17 | return ContextCompat.checkSelfPermission(this, permission); 18 | } 19 | 20 | @Override 21 | protected void onCreate(@Nullable Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | if (getLayoutId() != NO_LAYOUT) { 24 | setContentView(getLayoutId()); 25 | } 26 | } 27 | 28 | @Override 29 | public boolean onOptionsItemSelected(MenuItem item) { 30 | if (item.getItemId() == android.R.id.home) { 31 | finish(); 32 | return true; 33 | } 34 | return super.onOptionsItemSelected(item); 35 | } 36 | 37 | @LayoutRes 38 | protected abstract int getLayoutId(); 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/activities/BaseIntroActivity.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.activities; 2 | 3 | import android.os.Bundle; 4 | import android.view.View; 5 | 6 | import androidx.appcompat.app.AppCompatActivity; 7 | import androidx.viewpager.widget.ViewPager; 8 | 9 | import com.google.android.material.button.MaterialButton; 10 | import com.huanchengfly.icebridge.R; 11 | import com.huanchengfly.icebridge.adapters.ViewPagerAdapter; 12 | import com.huanchengfly.icebridge.fragments.intro.BaseIntroFragment; 13 | import com.huanchengfly.icebridge.widgets.MyViewPager; 14 | 15 | public abstract class BaseIntroActivity extends AppCompatActivity implements View.OnClickListener, ViewPager.OnPageChangeListener { 16 | private MaterialButton nextButton; 17 | private MaterialButton prevButton; 18 | private ViewPagerAdapter adapter; 19 | private MyViewPager myViewPager; 20 | 21 | public void setNextButtonEnabled(boolean enabled) { 22 | if (enabled) { 23 | nextButton.setVisibility(View.VISIBLE); 24 | } else { 25 | nextButton.setVisibility(View.INVISIBLE); 26 | } 27 | } 28 | 29 | private void refreshButtonState(int position) { 30 | BaseIntroFragment introFragment = (BaseIntroFragment) adapter.getItem(position); 31 | setNextButtonEnabled(introFragment.getDefaultNextButtonEnabled()); 32 | if (introFragment.getNextButton() != null) { 33 | nextButton.setText(introFragment.getNextButton()); 34 | } else { 35 | if (position + 1 >= adapter.getCount()) { 36 | nextButton.setText(R.string.button_next_last); 37 | } else { 38 | nextButton.setText(R.string.button_next_default); 39 | } 40 | } 41 | if (position > 0 && adapter.getCount() > 0) { 42 | prevButton.setVisibility(View.VISIBLE); 43 | } else { 44 | prevButton.setVisibility(View.INVISIBLE); 45 | } 46 | } 47 | 48 | @Override 49 | protected void onCreate(Bundle savedInstanceState) { 50 | super.onCreate(savedInstanceState); 51 | setContentView(R.layout.activity_intro); 52 | nextButton = findViewById(R.id.button_next); 53 | prevButton = findViewById(R.id.button_prev); 54 | nextButton.setOnClickListener(this); 55 | prevButton.setOnClickListener(this); 56 | myViewPager = findViewById(R.id.view_pager); 57 | adapter = new ViewPagerAdapter(getSupportFragmentManager()); 58 | myViewPager.addOnPageChangeListener(this); 59 | myViewPager.setCanScroll(false); 60 | onCreateIntro(); 61 | myViewPager.setAdapter(adapter); 62 | } 63 | 64 | public ViewPagerAdapter getAdapter() { 65 | return adapter; 66 | } 67 | 68 | protected abstract void onCreateIntro(); 69 | 70 | @Override 71 | public void onClick(View v) { 72 | switch (v.getId()) { 73 | case R.id.button_next: 74 | if (adapter.getCurrentFragmentPosition() + 1 >= adapter.getCount()) { 75 | finish(); 76 | onFinish(); 77 | break; 78 | } 79 | if (((BaseIntroFragment) adapter.getItem(adapter.getCurrentFragmentPosition() + 1)).onNext()) { 80 | break; 81 | } 82 | myViewPager.setCurrentItem(adapter.getCurrentFragmentPosition() + 1, true); 83 | break; 84 | case R.id.button_prev: 85 | if (adapter.getCurrentFragmentPosition() > 0) { 86 | myViewPager.setCurrentItem(adapter.getCurrentFragmentPosition() - 1, true); 87 | } 88 | break; 89 | } 90 | } 91 | 92 | protected void onFinish() {} 93 | 94 | @Override 95 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {} 96 | 97 | @Override 98 | public void onPageSelected(int position) { 99 | refreshButtonState(position); 100 | ((BaseIntroFragment) adapter.getItem(position)).onVisible(); 101 | } 102 | 103 | @Override 104 | public void onPageScrollStateChanged(int state) {} 105 | 106 | public void next() { 107 | if (myViewPager.getCurrentItem() + 1 < adapter.getCount()) { 108 | myViewPager.setCurrentItem(myViewPager.getCurrentItem() + 1, true); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/activities/BridgeActivity.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.activities; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.ComponentName; 5 | import android.content.Intent; 6 | import android.content.pm.PackageManager; 7 | import android.content.pm.ResolveInfo; 8 | import android.os.Build; 9 | import android.os.Bundle; 10 | import android.widget.Toast; 11 | 12 | import androidx.annotation.Nullable; 13 | import androidx.appcompat.app.AlertDialog; 14 | import androidx.recyclerview.widget.GridLayoutManager; 15 | import androidx.recyclerview.widget.RecyclerView; 16 | import androidx.recyclerview.widget.SimpleItemAnimator; 17 | 18 | import com.catchingnow.icebox.sdk_client.IceBox; 19 | import com.huanchengfly.about.utils.DisplayUtil; 20 | import com.huanchengfly.icebridge.R; 21 | import com.huanchengfly.icebridge.adapters.GridChooseResolveInfoAdapter; 22 | import com.huanchengfly.icebridge.dividers.SpacesItemDecoration; 23 | import com.huanchengfly.icebridge.engines.BaseEngine; 24 | import com.huanchengfly.icebridge.engines.EngineManager; 25 | import com.huanchengfly.icebridge.utils.BridgeUtil; 26 | 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | 30 | import io.michaelrocks.paranoid.Obfuscate; 31 | 32 | @Obfuscate 33 | public class BridgeActivity extends BaseActivity { 34 | public static final String TAG = "Bridge"; 35 | 36 | private BaseEngine mEngine; 37 | 38 | private void start() { 39 | mEngine = EngineManager.getEngine(this); 40 | if (mEngine.checkPermission() != PackageManager.PERMISSION_GRANTED) { 41 | mEngine.requestPermission(new BaseEngine.Callback() { 42 | @Override 43 | public void onSuccess() { 44 | bridge(); 45 | } 46 | 47 | @Override 48 | public void onFailure() { 49 | Toast.makeText(BridgeActivity.this, R.string.request_permission_failure, Toast.LENGTH_SHORT).show(); 50 | finish(); 51 | } 52 | }); 53 | } else bridge(); 54 | } 55 | 56 | @Override 57 | protected void onCreate(@Nullable Bundle savedInstanceState) { 58 | super.onCreate(savedInstanceState); 59 | BridgeUtil.init(this); 60 | start(); 61 | } 62 | 63 | private int getSpanCount(int itemCount) { 64 | if (itemCount < 2) { 65 | return 1; 66 | } else if (itemCount == 2) { 67 | return 2; 68 | } else { 69 | return 3; 70 | } 71 | } 72 | 73 | @SuppressLint("WrongConstant") 74 | private void bridge() { 75 | Intent intent = getIntent(); 76 | if (intent.getScheme() == null) { 77 | finish(); 78 | return; 79 | } 80 | intent.setComponent(null); 81 | List resolveInfoList; 82 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 83 | resolveInfoList = getPackageManager().queryIntentActivities(intent, PackageManager.GET_ACTIVITIES | PackageManager.MATCH_DISABLED_COMPONENTS); 84 | } else { 85 | resolveInfoList = getPackageManager().queryIntentActivities(intent, PackageManager.GET_ACTIVITIES | PackageManager.GET_DISABLED_COMPONENTS); 86 | } 87 | List resolveInfos = new ArrayList<>(); 88 | List enabledResolveInfos = new ArrayList<>(); 89 | for (ResolveInfo resolveInfo : resolveInfoList) { 90 | if (resolveInfo.activityInfo == null) { 91 | continue; 92 | } 93 | if (resolveInfo.activityInfo.packageName.equalsIgnoreCase(getPackageName())) { 94 | continue; 95 | } 96 | if (IceBox.getAppEnabledSetting(resolveInfo.activityInfo.applicationInfo) == 0) { 97 | enabledResolveInfos.add(resolveInfo); 98 | } else { 99 | resolveInfos.add(resolveInfo); 100 | } 101 | } 102 | boolean showEnableApp = getSharedPreferences("preferences", MODE_PRIVATE).getBoolean("show_enable_app", true); 103 | if (showEnableApp) resolveInfos.addAll(enabledResolveInfos); 104 | if (resolveInfos.size() == 0) { 105 | Toast.makeText(this, R.string.no_apps_for_bridge, Toast.LENGTH_SHORT).show(); 106 | finish(); 107 | } else if (resolveInfos.size() == 1) { 108 | start(intent, resolveInfos.get(0)); 109 | } else { 110 | RecyclerView recyclerView = new RecyclerView(this); 111 | recyclerView.addItemDecoration(new SpacesItemDecoration(DisplayUtil.dp2px(this, 8))); 112 | recyclerView.getItemAnimator().setAddDuration(0); 113 | recyclerView.getItemAnimator().setChangeDuration(0); 114 | recyclerView.getItemAnimator().setMoveDuration(0); 115 | recyclerView.getItemAnimator().setRemoveDuration(0); 116 | ((SimpleItemAnimator) recyclerView.getItemAnimator()).setSupportsChangeAnimations(false); 117 | recyclerView.setLayoutManager(new GridLayoutManager(this, getSpanCount(resolveInfos.size()))); 118 | GridChooseResolveInfoAdapter appAdapter = new GridChooseResolveInfoAdapter(this, resolveInfos); 119 | recyclerView.setAdapter(appAdapter); 120 | new AlertDialog.Builder(this) 121 | .setTitle(R.string.title_select_apps) 122 | .setView(recyclerView) 123 | .setCancelable(false) 124 | .setPositiveButton(R.string.button_sure, (dialog, which) -> { 125 | start(intent, resolveInfos.get(appAdapter.getSelectedPosition())); 126 | }) 127 | .show(); 128 | } 129 | } 130 | 131 | private void start(Intent intent, ResolveInfo resolveInfo) { 132 | mEngine.setEnabled(resolveInfo.activityInfo.packageName, true, new BaseEngine.Callback() { 133 | @Override 134 | public void onSuccess() { 135 | intent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name)); 136 | startActivity(intent); 137 | finish(); 138 | } 139 | 140 | @Override 141 | public void onFailure() { 142 | finish(); 143 | } 144 | }); 145 | } 146 | 147 | @Override 148 | protected int getLayoutId() { 149 | return R.layout.activity_bridge; 150 | } 151 | } -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/activities/BridgePreferencesActivity.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.activities; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.google.android.material.appbar.MaterialToolbar; 6 | import com.huanchengfly.icebridge.R; 7 | 8 | public class BridgePreferencesActivity extends BaseActivity { 9 | @Override 10 | protected void onCreate(Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | MaterialToolbar mToolbar = findViewById(R.id.toolbar); 13 | setSupportActionBar(mToolbar); 14 | if (getSupportActionBar() != null) { 15 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 16 | getSupportActionBar().setTitle(R.string.title_bridge_preferences); 17 | } 18 | } 19 | 20 | @Override 21 | protected int getLayoutId() { 22 | return R.layout.activity_bridge_preferences; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/activities/EnhanceModeActivity.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.activities; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.google.android.material.appbar.MaterialToolbar; 6 | import com.huanchengfly.icebridge.R; 7 | 8 | public class EnhanceModeActivity extends BaseActivity { 9 | @Override 10 | protected void onCreate(Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | MaterialToolbar mToolbar = findViewById(R.id.toolbar); 13 | setSupportActionBar(mToolbar); 14 | if (getSupportActionBar() != null) { 15 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 16 | getSupportActionBar().setTitle(R.string.title_enhance_mode); 17 | } 18 | } 19 | 20 | @Override 21 | protected int getLayoutId() { 22 | return R.layout.activity_enhance_mode; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/activities/IntroActivity.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.activities; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Intent; 5 | 6 | import com.huanchengfly.icebridge.fragments.intro.WelcomeFragment; 7 | import com.huanchengfly.icebridge.fragments.intro.WorkModeFragment; 8 | 9 | public class IntroActivity extends BaseIntroActivity { 10 | @Override 11 | protected void onCreateIntro() { 12 | getAdapter().addFragment(new WelcomeFragment()); 13 | getAdapter().addFragment(new WorkModeFragment()); 14 | } 15 | 16 | @SuppressLint("ApplySharedPref") 17 | @Override 18 | protected void onFinish() { 19 | getSharedPreferences("app_data", MODE_PRIVATE).edit().putBoolean("first", false).commit(); 20 | startActivity(new Intent(this, MainActivity.class)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/activities/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.activities; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | 6 | import com.google.android.material.appbar.MaterialToolbar; 7 | import com.huanchengfly.icebridge.R; 8 | import com.huanchengfly.icebridge.utils.BridgeUtil; 9 | 10 | public class MainActivity extends BaseActivity { 11 | @Override 12 | protected void onCreate(Bundle savedInstanceState) { 13 | super.onCreate(savedInstanceState); 14 | if (getSharedPreferences("app_data", MODE_PRIVATE).getBoolean("first", true)) { 15 | startActivity(new Intent(this, IntroActivity.class)); 16 | finish(); 17 | return; 18 | } 19 | init(); 20 | MaterialToolbar mToolbar = findViewById(R.id.toolbar); 21 | setSupportActionBar(mToolbar); 22 | if (getSupportActionBar() != null) { 23 | getSupportActionBar().setDisplayHomeAsUpEnabled(false); 24 | getSupportActionBar().setTitle(R.string.app_name); 25 | } 26 | } 27 | 28 | private void init() { 29 | BridgeUtil.init(this); 30 | } 31 | 32 | @Override 33 | protected int getLayoutId() { 34 | return R.layout.activity_main; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/activities/ShareBridgeActivity.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.activities; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.ComponentName; 5 | import android.content.Intent; 6 | import android.content.pm.PackageManager; 7 | import android.content.pm.ResolveInfo; 8 | import android.os.Build; 9 | import android.os.Bundle; 10 | import android.widget.Toast; 11 | 12 | import androidx.annotation.Nullable; 13 | import androidx.appcompat.app.AlertDialog; 14 | import androidx.recyclerview.widget.GridLayoutManager; 15 | import androidx.recyclerview.widget.RecyclerView; 16 | import androidx.recyclerview.widget.SimpleItemAnimator; 17 | 18 | import com.catchingnow.icebox.sdk_client.IceBox; 19 | import com.huanchengfly.about.utils.DisplayUtil; 20 | import com.huanchengfly.icebridge.R; 21 | import com.huanchengfly.icebridge.adapters.GridChooseResolveInfoAdapter; 22 | import com.huanchengfly.icebridge.dividers.SpacesItemDecoration; 23 | import com.huanchengfly.icebridge.engines.BaseEngine; 24 | import com.huanchengfly.icebridge.engines.EngineManager; 25 | 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | 29 | import io.michaelrocks.paranoid.Obfuscate; 30 | 31 | @Obfuscate 32 | public class ShareBridgeActivity extends BaseActivity { 33 | private BaseEngine mEngine; 34 | 35 | @SuppressLint("WrongConstant") 36 | @Override 37 | protected void onCreate(@Nullable Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | mEngine = EngineManager.getEngine(this); 40 | Intent intent = getIntent(); 41 | intent.setComponent(null); 42 | List resolveInfoList; 43 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 44 | resolveInfoList = getPackageManager().queryIntentActivities(intent, PackageManager.GET_ACTIVITIES | PackageManager.MATCH_DISABLED_COMPONENTS); 45 | } else { 46 | resolveInfoList = getPackageManager().queryIntentActivities(intent, PackageManager.GET_ACTIVITIES | PackageManager.GET_DISABLED_COMPONENTS); 47 | } 48 | List resolveInfos = new ArrayList<>(); 49 | List enabledResolveInfos = new ArrayList<>(); 50 | for (ResolveInfo resolveInfo : resolveInfoList) { 51 | if (resolveInfo.activityInfo == null) { 52 | continue; 53 | } 54 | if (resolveInfo.activityInfo.packageName.equalsIgnoreCase(getPackageName())) { 55 | continue; 56 | } 57 | if (IceBox.getAppEnabledSetting(resolveInfo.activityInfo.applicationInfo) == 0) { 58 | enabledResolveInfos.add(resolveInfo); 59 | } else { 60 | resolveInfos.add(resolveInfo); 61 | } 62 | } 63 | boolean showEnableApp = getSharedPreferences("preferences", MODE_PRIVATE).getBoolean("show_enable_app", true); 64 | if (showEnableApp) resolveInfos.addAll(enabledResolveInfos); 65 | if (resolveInfos.size() == 0) { 66 | Toast.makeText(this, R.string.no_apps_for_share, Toast.LENGTH_SHORT).show(); 67 | finish(); 68 | } else if (resolveInfos.size() == 1) { 69 | start(intent, resolveInfos.get(0)); 70 | } else { 71 | RecyclerView recyclerView = new RecyclerView(this); 72 | recyclerView.addItemDecoration(new SpacesItemDecoration(DisplayUtil.dp2px(this, 8))); 73 | recyclerView.getItemAnimator().setAddDuration(0); 74 | recyclerView.getItemAnimator().setChangeDuration(0); 75 | recyclerView.getItemAnimator().setMoveDuration(0); 76 | recyclerView.getItemAnimator().setRemoveDuration(0); 77 | ((SimpleItemAnimator) recyclerView.getItemAnimator()).setSupportsChangeAnimations(false); 78 | recyclerView.setLayoutManager(new GridLayoutManager(this, getSpanCount(resolveInfos.size()))); 79 | GridChooseResolveInfoAdapter appAdapter = new GridChooseResolveInfoAdapter(this, resolveInfos); 80 | recyclerView.setAdapter(appAdapter); 81 | new AlertDialog.Builder(this) 82 | .setTitle(R.string.title_select_apps) 83 | .setView(recyclerView) 84 | //.setCancelable(false) 85 | .setPositiveButton(R.string.button_sure, (dialog, which) -> { 86 | start(intent, resolveInfos.get(appAdapter.getSelectedPosition())); 87 | }) 88 | .setOnDismissListener(dialog -> finish()) 89 | .show(); 90 | } 91 | } 92 | 93 | private int getSpanCount(int itemCount) { 94 | if (itemCount < 2) { 95 | return 1; 96 | } else if (itemCount == 2) { 97 | return 2; 98 | } else { 99 | return 3; 100 | } 101 | } 102 | 103 | @Override 104 | protected int getLayoutId() { 105 | return R.layout.activity_bridge; 106 | } 107 | 108 | 109 | private void start(Intent intent, ResolveInfo resolveInfo) { 110 | mEngine.setEnabled(resolveInfo.activityInfo.packageName, true, new BaseEngine.Callback() { 111 | @Override 112 | public void onSuccess() { 113 | intent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name)); 114 | startActivity(intent); 115 | finish(); 116 | } 117 | 118 | @Override 119 | public void onFailure() { 120 | finish(); 121 | } 122 | }); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/activities/XposedBridgeActivity.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.activities; 2 | 3 | import android.content.pm.PackageManager; 4 | import android.os.Bundle; 5 | import android.widget.Toast; 6 | 7 | import androidx.annotation.Nullable; 8 | 9 | import com.huanchengfly.icebridge.R; 10 | import com.huanchengfly.icebridge.base.MyApplication; 11 | import com.huanchengfly.icebridge.engines.BaseEngine; 12 | import com.huanchengfly.icebridge.engines.EngineManager; 13 | import com.huanchengfly.icebridge.utils.PackageUtil; 14 | 15 | import io.michaelrocks.paranoid.Obfuscate; 16 | 17 | @Obfuscate 18 | public class XposedBridgeActivity extends BaseActivity { 19 | public static final String INTENT_ACTION = "com.huanchengfly.icebridge.intent.action.XPOSED_BRIDGE"; 20 | public static final String EXTRA_PACKAGE = "package"; 21 | 22 | private String packageName; 23 | 24 | private BaseEngine mEngine; 25 | 26 | @Override 27 | protected void onCreate(@Nullable Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | packageName = getIntent().getStringExtra(EXTRA_PACKAGE); 30 | mEngine = EngineManager.getEngine(this); 31 | start(); 32 | } 33 | 34 | private void bridge() { 35 | if (packageName == null) { 36 | finish(); 37 | return; 38 | } 39 | mEngine.setEnabled(packageName, true, new BaseEngine.Callback() { 40 | @Override 41 | public void onSuccess() { 42 | Toast.makeText(MyApplication.getInstance(), getString(R.string.toast_retry_share, PackageUtil.getAppName(XposedBridgeActivity.this, packageName)), Toast.LENGTH_SHORT).show(); 43 | finish(); 44 | } 45 | 46 | @Override 47 | public void onFailure() { 48 | finish(); 49 | } 50 | }); 51 | } 52 | 53 | private void requestPermission() { 54 | mEngine.requestPermission(new BaseEngine.Callback() { 55 | @Override 56 | public void onSuccess() { 57 | bridge(); 58 | } 59 | 60 | @Override 61 | public void onFailure() { 62 | Toast.makeText(XposedBridgeActivity.this, R.string.toast_permission_cancel, Toast.LENGTH_SHORT).show(); 63 | finish(); 64 | } 65 | }); 66 | } 67 | 68 | private void start() { 69 | if (mEngine.checkPermission() != PackageManager.PERMISSION_GRANTED) { 70 | requestPermission(); 71 | return; 72 | } 73 | bridge(); 74 | } 75 | 76 | @Override 77 | protected int getLayoutId() { 78 | return R.layout.activity_bridge; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/adapters/ChooseAppAdapter.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.adapters; 2 | 3 | import android.content.Context; 4 | import android.view.ViewGroup; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.recyclerview.widget.RecyclerView; 8 | 9 | import com.huanchengfly.icebridge.R; 10 | import com.huanchengfly.icebridge.utils.MyViewHolder; 11 | import com.huanchengfly.icebridge.utils.PackageUtil; 12 | import com.huanchengfly.icebridge.widgets.SingleChooseView; 13 | 14 | import java.lang.ref.WeakReference; 15 | import java.util.List; 16 | 17 | public class ChooseAppAdapter extends RecyclerView.Adapter { 18 | private WeakReference contextWeakReference; 19 | private int selectedPos; 20 | private List packages; 21 | private OnItemSelectedListener onItemSelectedListener; 22 | 23 | public int getSelectedPosition() { 24 | return selectedPos; 25 | } 26 | 27 | public OnItemSelectedListener getOnItemSelectedListener() { 28 | return onItemSelectedListener; 29 | } 30 | 31 | public void setOnItemSelectedListener(OnItemSelectedListener onItemSelectedListener) { 32 | this.onItemSelectedListener = onItemSelectedListener; 33 | } 34 | 35 | public ChooseAppAdapter(Context context, List packages) { 36 | this(context, packages, -1); 37 | } 38 | 39 | public ChooseAppAdapter(Context context, List packages, int defaultSelectedPos) { 40 | this.contextWeakReference = new WeakReference<>(context); 41 | this.packages = packages; 42 | this.selectedPos = defaultSelectedPos; 43 | } 44 | 45 | public Context getContext() { 46 | return contextWeakReference.get(); 47 | } 48 | 49 | @NonNull 50 | @Override 51 | public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 52 | return MyViewHolder.create(getContext(), R.layout.item_single_choose); 53 | } 54 | 55 | @Override 56 | public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { 57 | String pkgName = packages.get(position); 58 | SingleChooseView singleChooseView = holder.getView(R.id.single_choose); 59 | singleChooseView.setOnClickListener(v -> { 60 | ((SingleChooseView) v).setChecked(true); 61 | int oldPos = selectedPos + 0; 62 | notifyItemChanged(oldPos); 63 | selectedPos = position; 64 | if (getOnItemSelectedListener() != null) { 65 | getOnItemSelectedListener().onSelected(position, pkgName); 66 | } 67 | }); 68 | singleChooseView.setChecked(position == selectedPos); 69 | singleChooseView.setTitle(PackageUtil.getAppName(getContext(), pkgName)); 70 | singleChooseView.setIcon(PackageUtil.getAppIcon(getContext(), pkgName)); 71 | singleChooseView.setSubtitle(pkgName); 72 | } 73 | 74 | @Override 75 | public int getItemCount() { 76 | return packages.size(); 77 | } 78 | 79 | public interface OnItemSelectedListener { 80 | void onSelected(int position, String pkgName); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/adapters/GridChooseAppAdapter.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.adapters; 2 | 3 | import android.content.Context; 4 | import android.content.res.ColorStateList; 5 | import android.view.ViewGroup; 6 | import android.widget.ImageView; 7 | import android.widget.TextView; 8 | 9 | import androidx.annotation.NonNull; 10 | import androidx.recyclerview.widget.RecyclerView; 11 | 12 | import com.huanchengfly.icebridge.R; 13 | import com.huanchengfly.icebridge.utils.MyViewHolder; 14 | import com.huanchengfly.icebridge.utils.PackageUtil; 15 | 16 | import java.lang.ref.WeakReference; 17 | import java.util.List; 18 | 19 | public class GridChooseAppAdapter extends RecyclerView.Adapter { 20 | private WeakReference contextWeakReference; 21 | private int selectedPos; 22 | private List packages; 23 | private OnItemSelectedListener onItemSelectedListener; 24 | 25 | public int getSelectedPosition() { 26 | return selectedPos; 27 | } 28 | 29 | public OnItemSelectedListener getOnItemSelectedListener() { 30 | return onItemSelectedListener; 31 | } 32 | 33 | public void setOnItemSelectedListener(OnItemSelectedListener onItemSelectedListener) { 34 | this.onItemSelectedListener = onItemSelectedListener; 35 | } 36 | 37 | public GridChooseAppAdapter(Context context, List packages) { 38 | this(context, packages, -1); 39 | } 40 | 41 | public GridChooseAppAdapter(Context context, List packages, int defaultSelectedPos) { 42 | this.contextWeakReference = new WeakReference<>(context); 43 | this.packages = packages; 44 | this.selectedPos = defaultSelectedPos; 45 | } 46 | 47 | public Context getContext() { 48 | return contextWeakReference.get(); 49 | } 50 | 51 | @NonNull 52 | @Override 53 | public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 54 | return MyViewHolder.create(getContext(), R.layout.item_grid_choose); 55 | } 56 | 57 | @Override 58 | public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { 59 | if (position == selectedPos) { 60 | holder.itemView.setBackgroundTintList(ColorStateList.valueOf(getContext().getResources().getColor(R.color.colorPrimaryTrans))); 61 | } else { 62 | holder.itemView.setBackgroundTintList(ColorStateList.valueOf(getContext().getResources().getColor(R.color.white))); 63 | } 64 | String pkgName = packages.get(position); 65 | TextView title = holder.getView(R.id.title); 66 | TextView subtitle = holder.getView(R.id.subtitle); 67 | ImageView icon = holder.getView(R.id.icon); 68 | holder.itemView.setOnClickListener(v -> { 69 | int oldPos = selectedPos; 70 | notifyItemChanged(oldPos); 71 | selectedPos = position; 72 | notifyItemChanged(selectedPos); 73 | if (getOnItemSelectedListener() != null) { 74 | getOnItemSelectedListener().onSelected(position, pkgName); 75 | } 76 | }); 77 | title.setText(PackageUtil.getAppName(getContext(), pkgName)); 78 | icon.setImageDrawable(PackageUtil.getAppIcon(getContext(), pkgName)); 79 | subtitle.setText(pkgName); 80 | } 81 | 82 | @Override 83 | public int getItemCount() { 84 | return packages.size(); 85 | } 86 | 87 | public interface OnItemSelectedListener { 88 | void onSelected(int position, String pkgName); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/adapters/GridChooseResolveInfoAdapter.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.adapters; 2 | 3 | import android.content.Context; 4 | import android.content.pm.ResolveInfo; 5 | import android.content.res.ColorStateList; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.ImageView; 9 | import android.widget.TextView; 10 | 11 | import androidx.annotation.NonNull; 12 | import androidx.recyclerview.widget.RecyclerView; 13 | 14 | import com.catchingnow.icebox.sdk_client.IceBox; 15 | import com.huanchengfly.icebridge.R; 16 | import com.huanchengfly.icebridge.utils.MyViewHolder; 17 | 18 | import java.lang.ref.WeakReference; 19 | import java.util.List; 20 | 21 | public class GridChooseResolveInfoAdapter extends RecyclerView.Adapter { 22 | private WeakReference contextWeakReference; 23 | private int selectedPos; 24 | private List mResolveInfos; 25 | private OnItemSelectedListener mOnItemSelectedListener; 26 | 27 | public int getSelectedPosition() { 28 | return selectedPos; 29 | } 30 | 31 | public OnItemSelectedListener getOnItemSelectedListener() { 32 | return mOnItemSelectedListener; 33 | } 34 | 35 | public void setOnItemSelectedListener(OnItemSelectedListener onItemSelectedListener) { 36 | this.mOnItemSelectedListener = onItemSelectedListener; 37 | } 38 | 39 | public GridChooseResolveInfoAdapter(Context context, List resolveInfos) { 40 | this(context, resolveInfos, -1); 41 | } 42 | 43 | public GridChooseResolveInfoAdapter(Context context, List resolveInfos, int defaultSelectedPos) { 44 | this.contextWeakReference = new WeakReference<>(context); 45 | this.mResolveInfos = resolveInfos; 46 | this.selectedPos = defaultSelectedPos; 47 | } 48 | 49 | public Context getContext() { 50 | return contextWeakReference.get(); 51 | } 52 | 53 | @NonNull 54 | @Override 55 | public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 56 | return MyViewHolder.create(getContext(), R.layout.item_grid_choose); 57 | } 58 | 59 | @Override 60 | public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { 61 | if (position == selectedPos) { 62 | holder.itemView.setBackgroundTintList(ColorStateList.valueOf(getContext().getResources().getColor(R.color.colorPrimaryTrans))); 63 | } else { 64 | holder.itemView.setBackgroundTintList(ColorStateList.valueOf(getContext().getResources().getColor(R.color.white))); 65 | } 66 | ResolveInfo resolveInfo = mResolveInfos.get(position); 67 | TextView title = holder.getView(R.id.title); 68 | TextView subtitle = holder.getView(R.id.subtitle); 69 | ImageView icon = holder.getView(R.id.icon); 70 | View disabled = holder.getView(R.id.disabled); 71 | holder.itemView.setOnClickListener(v -> { 72 | int oldPos = selectedPos; 73 | notifyItemChanged(oldPos); 74 | selectedPos = position; 75 | notifyItemChanged(position); 76 | if (getOnItemSelectedListener() != null) { 77 | getOnItemSelectedListener().onSelected(position, resolveInfo); 78 | } 79 | }); 80 | title.setText(resolveInfo.loadLabel(getContext().getPackageManager())); 81 | icon.setImageDrawable(resolveInfo.loadIcon(getContext().getPackageManager())); 82 | subtitle.setVisibility(View.GONE); 83 | if (IceBox.getAppEnabledSetting(resolveInfo.activityInfo.applicationInfo) == 0) { 84 | disabled.setVisibility(View.GONE); 85 | } else { 86 | disabled.setVisibility(View.VISIBLE); 87 | } 88 | } 89 | 90 | @Override 91 | public int getItemCount() { 92 | return mResolveInfos.size(); 93 | } 94 | 95 | public interface OnItemSelectedListener { 96 | void onSelected(int position, T t); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/adapters/SingleChooseAdapter.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.adapters; 2 | 3 | import android.content.Context; 4 | import android.view.ViewGroup; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.annotation.StringRes; 8 | import androidx.recyclerview.widget.RecyclerView; 9 | 10 | import com.huanchengfly.icebridge.R; 11 | import com.huanchengfly.icebridge.utils.MyViewHolder; 12 | import com.huanchengfly.icebridge.widgets.SingleChooseView; 13 | 14 | import java.lang.ref.WeakReference; 15 | import java.util.List; 16 | 17 | public class SingleChooseAdapter extends RecyclerView.Adapter { 18 | private WeakReference contextWeakReference; 19 | private int selectedPos; 20 | private List itemBeans; 21 | private OnItemSelectedListener onItemSelectedListener; 22 | 23 | public int getSelectedPosition() { 24 | return selectedPos; 25 | } 26 | 27 | public OnItemSelectedListener getOnItemSelectedListener() { 28 | return onItemSelectedListener; 29 | } 30 | 31 | public void setOnItemSelectedListener(OnItemSelectedListener onItemSelectedListener) { 32 | this.onItemSelectedListener = onItemSelectedListener; 33 | } 34 | 35 | public SingleChooseAdapter(Context context, List itemBeans) { 36 | this(context, itemBeans, -1); 37 | } 38 | 39 | public SingleChooseAdapter(Context context, List itemBeans, int defaultSelectedPos) { 40 | this.contextWeakReference = new WeakReference<>(context); 41 | this.itemBeans = itemBeans; 42 | this.selectedPos = defaultSelectedPos; 43 | } 44 | 45 | public Context getContext() { 46 | return contextWeakReference.get(); 47 | } 48 | 49 | @NonNull 50 | @Override 51 | public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 52 | return MyViewHolder.create(getContext(), R.layout.item_single_choose); 53 | } 54 | 55 | @Override 56 | public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { 57 | ItemBean itemBean = itemBeans.get(position); 58 | SingleChooseView singleChooseView = holder.getView(R.id.single_choose); 59 | singleChooseView.setOnClickListener(v -> { 60 | ((SingleChooseView) v).setChecked(true); 61 | int oldPos = selectedPos + 0; 62 | notifyItemChanged(oldPos); 63 | selectedPos = position; 64 | if (getOnItemSelectedListener() != null) { 65 | getOnItemSelectedListener().onSelected(position, itemBean); 66 | } 67 | }); 68 | singleChooseView.setChecked(position == selectedPos); 69 | singleChooseView.setEnabled(itemBean.isEnabled()); 70 | singleChooseView.setTitle(itemBean.getTitle()); 71 | singleChooseView.setSubtitle(itemBean.getSubtitle()); 72 | } 73 | 74 | @Override 75 | public int getItemCount() { 76 | return itemBeans.size(); 77 | } 78 | 79 | public static class ItemBean { 80 | private CharSequence title; 81 | private CharSequence subtitle; 82 | private boolean enabled; 83 | 84 | public CharSequence getTitle() { 85 | return title; 86 | } 87 | 88 | public void setTitle(CharSequence title) { 89 | this.title = title; 90 | } 91 | 92 | public void setSubtitle(CharSequence subtitle) { 93 | this.subtitle = subtitle; 94 | } 95 | 96 | public void setTitle(Context context, @StringRes int resId) { 97 | this.title = context.getString(resId); 98 | } 99 | 100 | public void setSubtitle(Context context, @StringRes int resId) { 101 | this.subtitle = context.getString(resId); 102 | } 103 | 104 | public CharSequence getSubtitle() { 105 | return subtitle; 106 | } 107 | 108 | public ItemBean(CharSequence title, CharSequence subtitle) { 109 | this(title, subtitle, true); 110 | } 111 | 112 | public ItemBean(Context context, @StringRes int title, @StringRes int subtitle) { 113 | this(context, title, subtitle, true); 114 | } 115 | 116 | public ItemBean(CharSequence title, CharSequence subtitle, boolean enabled) { 117 | setTitle(title); 118 | setSubtitle(subtitle); 119 | setEnabled(enabled); 120 | } 121 | 122 | public ItemBean(Context context, @StringRes int title, @StringRes int subtitle, boolean enabled) { 123 | setTitle(context, title); 124 | setSubtitle(context, subtitle); 125 | setEnabled(enabled); 126 | } 127 | 128 | public boolean isEnabled() { 129 | return enabled; 130 | } 131 | 132 | public void setEnabled(boolean enabled) { 133 | this.enabled = enabled; 134 | } 135 | } 136 | 137 | public interface OnItemSelectedListener { 138 | void onSelected(int position, ItemBean itemBean); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/adapters/ViewPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.adapters; 2 | 3 | import android.view.ViewGroup; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.fragment.app.FragmentManager; 7 | import androidx.fragment.app.FragmentPagerAdapter; 8 | 9 | import com.huanchengfly.icebridge.fragments.BaseFragment; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class ViewPagerAdapter extends FragmentPagerAdapter { 15 | private BaseFragment currentFragment; 16 | private int currentFragmentPosition; 17 | private List fragments = new ArrayList<>(); 18 | 19 | public ViewPagerAdapter(FragmentManager fm) { 20 | super(fm); 21 | } 22 | 23 | public List getFragments() { 24 | return fragments; 25 | } 26 | 27 | @NonNull 28 | @Override 29 | public BaseFragment getItem(int position) { 30 | return fragments.get(position); 31 | } 32 | 33 | @Override 34 | public int getCount() { 35 | return fragments.size(); 36 | } 37 | 38 | public void addFragment(BaseFragment fragment) { 39 | fragments.add(fragment); 40 | } 41 | 42 | @Override 43 | public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) { 44 | currentFragment = (BaseFragment) object; 45 | currentFragmentPosition = position; 46 | super.setPrimaryItem(container, position, object); 47 | } 48 | 49 | public BaseFragment getCurrentFragment() { 50 | return currentFragment; 51 | } 52 | 53 | public int getCurrentFragmentPosition() { 54 | return currentFragmentPosition; 55 | } 56 | 57 | public void clear() { 58 | fragments.clear(); 59 | notifyDataSetChanged(); 60 | } 61 | } -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/api/CommonAPICallback.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.api; 2 | 3 | public interface CommonAPICallback { 4 | void onSuccess(T t); 5 | 6 | void onFailure(int errorCode, String errorMsg); 7 | } -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/api/HttpConstant.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.api; 2 | 3 | final class HttpConstant { 4 | public static final String URL_UPDATE = "https://huancheng65.github.io/icebridge/update.json"; 5 | public static final String URL_CONFIG = "https://huancheng65.github.io/icebridge/bridge.json"; 6 | } 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/api/MyAPI.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.api; 2 | 3 | import com.huanchengfly.icebridge.beans.BridgeInfo; 4 | import com.huanchengfly.icebridge.beans.UpdateInfo; 5 | import com.tsy.sdk.myokhttp.MyOkHttp; 6 | import com.tsy.sdk.myokhttp.response.GsonResponseHandler; 7 | 8 | import okhttp3.OkHttpClient; 9 | 10 | public final class MyAPI { 11 | private static MyAPI instance; 12 | private MyOkHttp myOkHttp; 13 | 14 | private MyAPI() { 15 | this.myOkHttp = new MyOkHttp(new OkHttpClient.Builder() 16 | .build()); 17 | } 18 | 19 | public static MyAPI getInstance() { 20 | if (instance == null) { 21 | synchronized (MyAPI.class) { 22 | if (instance == null) { 23 | instance = new MyAPI(); 24 | } 25 | } 26 | } 27 | return instance; 28 | } 29 | 30 | public void checkUpdate(CommonAPICallback commonAPICallback) { 31 | myOkHttp.get() 32 | .url(HttpConstant.URL_UPDATE) 33 | .enqueue(new GsonResponseHandler() { 34 | @Override 35 | public void onFailure(int statusCode, String error_msg) { 36 | commonAPICallback.onFailure(statusCode, error_msg); 37 | } 38 | 39 | @Override 40 | public void onSuccess(int statusCode, UpdateInfo response) { 41 | commonAPICallback.onSuccess(response); 42 | } 43 | }); 44 | } 45 | 46 | public void updateConfig(CommonAPICallback commonAPICallback) { 47 | myOkHttp.get() 48 | .url(HttpConstant.URL_CONFIG) 49 | .enqueue(new GsonResponseHandler() { 50 | @Override 51 | public void onFailure(int statusCode, String error_msg) { 52 | commonAPICallback.onFailure(statusCode, error_msg); 53 | } 54 | 55 | @Override 56 | public void onSuccess(int statusCode, BridgeInfo response) { 57 | commonAPICallback.onSuccess(response); 58 | } 59 | }); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/base/MyApplication.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.base; 2 | 3 | import org.litepal.LitePalApplication; 4 | 5 | public class MyApplication extends LitePalApplication { 6 | private static MyApplication instance; 7 | 8 | public static MyApplication getInstance() { 9 | return instance; 10 | } 11 | 12 | @Override 13 | public void onCreate() { 14 | instance = this; 15 | super.onCreate(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/beans/BridgeInfo.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.beans; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.google.gson.annotations.SerializedName; 6 | import com.huanchengfly.icebridge.utils.GsonUtil; 7 | 8 | import java.util.List; 9 | 10 | public class BridgeInfo { 11 | private int version; 12 | private List hooks; 13 | 14 | @NonNull 15 | @Override 16 | public String toString() { 17 | return GsonUtil.getGson().toJson(this); 18 | } 19 | 20 | public List getHooks() { 21 | return hooks; 22 | } 23 | 24 | public int getVersion() { 25 | return version; 26 | } 27 | 28 | public static class Hook { 29 | @SerializedName("unfreeze_packages") 30 | private List unfreezePackages; 31 | @SerializedName("target_packages") 32 | private List targetPackages; 33 | @SerializedName("target_methods") 34 | private List targetMethods; 35 | 36 | public List getUnfreezePackages() { 37 | return unfreezePackages; 38 | } 39 | 40 | public List getTargetPackages() { 41 | return targetPackages; 42 | } 43 | 44 | public List getTargetMethods() { 45 | return targetMethods; 46 | } 47 | } 48 | 49 | public static class TargetMethod { 50 | @SerializedName("class_name") 51 | private String className; 52 | @SerializedName("method_name") 53 | private String methodName; 54 | @SerializedName("params_classes") 55 | private List paramsClasses; 56 | @SerializedName("return_value") 57 | private ReturnValue returnValue; 58 | 59 | public String getClassName() { 60 | return className; 61 | } 62 | 63 | public String getMethodName() { 64 | return methodName; 65 | } 66 | 67 | public List getParamsClasses() { 68 | return paramsClasses; 69 | } 70 | 71 | public ReturnValue getReturnValue() { 72 | return returnValue; 73 | } 74 | } 75 | 76 | public static class ReturnValue { 77 | private String type; 78 | private String value; 79 | 80 | public String getType() { 81 | return type; 82 | } 83 | 84 | public String getValue() { 85 | return value; 86 | } 87 | 88 | public Object get() { 89 | switch (getType()) { 90 | case "boolean": 91 | return Boolean.valueOf(getValue()); 92 | case "int": 93 | return Integer.valueOf(getValue()); 94 | case "String": 95 | default: 96 | return getValue(); 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/beans/UpdateInfo.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.beans; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | public class UpdateInfo { 6 | @SerializedName("config_version") 7 | private int configVersion; 8 | @SerializedName("config_version_name") 9 | private String configVersionName; 10 | @SerializedName("config_download_url") 11 | private String configDownloadUrl; 12 | @SerializedName("app_version") 13 | private int appVersion; 14 | @SerializedName("app_version_name") 15 | private String appVersionName; 16 | @SerializedName("app_download_url") 17 | private String appDownloadUrl; 18 | 19 | public int getConfigVersion() { 20 | return configVersion; 21 | } 22 | 23 | public String getConfigVersionName() { 24 | return configVersionName; 25 | } 26 | 27 | public String getConfigDownloadUrl() { 28 | return configDownloadUrl; 29 | } 30 | 31 | public int getAppVersion() { 32 | return appVersion; 33 | } 34 | 35 | public String getAppVersionName() { 36 | return appVersionName; 37 | } 38 | 39 | public String getAppDownloadUrl() { 40 | return appDownloadUrl; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/databases/AppConfig.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.databases; 2 | 3 | import org.litepal.crud.LitePalSupport; 4 | 5 | public class AppConfig extends LitePalSupport { 6 | private String key; 7 | private String value; 8 | 9 | public AppConfig(String key, String value) { 10 | this.key = key; 11 | this.value = value; 12 | } 13 | 14 | public String getKey() { 15 | return key; 16 | } 17 | 18 | public String getValue() { 19 | return value; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/dividers/SpacesItemDecoration.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.dividers; 2 | 3 | import android.graphics.Rect; 4 | import android.view.View; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.recyclerview.widget.RecyclerView; 8 | 9 | public class SpacesItemDecoration extends RecyclerView.ItemDecoration { 10 | private int space; 11 | 12 | public SpacesItemDecoration(int space) { 13 | this.space = space; 14 | } 15 | 16 | @Override 17 | public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, 18 | @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { 19 | outRect.left = space; 20 | outRect.right = space; 21 | outRect.bottom = space; 22 | outRect.top = space; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/engines/BaseEngine.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.engines; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.annotation.IntDef; 6 | 7 | import java.lang.ref.WeakReference; 8 | 9 | public abstract class BaseEngine { 10 | public static final int STATUS_NOT_AVAILABLE = 0; 11 | public static final int STATUS_NEED_PERMISSION = 1; 12 | public static final int STATUS_WORKING = 2; 13 | 14 | @IntDef({STATUS_NOT_AVAILABLE, STATUS_NEED_PERMISSION, STATUS_WORKING}) 15 | @interface Status {} 16 | 17 | private WeakReference reference; 18 | 19 | public BaseEngine(Context context) { 20 | reference = new WeakReference<>(context); 21 | } 22 | 23 | public final Context getContext() { 24 | return reference.get(); 25 | } 26 | 27 | public abstract void setEnabled(String pkgName, boolean enabled, Callback callback); 28 | 29 | public abstract @Status int getEngineStatus(); 30 | 31 | public abstract CharSequence getStatusDescription(); 32 | 33 | public abstract int checkPermission(); 34 | 35 | public abstract void requestPermission(Callback callback); 36 | 37 | protected void notifyStatusChanged() { 38 | if (getContext() instanceof OnStatusChangedListener) { 39 | ((OnStatusChangedListener) getContext()).onStatusChanged(getEngineStatus()); 40 | } 41 | } 42 | 43 | public interface OnStatusChangedListener { 44 | void onStatusChanged(int status); 45 | } 46 | 47 | public interface Callback { 48 | void onSuccess(); 49 | 50 | void onFailure(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/engines/EngineManager.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.engines; 2 | 3 | import android.content.Context; 4 | 5 | import static com.huanchengfly.icebridge.fragments.intro.WorkModeFragment.SP_WORK_MODE; 6 | 7 | public final class EngineManager { 8 | public static BaseEngine getEngine(Context context) { 9 | if (context.getSharedPreferences("settings", Context.MODE_PRIVATE).getInt(SP_WORK_MODE, 0) == 1) { 10 | return new RootEngine(context); 11 | } 12 | return new IceBoxEngine(context); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/engines/IceBoxEngine.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.engines; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageManager; 5 | 6 | import androidx.core.content.ContextCompat; 7 | 8 | import com.catchingnow.icebox.sdk_client.IceBox; 9 | import com.huanchengfly.icebridge.R; 10 | import com.huanchengfly.icebridge.utils.PackageUtil; 11 | import com.huanchengfly.icebridge.utils.Util; 12 | 13 | public final class IceBoxEngine extends BaseEngine { 14 | public static final int STATUS_NOT_INSTALL_ICEBOX = 0; 15 | public static final int STATUS_TOO_OLD_ICEBOX = 1; 16 | public static final int STATUS_ICEBOX_NOT_ACTIVE = 2; 17 | public static final int STATUS_NO_PERMISSION = 3; 18 | public static final int STATUS_WORKING = 4; 19 | 20 | public IceBoxEngine(Context context) { 21 | super(context); 22 | } 23 | 24 | @Override 25 | public void setEnabled(String pkgName, boolean enabled, Callback callback) { 26 | IceBox.setAppEnabledSettings(getContext(), true, pkgName); 27 | callback.onSuccess(); 28 | } 29 | 30 | @Override 31 | public int getEngineStatus() { 32 | if (IceBox.queryWorkMode(getContext()) == IceBox.WorkMode.MODE_NOT_AVAILABLE) { 33 | return STATUS_NOT_AVAILABLE; 34 | } else if (ContextCompat.checkSelfPermission(getContext(), IceBox.SDK_PERMISSION) != PackageManager.PERMISSION_GRANTED) { 35 | return STATUS_NEED_PERMISSION; 36 | } 37 | return BaseEngine.STATUS_WORKING; 38 | } 39 | 40 | private int getMyStatus() { 41 | if (IceBox.queryWorkMode(getContext()) == IceBox.WorkMode.MODE_NOT_AVAILABLE) { 42 | if (!PackageUtil.checkAppInstalled(getContext(), IceBox.PACKAGE_NAME)) { 43 | return STATUS_NOT_INSTALL_ICEBOX; 44 | } else if (PackageUtil.getVersionCode(getContext(), IceBox.PACKAGE_NAME) < IceBox.AVAILABLE_VERSION_CODE) { 45 | return STATUS_TOO_OLD_ICEBOX; 46 | } else { 47 | return STATUS_ICEBOX_NOT_ACTIVE; 48 | } 49 | } else if (ContextCompat.checkSelfPermission(getContext(), IceBox.SDK_PERMISSION) != PackageManager.PERMISSION_GRANTED) { 50 | return STATUS_NO_PERMISSION; 51 | } 52 | return STATUS_WORKING; 53 | } 54 | 55 | @Override 56 | public CharSequence getStatusDescription() { 57 | switch (getMyStatus()) { 58 | case STATUS_NOT_INSTALL_ICEBOX: 59 | return getContext().getString(R.string.status_icebox_not_installed); 60 | case STATUS_TOO_OLD_ICEBOX: 61 | return getContext().getString(R.string.status_icebox_old); 62 | case STATUS_ICEBOX_NOT_ACTIVE: 63 | return getContext().getString(R.string.status_icebox_not_available); 64 | case STATUS_NO_PERMISSION: 65 | return getContext().getString(R.string.status_need_permission); 66 | case STATUS_WORKING: 67 | if (Util.isEnhanceModeActive(getContext())) { 68 | return getContext().getString(R.string.status_description_icebox_mode_enhance); 69 | } 70 | return getContext().getString(R.string.status_description_icebox_mode); 71 | } 72 | return null; 73 | } 74 | 75 | @Override 76 | public int checkPermission() { 77 | return ContextCompat.checkSelfPermission(getContext(), IceBox.SDK_PERMISSION); 78 | } 79 | 80 | @Override 81 | public void requestPermission(Callback callback) { 82 | Util.requestPermission(getContext(), data -> { 83 | callback.onSuccess(); 84 | notifyStatusChanged(); 85 | }, data -> callback.onFailure()); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/engines/RootEngine.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.engines; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageManager; 5 | import android.widget.Toast; 6 | 7 | import com.catchingnow.icebox.sdk_client.IceBox; 8 | import com.huanchengfly.icebridge.R; 9 | import com.huanchengfly.icebridge.utils.Util; 10 | import com.stericson.RootShell.exceptions.RootDeniedException; 11 | import com.stericson.RootShell.execution.Command; 12 | import com.stericson.RootTools.RootTools; 13 | 14 | import java.io.IOException; 15 | import java.util.concurrent.TimeoutException; 16 | 17 | public final class RootEngine extends BaseEngine { 18 | public static final String TAG = RootEngine.class.getSimpleName(); 19 | 20 | public static final int STATUS_NO_ROOT = 0; 21 | public static final int STATUS_NOT_GIVEN_ROOT = 1; 22 | public static final int STATUS_WORKING = 2; 23 | 24 | public RootEngine(Context context) { 25 | super(context); 26 | } 27 | 28 | public static final String PACKAGE_MANAGER_COMMAND_BASE = "pm "; 29 | 30 | @Override 31 | public void setEnabled(String pkgName, boolean enabled, Callback callback) { 32 | String commandStr = PACKAGE_MANAGER_COMMAND_BASE; 33 | String command2Str = PACKAGE_MANAGER_COMMAND_BASE; 34 | try { 35 | int flags = IceBox.getAppEnabledSetting(getContext(), pkgName); 36 | if ((flags != 0 && !enabled) || (flags == 0 && enabled)) { 37 | callback.onSuccess(); 38 | return; 39 | } 40 | if (!enabled) { 41 | commandStr += "disable-user "; 42 | } else if (enabled && flags == IceBox.FLAG_PM_DISABLE_USER) { 43 | commandStr += "enable "; 44 | } else if (enabled && flags == IceBox.FLAG_PM_HIDE) { 45 | commandStr += "unhide "; 46 | } else if (enabled && flags == IceBox.FLAG_PM_HIDE + IceBox.FLAG_PM_DISABLE_USER) { 47 | commandStr += "enable "; 48 | command2Str += "unhide "; 49 | } 50 | Command command; 51 | commandStr += pkgName; 52 | if (PACKAGE_MANAGER_COMMAND_BASE.equalsIgnoreCase(command2Str)) { 53 | command = new Command(0, commandStr) { 54 | @Override 55 | public void commandCompleted(int id, int exitcode) { 56 | super.commandCompleted(id, exitcode); 57 | if (exitcode == 0) { 58 | callback.onSuccess(); 59 | } else { 60 | callback.onFailure(); 61 | } 62 | } 63 | }; 64 | } else { 65 | command2Str += pkgName; 66 | command = new Command(0, commandStr, command2Str) { 67 | @Override 68 | public void commandCompleted(int id, int exitcode) { 69 | super.commandCompleted(id, exitcode); 70 | if (exitcode == 0) { 71 | callback.onSuccess(); 72 | } else { 73 | callback.onFailure(); 74 | } 75 | } 76 | }; 77 | } 78 | try { 79 | RootTools.getShell(true).add(command); 80 | } catch (IOException | RootDeniedException | TimeoutException ex) { 81 | ex.printStackTrace(); 82 | callback.onFailure(); 83 | } 84 | } catch (PackageManager.NameNotFoundException e) { 85 | e.printStackTrace(); 86 | callback.onFailure(); 87 | } 88 | } 89 | 90 | @Override 91 | public int getEngineStatus() { 92 | if (!RootTools.isRootAvailable()) { 93 | return STATUS_NOT_AVAILABLE; 94 | } else if (!RootTools.isAccessGiven(0, 0)) { 95 | return STATUS_NEED_PERMISSION; 96 | } 97 | return BaseEngine.STATUS_WORKING; 98 | } 99 | 100 | private int getMyStatus() { 101 | if (!RootTools.isRootAvailable()) { 102 | return STATUS_NO_ROOT; 103 | } else if (!RootTools.isAccessGiven(0, 0)) { 104 | return STATUS_NOT_GIVEN_ROOT; 105 | } 106 | return STATUS_WORKING; 107 | } 108 | 109 | @Override 110 | public CharSequence getStatusDescription() { 111 | switch (getMyStatus()) { 112 | case STATUS_NO_ROOT: 113 | return getContext().getString(R.string.status_description_no_root); 114 | case STATUS_NOT_GIVEN_ROOT: 115 | return getContext().getString(R.string.status_description_not_given_root); 116 | case STATUS_WORKING: 117 | if (Util.isEnhanceModeActive(getContext())) { 118 | return getContext().getString(R.string.status_description_root_working_enhance); 119 | } 120 | return getContext().getString(R.string.status_description_root_working); 121 | } 122 | return null; 123 | } 124 | 125 | @Override 126 | public int checkPermission() { 127 | if (RootTools.isAccessGiven() && RootTools.isRootAvailable()) { 128 | return PackageManager.PERMISSION_GRANTED; 129 | } 130 | return PackageManager.PERMISSION_DENIED; 131 | } 132 | 133 | @Override 134 | public void requestPermission(Callback callback) { 135 | if (RootTools.isAccessGiven()) { 136 | Toast.makeText(getContext(), R.string.got_root_permission, Toast.LENGTH_SHORT).show(); 137 | callback.onSuccess(); 138 | notifyStatusChanged(); 139 | } else { 140 | callback.onFailure(); 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/fragments/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.fragments; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Activity; 5 | import android.content.Context; 6 | import android.os.Build; 7 | 8 | import androidx.annotation.CallSuper; 9 | import androidx.annotation.NonNull; 10 | import androidx.fragment.app.Fragment; 11 | 12 | public class BaseFragment extends Fragment { 13 | private Context attachContext; 14 | 15 | @TargetApi(23) 16 | @Override 17 | public void onAttach(Context context) { 18 | super.onAttach(context); 19 | onAttachToContext(context); 20 | } 21 | 22 | @SuppressWarnings("deprecation") 23 | @Override 24 | public void onAttach(Activity activity) { 25 | super.onAttach(activity); 26 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 27 | onAttachToContext(activity); 28 | } 29 | } 30 | 31 | @CallSuper 32 | private void onAttachToContext(Context context) { 33 | attachContext = context; 34 | } 35 | 36 | @NonNull 37 | protected Context getAttachContext() { 38 | return attachContext; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/fragments/BridgePreferencesFragment.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.fragments; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.huanchengfly.icebridge.R; 6 | 7 | public class BridgePreferencesFragment extends PreferencesFragment { 8 | @Override 9 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 10 | getPreferenceManager().setSharedPreferencesName("preferences"); 11 | addPreferencesFromResource(R.xml.bridge_preferences); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/fragments/EnhanceModeFragment.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.fragments; 2 | 3 | import android.os.Bundle; 4 | 5 | import androidx.preference.SwitchPreference; 6 | 7 | import com.huanchengfly.icebridge.R; 8 | import com.huanchengfly.icebridge.databases.AppConfig; 9 | import com.huanchengfly.icebridge.utils.Util; 10 | 11 | import org.litepal.LitePal; 12 | 13 | public class EnhanceModeFragment extends PreferencesFragment { 14 | @Override 15 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 16 | getPreferenceManager().setSharedPreferencesName("preferences"); 17 | addPreferencesFromResource(R.xml.preferences); 18 | refreshStatus(); 19 | SwitchPreference enhancePreference = findPreference(R.string.key_enhance_mode_status); 20 | enhancePreference.setOnPreferenceChangeListener((preference, newValue) -> { 21 | boolean enabled = (boolean) newValue; 22 | new AppConfig("enhance_mode_enabled", enabled ? "1" : "0").saveOrUpdate("key = ?", "enhance_mode_enabled"); 23 | refreshStatus(); 24 | return true; 25 | }); 26 | } 27 | 28 | private void refreshStatus() { 29 | SwitchPreference enhancePreference = findPreference(R.string.key_enhance_mode_status); 30 | if (isXposedActive()) { 31 | if (isEnabled()) { 32 | enhancePreference.setSummary(R.string.summary_enhance_mode_active); 33 | } else { 34 | enhancePreference.setSummary(R.string.summary_enhance_mode_active_not_enable); 35 | } 36 | } else { 37 | enhancePreference.setSummary(R.string.summary_enhance_mode); 38 | } 39 | enhancePreference.setEnabled(isXposedActive()); 40 | enhancePreference.setChecked(isEnabled()); 41 | } 42 | 43 | private boolean isEnabled() { 44 | AppConfig appConfig = LitePal.where("key = ?", "enhance_mode_enabled").findFirst(AppConfig.class); 45 | if (appConfig == null) { 46 | return true; 47 | } 48 | return "1".equals(appConfig.getValue()); 49 | } 50 | 51 | private boolean isXposedActive() { 52 | return Util.isXposedActive(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/fragments/PreferencesFragment.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.fragments; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Activity; 5 | import android.content.Context; 6 | import android.os.Build; 7 | 8 | import androidx.annotation.CallSuper; 9 | import androidx.annotation.NonNull; 10 | import androidx.annotation.StringRes; 11 | import androidx.preference.Preference; 12 | import androidx.preference.PreferenceFragmentCompat; 13 | 14 | public abstract class PreferencesFragment extends PreferenceFragmentCompat { 15 | private Context attachContext; 16 | 17 | /* 18 | * onAttach(Context) is not called on pre API 23 versions of Android and onAttach(Activity) is deprecated 19 | * Use onAttachToContext instead 20 | */ 21 | @TargetApi(23) 22 | @Override 23 | public void onAttach(Context context) { 24 | super.onAttach(context); 25 | onAttachToContext(context); 26 | } 27 | 28 | /* 29 | * Deprecated on API 23 30 | * Use onAttachToContext instead 31 | */ 32 | @SuppressWarnings("deprecation") 33 | @Override 34 | public void onAttach(Activity activity) { 35 | super.onAttach(activity); 36 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 37 | onAttachToContext(activity); 38 | } 39 | } 40 | 41 | /* 42 | * Called when the fragment attaches to the context 43 | */ 44 | @CallSuper 45 | private void onAttachToContext(Context context) { 46 | attachContext = context; 47 | } 48 | 49 | @NonNull 50 | protected Context getAttachContext() { 51 | return attachContext; 52 | } 53 | 54 | protected T findPreference(@StringRes int keyStringId) { 55 | return (T) findPreference(getAttachContext().getString(keyStringId)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/fragments/intro/BaseIntroFragment.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.fragments.intro; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.LinearLayout; 8 | import android.widget.TextView; 9 | 10 | import androidx.annotation.NonNull; 11 | import androidx.annotation.Nullable; 12 | 13 | import com.huanchengfly.icebridge.R; 14 | import com.huanchengfly.icebridge.activities.BaseIntroActivity; 15 | import com.huanchengfly.icebridge.fragments.BaseFragment; 16 | 17 | public abstract class BaseIntroFragment extends BaseFragment { 18 | private static final int NO_CUSTOM_LAYOUT = -1; 19 | 20 | @Nullable 21 | abstract CharSequence getTitle(); 22 | 23 | @Nullable 24 | abstract CharSequence getSubtitle(); 25 | 26 | public CharSequence getNextButton() { 27 | return getAttachContext().getString(R.string.button_next_default); 28 | } 29 | 30 | protected int getCustomLayoutResId() { 31 | return NO_CUSTOM_LAYOUT; 32 | } 33 | 34 | protected void initCustomLayout(LinearLayout container) {} 35 | 36 | @Nullable 37 | @Override 38 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 39 | View contentView = inflater.inflate(R.layout.fragment_intro, container, false); 40 | if (NO_CUSTOM_LAYOUT != getCustomLayoutResId()) { 41 | LinearLayout customLayoutContainer = contentView.findViewById(R.id.custom_layout); 42 | inflater.inflate(getCustomLayoutResId(), customLayoutContainer, true); 43 | } 44 | return contentView; 45 | } 46 | 47 | @Override 48 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 49 | super.onViewCreated(view, savedInstanceState); 50 | if (NO_CUSTOM_LAYOUT != getCustomLayoutResId()) { 51 | initCustomLayout(view.findViewById(R.id.custom_layout)); 52 | } 53 | TextView title = view.findViewById(R.id.title); 54 | TextView subtitle = view.findViewById(R.id.subtitle); 55 | title.setText(getTitle()); 56 | subtitle.setText(getSubtitle()); 57 | } 58 | 59 | protected void setNextButtonEnabled(boolean enabled) { 60 | if (getAttachContext() instanceof BaseIntroActivity) { 61 | ((BaseIntroActivity) getAttachContext()).setNextButtonEnabled(enabled); 62 | } 63 | } 64 | 65 | public void onVisible() {} 66 | 67 | public boolean getDefaultNextButtonEnabled() { 68 | return true; 69 | } 70 | 71 | public boolean onNext() { 72 | return false; 73 | } 74 | 75 | public void next() { 76 | if (getAttachContext() instanceof BaseIntroActivity) { 77 | ((BaseIntroActivity) getAttachContext()).next(); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/fragments/intro/WelcomeFragment.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.fragments.intro; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | import com.huanchengfly.icebridge.R; 6 | 7 | public class WelcomeFragment extends BaseIntroFragment { 8 | public WelcomeFragment() {} 9 | 10 | @Nullable 11 | @Override 12 | CharSequence getTitle() { 13 | return getAttachContext().getString(R.string.title_intro_welcome); 14 | } 15 | 16 | @Nullable 17 | @Override 18 | CharSequence getSubtitle() { 19 | return null; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/fragments/intro/WorkModeFragment.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.fragments.intro; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.content.SharedPreferences; 6 | import android.widget.LinearLayout; 7 | import android.widget.Toast; 8 | 9 | import androidx.annotation.Nullable; 10 | import androidx.recyclerview.widget.LinearLayoutManager; 11 | import androidx.recyclerview.widget.RecyclerView; 12 | import androidx.recyclerview.widget.SimpleItemAnimator; 13 | 14 | import com.catchingnow.icebox.sdk_client.IceBox; 15 | import com.huanchengfly.icebridge.R; 16 | import com.huanchengfly.icebridge.adapters.SingleChooseAdapter; 17 | import com.huanchengfly.icebridge.utils.OSUtils; 18 | import com.huanchengfly.icebridge.utils.PackageUtil; 19 | import com.stericson.RootTools.RootTools; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | import static com.huanchengfly.icebridge.adapters.SingleChooseAdapter.ItemBean; 25 | 26 | public class WorkModeFragment extends BaseIntroFragment { 27 | private SingleChooseAdapter singleChooseAdapter; 28 | 29 | public WorkModeFragment() { 30 | } 31 | 32 | @Override 33 | public boolean getDefaultNextButtonEnabled() { 34 | return false; 35 | } 36 | 37 | @Override 38 | protected int getCustomLayoutResId() { 39 | return R.layout.layout_work_mode; 40 | } 41 | 42 | @SuppressLint("ApplySharedPref") 43 | @Override 44 | protected void initCustomLayout(LinearLayout container) { 45 | super.initCustomLayout(container); 46 | RecyclerView recyclerView = container.findViewById(R.id.recycler_view); 47 | recyclerView.setLayoutManager(new LinearLayoutManager(getAttachContext())); 48 | if (recyclerView.getItemAnimator() instanceof SimpleItemAnimator) ((SimpleItemAnimator) recyclerView.getItemAnimator()).setSupportsChangeAnimations(false); 49 | List itemBeans = new ArrayList<>(); 50 | itemBeans.add(getIceBoxItemBean()); 51 | itemBeans.add(getRootItemBean()); 52 | SharedPreferences.Editor editor = getAttachContext().getSharedPreferences("settings", Context.MODE_PRIVATE).edit(); 53 | singleChooseAdapter = new SingleChooseAdapter(getAttachContext(), itemBeans); 54 | singleChooseAdapter.setOnItemSelectedListener((position, itemBean) -> { 55 | switch (position) { 56 | case 0: 57 | setNextButtonEnabled(true); 58 | editor.putInt(SP_WORK_MODE, MODE_ICEBOX).commit(); 59 | break; 60 | case 1: 61 | if (RootTools.isRootAvailable() && RootTools.isAccessGiven()) { 62 | Toast.makeText(getAttachContext(), R.string.got_root_permission, Toast.LENGTH_SHORT).show(); 63 | } 64 | setNextButtonEnabled(true); 65 | editor.putInt(SP_WORK_MODE, MODE_ROOT).commit(); 66 | break; 67 | } 68 | }); 69 | recyclerView.setAdapter(singleChooseAdapter); 70 | } 71 | 72 | private ItemBean getIceBoxItemBean() { 73 | ItemBean itemBean = new ItemBean(getAttachContext(), R.string.title_work_mode_ice_box, R.string.subtitle_work_mode_ice_box_not_available, false); 74 | if (PackageUtil.checkAppInstalled(getAttachContext(), IceBox.PACKAGE_NAME) && PackageUtil.getVersionCode(getAttachContext(), IceBox.PACKAGE_NAME) >= IceBox.AVAILABLE_VERSION_CODE) { 75 | itemBean.setEnabled(true); 76 | if (OSUtils.isMIUI11OrLater()) { 77 | itemBean.setSubtitle(getAttachContext(), R.string.subtitle_work_mode_ice_box_is_miui); 78 | } else { 79 | itemBean.setSubtitle(getAttachContext(), R.string.subtitle_work_mode_ice_box); 80 | } 81 | } 82 | return itemBean; 83 | } 84 | 85 | private ItemBean getRootItemBean() { 86 | ItemBean itemBean = new ItemBean(getAttachContext(), R.string.title_work_mode_root, R.string.subtitle_work_mode_root); 87 | if (!RootTools.isRootAvailable()) { 88 | itemBean.setEnabled(false); 89 | itemBean.setSubtitle(getAttachContext(), R.string.device_no_root); 90 | } 91 | return itemBean; 92 | } 93 | 94 | @Nullable 95 | @Override 96 | CharSequence getTitle() { 97 | return getAttachContext().getString(R.string.title_intro_work_mode); 98 | } 99 | 100 | @Nullable 101 | @Override 102 | CharSequence getSubtitle() { 103 | return getAttachContext().getString(R.string.subtitle_intro_work_mode); 104 | } 105 | 106 | public static final String SP_WORK_MODE = "work_mode"; 107 | public static final int MODE_ICEBOX = 0; 108 | public static final int MODE_ROOT = 1; 109 | } 110 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/modules/FixCoolapkShareModule.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.modules; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.content.pm.PackageManager; 6 | import android.os.Build; 7 | 8 | import de.robv.android.xposed.IXposedHookLoadPackage; 9 | import de.robv.android.xposed.XC_MethodReplacement; 10 | import de.robv.android.xposed.XposedHelpers; 11 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 12 | import io.michaelrocks.paranoid.Obfuscate; 13 | 14 | @Obfuscate 15 | public class FixCoolapkShareModule implements IXposedHookLoadPackage { 16 | public static final String PACKAGE_COOLAPK = "com.coolapk.market"; 17 | 18 | @Override 19 | public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) { 20 | if (PACKAGE_COOLAPK.equals(loadPackageParam.packageName)) { 21 | XposedHelpers.findAndHookMethod("com.coolapk.market.util.PackageUtils", loadPackageParam.classLoader, "getShareApps", Context.class, new XC_MethodReplacement() { 22 | @Override 23 | protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable { 24 | Context context = (Context) methodHookParam.args[0]; 25 | Intent intent = new Intent(Intent.ACTION_SEND, null) 26 | .addCategory(Intent.CATEGORY_DEFAULT) 27 | .setType("text/plain"); 28 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 29 | return context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_ALL | PackageManager.MATCH_DISABLED_COMPONENTS); 30 | } else { 31 | return context.getPackageManager().queryIntentActivities(intent, PackageManager.GET_DISABLED_COMPONENTS); 32 | } 33 | } 34 | }); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/providers/ConfigProvider.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.providers; 2 | 3 | import android.content.ContentProvider; 4 | import android.content.ContentValues; 5 | import android.content.UriMatcher; 6 | import android.database.Cursor; 7 | import android.net.Uri; 8 | import android.util.Log; 9 | 10 | import androidx.annotation.NonNull; 11 | import androidx.annotation.Nullable; 12 | 13 | import com.huanchengfly.icebridge.databases.AppConfig; 14 | 15 | import org.litepal.LitePal; 16 | 17 | import java.util.Map; 18 | 19 | public class ConfigProvider extends ContentProvider { 20 | public static final String TAG = "ConfigProvider"; 21 | 22 | private static final String AUTHORITY = "com.huanchengfly.icebridge.providers.ConfigProvider"; 23 | public static final Uri URI = Uri.parse("content://" + AUTHORITY + "/config"); 24 | private static final int MATCH_CODE = 200; 25 | private static UriMatcher uriMatcher; 26 | 27 | static { 28 | uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 29 | uriMatcher.addURI(AUTHORITY, "config", MATCH_CODE); 30 | } 31 | 32 | private void notifyChange() { 33 | getContext().getContentResolver().notifyChange(URI, null); 34 | } 35 | 36 | @Override 37 | public boolean onCreate() { 38 | return true; 39 | } 40 | 41 | @Nullable 42 | @Override 43 | public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, 44 | @Nullable String[] selectionArgs, @Nullable String sortOrder) { 45 | int match = uriMatcher.match(uri); 46 | if (match == MATCH_CODE) { 47 | Log.i(TAG, "query"); 48 | try { 49 | return LitePal.findBySQL("select * from AppConfig"); 50 | } catch (Exception e) { 51 | e.printStackTrace(); 52 | } 53 | } 54 | return null; 55 | } 56 | 57 | @Nullable 58 | @Override 59 | public String getType(@NonNull Uri uri) { 60 | return null; 61 | } 62 | 63 | @Nullable 64 | @Override 65 | public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { 66 | if (uriMatcher.match(uri) == MATCH_CODE && values != null) { 67 | Log.i(TAG, "insert"); 68 | for (Map.Entry entry : values.valueSet()) { 69 | new AppConfig(entry.getKey(), (String) entry.getValue()).saveOrUpdate("key = ?", entry.getKey()); 70 | } 71 | notifyChange(); 72 | } 73 | return null; 74 | } 75 | 76 | @Override 77 | public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { 78 | return 0; 79 | } 80 | 81 | @Override 82 | public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { 83 | return 0; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/utils/AssetUtil.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.utils; 2 | 3 | import android.content.Context; 4 | import android.webkit.WebResourceResponse; 5 | 6 | import java.io.ByteArrayInputStream; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.nio.charset.StandardCharsets; 10 | 11 | public class AssetUtil { 12 | public static String TYPE_CSS = "text/css"; 13 | public static String TYPE_JS = "application/javascript"; 14 | public static String TYPE_FONT_WOFF = "application/x-font-woff"; 15 | 16 | public static WebResourceResponse getResponseFromAssets(Context context, String filename, String mimeType) { 17 | if (filename.equals("")) { 18 | return null; 19 | } 20 | InputStream is = null; 21 | try { 22 | is = context.getAssets().open(filename); 23 | } catch (IOException e) { 24 | e.printStackTrace(); 25 | return null; 26 | } 27 | return new WebResourceResponse(mimeType, "utf-8", is); 28 | } 29 | 30 | public static WebResourceResponse getEmptyResponse() { 31 | return new WebResourceResponse("text/plain", "utf-8", new ByteArrayInputStream("".getBytes())); 32 | } 33 | 34 | public static String getStringFromAsset(Context context, String file) { 35 | if (context == null) { 36 | return ""; 37 | } 38 | try { 39 | InputStream is = context.getAssets().open(file); 40 | int length = is.available(); 41 | byte[] buffer = new byte[length]; 42 | is.read(buffer); 43 | return new String(buffer, StandardCharsets.UTF_8); 44 | } catch (IOException e) { 45 | e.printStackTrace(); 46 | } 47 | return ""; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/utils/BridgeUtil.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.utils; 2 | 3 | import android.content.Context; 4 | 5 | import com.huanchengfly.icebridge.beans.BridgeInfo; 6 | import com.huanchengfly.icebridge.databases.AppConfig; 7 | 8 | import org.litepal.LitePal; 9 | 10 | public final class BridgeUtil { 11 | public static final String SP_BRIDGE_CONFIG = "bridge_config"; 12 | public static final String ASSET_BRIDGE_JSON = "bridge.json"; 13 | public static final String SP_VERSION = "version"; 14 | public static final String SP_CONFIG = "config"; 15 | 16 | public static final String TAG = "BridgeUtil"; 17 | 18 | private BridgeUtil() { 19 | } 20 | 21 | public static String getVersion(Context context) { 22 | try { 23 | return String.valueOf(getBridgeInfo(context).getVersion()); 24 | } catch (Exception e) { 25 | e.printStackTrace(); 26 | } 27 | return String.valueOf(parse(AssetUtil.getStringFromAsset(context, ASSET_BRIDGE_JSON)).getVersion()); 28 | } 29 | 30 | public static int getVersionCode(Context context) { 31 | try { 32 | return getBridgeInfo(context).getVersion(); 33 | } catch (Exception e) { 34 | e.printStackTrace(); 35 | } 36 | return parse(AssetUtil.getStringFromAsset(context, ASSET_BRIDGE_JSON)).getVersion(); 37 | } 38 | 39 | public static String getVersionName(Context context) { 40 | return "v" + getVersion(context); 41 | } 42 | 43 | public static BridgeInfo getBridgeInfo(Context context) { 44 | return parse(getJSON(context)); 45 | } 46 | 47 | public static String getJSON(Context context) { 48 | AppConfig appConfig = LitePal.where("key = ?", SP_CONFIG).findFirst(AppConfig.class); 49 | if (appConfig == null) { 50 | return AssetUtil.getStringFromAsset(context, ASSET_BRIDGE_JSON); 51 | } 52 | return appConfig.getValue(); 53 | } 54 | 55 | public static BridgeInfo parse(String json) { 56 | return GsonUtil.getGson().fromJson(json, BridgeInfo.class); 57 | } 58 | 59 | public static void init(Context context) { 60 | BridgeInfo bridgeInfo = parse(AssetUtil.getStringFromAsset(context, ASSET_BRIDGE_JSON)); 61 | int nowVersion = context.getSharedPreferences(SP_BRIDGE_CONFIG, Context.MODE_PRIVATE) 62 | .getInt(SP_VERSION, 0); 63 | int newVersion = bridgeInfo.getVersion(); 64 | if (newVersion > nowVersion) { 65 | reset(context); 66 | } 67 | } 68 | 69 | public static void reset(Context context) { 70 | BridgeInfo bridgeInfo = parse(AssetUtil.getStringFromAsset(context, ASSET_BRIDGE_JSON)); 71 | write(context, bridgeInfo); 72 | } 73 | 74 | public static void write(Context context, BridgeInfo bridgeInfo) { 75 | context.getSharedPreferences(SP_BRIDGE_CONFIG, Context.MODE_PRIVATE) 76 | .edit() 77 | .putInt(SP_VERSION, bridgeInfo.getVersion()) 78 | .apply(); 79 | new AppConfig(SP_CONFIG, bridgeInfo.toString()).saveOrUpdate("key = ?", SP_CONFIG); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/utils/GsonUtil.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.utils; 2 | 3 | import com.google.gson.Gson; 4 | 5 | public class GsonUtil { 6 | private static Gson gson; 7 | 8 | public static synchronized Gson getGson() { 9 | if (gson == null) { 10 | synchronized (GsonUtil.class) { 11 | if (gson == null) { 12 | gson = new Gson(); 13 | } 14 | } 15 | } 16 | return gson; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/utils/MyViewHolder.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.utils; 2 | 3 | import android.content.Context; 4 | import android.util.SparseArray; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.TextView; 9 | 10 | import androidx.annotation.IdRes; 11 | import androidx.annotation.LayoutRes; 12 | import androidx.annotation.NonNull; 13 | import androidx.annotation.Nullable; 14 | import androidx.recyclerview.widget.RecyclerView; 15 | 16 | public class MyViewHolder extends RecyclerView.ViewHolder { 17 | private SparseArray mViews; 18 | public View itemView; 19 | 20 | private MyViewHolder(@NonNull View itemView) { 21 | super(itemView); 22 | this.itemView = itemView; 23 | this.mViews = new SparseArray(); 24 | } 25 | 26 | public static MyViewHolder create(Context context, @LayoutRes int layoutId) { 27 | return create(context, layoutId, null); 28 | } 29 | 30 | public static MyViewHolder create(Context context, @LayoutRes int layoutId, @Nullable ViewGroup parent) { 31 | return create(context, layoutId, parent, parent != null); 32 | } 33 | 34 | public static MyViewHolder create(Context context, @LayoutRes int layoutId, @Nullable ViewGroup parent, boolean attachToRoot) { 35 | return new MyViewHolder(LayoutInflater.from(context).inflate(layoutId, parent, attachToRoot)); 36 | } 37 | 38 | public static MyViewHolder create(View view) { 39 | return new MyViewHolder(view); 40 | } 41 | 42 | public T getView(@IdRes int id) { 43 | View view = this.mViews.get(id); 44 | if (view == null) { 45 | view = this.itemView.findViewById(id); 46 | this.mViews.put(id, view); 47 | } 48 | return (T) view; 49 | } 50 | 51 | public void setText(int viewId, CharSequence text) { 52 | TextView textView = this.getView(viewId); 53 | textView.setText(text); 54 | } 55 | 56 | public void setText(int viewId, int textId) { 57 | TextView textView = this.getView(viewId); 58 | textView.setText(textId); 59 | } 60 | 61 | public void setTextColor(int viewId, int colorId) { 62 | TextView textView = this.getView(viewId); 63 | textView.setTextColor(colorId); 64 | } 65 | 66 | public void setTextSize(int viewId, int size) { 67 | TextView textView = this.getView(viewId); 68 | textView.setTextSize(size); 69 | } 70 | 71 | public void setOnClickListener(int viewId, View.OnClickListener clickListener) { 72 | View view = this.getView(viewId); 73 | view.setOnClickListener(clickListener); 74 | } 75 | 76 | public void setVisibility(int viewId, int visibility) { 77 | View view = this.getView(viewId); 78 | view.setVisibility(visibility); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/utils/OSUtils.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.utils; 2 | 3 | import android.text.TextUtils; 4 | import android.util.Log; 5 | 6 | import java.io.BufferedReader; 7 | import java.io.IOException; 8 | import java.io.InputStreamReader; 9 | 10 | public final class OSUtils { 11 | public static final String TAG = OSUtils.class.getSimpleName(); 12 | 13 | public static boolean isMIUI() { 14 | return !TextUtils.isEmpty(getSystemProperty("ro.miui.ui.version.name")); 15 | } 16 | 17 | public static boolean isMIUI11OrLater() { 18 | if (!OSUtils.isMIUI()) { 19 | return false; 20 | } 21 | int miuiVersionCode = Integer.parseInt(getSystemProperty("ro.miui.ui.version.code")); 22 | return miuiVersionCode >= 9; 23 | } 24 | 25 | public static String getSystemProperty(String propName) { 26 | String line; 27 | BufferedReader input = null; 28 | try { 29 | Process p = Runtime.getRuntime().exec("getprop " + propName); 30 | input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024); 31 | line = input.readLine(); 32 | input.close(); 33 | } catch (IOException ex) { 34 | Log.e(TAG, "Unable to read sysprop " + propName, ex); 35 | return null; 36 | } finally { 37 | if (input != null) { 38 | try { 39 | input.close(); 40 | } catch (IOException e) { 41 | Log.e(TAG, "Exception while closing InputStream", e); 42 | } 43 | } 44 | } 45 | return line; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/utils/PackageUtil.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.utils; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageInfo; 5 | import android.content.pm.PackageManager; 6 | import android.graphics.drawable.Drawable; 7 | 8 | public final class PackageUtil { 9 | private PackageUtil() { 10 | } 11 | 12 | public static boolean checkAppInstalled(Context context, String pkgName) { 13 | if (pkgName == null || pkgName.isEmpty()) { 14 | return false; 15 | } 16 | PackageInfo packageInfo; 17 | try { 18 | packageInfo = context.getPackageManager().getPackageInfo(pkgName, 0); 19 | } catch (PackageManager.NameNotFoundException e) { 20 | packageInfo = null; 21 | e.printStackTrace(); 22 | } 23 | return packageInfo != null; 24 | } 25 | 26 | public static int getVersionCode(Context context) { 27 | return getVersionCode(context, context.getPackageName()); 28 | } 29 | 30 | public static int getVersionCode(Context context, String pkgName) { 31 | int versionCode = 0; 32 | try { 33 | versionCode = context.getPackageManager(). 34 | getPackageInfo(pkgName, 0).versionCode; 35 | } catch (PackageManager.NameNotFoundException e) { 36 | e.printStackTrace(); 37 | } 38 | return versionCode; 39 | } 40 | 41 | public static String getAppName(Context context, String pkgName) { 42 | String appName = null; 43 | try { 44 | appName = context.getPackageManager(). 45 | getPackageInfo(pkgName, 0).applicationInfo.loadLabel(context.getPackageManager()).toString(); 46 | } catch (PackageManager.NameNotFoundException e) { 47 | e.printStackTrace(); 48 | } 49 | return appName; 50 | } 51 | 52 | public static Drawable getAppIcon(Context context, String pkgName) { 53 | Drawable appIcon = null; 54 | try { 55 | appIcon = context.getPackageManager(). 56 | getPackageInfo(pkgName, 0).applicationInfo.loadIcon(context.getPackageManager()); 57 | } catch (PackageManager.NameNotFoundException e) { 58 | e.printStackTrace(); 59 | } 60 | return appIcon; 61 | } 62 | 63 | public static String getPackageName(Context context) { 64 | String packageName = ""; 65 | try { 66 | packageName = context.getPackageManager(). 67 | getPackageInfo(context.getPackageName(), 0).packageName; 68 | } catch (PackageManager.NameNotFoundException e) { 69 | e.printStackTrace(); 70 | } 71 | return packageName; 72 | } 73 | 74 | public static String getVersionName(Context context) { 75 | String verName = ""; 76 | try { 77 | verName = context.getPackageManager(). 78 | getPackageInfo(context.getPackageName(), 0).versionName; 79 | } catch (PackageManager.NameNotFoundException e) { 80 | e.printStackTrace(); 81 | } 82 | return verName; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/utils/Util.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.utils; 2 | 3 | import android.app.AlertDialog; 4 | import android.content.ContentResolver; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.net.Uri; 8 | import android.os.Bundle; 9 | import android.widget.Toast; 10 | 11 | import com.catchingnow.icebox.sdk_client.IceBox; 12 | import com.huanchengfly.icebridge.R; 13 | import com.yanzhenjie.permission.Action; 14 | import com.yanzhenjie.permission.AndPermission; 15 | 16 | import java.util.Arrays; 17 | import java.util.List; 18 | 19 | public final class Util { 20 | private Util() { 21 | } 22 | 23 | public static void requestPermission(Context context, Action> onGranted, Action> onDenied) { 24 | if (AndPermission.hasPermissions(context, IceBox.SDK_PERMISSION)) { 25 | onGranted.onAction(Arrays.asList(IceBox.SDK_PERMISSION)); 26 | } 27 | AndPermission.with(context) 28 | .runtime() 29 | .permission(IceBox.SDK_PERMISSION) 30 | .onGranted(onGranted) 31 | .onDenied(data -> { 32 | boolean hasAlwaysDeniedPermission = AndPermission.hasAlwaysDeniedPermission(context, data); 33 | new AlertDialog.Builder(context) 34 | .setCancelable(false) 35 | .setTitle(R.string.title_dialog_permission) 36 | .setMessage(R.string.message_dialog_permission) 37 | .setPositiveButton(R.string.button_sure, (dialog, which) -> { 38 | if (hasAlwaysDeniedPermission) { 39 | AndPermission.with(context) 40 | .runtime() 41 | .setting() 42 | .onComeback(() -> requestPermission(context, onGranted)) 43 | .start(); 44 | } else { 45 | requestPermission(context, onGranted); 46 | } 47 | }) 48 | .setNegativeButton(R.string.button_cancel, (dialog, which) -> { 49 | Toast.makeText(context, R.string.toast_permission_cancel, Toast.LENGTH_SHORT).show(); 50 | if (onDenied != null) onDenied.onAction(data); 51 | }) 52 | .create() 53 | .show(); 54 | }) 55 | .start(); 56 | } 57 | 58 | public static void requestPermission(Context context, Action> onGranted) { 59 | requestPermission(context, onGranted, null); 60 | } 61 | 62 | public static boolean isEnhanceModeActive(Context context) { 63 | return isXposedActive() || isExpModuleActive(context); 64 | } 65 | 66 | public static boolean isXposedActive() { 67 | return false; 68 | } 69 | 70 | public static boolean isExpModuleActive(Context context) { 71 | boolean isExp = false; 72 | if (context == null) { 73 | throw new IllegalArgumentException("context must not be null!!"); 74 | } 75 | try { 76 | ContentResolver contentResolver = context.getContentResolver(); 77 | Uri uri = Uri.parse("content://me.weishu.exposed.CP/"); 78 | Bundle result = null; 79 | try { 80 | result = contentResolver.call(uri, "active", null, null); 81 | } catch (RuntimeException e) { 82 | // TaiChi is killed, try invoke 83 | try { 84 | Intent intent = new Intent("me.weishu.exp.ACTION_ACTIVE"); 85 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 86 | context.startActivity(intent); 87 | } catch (Throwable e1) { 88 | return false; 89 | } 90 | } 91 | if (result == null) { 92 | result = contentResolver.call(uri, "active", null, null); 93 | } 94 | 95 | if (result == null) { 96 | return false; 97 | } 98 | isExp = result.getBoolean("active", false); 99 | } catch (Throwable ignored) { 100 | } 101 | return isExp; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/widgets/MyViewPager.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.widgets; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.MotionEvent; 6 | 7 | import androidx.viewpager.widget.ViewPager; 8 | 9 | /** 10 | * Created by Administrator on 2017/5/19. 11 | */ 12 | 13 | public class MyViewPager extends ViewPager { 14 | 15 | private boolean isCanScroll = true; 16 | 17 | public MyViewPager(Context context) { 18 | super(context); 19 | } 20 | 21 | public MyViewPager(Context context, AttributeSet attrs) { 22 | super(context, attrs); 23 | } 24 | 25 | /** 26 | * 设置其是否能滑动换页 27 | * 28 | * @param isCanScroll false 不能换页, true 可以滑动换页 29 | */ 30 | public void setCanScroll(boolean isCanScroll) { 31 | this.isCanScroll = isCanScroll; 32 | } 33 | 34 | @Override 35 | public boolean onInterceptTouchEvent(MotionEvent ev) { 36 | return isCanScroll && super.onInterceptTouchEvent(ev); 37 | } 38 | 39 | @Override 40 | public boolean onTouchEvent(MotionEvent ev) { 41 | return isCanScroll && super.onTouchEvent(ev); 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/widgets/ShadowLayout.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.widgets; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Bitmap; 6 | import android.graphics.Bitmap.Config; 7 | import android.graphics.Canvas; 8 | import android.graphics.Color; 9 | import android.graphics.Paint; 10 | import android.graphics.Paint.Style; 11 | import android.graphics.RectF; 12 | import android.graphics.drawable.BitmapDrawable; 13 | import android.util.AttributeSet; 14 | import android.view.View; 15 | import android.widget.FrameLayout; 16 | 17 | import androidx.annotation.NonNull; 18 | 19 | import com.huanchengfly.icebridge.R; 20 | 21 | public class ShadowLayout extends FrameLayout { 22 | public static final String TAG = ShadowLayout.class.getSimpleName(); 23 | 24 | private int mShadowColor; 25 | private float mShadowRadius; 26 | private float mCornerRadius; 27 | private float mDx; 28 | private float mDy; 29 | private int mBackgroundColor; 30 | private boolean mInvalidateShadowOnSizeChanged; 31 | private boolean mForceInvalidateShadow; 32 | 33 | public ShadowLayout(@NonNull Context context) { 34 | this(context, null); 35 | } 36 | 37 | public ShadowLayout(@NonNull Context context, AttributeSet attrs) { 38 | this(context, attrs, 0); 39 | } 40 | 41 | public ShadowLayout(@NonNull Context context, AttributeSet attrs, int defStyleAttr) { 42 | this(context, attrs, 0, 0); 43 | } 44 | 45 | public ShadowLayout(@NonNull Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 46 | super(context, attrs, defStyleAttr, defStyleRes); 47 | this.mInvalidateShadowOnSizeChanged = true; 48 | this.initView(context, attrs); 49 | } 50 | 51 | public final int getShadowColor() { 52 | return this.mShadowColor; 53 | } 54 | 55 | public final void setShadowColor(int var1) { 56 | this.mShadowColor = var1; 57 | invalidateShadow(); 58 | } 59 | 60 | public final float getShadowRadius() { 61 | return this.mShadowRadius; 62 | } 63 | 64 | public final void setShadowRadius(float var1) { 65 | this.mShadowRadius = var1; 66 | invalidateShadow(); 67 | } 68 | 69 | public final float getCornerRadius() { 70 | return this.mCornerRadius; 71 | } 72 | 73 | public final void setCornerRadius(float var1) { 74 | this.mCornerRadius = var1; 75 | invalidateShadow(); 76 | } 77 | 78 | public final float getDx() { 79 | return this.mDx; 80 | } 81 | 82 | public final void setDx(float var1) { 83 | this.mDx = var1; 84 | invalidateShadow(); 85 | } 86 | 87 | public final float getDy() { 88 | return this.mDy; 89 | } 90 | 91 | public final void setDy(float var1) { 92 | this.mDy = var1; 93 | invalidateShadow(); 94 | } 95 | 96 | public final int getBackgroundColor() { 97 | return this.mBackgroundColor; 98 | } 99 | 100 | public final void setBackgroundColor(int var1) { 101 | this.mBackgroundColor = var1; 102 | invalidateShadow(); 103 | } 104 | 105 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 106 | super.onSizeChanged(w, h, oldw, oldh); 107 | if (w > 0 && h > 0 && (this.getBackground() == null || this.mInvalidateShadowOnSizeChanged || this.mForceInvalidateShadow)) { 108 | this.mForceInvalidateShadow = false; 109 | this.setBackgroundCompat(w, h); 110 | } 111 | } 112 | 113 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 114 | super.onLayout(changed, left, top, right, bottom); 115 | if (this.mForceInvalidateShadow) { 116 | this.mForceInvalidateShadow = false; 117 | this.setBackgroundCompat(right - left, bottom - top); 118 | } 119 | 120 | } 121 | 122 | public final void setInvalidateShadowOnSizeChanged(boolean invalidateShadowOnSizeChanged) { 123 | this.mInvalidateShadowOnSizeChanged = invalidateShadowOnSizeChanged; 124 | } 125 | 126 | public final void invalidateShadow() { 127 | this.mForceInvalidateShadow = true; 128 | this.requestLayout(); 129 | this.invalidate(); 130 | View view = getChildAt(0); 131 | view.measure(0, 0); 132 | measure(0, 0); 133 | } 134 | 135 | private void initView(Context context, AttributeSet attrs) { 136 | this.initAttributes(context, attrs); 137 | this.refreshPadding(); 138 | } 139 | 140 | public final void refreshPadding() { 141 | int xPadding = (int) (this.mShadowRadius + Math.abs(this.mDx)); 142 | int yPadding = (int) (this.mShadowRadius + Math.abs(this.mDy)); 143 | this.setPadding(xPadding, yPadding, xPadding, yPadding); 144 | } 145 | 146 | private void setBackgroundCompat(int w, int h) { 147 | Bitmap bitmap = this.createShadowBitmap(w, h, this.mCornerRadius, this.mShadowRadius, this.mDx, this.mDy, this.mShadowColor, 0); 148 | BitmapDrawable drawable = new BitmapDrawable(this.getResources(), bitmap); 149 | this.setBackground(drawable); 150 | } 151 | 152 | private void initAttributes(Context context, AttributeSet attrs) { 153 | int[] styleable = R.styleable.ShadowLayout; 154 | TypedArray attr = this.getTypedArray(context, attrs, styleable); 155 | if (attr != null) { 156 | try { 157 | this.mCornerRadius = attr.getDimension(R.styleable.ShadowLayout_shadow_layout_radius, 0.0F); 158 | this.mShadowRadius = attr.getDimension(R.styleable.ShadowLayout_shadow_layout_blur, 0.0F); 159 | this.mDx = attr.getDimension(R.styleable.ShadowLayout_shadow_layout_offsetX, 0.0F); 160 | this.mDy = attr.getDimension(R.styleable.ShadowLayout_shadow_layout_offsetY, 0.0F); 161 | this.mShadowColor = attr.getColor(R.styleable.ShadowLayout_shadow_layout_color, Color.parseColor("#22000000")); 162 | this.mBackgroundColor = attr.getColor(R.styleable.ShadowLayout_shadow_layout_background_color, Integer.MIN_VALUE); 163 | } finally { 164 | attr.recycle(); 165 | } 166 | 167 | } 168 | } 169 | 170 | private final TypedArray getTypedArray(Context context, AttributeSet attributeSet, int[] attr) { 171 | return context.obtainStyledAttributes(attributeSet, attr, 0, 0); 172 | } 173 | 174 | private final Bitmap createShadowBitmap(int shadowWidth, int shadowHeight, float cornerRadius, float shadowRadius, float dx, float dy, int shadowColor, int fillColor) { 175 | Bitmap output = Bitmap.createBitmap(shadowWidth, shadowHeight, Config.ARGB_8888); 176 | Canvas canvas = new Canvas(output); 177 | RectF shadowRect = new RectF(shadowRadius, shadowRadius, (float) shadowWidth - shadowRadius, (float) shadowHeight - shadowRadius); 178 | if (dy > (float) 0) { 179 | shadowRect.top += dy; 180 | shadowRect.bottom -= dy; 181 | } else if (dy < (float) 0) { 182 | shadowRect.top += Math.abs(dy); 183 | shadowRect.bottom -= Math.abs(dy); 184 | } 185 | 186 | if (dx > (float) 0) { 187 | shadowRect.left += dx; 188 | shadowRect.right -= dx; 189 | } else if (dx < (float) 0) { 190 | shadowRect.left += Math.abs(dx); 191 | shadowRect.right -= Math.abs(dx); 192 | } 193 | 194 | Paint paint = new Paint(); 195 | paint.setAntiAlias(true); 196 | paint.setColor(fillColor); 197 | paint.setStyle(Style.FILL); 198 | paint.setShadowLayer(shadowRadius, dx, dy, shadowColor); 199 | canvas.drawRoundRect(shadowRect, cornerRadius, cornerRadius, paint); 200 | if (this.mBackgroundColor != Integer.MIN_VALUE) { 201 | paint.clearShadowLayer(); 202 | paint.setColor(this.mBackgroundColor); 203 | RectF backgroundRect = new RectF((float) this.getPaddingLeft(), (float) this.getPaddingTop(), (float) (this.getWidth() - this.getPaddingRight()), (float) (this.getHeight() - this.getPaddingBottom())); 204 | canvas.drawRoundRect(backgroundRect, cornerRadius, cornerRadius, paint); 205 | } 206 | 207 | return output; 208 | } 209 | } -------------------------------------------------------------------------------- /app/src/main/java/com/huanchengfly/icebridge/widgets/SingleChooseView.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge.widgets; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import android.util.AttributeSet; 6 | import android.view.LayoutInflater; 7 | import android.widget.Checkable; 8 | import android.widget.ImageView; 9 | import android.widget.RelativeLayout; 10 | import android.widget.TextView; 11 | 12 | import androidx.annotation.StringRes; 13 | 14 | import com.google.android.material.radiobutton.MaterialRadioButton; 15 | import com.huanchengfly.icebridge.R; 16 | 17 | public class SingleChooseView extends RelativeLayout implements Checkable { 18 | private static String TAG = SingleChooseView.class.getSimpleName(); 19 | 20 | private TextView title; 21 | private TextView subtitle; 22 | private ImageView icon; 23 | private MaterialRadioButton radioButton; 24 | 25 | @Override 26 | public void setEnabled(boolean enabled) { 27 | if (enabled == isEnabled()) { 28 | return; 29 | } 30 | super.setEnabled(enabled); 31 | title.setEnabled(enabled); 32 | subtitle.setEnabled(enabled); 33 | radioButton.setEnabled(enabled); 34 | } 35 | 36 | public SingleChooseView(Context context) { 37 | this(context, null); 38 | } 39 | 40 | public SingleChooseView(Context context, AttributeSet attrs) { 41 | this(context, attrs, 0); 42 | } 43 | 44 | public SingleChooseView(Context context, AttributeSet attrs, int defStyleAttr) { 45 | this(context, attrs, defStyleAttr, 0); 46 | } 47 | 48 | public SingleChooseView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 49 | super(context, attrs, defStyleAttr, defStyleRes); 50 | LayoutInflater.from(context).inflate(R.layout.layout_single_choose, this, true); 51 | title = findViewById(R.id.title); 52 | subtitle = findViewById(R.id.subtitle); 53 | icon = findViewById(R.id.icon); 54 | radioButton = findViewById(R.id.radio); 55 | } 56 | 57 | public void setIcon(Drawable drawable) { 58 | icon.setImageDrawable(drawable); 59 | icon.setVisibility(drawable == null ? GONE : VISIBLE); 60 | } 61 | 62 | public void setTitle(CharSequence charSequence) { 63 | title.setText(charSequence); 64 | } 65 | 66 | public void setSubtitle(CharSequence charSequence) { 67 | subtitle.setText(charSequence); 68 | } 69 | 70 | public void setTitle(@StringRes int resId) { 71 | title.setText(resId); 72 | } 73 | 74 | public void setSubtitle(@StringRes int resId) { 75 | subtitle.setText(resId); 76 | } 77 | 78 | @Override 79 | public void setChecked(boolean checked) { 80 | radioButton.setChecked(checked); 81 | } 82 | 83 | @Override 84 | public boolean isChecked() { 85 | return radioButton.isChecked(); 86 | } 87 | 88 | @Override 89 | public void toggle() { 90 | radioButton.toggle(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /app/src/main/res/animator/appbar_elevation.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/avatar_huanchengfly.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/drawable/avatar_huanchengfly.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/avatar_icebox.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/drawable/avatar_icebox.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_placeholder_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_radius_16dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_radius_4dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_round_top.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_alipay.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_archive.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_logo_github.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_arrow_back.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_check.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_check_circle.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_cloud_upload.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_error.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_info.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_local_mall.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_offline_bolt.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_warning.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings_applications.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/text_color_secondary_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/text_color_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/layout/about_page.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_about.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_bridge.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 20 | 21 | 28 | 29 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_bridge_preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_enhance_mode.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_intro.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 15 | 16 | 25 | 36 | 37 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_intro.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 16 | 17 | 25 | 26 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_about.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 11 | 12 | 21 | 27 | 33 | 34 | 35 | 40 | 47 | 48 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_grid_choose.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 16 | 17 | 33 | 34 | 40 | 41 | 42 | 49 | 50 | 59 | 60 | 70 | 71 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_single_choose.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_appbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_preference.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 32 | 33 | 34 | 35 | 41 | 42 | 50 | 51 | 64 | 65 | 66 | 67 | 68 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_preference_icon.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 33 | 34 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_single_choose.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 14 | 15 | 25 | 26 | 33 | 41 | 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_work_mode.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main_nav.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/mipmap-hdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/mipmap-mdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values-v23/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values-v27/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #2979FF 4 | #552979FF 5 | #2979FF 6 | #2979FF 7 | #262979FF 8 | #FFFFFFFF 9 | #FF4CAF50 10 | #D32F2F 11 | #DE000000 12 | #8A000000 13 | #8A000000 14 | #61000000 15 | #FEE9E9E9 16 | #1F000000 17 | #F3F3F3 18 | #F57C00 19 | #f0f0f0 20 | #CC000000 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8dp 4 | 0dp 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Ice Bridge 3 | 已授权 4 | 权限未授予 5 | 未授予权限,应用不可用。\n请您授予权限以使用本程序。 6 | 7 | 取消 8 | 未授予权限,无法使用本应用。 9 | permission_status 10 | 权限状态 11 | 已授予权限 12 | 未授予权限,轻按开始授权 13 | 已解冻 %1$s,请重新执行刚才的操作。 14 | 未授予存储空间权限,应用无法正常工作。\n请您授予该权限以使用本程序。 15 | version 16 | 当前版本 17 | 配置文件版本: %1$s\n程序版本: %2$s 18 | 配置文件无法读取,您是否打开过 Ice Bridge? 19 | enhance_mode 20 | 增强模式 21 | 增强模式已启用 22 | 增强模式未激活 23 | enhance_mode_status 24 | 增强模式需要 Xposed 支持。开启后应用可支持以下操作的自动解冻:\n· 微信 分享/支付/一键登录……\n· QQ 一键登录 25 | 26 | IceBox 模式已激活。 27 | %1$s已是最新版本 28 | 正在检查更新 29 | %1$s有更新! 30 | 当前版本: %1$s\n最新版本: %2$s 31 | 配置文件 32 | 检查更新时出错 33 | 冰箱未激活,应用不可用。 34 | 冰箱未安装,程序不可用。 35 | 冰箱版本过旧,需要升级。 36 | IceBox 模式(增强)已激活。 37 | 需要授权,轻按此处以开始授权。 38 | 更新 39 | 正在安装更新 40 | 正在下载更新 41 | 下载更新时出错 42 | 配置文件已更新至 %1$s 43 | 关于 44 | 开发参与 45 | 应用信息 46 | 支持我 47 | 开源相关 48 | 增强模式已激活,但未启用 49 | 欢迎使用 Ice Bridge 50 | 继续 51 | 52 | 53 | Hello blank fragment 54 | 上一步 55 | 完成 56 | 选择工作模式 57 | 每种工作模式原理不同,效果一致 58 | 冰箱 Ice Box 59 | 使用冰箱 Ice Box 提供的开放 SDK 完成解冻,需安装冰箱 3.6.0 或更高版本 60 | 您还没有安装冰箱或冰箱版本过旧,无法使用此模式 61 | Root 62 | 使用 Root 权限解冻应用 63 | 设备未获取 Root 权限。 64 | Root 权限未授予,轻触以授权。 65 | Root 模式已激活。 66 | Root 模式(增强)已激活。 67 | 已获得 Root 权限 68 | 从被冻结的应用打开 69 | 权限申请失败 70 | 选择要使用的应用 71 | 正在跳转中… 72 | 由于系统 Bug,MIUI 系统 可能不支持此模式 73 | 设备未获取 Root 权限,无法使用此模式 74 | 从被冻结的应用分享 75 | 没有可用来分享的应用 76 | show_enable_app 77 | 在列表中显示未冻结应用 78 | 已冻结 79 | 设置 80 | 没有可跳转的应用 81 | 82 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 25 | 26 | 29 | 30 | 33 | 34 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/xml/bridge_preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/xml/preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | 11 | -------------------------------------------------------------------------------- /app/src/test/java/com/huanchengfly/icebridge/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.huanchengfly.icebridge; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | google() 6 | jcenter() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.0.0' 10 | 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | google() 19 | jcenter() 20 | 21 | } 22 | } 23 | 24 | task clean(type: Delete) { 25 | delete rootProject.buildDir 26 | } 27 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | 19 | # AndroidX package structure to make it clearer which packages are bundled with the 20 | # Android operating system, and which are packaged with your app's APK 21 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 22 | android.useAndroidX=true 23 | # Automatically convert third-party libraries to use AndroidX 24 | android.enableJetifier=true 25 | 26 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuanCheng65/IceBridge/0f97a30b9f6ee25dd12bff589a155ba5335f6c49/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jul 31 21:26:17 CST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------