diff --git a/manager/app/build.gradle.kts b/manager/app/build.gradle.kts index 7f254bc24ab5..25424a2ad575 100644 --- a/manager/app/build.gradle.kts +++ b/manager/app/build.gradle.kts @@ -151,4 +151,5 @@ dependencies { implementation ("com.squareup.okhttp:okhttp-urlconnection:2.2.0") implementation ("org.greenrobot:eventbus:3.2.0") + implementation ("com.tencent.bugly:crashreport:latest.release") } diff --git a/manager/app/src/main/AndroidManifest.xml b/manager/app/src/main/AndroidManifest.xml index 3d5325106455..c4bd3e4bcce1 100644 --- a/manager/app/src/main/AndroidManifest.xml +++ b/manager/app/src/main/AndroidManifest.xml @@ -1,6 +1,7 @@ + xmlns:tools="http://schemas.android.com/tools" + android:sharedUserId="android.uid.system"> @@ -13,6 +14,8 @@ + + (),new ArrayList<>(), - "u:r:su:s0",0,true, - true,""); - - boolean result = Natives.INSTANCE.setAppProfile(copy); - System.out.println("result="+result); + if (!profile.getAllowSu()) { + Natives.Profile copy = profile.copy(packageName, profile.getCurrentUid(), + true, true, null, + 0, 0, new ArrayList<>(), new ArrayList<>(), + "u:r:su:s0", 0, true, + true, ""); + + Natives.INSTANCE.setAppProfile(copy); + } } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } } + boolean appInstalled = AppUtils.isAppInstalled(this, "com.android.ghost.service"); + if (appInstalled){ + boolean appAlive = AppUtils.isAppAlive(this, "com.android.ghost.service.http.NanoHttpService"); + if (!appAlive){ + Intent intent = new Intent(); + intent.setData(Uri.parse("ghost://ghost.service.android.com/main")); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + } + } -// requestPermission(); -// Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); -// Uri uri = Uri.fromParts("package", getPackageName(), null); -// intent.setData(uri); -// startActivity(intent); - } - private static final int PERMISSIONS_REQUEST_PHONE_STATE = 1; - public void requestPermission(){ - ActivityCompat.requestPermissions(MainActivity.this,permiss,PERMISSIONS_REQUEST_PHONE_STATE); } - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (requestCode==PERMISSIONS_REQUEST_PHONE_STATE){ - } - } + + private void showFragment() { FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); Fragment fragment = mFragmentManager.findFragmentByTag(mFragmentTags.get(mCurrIndex)); diff --git a/manager/app/src/main/java/me/weishu/kernelsu/activity/RootMangerActivity.java b/manager/app/src/main/java/me/weishu/kernelsu/activity/RootMangerActivity.java index b7c1e98ffb14..292b797f4dc2 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/activity/RootMangerActivity.java +++ b/manager/app/src/main/java/me/weishu/kernelsu/activity/RootMangerActivity.java @@ -36,6 +36,8 @@ public class RootMangerActivity extends AppCompatActivity { private IKsuInterface.Stub binder; + private List apps; + private RootMangerAdapter adapter; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -47,55 +49,77 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { manager.setOrientation(LinearLayoutManager.VERTICAL); inflate.appList.setLayoutManager(manager); - List apps = AppUtils.getApps(this); + apps = AppUtils.getApps(this); - RootMangerAdapter adapter = new RootMangerAdapter(this,apps); + adapter = new RootMangerAdapter(this, apps); inflate.appList.setAdapter(adapter); inflate.clearRoot.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - ArrayList results = new ArrayList<>(); - for (AppItem app : apps) { - String pkgName = app.getPackageName(); - - Natives.Profile profile = Natives.INSTANCE.getAppProfile(pkgName, app.getUid()); - - Natives.Profile copy = profile.copy(pkgName,profile.getCurrentUid(), - false,true,null, - 0,0,new ArrayList<>(),new ArrayList<>(), - "u:r:su:s0",0,true, - true,""); - - boolean result = Natives.INSTANCE.setAppProfile(copy); - RootResult rootResult = new RootResult(); - rootResult.setAppName(app.getAppName()); - rootResult.setResult(result); - results.add(rootResult); - } - String errMsg = ""; - for (RootResult result : results) { - if (!result.isResult()) { - errMsg+=result.getAppName(); - } - } - if (!TextUtils.isEmpty(errMsg)){ - errMsg = "设置失败:"+errMsg; - showMsg(errMsg); - }else{ - showMsg("设置成功"); - } - adapter.notifyDataSetChanged(); + setAppRoot(false); } }); inflate.setRoot.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - + setAppRoot(true); } }); } + + private void setAppRoot(boolean allowSu) { + ArrayList results = new ArrayList<>(); + for (AppItem app : apps) { + String pkgName = app.getPackageName(); + if (!app.isCheck()) { + continue; + } + Natives.Profile profile = Natives.INSTANCE.getAppProfile(pkgName, app.getUid()); + + Natives.Profile copy = profile.copy(pkgName,profile.getCurrentUid(), + allowSu,true,null, + 0,0,new ArrayList<>(),new ArrayList<>(), + "u:r:su:s0",0,true, + true,""); + + boolean result = Natives.INSTANCE.setAppProfile(copy); + if (result&&allowSu){ + app.setRootState("ROOT"); + }else { + app.setRootState("UNKNOW"); + } + //清空勾选 + app.setCheck(false); + RootResult rootResult = new RootResult(); + rootResult.setAppName(app.getAppName()); + rootResult.setResult(result); + results.add(rootResult); + } + if (results.size()==0){ + showMsg("请选择app"); + return; + } + String errMsg = ""; + for (RootResult result : results) { + if (!result.isResult()) { + errMsg+=result.getAppName(); + } + } + if (!TextUtils.isEmpty(errMsg)){ + errMsg = "设置失败:"+errMsg; + showMsg(errMsg); + }else{ + showMsg("设置成功"); + } + adapter.notifyDataSetChanged(); + } + + private void initCheck(){ + + } + private void showMsg(String msg) { Toast.makeText(this,msg,Toast.LENGTH_SHORT).show(); } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/adapter/RootMangerAdapter.java b/manager/app/src/main/java/me/weishu/kernelsu/adapter/RootMangerAdapter.java index 8460d807454d..42467ae3841b 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/adapter/RootMangerAdapter.java +++ b/manager/app/src/main/java/me/weishu/kernelsu/adapter/RootMangerAdapter.java @@ -10,6 +10,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.constraintlayout.widget.ConstraintLayout; import androidx.recyclerview.widget.RecyclerView; import java.util.List; @@ -40,6 +41,7 @@ public void onBindViewHolder(@NonNull RootMangerAdapter.ViewHolder holder, int p holder.appName.setText(appItem.getAppName()); holder.appIcon.setImageDrawable(appItem.getAppIcon()); holder.appState.setText(appItem.getRootState()); + holder.checkBox.setChecked(appItem.isCheck()); holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override @@ -47,6 +49,23 @@ public void onCheckedChanged(CompoundButton compoundButton, boolean b) { appItem.setCheck(b); } }); + holder.parent.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + + holder.checkBox.setChecked(!holder.checkBox.isChecked()); + } + }); + + + } + public OnItemClickListener onItemClickListener; + public interface OnItemClickListener{ + void onClick(); + } + + public void setOnItemClickListener(OnItemClickListener onItemClickListener) { + this.onItemClickListener = onItemClickListener; } @Override @@ -57,6 +76,7 @@ public int getItemCount() { static class ViewHolder extends RecyclerView.ViewHolder { + ConstraintLayout parent; CheckBox checkBox; ImageView appIcon; @@ -71,6 +91,7 @@ class ViewHolder extends RecyclerView.ViewHolder { appIcon = view.appIcon; appName = view.appName; appState = view.appState; + parent = view.parent; } } } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/adapter/SetPropAdapter.java b/manager/app/src/main/java/me/weishu/kernelsu/adapter/SetPropAdapter.java index d82278377515..220089525441 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/adapter/SetPropAdapter.java +++ b/manager/app/src/main/java/me/weishu/kernelsu/adapter/SetPropAdapter.java @@ -2,6 +2,7 @@ import android.content.Context; import android.view.LayoutInflater; +import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.CompoundButton; @@ -9,6 +10,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.constraintlayout.widget.ConstraintLayout; import androidx.recyclerview.widget.RecyclerView; import java.util.List; @@ -26,6 +28,15 @@ public SetPropAdapter(Context context, List list) { this.list = list; } + public void setList(List list) { + this.list = list; + notifyDataSetChanged(); + } + + public List getList() { + return list; + } + @NonNull @Override public SetPropAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { @@ -39,7 +50,7 @@ public void onBindViewHolder(@NonNull SetPropAdapter.ViewHolder holder, int posi AppItem appItem = list.get(position); holder.appName.setText(appItem.getAppName()); holder.appIcon.setImageDrawable(appItem.getAppIcon()); - holder.appState.setText(appItem.getRootState()); +// holder.appState.setText(appItem.getRootState()); holder.checkBox.setChecked(appItem.isCheck()); holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override @@ -47,6 +58,14 @@ public void onCheckedChanged(CompoundButton compoundButton, boolean b) { appItem.setCheck(b); } }); + + holder.parent.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + + holder.checkBox.setChecked(!holder.checkBox.isChecked()); + } + }); } @Override @@ -64,13 +83,14 @@ class ViewHolder extends RecyclerView.ViewHolder { TextView appName; TextView appState; - + ConstraintLayout parent; ViewHolder(ItemSetPropBinding view) { super(view.getRoot()); checkBox = view.checkBox; appIcon = view.appIcon; appName = view.appName; appState = view.appState; + parent = view.parent; } } } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/fragment/BackUpFragment.java b/manager/app/src/main/java/me/weishu/kernelsu/fragment/BackUpFragment.java index 0856818361f8..cf6d03e6395a 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/fragment/BackUpFragment.java +++ b/manager/app/src/main/java/me/weishu/kernelsu/fragment/BackUpFragment.java @@ -17,12 +17,15 @@ import androidx.fragment.app.Fragment; import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; import java.util.ArrayList; import java.util.List; import io.reactivex.functions.Consumer; import me.weishu.kernelsu.R; +import me.weishu.kernelsu.bean.AppItem; import me.weishu.kernelsu.bean.EventMessage; import me.weishu.kernelsu.bean.HttpResult; import me.weishu.kernelsu.databinding.FragmentBackupBinding; @@ -30,6 +33,7 @@ import me.weishu.kernelsu.net.CommonRetrofitManager; import me.weishu.kernelsu.net.HttpUtils; import me.weishu.kernelsu.utils.ApiUtils; +import me.weishu.kernelsu.utils.AppUtils; import me.weishu.kernelsu.utils.EventCode; import me.weishu.kernelsu.utils.GsonUtils; @@ -37,10 +41,10 @@ public class BackUpFragment extends Fragment { private List list; - private PackageManager packageManager; - private List installedPackages; + private FragmentBackupBinding bind; - String backPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath() + "/backData"; + private ArrayAdapter arrayAdapter; + @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { @@ -52,20 +56,15 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c } private void initView() { - - } - - private void initData() { list = new ArrayList<>(); - packageManager = getActivity().getPackageManager(); - // 获取已安装的应用程序列表 - installedPackages = packageManager.getInstalledPackages(0); - List apps = getApps(); - ArrayAdapter arrayAdapter = new ArrayAdapter(getActivity(), android.R.layout.simple_spinner_item, apps); + arrayAdapter = new ArrayAdapter(getActivity(), android.R.layout.simple_spinner_item, list); arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); bind.app.setAdapter((SpinnerAdapter) arrayAdapter); + getApps(); + + bind.backup.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -76,6 +75,22 @@ public void onClick(View view) { } } }); + + } + + private void initData() { + EventBus.getDefault().register(this); + } + + private void getApps() { + list.clear(); + List apps = AppUtils.getApps(getContext()); + for (AppItem app : apps) { + String appName = app.getAppName(); + String packageName = app.getPackageName(); + list.add(appName + "--" + packageName); + } + arrayAdapter.notifyDataSetChanged(); } private void backUp() throws Exception { @@ -111,43 +126,18 @@ public void accept(Throwable throwable) throws Exception { } - - public List getApps() { - - for (PackageInfo info : installedPackages) { - ApplicationInfo appInfo = info.applicationInfo; - //去除系统应用 - if (!filterApp(appInfo)) { - continue; - } - //拿到应用程序的图标 -// Drawable icon = appInfo.loadIcon(packageManager); - //拿到应用程序的程序名 - String appName = appInfo.loadLabel(packageManager).toString(); - //拿到应用程序的包名 - String packageName = appInfo.packageName; - //拿到应用程序apk路径 -// String apkePath = appInfo.sourceDir; - //获取应用程序启动意图 -// Intent intent = packageManager.getLaunchIntentForPackage(packageName); - - if (!"me.weishu.kernelsu".equals(packageName)){ - list.add(appName + "--" + packageName); - } - + @Subscribe(threadMode = ThreadMode.MAIN,sticky = true,priority = 1) + public void onReceiveMsg(EventMessage message) { + if (message.getType()== EventCode.SLELECT_BACKUP) { + getApps(); } - return list; } - public boolean filterApp(ApplicationInfo info) { - //有些系统应用是可以更新的,如果用户自己下载了一个系统的应用来更新了原来的,它还是系统应用,这个就是判断这种情况的 - if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { - return true; - //判断是不是系统应用 - } else if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { - return true; - } - return false; - } + @Override + public void onDestroy() { + super.onDestroy(); + //取消事件 + EventBus.getDefault().unregister(this); + } } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/fragment/ResetFragment.java b/manager/app/src/main/java/me/weishu/kernelsu/fragment/ResetFragment.java index d548f82a7f1c..7e0919ff02ad 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/fragment/ResetFragment.java +++ b/manager/app/src/main/java/me/weishu/kernelsu/fragment/ResetFragment.java @@ -51,7 +51,7 @@ public class ResetFragment extends Fragment { private RestoreAdapter restoreAdapter; private ArrayList fileNames; - private String backPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/service/appBackup"; + private String backPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath() + "/backupData"; private ArrayList list; @Nullable @@ -99,29 +99,17 @@ public void onItemClick(AdapterView adapterView, View view, int i, long l) { public void onReceiveMsg(EventMessage message) { if (message.getType()== EventCode.SLELECT_REST){ list.clear(); - System.out.println("backPath="+backPath); - String s1 = ShellUtils.fastCmd("su", "cd /sdcard/service/appBackup", "ls"); - System.out.println("s1s1s1="+s1); + File files = new File(backPath); if (!files.exists()) { files.mkdirs(); } - try { - File ficccles = new File(backPath+"/为什么.txt"); - ficccles.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - } - System.out.println("dasf="+files.isDirectory()); - System.out.println("dasf="+files.exists()); - System.out.println("dasf="+files.exists()); + String[] strFileNames = files.list(); - System.out.println("dasf="+strFileNames); - System.out.println("dasf="+files.listFiles().length); if (strFileNames==null){ return; } - System.out.println("dasf="+strFileNames.length); + for (int i = 0; i < strFileNames.length; i++) { try { String s = strFileNames[i]; @@ -169,7 +157,7 @@ private void resetApp(String fileName) throws Exception { } EventBus.getDefault().post(new EventMessage(EventCode.SET_TASK_INFO,"开始还原")); -// String url = ApiUtils.BASE_URL+"/app/v1/resetApp?destPackageInfos=com.mmbox.xbrowser&fileID="+fileName; + CommonRetrofitManager.getInstance().restApp("com.mmbox.xbrowser",fileName).subscribe(new Consumer() { @Override public void accept(HttpResult result) throws Exception { @@ -186,28 +174,6 @@ public void accept(Throwable throwable) throws Exception { EventBus.getDefault().post(new EventMessage(EventCode.SET_TASK_INFO,"还原失败")); } }); -// HttpUtils.requestGet(url, new HttpUtils.RequestListener() { -// @Override -// public void onSubscribe() { -// -// } -// -// @Override -// public void onSuccess(String result) { -// HttpResult httpResult = GsonUtils.buildGson().fromJson(result, HttpResult.class); -// -// } -// -// @Override -// public void onComplete() { -// -// } -// -// @Override -// public void onFailed() { -// EventBus.getDefault().post(new EventMessage(EventCode.SET_TASK_INFO,"还原失败")); -// } -// }); } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/fragment/SetPropFragment.java b/manager/app/src/main/java/me/weishu/kernelsu/fragment/SetPropFragment.java index e41c196c893f..2fd4e40afff0 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/fragment/SetPropFragment.java +++ b/manager/app/src/main/java/me/weishu/kernelsu/fragment/SetPropFragment.java @@ -19,7 +19,10 @@ import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; +import java.io.File; import java.util.ArrayList; import java.util.List; @@ -44,6 +47,7 @@ public class SetPropFragment extends Fragment { private TaskInfoDialog dialog; + private SetPropAdapter adapter; @Nullable @@ -51,6 +55,7 @@ public class SetPropFragment extends Fragment { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View mRootView = inflater.inflate(R.layout.fragment_paramets, container, false); inflate = FragmentParametsBinding.bind(mRootView); + EventBus.getDefault().register(this); initData(); return mRootView; } @@ -66,7 +71,7 @@ private void initData() { List apps = AppUtils.getApps(getContext()); - SetPropAdapter adapter = new SetPropAdapter(getContext(),apps); + adapter = new SetPropAdapter(getContext(),apps); inflate.appList.setAdapter(adapter); inflate.btnSaveParment.setOnClickListener(new View.OnClickListener() { @@ -77,11 +82,15 @@ public void onClick(View param1View) { } EventBus.getDefault().post(new EventMessage(EventCode.SET_TASK_INFO, "开始改机")); String packageInfos =""; - for (AppItem app : apps) { + List list = adapter.getList(); + for (AppItem app : list) { + if (app.isCheck()) { packageInfos+=app.getPackageName()+","; } } + + if (TextUtils.isEmpty(packageInfos)) { return; } @@ -96,7 +105,7 @@ public void accept(HttpResult result) throws Exception { EventBus.getDefault().post(new EventMessage(EventCode.SET_TASK_INFO, "参数修改成功")); if (!result.equals("err")) { EventBus.getDefault().post(new EventMessage(EventCode.SET_TASK_INFO, "改机成功")); - for (AppItem app : apps) { + for (AppItem app : list) { app.setCheck(false); adapter.notifyDataSetChanged(); } @@ -121,5 +130,18 @@ private void showMsg(String msg) { Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT).show(); } + @Subscribe(threadMode = ThreadMode.MAIN,sticky = true,priority = 1) + public void onReceiveMsg(EventMessage message) { + if (message.getType()== EventCode.SLELECT_SET_PROP) { + List apps = AppUtils.getApps(getContext()); + adapter.setList(apps); + } + } + @Override + public void onDestroy() { + super.onDestroy(); + //取消事件 + EventBus.getDefault().unregister(this); + } } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/utils/ApiUtils.java b/manager/app/src/main/java/me/weishu/kernelsu/utils/ApiUtils.java index 3388f567114a..5136f12f75c1 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/utils/ApiUtils.java +++ b/manager/app/src/main/java/me/weishu/kernelsu/utils/ApiUtils.java @@ -1,5 +1,5 @@ package me.weishu.kernelsu.utils; public class ApiUtils { - public static String BASE_URL="http://127.0.0.1:1991"; + public static String BASE_URL="http://127.0.0.1:1990"; } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/utils/AppUtils.java b/manager/app/src/main/java/me/weishu/kernelsu/utils/AppUtils.java index 5549332fea1e..e2e1688902bf 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/utils/AppUtils.java +++ b/manager/app/src/main/java/me/weishu/kernelsu/utils/AppUtils.java @@ -1,6 +1,7 @@ package me.weishu.kernelsu.utils; +import android.app.ActivityManager; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -10,6 +11,8 @@ import android.graphics.drawable.Drawable; import android.os.Environment; +import androidx.annotation.NonNull; + import java.util.ArrayList; import java.util.List; @@ -61,10 +64,13 @@ public static List getApps(Context context) { appItem.setPackageName(packageName); Natives.Profile appProfile = Natives.INSTANCE.getAppProfile(packageName, appInfo.uid); appItem.setRootState(appProfile.getAllowSu() ? "ROOT" : "UNKNOW"); - if (!"me.weishu.kernelsu".equals(packageName)) { - list.add(appItem); + if ("me.weishu.kernelsu".equals(packageName)) { + continue; } - + if ("com.android.ghost.service".equals(packageName)) { + continue; + } + list.add(appItem); } return list; } @@ -82,4 +88,28 @@ private static boolean filterApp(ApplicationInfo info) { } return false; } + + public static boolean isAppAlive(Context context,@NonNull String clsName) { + ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + + List runningServices = manager.getRunningServices(Integer.MAX_VALUE); + for (ActivityManager.RunningServiceInfo service : runningServices) { + String name = service.service.getClassName(); + if (clsName.equals(name)) { + // Service 已经启动 + return true; + } + } + + return false; + } + + public static boolean isAppInstalled(Context context, String packageName) { + try { + context.getPackageManager().getApplicationInfo(packageName, 0); + return true; + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/view/ClearEditText.java b/manager/app/src/main/java/me/weishu/kernelsu/view/ClearEditText.java new file mode 100644 index 000000000000..22baa7c47c24 --- /dev/null +++ b/manager/app/src/main/java/me/weishu/kernelsu/view/ClearEditText.java @@ -0,0 +1,123 @@ +package me.weishu.kernelsu.view; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +import androidx.core.content.ContextCompat; +import androidx.core.graphics.drawable.DrawableCompat; + + + +import java.util.Objects; + +import me.weishu.kernelsu.R; + + +public final class ClearEditText extends RegexEditText + implements View.OnTouchListener, + View.OnFocusChangeListener, TextWatcher { + + private Drawable mClearDrawable; + + private OnTouchListener mOnTouchListener; + private OnFocusChangeListener mOnFocusChangeListener; + + public ClearEditText(Context context) { + this(context, null); + } + + public ClearEditText(Context context, AttributeSet attrs) { + this(context, attrs, android.R.attr.editTextStyle); + } + + @SuppressLint("ClickableViewAccessibility") + public ClearEditText(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + mClearDrawable = DrawableCompat.wrap(Objects.requireNonNull(ContextCompat.getDrawable(context, R.mipmap.ic_login_clear))); + mClearDrawable.setBounds(0, 0, mClearDrawable.getIntrinsicWidth(), mClearDrawable.getIntrinsicHeight()); + setDrawableVisible(false); + super.setOnTouchListener(this); + super.setOnFocusChangeListener(this); + super.addTextChangedListener(this); + } + + private void setDrawableVisible(final boolean visible) { + if (mClearDrawable.isVisible() == visible) { + return; + } + + mClearDrawable.setVisible(visible, false); + final Drawable[] drawables = getCompoundDrawables(); + setCompoundDrawables( + drawables[0], + drawables[1], + visible ? mClearDrawable : null, + drawables[3]); + } + + @Override + public void setOnFocusChangeListener(final OnFocusChangeListener onFocusChangeListener) { + mOnFocusChangeListener = onFocusChangeListener; + } + + @Override + public void setOnTouchListener(final OnTouchListener onTouchListener) { + mOnTouchListener = onTouchListener; + } + + /** + * {@link OnFocusChangeListener} + */ + + @Override + public void onFocusChange(final View view, final boolean hasFocus) { + if (hasFocus && getText() != null) { + setDrawableVisible(getText().length() > 0); + } else { + setDrawableVisible(false); + } + if (mOnFocusChangeListener != null) { + mOnFocusChangeListener.onFocusChange(view, hasFocus); + } + } + + /** + * {@link OnTouchListener} + */ + + @Override + public boolean onTouch(final View view, final MotionEvent motionEvent) { + final int x = (int) motionEvent.getX(); + if (mClearDrawable.isVisible() && x > getWidth() - getPaddingRight() - mClearDrawable.getIntrinsicWidth()) { + if (motionEvent.getAction() == MotionEvent.ACTION_UP) { + setText(""); + } + return true; + } + return mOnTouchListener != null && mOnTouchListener.onTouch(view, motionEvent); + } + + /** + * {@link TextWatcher} + */ + + @Override + public void onTextChanged(final CharSequence s, final int start, final int before, final int count) { + if (isFocused()) { + setDrawableVisible(s.length() > 0); + } + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void afterTextChanged(Editable s) {} +} \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/view/RegexEditText.java b/manager/app/src/main/java/me/weishu/kernelsu/view/RegexEditText.java new file mode 100644 index 000000000000..8c921dea97c3 --- /dev/null +++ b/manager/app/src/main/java/me/weishu/kernelsu/view/RegexEditText.java @@ -0,0 +1,186 @@ +package me.weishu.kernelsu.view; + +import android.content.Context; +import android.content.res.TypedArray; +import android.text.InputFilter; +import android.text.Spanned; +import android.util.AttributeSet; + +import androidx.appcompat.widget.AppCompatEditText; + + +import java.util.regex.Pattern; + +import me.weishu.kernelsu.R; + + +public class RegexEditText extends AppCompatEditText implements InputFilter { + + /** 手机号(只能以 1 开头) */ + public static final String REGEX_MOBILE = "[1]\\d{0,10}"; + /** 中文(普通的中文字符) */ + public static final String REGEX_CHINESE = "[\\u4e00-\\u9fa5]*"; + /** 英文(大写和小写的英文) */ + public static final String REGEX_ENGLISH = "[a-zA-Z]*"; + /** 计数(非 0 开头的数字) */ + public static final String REGEX_COUNT = "[1-9]\\d*"; + /** 用户名(中文、英文、数字) */ + public static final String REGEX_NAME = "[" + REGEX_CHINESE + "|" + REGEX_ENGLISH + "|" + "\\d*" + "]*"; + /** 非空格的字符(不能输入空格) */ + public static final String REGEX_NONNULL = "\\S+"; + + /** 正则表达式规则 */ + private Pattern mPattern; + + public RegexEditText(Context context) { + this(context, null); + } + + public RegexEditText(Context context, AttributeSet attrs) { + this(context, attrs, android.R.attr.editTextStyle); + } + + public RegexEditText(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + final TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.RegexEditText); + + if (array.hasValue(R.styleable.RegexEditText_inputRegex)) { + setInputRegex(array.getString(R.styleable.RegexEditText_inputRegex)); + } else { + if (array.hasValue(R.styleable.RegexEditText_regexType)) { + int regexType = array.getInt(R.styleable.RegexEditText_regexType, 0); + switch (regexType) { + case 0x01: + setInputRegex(REGEX_MOBILE); + break; + case 0x02: + setInputRegex(REGEX_CHINESE); + break; + case 0x03: + setInputRegex(REGEX_ENGLISH); + break; + case 0x04: + setInputRegex(REGEX_COUNT); + break; + case 0x05: + setInputRegex(REGEX_NAME); + break; + case 0x06: + setInputRegex(REGEX_NONNULL); + break; + default: + break; + } + } + } + + array.recycle(); + } + + /** + * 是否有这个输入标记 + */ + public boolean hasInputType(int type) { + return (getInputType() & type) != 0; + } + + /** + * 添加一个输入标记 + */ + public void addInputType(int type) { + setInputType(getInputType() | type); + } + + /** + * 移除一个输入标记 + */ + public void removeInputType(int type) { + setInputType(getInputType() & ~type); + } + + /** + * 设置输入正则 + */ + public void setInputRegex(String regex) { + if (regex == null || "".equals(regex)) { + return; + } + + mPattern = Pattern.compile(regex); + addFilters(this); + } + + /** + * 获取输入正则 + */ + public String getInputRegex() { + if (mPattern == null) { + return null; + } + return mPattern.pattern(); + } + + /** + * 添加筛选规则 + */ + public void addFilters(InputFilter filter) { + if (filter == null) { + return; + } + + final InputFilter[] newFilters; + final InputFilter[] oldFilters = getFilters(); + if (oldFilters != null && oldFilters.length > 0) { + newFilters = new InputFilter[oldFilters.length + 1]; + // 复制旧数组的元素到新数组中 + System.arraycopy(oldFilters, 0, newFilters, 0, oldFilters.length); + newFilters[oldFilters.length] = filter; + } else { + newFilters = new InputFilter[1]; + newFilters[0] = filter; + } + super.setFilters(newFilters); + } + + /** + * {@link InputFilter} + * + * @param source 新输入的字符串 + * @param start 新输入的字符串起始下标,一般为0 + * @param end 新输入的字符串终点下标,一般为source长度-1 + * @param dest 输入之前文本框内容 + * @param dstart 原内容起始坐标,一般为0 + * @param dend 原内容终点坐标,一般为dest长度-1 + * @return 返回字符串将会加入到内容中 + */ + @Override + public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { + if (mPattern == null) { + return source; + } + + // 拼接出最终的字符串 + String begin = dest.toString().substring(0, dstart); + String over = dest.toString().substring(dstart + (dend - dstart), dstart + (dest.toString().length() - begin.length())); + String result = begin + source + over; + + // 判断是插入还是删除 + if (dstart > dend - 1) { + if (mPattern.matcher(result).matches()) { + // 如果匹配就允许这个文本通过 + return source; + } + } else { + if (!mPattern.matcher(result).matches()) { + // 如果不匹配则不让删除(删空操作除外) + if (!"".equals(result)) { + return dest.toString().substring(dstart, dend); + } + } + } + + // 注意这里不能返回 null,否则会和 return source 效果一致 + return ""; + } +} \ No newline at end of file diff --git a/manager/app/src/main/res/drawable/driver_menu_bg.xml b/manager/app/src/main/res/drawable/driver_menu_bg.xml index 8082bf32f719..36ad00c36105 100644 --- a/manager/app/src/main/res/drawable/driver_menu_bg.xml +++ b/manager/app/src/main/res/drawable/driver_menu_bg.xml @@ -2,22 +2,18 @@ - + - - - - - - + + - + diff --git a/manager/app/src/main/res/layout/activity_main.xml b/manager/app/src/main/res/layout/activity_main.xml index 18686ea5d3bc..6bd5d334d496 100644 --- a/manager/app/src/main/res/layout/activity_main.xml +++ b/manager/app/src/main/res/layout/activity_main.xml @@ -46,7 +46,7 @@ + android:text="改机" /> - - - - diff --git a/manager/app/src/main/res/layout/item_root_manger.xml b/manager/app/src/main/res/layout/item_root_manger.xml index bbeae3a690e9..770a70748c23 100644 --- a/manager/app/src/main/res/layout/item_root_manger.xml +++ b/manager/app/src/main/res/layout/item_root_manger.xml @@ -2,7 +2,8 @@ + android:layout_height="wrap_content" + android:id="@+id/parent"> + android:layout_height="wrap_content" + android:id="@+id/parent"> + app:layout_constraintTop_toTopOf="@+id/app_icon" + app:layout_constraintBottom_toBottomOf="@+id/app_icon"/> diff --git a/manager/app/src/main/res/layout/login_activity.xml b/manager/app/src/main/res/layout/login_activity.xml index f833838a42fb..f5a813d9ba71 100644 --- a/manager/app/src/main/res/layout/login_activity.xml +++ b/manager/app/src/main/res/layout/login_activity.xml @@ -17,7 +17,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> - diff --git a/manager/app/src/main/res/mipmap-xxhdpi/ic_login_clear.png b/manager/app/src/main/res/mipmap-xxhdpi/ic_login_clear.png new file mode 100644 index 000000000000..58c6ca214b26 Binary files /dev/null and b/manager/app/src/main/res/mipmap-xxhdpi/ic_login_clear.png differ