diff --git a/.github/workflows/PermissionManager.yml b/.github/workflows/PermissionManager.yml new file mode 100644 index 00000000..8911f264 --- /dev/null +++ b/.github/workflows/PermissionManager.yml @@ -0,0 +1,30 @@ +name: PermissionManager_CIBuild + +on: + push: + branches: [master] + pull_request: + branches: [master] + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup JDK + uses: actions/setup-java@v2 + with: + distribution: "adopt" + java-version: 11 + - name: Build with Gradle + run: | + cd PermissionManager + chmod +x ./gradlew + ./gradlew assembleRelease + - name: Upload Artifacts + uses: actions/upload-artifact@v2 + if: ${{ !github.event.pull_request }} + with: + path: "PermissionManager/app/build/outputs/apk/release/app-release-unsigned.apk" + name: app-release-unsigned.apk diff --git a/.github/workflows/SKRootKernelRoot_Windows.yml b/.github/workflows/SKRootKernelRoot_Windows.yml new file mode 100644 index 00000000..6d6e3dd7 --- /dev/null +++ b/.github/workflows/SKRootKernelRoot_Windows.yml @@ -0,0 +1,37 @@ +name: SKRootKernelRoot_Windows_CIBuild +on: + push: + branches: [master] + pull_request: + branches: [master] + workflow_dispatch: + +jobs: + build: + name: Compile SKRoot Kernel Root on Windows + runs-on: windows-latest + env: + ACTIONS_ALLOW_UNSECURE_COMMANDS: true + steps: + - uses: actions/checkout@v2 + - name: Add MSBuild to PATH + uses: microsoft/setup-msbuild@v1.1 + - name: List files in current directory + run: | + Get-ChildItem -Path "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\" -Filter vcvarsall.bat -File -Recurse + Get-ChildItem -Path "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\" -Filter vcvars64.bat -File -Recurse + - name: Compile patch_kernel_root + shell: cmd + run: | + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + cl patch_kernel_root\patch_kernel_root.cpp /EHsc /Fe:patch_kernel_root /Ipatch_kernel_root /I "C:\Program Files (x86)\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.35.32215\include" + - name: List files in current directory + run: | + ls patch_kernel_root + Get-ChildItem -Path . -Filter 'patch_kernel_root.*' -File -Recurse + Get-ChildItem -Path . -Filter '*.exe' -File -Recurse + - name: Upload artifacts + uses: Actions/upload-artifact@main + with: + name: patch_kernel_root + path: patch_kernel_root.exe diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..d1638636 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..96edc715 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,35 @@ +# 要求最低的 CMake 版本 +cmake_minimum_required(VERSION 3.0) + +# 启用 CMP0048 策略 +cmake_policy(SET CMP0048 NEW) + +# 设置项目名称和版本号 +project(patch_kernel_root VERSION 1.0) + + +# 要求 C++17 标准 +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# 添加头文件目录 +include_directories(include) + +# 添加所有源文件(排除测试文件) +file(GLOB SRC_FILES "${CMAKE_CURRENT_SOURCE_DIR}/patch_kernel_root/*.cpp") +list(FILTER SRC_FILES EXCLUDE REGEX "^Byte2HexTest.cpp$") + +# 添加可执行文件 +add_executable(patch_kernel_root ${SRC_FILES}) + +# 添加测试文件 +option(BUILD_TESTING "Build the tests" ON) +if(BUILD_TESTING) + enable_testing() + file(GLOB TEST_SOURCES "tests/*.cpp") + foreach(TEST_SOURCE ${TEST_SOURCES}) + get_filename_component(TEST_NAME ${TEST_SOURCE} NAME_WE) + add_executable(${TEST_NAME} ${TEST_SOURCE}) + target_link_libraries(${TEST_NAME} patch_kernel_root) + endforeach() +endif() diff --git a/PermissionManager/.gitignore b/PermissionManager/.gitignore new file mode 100644 index 00000000..aa724b77 --- /dev/null +++ b/PermissionManager/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/PermissionManager/.idea/.gitignore b/PermissionManager/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/PermissionManager/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/PermissionManager/.idea/compiler.xml b/PermissionManager/.idea/compiler.xml new file mode 100644 index 00000000..fb7f4a8a --- /dev/null +++ b/PermissionManager/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/PermissionManager/.idea/gradle.xml b/PermissionManager/.idea/gradle.xml new file mode 100644 index 00000000..526b4c25 --- /dev/null +++ b/PermissionManager/.idea/gradle.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/PermissionManager/.idea/misc.xml b/PermissionManager/.idea/misc.xml new file mode 100644 index 00000000..73acc52f --- /dev/null +++ b/PermissionManager/.idea/misc.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionManager/app/.gitignore b/PermissionManager/app/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/PermissionManager/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/PermissionManager/app/build.gradle b/PermissionManager/app/build.gradle new file mode 100644 index 00000000..4147f11e --- /dev/null +++ b/PermissionManager/app/build.gradle @@ -0,0 +1,53 @@ +plugins { + id 'com.android.application' +} + +android { + compileSdk 31 + + defaultConfig { + applicationId "com.linux.permissionmanager" + minSdk 23 + targetSdk 31 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + externalNativeBuild { + cmake { + cppFlags '-std=c++17' + abiFilters "arm64-v8a" //需要什么构架的so,就在这边添加即 + } + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + externalNativeBuild { + cmake { + path file('src/main/cpp/CMakeLists.txt') + version '3.18.1' + } + } + buildFeatures { + viewBinding true + } +} + +dependencies { + + implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'com.google.android.material:material:1.5.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.3' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' +} \ No newline at end of file diff --git a/PermissionManager/app/proguard-rules.pro b/PermissionManager/app/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/PermissionManager/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/PermissionManager/app/src/androidTest/java/com/linux/permissionmanager/ExampleInstrumentedTest.java b/PermissionManager/app/src/androidTest/java/com/linux/permissionmanager/ExampleInstrumentedTest.java new file mode 100644 index 00000000..e63884ad --- /dev/null +++ b/PermissionManager/app/src/androidTest/java/com/linux/permissionmanager/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.linux.permissionmanager; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.linux.permissionmanager", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/PermissionManager/app/src/main/AndroidManifest.xml b/PermissionManager/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..ac7de7ed --- /dev/null +++ b/PermissionManager/app/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionManager/app/src/main/assets/su b/PermissionManager/app/src/main/assets/su new file mode 100644 index 00000000..caf06dfb Binary files /dev/null and b/PermissionManager/app/src/main/assets/su differ diff --git a/PermissionManager/app/src/main/cpp/CMakeLists.txt b/PermissionManager/app/src/main/cpp/CMakeLists.txt new file mode 100644 index 00000000..d70571ec --- /dev/null +++ b/PermissionManager/app/src/main/cpp/CMakeLists.txt @@ -0,0 +1,52 @@ +# For more information about using CMake with Android Studio, read the +# documentation: https://d.android.com/studio/projects/add-native-code.html + +# Sets the minimum version of CMake required to build the native library. + +cmake_minimum_required(VERSION 3.18.1) + +# Declares and names the project. + +project("permissionmanager") + +# Creates and names a library, sets it as either STATIC +# or SHARED, and provides the relative paths to its source code. +# You can define multiple libraries, and CMake builds them for you. +# Gradle automatically packages shared libraries with your APK. + +add_library( # Sets the name of the library. + permissionmanager + + # Sets the library as a shared library. + SHARED + + # Provides a relative path to your source file(s). + ../../../../../testRoot/process64_inject.cpp + ../../../../../testRoot/ptrace_arm64_utils.cpp + ../../../../../testRoot/su_install_helper.cpp + ../../../../../testRoot/base64.cpp + root.cpp) + +# Searches for a specified prebuilt library and stores the path as a +# variable. Because CMake includes system libraries in the search path by +# default, you only need to specify the name of the public NDK library +# you want to add. CMake verifies that the library exists before +# completing its build. + +find_library( # Sets the name of the path variable. + log-lib + + # Specifies the name of the NDK library that + # you want CMake to locate. + log) + +# Specifies libraries CMake should link to your target library. You +# can link multiple libraries, such as libraries you define in this +# build script, prebuilt third-party libraries, or system libraries. + +target_link_libraries( # Specifies the target library. + permissionmanager + + # Links the target library to the log library + # included in the NDK. + ${log-lib}) \ No newline at end of file diff --git a/PermissionManager/app/src/main/cpp/root.cpp b/PermissionManager/app/src/main/cpp/root.cpp new file mode 100644 index 00000000..3dd0bb42 --- /dev/null +++ b/PermissionManager/app/src/main/cpp/root.cpp @@ -0,0 +1,274 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../../testRoot/testRoot.h" +#include "../../../../../testRoot/kernel_root_helper.h" +#include "../../../../../testRoot/process64_inject.h" +#include "../../../../../testRoot/init64_process_helper.h" +#include "../../../../../testRoot/su_install_helper.h" + +using namespace std; + +std::string g_last_su_full_path; + +string getCapabilityInfo() +{ + __uid_t now_uid, now_euid, now_suid; + if (getresuid(&now_uid, &now_euid, &now_suid)) { + return "FAILED getresuid()"; + } + + + __gid_t now_gid, now_egid, now_sgid; + if (getresgid(&now_gid, &now_egid, &now_sgid)) { + return "FAILED getresgid()"; + } + + stringstream sstrCapInfo; + sstrCapInfo<< "Current process information:\n"; + sstrCapInfo<< "uid:"<pid = getpid(); + cap_header->version = _LINUX_CAPABILITY_VERSION_3; //_1、_2、_3 + + if (capget(cap_header, cap_data) < 0) { + return "FAILED capget()"; + // perror("FAILED capget()"); + //exit(1); + } + sstrCapInfo << "cap effective:" << hex <effective << "," << std::endl + <<"cap permitted:"<< hex << cap_data->permitted<< "," << std::endl + <<"cap inheritable:"<< hex <inheritable<< std::endl; + FILE * fp = popen("getenforce", "r"); + if (fp) + { + char cmd[512] = { 0 }; + fread(cmd, 1, sizeof(cmd), fp); + pclose(fp); + + sstrCapInfo<< "read system SELinux status:"<< cmd; + } + + return sstrCapInfo.str(); +} + +extern "C" JNIEXPORT jstring JNICALL +Java_com_linux_permissionmanager_MainActivity_testRoot( + JNIEnv* env, + jobject /* this */, + jstring rootKey) { + const char *str1 = env->GetStringUTFChars(rootKey, 0); + string strRootKey= str1; + env->ReleaseStringUTFChars(rootKey, str1); + + std::string result; + fork_pipe_info finfo; + ssize_t err = 0; + if(fork_pipe_child_process(finfo)) { + err = kernel_root::get_root(strRootKey.c_str()); + result = "getRoot:"; + result += std::to_string(err); + result += "\n\n"; + if(err == 0) { + result += getCapabilityInfo(); + result += "\n\n"; + } + write_errcode_to_father(finfo, err); + write_string_to_father(finfo, result); + _exit(0); + return 0; + } + err = 0; + if(!wait_fork_child_process(finfo)) { + err = -1120001; + } else { + if(!read_errcode_from_child(finfo, err)) { + err = -1120002; + } else if(!read_string_from_child(finfo, result)) { + err = -1120003; + } + } + return env->NewStringUTF(result.c_str()); +} + +extern "C" JNIEXPORT jstring JNICALL +Java_com_linux_permissionmanager_MainActivity_runRootCmd( + JNIEnv* env, + jobject /* this */, + jstring rootKey, + jstring cmd) { + const char *str1 = env->GetStringUTFChars(rootKey, 0); + string strRootKey= str1; + env->ReleaseStringUTFChars(rootKey, str1); + + str1 = env->GetStringUTFChars(cmd, 0); + string strCmd= str1; + env->ReleaseStringUTFChars(cmd, str1); + + + ssize_t err; + string result = kernel_root::run_root_cmd(strRootKey.c_str(), strCmd.c_str(), err); + stringstream sstr; + sstr << "runRootCmd err:" << err << ", result:" << result; + return env->NewStringUTF(sstr.str().c_str()); +} + + +extern "C" JNIEXPORT jstring JNICALL +Java_com_linux_permissionmanager_MainActivity_runInit64ProcessCmd( + JNIEnv* env, + jobject /* this */, + jstring rootKey, + jstring cmd) { + const char *str1 = env->GetStringUTFChars(rootKey, 0); + string strRootKey= str1; + env->ReleaseStringUTFChars(rootKey, str1); + + str1 = env->GetStringUTFChars(cmd, 0); + string strCmd= str1; + env->ReleaseStringUTFChars(cmd, str1); + + + ssize_t err; + string result = safe_run_init64_cmd_wrapper(strRootKey.c_str(), strCmd.c_str(), err); + + stringstream sstr; + sstr << "runInit64Cmd err:" << err << ", result:" << result; + return env->NewStringUTF(sstr.str().c_str()); +} + +extern "C" JNIEXPORT jstring JNICALL +Java_com_linux_permissionmanager_MainActivity_installSu( + JNIEnv* env, + jobject /* this */, + jstring rootKey, + jstring basePath, + jstring originSuFullPath) { + + const char *str1 = env->GetStringUTFChars(rootKey, 0); + string strRootKey= str1; + env->ReleaseStringUTFChars(rootKey, str1); + + str1 = env->GetStringUTFChars(basePath, 0); + string strBasePath= str1; + env->ReleaseStringUTFChars(basePath, str1); + + str1 = env->GetStringUTFChars(originSuFullPath, 0); + string strOriginSuFullPath= str1; + env->ReleaseStringUTFChars(originSuFullPath, str1); + + stringstream sstr; + //安装su工具套件 + ssize_t err; + std::string su_hide_full_path = safe_install_su(strRootKey.c_str(), strBasePath.c_str(), strOriginSuFullPath.c_str(), err); + sstr << "install su err:" << err<<", su_hide_full_path:" << su_hide_full_path << std::endl; + g_last_su_full_path = su_hide_full_path; + if (err == 0) { + sstr << "installSu done."<< std::endl; + } + return env->NewStringUTF(sstr.str().c_str()); +} + +extern "C" JNIEXPORT jstring JNICALL +Java_com_linux_permissionmanager_MainActivity_getLastInstallSuFullPath( + JNIEnv* env, + jobject /* this */) { + return env->NewStringUTF(g_last_su_full_path.c_str()); +} + +extern "C" JNIEXPORT jstring JNICALL +Java_com_linux_permissionmanager_MainActivity_uninstallSu( + JNIEnv* env, + jobject /* this */, + jstring rootKey, + jstring basePath) { + + const char *str1 = env->GetStringUTFChars(rootKey, 0); + string strRootKey= str1; + env->ReleaseStringUTFChars(rootKey, str1); + + str1 = env->GetStringUTFChars(basePath, 0); + string strBasePath= str1; + env->ReleaseStringUTFChars(basePath, str1); + + stringstream sstr; + + ssize_t err = safe_uninstall_su(strRootKey.c_str(), strBasePath.c_str()); + sstr << "uninstallSu err:" << err << std::endl; + if (err != 0) { + return env->NewStringUTF(sstr.str().c_str()); + } + g_last_su_full_path.clear(); + sstr << "uninstallSu done."; + return env->NewStringUTF(sstr.str().c_str()); +} + +extern "C" JNIEXPORT jstring JNICALL +Java_com_linux_permissionmanager_MainActivity_autoSuEnvInject( + JNIEnv* env, + jobject /* this */, + jstring rootKey, + jstring targetProcessCmdline) { + + if(g_last_su_full_path.empty()) { + return env->NewStringUTF("【错误】请先安装部署su"); + } + const char *str1 = env->GetStringUTFChars(rootKey, 0); + string strRootKey= str1; + env->ReleaseStringUTFChars(rootKey, str1); + + str1 = env->GetStringUTFChars(targetProcessCmdline, 0); + string strTargetProcessCmdline = str1; + env->ReleaseStringUTFChars(targetProcessCmdline, str1); + + stringstream sstr; + + //杀光所有历史进程 + std::vector vOut; + ssize_t err = safe_find_all_cmdline_process(strRootKey.c_str(), strTargetProcessCmdline.c_str(), vOut); + sstr << "find_all_cmdline_process err:"<< err<<", cnt:"<NewStringUTF(sstr.str().c_str()); + } + std::string kill_cmd; + for (pid_t t : vOut) { + err = safe_kill_process(strRootKey.c_str(), t); + sstr << "kill_ret err:"<< err << std::endl; + if (err != 0) { + return env->NewStringUTF(sstr.str().c_str()); + } + } + pid_t pid; + err = safe_wait_and_find_cmdline_process(strRootKey.c_str(), strTargetProcessCmdline.c_str(), 60*1000, pid); + + std::string folder_path = g_last_su_full_path; + int n = folder_path.find_last_of("/"); + if(n != -1) { + folder_path = folder_path.substr(0,n); + } + sstr << "autoSuEnvInject("<< err<<", " << folder_path <<")" << std::endl; + if (err != 0) { + return env->NewStringUTF(sstr.str().c_str()); + } + err = safe_inject_process_env64_PATH_wrapper(strRootKey.c_str(), pid, folder_path.c_str()); + sstr << "autoSuEnvInject ret val:" << err << std::endl; + if (err != 0) { + return env->NewStringUTF(sstr.str().c_str()); + } + sstr << "autoSuEnvInject done."; + return env->NewStringUTF(sstr.str().c_str()); +} diff --git a/PermissionManager/app/src/main/java/com/linux/permissionmanager/Adapter/SelectAppRecyclerAdapter.java b/PermissionManager/app/src/main/java/com/linux/permissionmanager/Adapter/SelectAppRecyclerAdapter.java new file mode 100644 index 00000000..ee308d45 --- /dev/null +++ b/PermissionManager/app/src/main/java/com/linux/permissionmanager/Adapter/SelectAppRecyclerAdapter.java @@ -0,0 +1,110 @@ +package com.linux.permissionmanager.Adapter; + +import android.content.Context; +import android.os.Handler; +import android.os.Message; +import android.text.Html; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.PopupWindow; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.linux.permissionmanager.Model.SelectAppRecyclerItem; +import com.linux.permissionmanager.R; + +import java.util.List; + +public class SelectAppRecyclerAdapter extends RecyclerView.Adapter { + + + + public static class ViewHolder extends RecyclerView.ViewHolder { + public View v; + public ImageView select_app_icon; + public TextView select_app_text; + public TextView select_package_name; + // TODO Auto-generated method stub + public ViewHolder(View v) { + super(v); + this.v = v; + } + + } + + private int resourceId; + private List objects; + private PopupWindow popupWindow; + private Handler selectAppItemCallback; + public SelectAppRecyclerAdapter(Context context, int textViewResourceId, List objects, PopupWindow popupWindow, Handler selectAppItemCallback) { + this.resourceId = textViewResourceId; + this.objects = objects; + this. popupWindow = popupWindow; + this. selectAppItemCallback = selectAppItemCallback; + } + + + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View v =LayoutInflater.from(parent.getContext()).inflate(resourceId,parent,false); + SelectAppRecyclerAdapter.ViewHolder holder = new SelectAppRecyclerAdapter.ViewHolder(v); + + holder.select_app_icon = v.findViewById(R.id.select_app_icon); + holder.select_app_text=v.findViewById(R.id.select_app_text) ; + holder.select_package_name=v.findViewById(R.id.select_package_name) ; + return holder; + } + + + @Override + public void onBindViewHolder(@NonNull SelectAppRecyclerAdapter.ViewHolder holder, int position) { + SelectAppRecyclerItem appItem=objects.get(position); //获取当前项的实例 + + //图标+进程PID+名字+内存 + holder.select_app_icon.setImageDrawable(appItem.getIcon()); + String showText=""+appItem.getShowName() +" " + +" "+" ("+appItem.getPackageName()+")"+""; + + holder.select_app_text.setText(Html.fromHtml(showText)); + holder.select_package_name.setText(appItem.getPackageName()); + + //item被点击 + holder.v.setOnClickListener(new ClickRecyclerItemListener(appItem)); + } + + + @Override + public int getItemCount() { + return objects.size(); + } + + @Override + public int getItemViewType(int position) { + return position; + } + + + class ClickRecyclerItemListener implements View.OnClickListener { + SelectAppRecyclerItem appItem; + public ClickRecyclerItemListener( SelectAppRecyclerItem appItem){ + this.appItem =appItem; + } + @Override + public void onClick(View v) { + popupWindow.dismiss(); + Message msg = new Message(); + msg.obj = (SelectAppRecyclerItem)appItem; + selectAppItemCallback.sendMessage(msg); + } + } + + + + +} diff --git a/PermissionManager/app/src/main/java/com/linux/permissionmanager/MainActivity.java b/PermissionManager/app/src/main/java/com/linux/permissionmanager/MainActivity.java new file mode 100644 index 00000000..3da4f37a --- /dev/null +++ b/PermissionManager/app/src/main/java/com/linux/permissionmanager/MainActivity.java @@ -0,0 +1,436 @@ +package com.linux.permissionmanager; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.ProgressDialog; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.graphics.Color; +import android.graphics.PixelFormat; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.PopupWindow; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.linux.permissionmanager.Adapter.SelectAppRecyclerAdapter; +import com.linux.permissionmanager.Model.PopupWindowOnTouchClose; +import com.linux.permissionmanager.Model.SelectAppRecyclerItem; +import com.linux.permissionmanager.Utils.ScreenInfoUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +public class MainActivity extends AppCompatActivity { + + private String rootKey = "mUBcNll4mhenWlvYEWZF4yOHm5aOPklyq0LAUIAbV0L7R0Ub"; + private String suBasePath = "/data/local/tmp"; + private String lastInputCmd = "id"; + + //保存的本地配置信息 + private SharedPreferences m_shareSave; + private ProgressDialog m_loadingDlg = null; + + // Used to load the 'permissionmanager' library on application startup. + static { + System.loadLibrary("permissionmanager"); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + + m_shareSave = getSharedPreferences("zhcs", Context.MODE_PRIVATE); + try { + rootKey = m_shareSave.getString("rootKey", rootKey); + } catch (Exception e) { + } + + //验证用户的KEY + final EditText inputKey = new EditText(MainActivity.this); + inputKey.setText(rootKey); + AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); + builder.setCancelable(false); + builder.setTitle("请输入ROOT权限的KEY").setIcon(android.R.drawable.ic_dialog_info).setView(inputKey) + .setPositiveButton("确定", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + rootKey = inputKey.getText().toString(); + //数值保存到本地 + SharedPreferences.Editor mEdit = m_shareSave.edit(); + mEdit.putString("rootKey", rootKey); + mEdit.commit(); + } + }); + builder.show(); + + + Button test_root_btn = findViewById(R.id.test_root_btn); + test_root_btn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + showConsoleMsg(testRoot(rootKey)); + } + }); + + Button run_root_cmd_btn = findViewById(R.id.run_root_cmd_btn); + run_root_cmd_btn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + final EditText inputCMD = new EditText(MainActivity.this); + inputCMD.setText(lastInputCmd); + inputCMD.setFocusable(true); + inputCMD.setSelection(inputCMD.length(), 0); + AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); + builder.setTitle("输入普通命令").setIcon(android.R.drawable.ic_dialog_info).setView(inputCMD) + .setNegativeButton("取消", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); + builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + String text = inputCMD.getText().toString(); + lastInputCmd = text; + showConsoleMsg(text + "\n" + runRootCmd(rootKey, text)); + } + }); + builder.show(); + + } + }); + Button run_init_process_cmd_btn = findViewById(R.id.run_init_process_cmd_btn); + run_init_process_cmd_btn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + final EditText inputCMD = new EditText(MainActivity.this); + inputCMD.setText(lastInputCmd); + inputCMD.setFocusable(true); + inputCMD.setSelection(inputCMD.length(), 0); + AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); + builder.setTitle("输入ROOT命令").setIcon(android.R.drawable.ic_dialog_info).setView(inputCMD) + .setNegativeButton("取消", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); + builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + String text = inputCMD.getText().toString(); + lastInputCmd = text; + showConsoleMsg(text + "\n" + runInit64ProcessCmd(rootKey, text)); + } + }); + builder.show(); + + } + }); + + Button su_env_install_btn = findViewById(R.id.su_env_install_btn); + su_env_install_btn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + //1.获取su工具文件路径 + String suOriginalFilePath = WirteSuToolsFilePath("su", MainActivity.this); + showConsoleMsg("su origin path:"+ suOriginalFilePath); + + //2.安装su工具 + String insRet = installSu(rootKey, suBasePath, suOriginalFilePath); + showConsoleMsg(insRet); + if(insRet.indexOf("installSu done.") == -1) { + return; + } + + //3.复制su的路径到剪贴板 + String suFullPath = getLastInstallSuFullPath(); + showConsoleMsg("lastInstallSuFullPath:" + suFullPath); + showConsoleMsg("安装部署su成功"); + showConsoleMsg("【请注意】su的有效期仅为当天,过期后则需卸载并重新安装部署su。(如需移除此过期限制,请自行更改源代码)"); + copyEditText(suFullPath); + Toast.makeText(v.getContext(), "su路径已复制到剪贴板", Toast.LENGTH_SHORT).show(); + + } + }); + + Button su_env_inject_btn = findViewById(R.id.su_env_inject_btn); + su_env_inject_btn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + showSelectAppWindow(); + } + }); + + Button clean_su_btn = findViewById(R.id.clean_su_btn); + clean_su_btn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + showConsoleMsg(uninstallSu(rootKey,suBasePath)); + } + }); + + Button copy_info_btn = findViewById(R.id.copy_info_btn); + copy_info_btn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + EditText edit = findViewById(R.id.console_edit); + copyEditText(edit.getText().toString()); + Toast.makeText(v.getContext(), "复制成功", Toast.LENGTH_SHORT).show(); + + } + }); + Button clean_info_btn = findViewById(R.id.clean_info_btn); + clean_info_btn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + cleanConsoleMsg(); + } + }); + } + + public void showConsoleMsg(String msg) { + EditText console_edit = findViewById(R.id.console_edit); + StringBuffer txt = new StringBuffer(); + txt.append(console_edit.getText().toString()); + if (txt.length() != 0) { + txt.append("\n"); + } + txt.append(msg); + txt.append("\n"); + console_edit.setText(txt.toString()); + console_edit.setSelection(txt.length()); + } + + public void cleanConsoleMsg() { + EditText console_edit = findViewById(R.id.console_edit); + console_edit.setText(""); + } + + public void copyEditText(String text) { + //获取剪贴板管理器: + ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); + // 创建普通字符型ClipData + ClipData mClipData = ClipData.newPlainText("Label", text); + // 将ClipData内容放到系统剪贴板里。 + cm.setPrimaryClip(mClipData); + + } + + Handler selectAppItemCallback = new Handler() { + @Override + public void handleMessage(@NonNull Message msg) { + + SelectAppRecyclerItem appItem = (SelectAppRecyclerItem) msg.obj; + + if (m_loadingDlg == null) { + m_loadingDlg = new ProgressDialog(MainActivity.this); + m_loadingDlg.setCancelable(false); + } + m_loadingDlg.setTitle(""); + m_loadingDlg.setMessage("请现在手动启动APP [" + appItem.getShowName() + "]"); + m_loadingDlg.show(); + + new Thread() { + public void run() { + String ret = autoSuEnvInject(rootKey, appItem.getPackageName()); + runOnUiThread(new Runnable() { + public void run() { + showConsoleMsg(ret); + m_loadingDlg.cancel(); + + if(ret.indexOf("autoSuEnvInject done.")!= -1) { + //弹个提示,通知一下成功了 + AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this) + .setCancelable(false) + .setTitle("提示") + .setIcon(appItem.getIcon()) + .setMessage("已经授予ROOT权限到APP [" + appItem.getShowName() + "]") + .setNegativeButton("确定", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); + AlertDialog dialog = builder.create(); + dialog.show(); + dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(Color.BLACK); + } + + } + }); + } + }.start(); + super.handleMessage(msg); + } + }; + + //显示选择应用程序窗口 + public void showSelectAppWindow() { + final PopupWindow popupWindow = new PopupWindow(this); + + View view = View.inflate(this, R.layout.select_app_wnd, null); + popupWindow.setContentView(view); + + popupWindow.setHeight(ViewGroup.LayoutParams.MATCH_PARENT); + popupWindow.setWidth(ViewGroup.LayoutParams.MATCH_PARENT); + popupWindow.setBackgroundDrawable(new ColorDrawable(0x9B000000)); //阴影半透明 + popupWindow.setOutsideTouchable(true); + popupWindow.setFocusable(true); + popupWindow.setTouchable(true); + + //全屏 + View parent = View.inflate(MainActivity.this, R.layout.activity_main, null); + popupWindow.showAtLocation(parent, Gravity.NO_GRAVITY, 0, 0); + popupWindow.showAsDropDown(parent, 0, 0); + + popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() { + @Override + public void onDismiss() { //窗口即将关闭 + + } + }); + + //设置中心布局大小 + final int screenWidth = ScreenInfoUtils.getRealWidth(this); + final int screenHeight = ScreenInfoUtils.getRealHeight(this); + + final double centerWidth = ((double) screenWidth) * 0.80; + final double centerHeight = ((double) screenHeight) * 0.90; + + LinearLayout center_layout = (LinearLayout) view.findViewById(R.id.center_layout); + android.view.ViewGroup.LayoutParams lp = center_layout.getLayoutParams(); + lp.width = (int) centerWidth; + lp.height = (int) centerHeight; + + //点击阴影部分可关闭窗口 + popupWindow.setTouchInterceptor(new PopupWindowOnTouchClose(popupWindow, + screenWidth, screenHeight, (int) centerWidth, (int) centerHeight)); + + //显示APP列表 + List appList = new ArrayList<>(); + + //获取已安装的APK列表 + List packages = getPackageManager().getInstalledPackages(0); + + //先判断cmdline与包名是否完全相同 + for (int i = 0; i < packages.size(); i++) { + PackageInfo packageInfo = packages.get(i); + + if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + //系统应用 + continue; + } + String showName = packageInfo.applicationInfo.loadLabel(getPackageManager()).toString(); + + Drawable icon = packageInfo.applicationInfo.loadIcon(getPackageManager()); + + String packageName = packageInfo.applicationInfo.packageName; + if(packageName.equals(getPackageName())){ + //不显示自己 + continue; + } + //加入到显示APP列表 + appList.add(new SelectAppRecyclerItem( + icon, + showName, + packageName)); + } + for (int i = 0; i < packages.size(); i++) { + PackageInfo packageInfo = packages.get(i); + + if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { + //非系统应用 + continue; + } + + String showName = packageInfo.applicationInfo.loadLabel(getPackageManager()).toString(); + + Drawable icon = packageInfo.applicationInfo.loadIcon(getPackageManager()); + + String packageName = packageInfo.applicationInfo.packageName; + + //加入到显示APP列表 + appList.add(new SelectAppRecyclerItem( + icon, + showName, + packageName)); + } + + SelectAppRecyclerAdapter adapter = new SelectAppRecyclerAdapter( + MainActivity.this, R.layout.select_app_recycler_item, appList, popupWindow, selectAppItemCallback); + + RecyclerView select_app_recycler_view = (RecyclerView) view.findViewById(R.id.select_app_recycler_view); + // 设置布局管理器 + LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this); + linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL); + select_app_recycler_view.setLayoutManager(linearLayoutManager); + select_app_recycler_view.setAdapter(adapter); + + } + + public static String WirteSuToolsFilePath(String suName, Context context) { + String suFilePath = ""; + try { + suFilePath = context.getFilesDir().getPath() + "/" + suName; + File file = new File(suFilePath); + if (!file.exists()) { + if (!file.getParentFile().exists()) file.getParentFile().mkdirs(); + file.createNewFile(); + } + if (file.exists()) { + InputStream inputStream = context.getAssets().open(suName); + FileOutputStream outputStream = new FileOutputStream(file); + byte[] content = new byte[1024]; + while (inputStream.read(content) > 0) { + outputStream.write(content); + } + inputStream.close(); + outputStream.flush(); + outputStream.close(); + } + } catch (Exception e) { + } + return suFilePath; + } + + public native String testRoot(String rootKey); + + public native String runRootCmd(String rootKey, String cmd); + + public native String runInit64ProcessCmd(String rootKey, String cmd); + + public native String installSu(String rootKey, String basePath, String originSuFullPath); + + public native String getLastInstallSuFullPath(); + + public native String uninstallSu(String rootKey, String basePath); + + public native String autoSuEnvInject(String rootKey, String targetProcessCmdline); +} \ No newline at end of file diff --git a/PermissionManager/app/src/main/java/com/linux/permissionmanager/Model/PopupWindowOnTouchClose.java b/PermissionManager/app/src/main/java/com/linux/permissionmanager/Model/PopupWindowOnTouchClose.java new file mode 100644 index 00000000..ca4889d7 --- /dev/null +++ b/PermissionManager/app/src/main/java/com/linux/permissionmanager/Model/PopupWindowOnTouchClose.java @@ -0,0 +1,48 @@ +package com.linux.permissionmanager.Model; + +import android.view.MotionEvent; +import android.view.View; +import android.widget.PopupWindow; + +public class PopupWindowOnTouchClose implements View.OnTouchListener { + private boolean lastVailedDown = true; + private int screenWidth, screenHeight, centerWidth, centerHeight; + private PopupWindow popupWindow; + + public PopupWindowOnTouchClose(PopupWindow popupWindow, int screenWidth, int screenHeight, int centerWidth, int centerHeight) { + this.popupWindow = popupWindow; + this.screenWidth = screenWidth; + this.screenHeight = screenHeight; + this.centerWidth = centerWidth; + this.centerHeight = centerHeight; + } + + private boolean isValiedRegion(View v, MotionEvent event) { + int x = (int) event.getX(); + int y = (int) event.getY(); + double wndLeft = (screenWidth - centerWidth) / 2; + double wndTop = (screenHeight - centerHeight) / 2; + if (x < wndLeft || x > wndLeft + centerWidth || y < wndTop || y > wndTop + centerHeight) { + return false; + } + return true; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + + if (event.getAction() == MotionEvent.ACTION_DOWN) { + lastVailedDown = isValiedRegion(v, event); + } else if (event.getAction() == MotionEvent.ACTION_UP) { + if (!lastVailedDown) { + if (!isValiedRegion(v, event)) { + popupWindow.dismiss(); + } + } + } + return false; + } +} + + + diff --git a/PermissionManager/app/src/main/java/com/linux/permissionmanager/Model/SelectAppRecyclerItem.java b/PermissionManager/app/src/main/java/com/linux/permissionmanager/Model/SelectAppRecyclerItem.java new file mode 100644 index 00000000..0a2eecba --- /dev/null +++ b/PermissionManager/app/src/main/java/com/linux/permissionmanager/Model/SelectAppRecyclerItem.java @@ -0,0 +1,40 @@ +package com.linux.permissionmanager.Model; + +import android.graphics.drawable.Drawable; + +public class SelectAppRecyclerItem { + private Drawable icon=null; + private String showName; + private String packageName; + + public SelectAppRecyclerItem(Drawable icon, String showName, String packageName){ + this.icon=icon; + this.showName = showName; + this.packageName = packageName; + } + + public Drawable getIcon() { + return icon; + } + + public void setIcon(Drawable icon) { + this.icon = icon; + } + + public String getShowName() { + return showName; + } + + public void setShowName(String showName) { + this.showName = showName; + } + + public String getPackageName() { + return packageName; + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + } + +} diff --git a/PermissionManager/app/src/main/java/com/linux/permissionmanager/Utils/ScreenInfoUtils.java b/PermissionManager/app/src/main/java/com/linux/permissionmanager/Utils/ScreenInfoUtils.java new file mode 100644 index 00000000..5e50159e --- /dev/null +++ b/PermissionManager/app/src/main/java/com/linux/permissionmanager/Utils/ScreenInfoUtils.java @@ -0,0 +1,187 @@ +package com.linux.permissionmanager.Utils; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Resources; +import android.util.DisplayMetrics; +import android.util.Log; +import android.util.TypedValue; +import android.view.Display; +import android.view.WindowManager; + +/** + * Get Screen Information Utils + * + * @author yh + * @date 2018/12/18. + */ +public class ScreenInfoUtils { + + private static final String TAG = "ScreenInfoUtils"; + + /** + * Get Screen Width + */ + public static int getScreenWidth(Context context) { + return getDisplayMetrics(context).widthPixels; + } + + /** + * Get Screen Height + */ + public static int getScreenHeight(Context context) { + return getDisplayMetrics(context).heightPixels; + } + + + /** + * Get Screen Real Height + * + * @param context Context + * @return Real Height + */ + public static int getRealHeight(Context context) { + Display display = getDisplay(context); + if (display == null) { + return 0; + } + DisplayMetrics dm = new DisplayMetrics(); + display.getRealMetrics(dm); + return dm.heightPixels; + } + + /** + * Get Screen Real Width + * + * @param context Context + * @return Real Width + */ + public static int getRealWidth(Context context) { + Display display = getDisplay(context); + if (display == null) { + return 0; + } + DisplayMetrics dm = new DisplayMetrics(); + display.getRealMetrics(dm); + return dm.widthPixels; + } + + /** + * Get StatusBar Height + */ + public static int getStatusBarHeight(Context mContext) { + int resourceId = mContext.getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + return mContext.getResources().getDimensionPixelSize(resourceId); + } + return 0; + } + + /** + * Get ActionBar Height + */ + public static int getActionBarHeight(Context mContext) { + TypedValue tv = new TypedValue(); + if (mContext.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) { + return TypedValue.complexToDimensionPixelSize(tv.data, mContext.getResources().getDisplayMetrics()); + } + return 0; + } + + /** + * Get NavigationBar Height + */ + public static int getNavigationBarHeight(Context mContext) { + Resources resources = mContext.getResources(); + int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android"); + if (resourceId > 0) { + return resources.getDimensionPixelSize(resourceId); + } + return 0; + } + + /** + * Get Density + */ + private static float getDensity(Context context) { + return getDisplayMetrics(context).density; + } + + /** + * Get Dpi + */ + private static int getDpi(Context context) { + return getDisplayMetrics(context).densityDpi; + } + + /** + * Get Display + * + * @param context Context for get WindowManager + * @return Display + */ + private static Display getDisplay(Context context) { + WindowManager wm; + if (context instanceof Activity) { + Activity activity = (Activity) context; + wm = activity.getWindowManager(); + } else { + wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + } + if (wm != null) { + return wm.getDefaultDisplay(); + } + return null; + } + + /** + * Get DisplayMetrics + * + * @param context Context for get Resources + * @return DisplayMetrics + */ + private static DisplayMetrics getDisplayMetrics(Context context) { + return context.getResources().getDisplayMetrics(); + } + + + /** + * Get ScreenInfo + */ + private static String getScreenInfo(Context context) { + return " \n" + + "--------ScreenInfo--------" + "\n" + + "Screen Width : " + getScreenWidth(context) + "px\n" + + "Screen RealWidth :" + getRealWidth(context) + "px\n" + + "Screen Height: " + getScreenHeight(context) + "px\n" + + "Screen RealHeight: " + getRealHeight(context) + "px\n" + + "Screen StatusBar Height: " + getStatusBarHeight(context)+ "px\n" + + "Screen ActionBar Height: " + getActionBarHeight(context)+ "px\n" + + "Screen NavigationBar Height: " + getNavigationBarHeight(context)+ "px\n" + + "Screen Dpi: " + getDpi(context) + "\n" + + "Screen Density: " + getDensity(context) + "\n" + + "--------------------------"; + } + + + /** + * 根据手机的分辨率从 dp 的单位 转成为 px(像素) + */ + public static int dip2px(Context context, float dpValue) { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (dpValue * scale); + } + + /** * 根据手机的分辨率从 px(像素) 的单位 转成为 dp */ + public static int px2dip(Context context, float pxValue) { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (pxValue / scale); + } + + /** + * Print screenInfo to logcat + */ + public static void printScreenInfo(Context context) { + Log.d(TAG, getScreenInfo(context)); + } +} diff --git a/PermissionManager/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/PermissionManager/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..2b068d11 --- /dev/null +++ b/PermissionManager/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionManager/app/src/main/res/drawable/ic_launcher_background.xml b/PermissionManager/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..07d5da9c --- /dev/null +++ b/PermissionManager/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PermissionManager/app/src/main/res/drawable/line.xml b/PermissionManager/app/src/main/res/drawable/line.xml new file mode 100644 index 00000000..597142f2 --- /dev/null +++ b/PermissionManager/app/src/main/res/drawable/line.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/PermissionManager/app/src/main/res/drawable/line_drawable.xml b/PermissionManager/app/src/main/res/drawable/line_drawable.xml new file mode 100644 index 00000000..4f79f470 --- /dev/null +++ b/PermissionManager/app/src/main/res/drawable/line_drawable.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/PermissionManager/app/src/main/res/drawable/ripple_grey.xml b/PermissionManager/app/src/main/res/drawable/ripple_grey.xml new file mode 100644 index 00000000..ac736de4 --- /dev/null +++ b/PermissionManager/app/src/main/res/drawable/ripple_grey.xml @@ -0,0 +1,6 @@ + +//点击时波纹的颜色 + //未点击时控件的背景(可以是图片,可以是颜色,也可以是drawable里的xml背景(比如圆角)) + \ No newline at end of file diff --git a/PermissionManager/app/src/main/res/drawable/shape_wnd_grey_corner.xml b/PermissionManager/app/src/main/res/drawable/shape_wnd_grey_corner.xml new file mode 100644 index 00000000..6bc80841 --- /dev/null +++ b/PermissionManager/app/src/main/res/drawable/shape_wnd_grey_corner.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/PermissionManager/app/src/main/res/drawable/thumb.xml b/PermissionManager/app/src/main/res/drawable/thumb.xml new file mode 100644 index 00000000..0646431b --- /dev/null +++ b/PermissionManager/app/src/main/res/drawable/thumb.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/PermissionManager/app/src/main/res/drawable/thumb_drawable.xml b/PermissionManager/app/src/main/res/drawable/thumb_drawable.xml new file mode 100644 index 00000000..a93b8b83 --- /dev/null +++ b/PermissionManager/app/src/main/res/drawable/thumb_drawable.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/PermissionManager/app/src/main/res/layout/activity_main.xml b/PermissionManager/app/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..5cb107be --- /dev/null +++ b/PermissionManager/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + +