xiongzhu пре 5 година
родитељ
комит
6ff767685d

+ 12 - 0
app/src/main/AndroidManifest.xml

@@ -8,6 +8,8 @@
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
     <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
+    <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
+
     <uses-permission
         android:name="android.permission.PACKAGE_USAGE_STATS"
         tools:ignore="ProtectedPermissions" />
@@ -19,6 +21,16 @@
         android:roundIcon="@mipmap/ic_launcher_round"
         android:supportsRtl="true"
         android:theme="@style/Theme.Booster">
+        <activity
+            android:name=".activity.AboutActivity"
+            android:exported="true"
+            android:screenOrientation="portrait"
+            android:theme="@style/Theme.Booster.Settings" />
+        <activity
+            android:name=".activity.UninstallActivity"
+            android:exported="true"
+            android:screenOrientation="portrait"
+            android:theme="@style/Theme.Booster.Settings" />
         <activity
             android:name=".activity.PermissionListActivity"
             android:exported="true"

+ 23 - 0
app/src/main/java/com/izouma/booster/activity/AboutActivity.java

@@ -0,0 +1,23 @@
+package com.izouma.booster.activity;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.os.Bundle;
+
+import com.izouma.booster.databinding.ActivityAboutBinding;
+
+public class AboutActivity extends AppCompatActivity {
+    private ActivityAboutBinding binding;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        binding = ActivityAboutBinding.inflate(getLayoutInflater());
+        setContentView(binding.getRoot());
+
+        binding.toolbar.setNavigationOnClickListener(v -> {
+            finish();
+        });
+
+    }
+}

+ 90 - 0
app/src/main/java/com/izouma/booster/activity/UninstallActivity.java

@@ -0,0 +1,90 @@
+package com.izouma.booster.activity;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.loader.app.LoaderManager;
+import androidx.loader.content.Loader;
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.View;
+
+import com.izouma.booster.R;
+import com.izouma.booster.Utils;
+import com.izouma.booster.adapter.UninstallAdapter;
+import com.izouma.booster.data.AppInfo;
+import com.izouma.booster.data.AppListLoader;
+import com.izouma.booster.databinding.ActivityUninstallBinding;
+import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class UninstallActivity extends AppCompatActivity {
+    private ActivityUninstallBinding binding;
+    private LoaderManager.LoaderCallbacks<List<AppInfo>> appListCallbacks;
+    private AppListLoader appListLoader;
+
+    private final int APP_LIST_LOADER_ID = 118;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        binding = ActivityUninstallBinding.inflate(getLayoutInflater());
+        setContentView(binding.getRoot());
+
+        binding.toolbar.setNavigationOnClickListener(v -> {
+            finish();
+        });
+
+        List<AppInfo> appInfoList = new ArrayList<>();
+        UninstallAdapter adapter = new UninstallAdapter(appInfoList);
+        adapter.setOnCheckedChangeListener(checked -> {
+            binding.btnUninstall.setEnabled(!checked.isEmpty());
+        });
+        binding.recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
+        binding.recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(this)
+                .colorResId(R.color.list_divider)
+                .build());
+        binding.recyclerView.setAdapter(adapter);
+
+        binding.btnUninstall.setOnClickListener(v -> {
+            for (String packageName : adapter.getChecked()) {
+                Intent intent = new Intent(Intent.ACTION_DELETE);
+                intent.setData(Uri.parse("package:" + packageName));
+                startActivity(intent);
+            }
+        });
+
+        appListCallbacks = new LoaderManager.LoaderCallbacks<List<AppInfo>>() {
+            @NonNull
+            @Override
+            public Loader<List<AppInfo>> onCreateLoader(int id, @Nullable Bundle args) {
+                if (appListLoader == null) {
+                    appListLoader = new AppListLoader(UninstallActivity.this);
+                }
+                return appListLoader;
+            }
+
+            @Override
+            public void onLoadFinished(@NonNull Loader<List<AppInfo>> loader, List<AppInfo> data) {
+                binding.progressBar.setVisibility(View.GONE);
+                binding.recyclerView.setVisibility(View.VISIBLE);
+                appInfoList.clear();
+                appInfoList.addAll(data);
+                adapter.notifyDataSetChanged();
+            }
+
+            @Override
+            public void onLoaderReset(@NonNull Loader<List<AppInfo>> loader) {
+                binding.progressBar.setVisibility(View.VISIBLE);
+                binding.recyclerView.setVisibility(View.GONE);
+            }
+        };
+        LoaderManager.getInstance(this).initLoader(APP_LIST_LOADER_ID, null, appListCallbacks).forceLoad();
+    }
+}

+ 101 - 0
app/src/main/java/com/izouma/booster/adapter/UninstallAdapter.java

@@ -0,0 +1,101 @@
+package com.izouma.booster.adapter;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.loader.content.AsyncTaskLoader;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.izouma.booster.data.AppInfo;
+import com.izouma.booster.databinding.ItemAppUninstallBinding;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class UninstallAdapter extends RecyclerView.Adapter<UninstallAdapter.UninstallHolder> {
+    private List<AppInfo> appInfoList;
+    private OnCheckedChangeListener onCheckedChangeListener;
+
+    public UninstallAdapter(List<AppInfo> appInfoList) {
+        this.appInfoList = appInfoList;
+    }
+
+    public void setOnCheckedChangeListener(OnCheckedChangeListener onCheckedChangeListener) {
+        this.onCheckedChangeListener = onCheckedChangeListener;
+    }
+
+    public List<String> getChecked() {
+        List<String> checked = new ArrayList<>();
+        for (AppInfo info : appInfoList) {
+            if (info.isChecked()) {
+                checked.add(info.getPackageName());
+            }
+        }
+        return checked;
+    }
+
+    @NonNull
+    @Override
+    public UninstallHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        return new UninstallHolder(ItemAppUninstallBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull UninstallHolder holder, int position) {
+        AppInfo appInfo = appInfoList.get(position);
+        holder.binding.tvName.setText(appInfo.getLabel());
+        holder.binding.tvDate.setText(appInfo.getInstallDate());
+        holder.binding.checkbox.setChecked(appInfo.isChecked());
+        try {
+            if (appInfo.getIcon() == null) {
+                appInfo.setIcon(holder.binding.getRoot().getContext().getPackageManager().getApplicationIcon(appInfo.getPackageName()));
+            }
+            holder.binding.icon.setImageDrawable(appInfo.getIcon());
+        } catch (PackageManager.NameNotFoundException e) {
+            e.printStackTrace();
+        }
+        holder.binding.checkbox.setOnClickListener(v -> {
+            appInfo.setChecked(holder.binding.checkbox.isChecked());
+            if (onCheckedChangeListener != null) {
+                onCheckedChangeListener.onCheckedChange(getChecked());
+            }
+        });
+        holder.itemView.setOnClickListener(v -> {
+            holder.binding.checkbox.setChecked(!holder.binding.checkbox.isChecked());
+            appInfo.setChecked(holder.binding.checkbox.isChecked());
+            if (onCheckedChangeListener != null) {
+                onCheckedChangeListener.onCheckedChange(getChecked());
+            }
+        });
+    }
+
+    @Override
+    public int getItemCount() {
+        return appInfoList.size();
+    }
+
+    public static class UninstallHolder extends RecyclerView.ViewHolder {
+        private ItemAppUninstallBinding binding;
+
+        public UninstallHolder(ItemAppUninstallBinding binding) {
+            super(binding.getRoot());
+            this.binding = binding;
+        }
+
+        public ItemAppUninstallBinding getBinding() {
+            return binding;
+        }
+    }
+
+    public interface OnCheckedChangeListener {
+        void onCheckedChange(List<String> checked);
+    }
+}

+ 116 - 0
app/src/main/java/com/izouma/booster/data/AppInfo.java

@@ -0,0 +1,116 @@
+package com.izouma.booster.data;
+
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class AppInfo implements Parcelable {
+    private String packageName;
+    private String label;
+    private String installDate;
+    private boolean checked;
+    private String[] permissions;
+    private String[] sensitivePermissions;
+    private Drawable icon;
+
+    public AppInfo() {
+    }
+
+    public AppInfo(String packageName, String label, String installDate) {
+        this.packageName = packageName;
+        this.label = label;
+        this.installDate = installDate;
+    }
+
+    protected AppInfo(Parcel in) {
+        packageName = in.readString();
+        label = in.readString();
+        installDate = in.readString();
+        checked = in.readByte() != 0;
+        permissions = in.createStringArray();
+        sensitivePermissions = in.createStringArray();
+    }
+
+    public static final Creator<AppInfo> CREATOR = new Creator<AppInfo>() {
+        @Override
+        public AppInfo createFromParcel(Parcel in) {
+            return new AppInfo(in);
+        }
+
+        @Override
+        public AppInfo[] newArray(int size) {
+            return new AppInfo[size];
+        }
+    };
+
+    public String getPackageName() {
+        return packageName;
+    }
+
+    public void setPackageName(String packageName) {
+        this.packageName = packageName;
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    public Drawable getIcon() {
+        return icon;
+    }
+
+    public void setIcon(Drawable icon) {
+        this.icon = icon;
+    }
+
+    public String getInstallDate() {
+        return installDate;
+    }
+
+    public void setInstallDate(String installDate) {
+        this.installDate = installDate;
+    }
+
+    public boolean isChecked() {
+        return checked;
+    }
+
+    public void setChecked(boolean checked) {
+        this.checked = checked;
+    }
+
+    public String[] getPermissions() {
+        return permissions;
+    }
+
+    public void setPermissions(String[] permissions) {
+        this.permissions = permissions;
+    }
+
+    public String[] getSensitivePermissions() {
+        return sensitivePermissions;
+    }
+
+    public void setSensitivePermissions(String[] sensitivePermissions) {
+        this.sensitivePermissions = sensitivePermissions;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(packageName);
+        dest.writeString(label);
+        dest.writeString(installDate);
+        dest.writeByte((byte) (checked ? 1 : 0));
+        dest.writeStringArray(permissions);
+        dest.writeStringArray(sensitivePermissions);
+    }
+}

+ 70 - 0
app/src/main/java/com/izouma/booster/data/AppListLoader.java

@@ -0,0 +1,70 @@
+package com.izouma.booster.data;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.loader.content.AsyncTaskLoader;
+
+import com.izouma.booster.Utils;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+public class AppListLoader extends AsyncTaskLoader<List<AppInfo>> {
+    public AppListLoader(@NonNull Context context) {
+        super(context);
+    }
+
+    @Nullable
+    @Override
+    public List<AppInfo> loadInBackground() {
+        JSONObject sensitivePermissions = null;
+        try {
+            InputStream inputStream = getContext().getAssets().open("sensitive_permissions.json");
+            StringBuilder textBuilder = new StringBuilder();
+            try (Reader reader = new BufferedReader(new InputStreamReader
+                    (inputStream, Charset.forName(StandardCharsets.UTF_8.name())))) {
+                int c = 0;
+                while ((c = reader.read()) != -1) {
+                    textBuilder.append((char) c);
+                }
+            }
+            sensitivePermissions = new JSONObject(textBuilder.toString());
+        } catch (IOException | JSONException e) {
+            e.printStackTrace();
+        }
+
+        List<PackageInfo> packageInfoList = Utils.getInstalledAppsPkgInfo(getContext(), false);
+        List<AppInfo> appInfoList = new ArrayList<>();
+        for (PackageInfo packageInfo : packageInfoList) {
+            AppInfo appInfo = new AppInfo(packageInfo.packageName, packageInfo.applicationInfo.loadLabel(getContext().getPackageManager()).toString(), SimpleDateFormat.getDateInstance().format(new Date(packageInfo.firstInstallTime)));
+            if (packageInfo.requestedPermissions != null) {
+                appInfo.setPermissions(packageInfo.requestedPermissions);
+                List<String> appSensitivePermissions = new ArrayList<>();
+                for (String perm : packageInfo.requestedPermissions) {
+                    if (sensitivePermissions != null && sensitivePermissions.has(perm)) {
+                        appSensitivePermissions.add(perm);
+                    }
+                }
+                appInfo.setSensitivePermissions(appSensitivePermissions.toArray(new String[0]));
+
+            }
+            appInfoList.add(appInfo);
+        }
+        return appInfoList;
+    }
+}

+ 14 - 1
app/src/main/java/com/izouma/booster/fragment/MyFragment.java

@@ -1,6 +1,7 @@
 package com.izouma.booster.fragment;
 
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
 
 import androidx.annotation.NonNull;
@@ -14,11 +15,14 @@ import android.view.ViewGroup;
 import android.widget.Toast;
 
 import com.izouma.booster.R;
+import com.izouma.booster.activity.AboutActivity;
 import com.izouma.booster.activity.SettingsActivity;
 import com.izouma.booster.activity.VipActivity;
 import com.izouma.booster.databinding.MyFragmentBinding;
 import com.izouma.booster.viewModel.HomeViewModel;
 
+import java.util.Date;
+
 public class MyFragment extends Fragment {
 
     private MyFragmentBinding binding;
@@ -41,7 +45,15 @@ public class MyFragment extends Fragment {
         viewModel = new ViewModelProvider(requireActivity()).get(HomeViewModel.class);
 
         binding.tvDesc.setText(getResources().getString(R.string.under_the_protection_of_booster).replace("$appName", getResources().getString(R.string.app_name)));
-
+        try {
+            long installed = getContext()
+                    .getPackageManager()
+                    .getPackageInfo(getContext().getPackageName(), 0)
+                    .firstInstallTime;
+            binding.tvDays.setText(Math.max(1, (new Date().getTime() - installed) / 1000 / 60 / 60 / 24) + "");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
         binding.cellManual.setOnClickListener(e -> {
         });
         binding.cellFeedback.setOnClickListener(e -> {
@@ -58,6 +70,7 @@ public class MyFragment extends Fragment {
             Toast.makeText(getContext(), getResources().getString(R.string.the_current_version_is_the_latest_version), Toast.LENGTH_SHORT).show();
         });
         binding.cellAbout.setOnClickListener(e -> {
+            startActivity(new Intent(getContext(), AboutActivity.class));
         });
         binding.vip.setOnClickListener(v -> {
             startActivity(new Intent(getActivity(), VipActivity.class));

+ 5 - 0
app/src/main/java/com/izouma/booster/fragment/ToolFragment.java

@@ -13,6 +13,7 @@ import android.view.View;
 import android.view.ViewGroup;
 
 import com.izouma.booster.activity.PermissionScanActivity;
+import com.izouma.booster.activity.UninstallActivity;
 import com.izouma.booster.activity.VipActivity;
 import com.izouma.booster.databinding.ToolFragmentBinding;
 import com.izouma.booster.viewModel.HomeViewModel;
@@ -41,6 +42,10 @@ public class ToolFragment extends Fragment {
         binding.cellPermission.setOnClickListener(v -> {
             startActivity(new Intent(getContext(), PermissionScanActivity.class));
         });
+        binding.cellUninstall.setOnClickListener(v -> {
+            startActivity(new Intent(getContext(), UninstallActivity.class));
+        });
+
     }
 
     @Override

+ 5 - 0
app/src/main/res/color/btn_uninstall_bg.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@color/list_divider" android:state_enabled="false" />
+    <item android:color="@color/light_green" />
+</selector>

+ 40 - 0
app/src/main/res/layout/activity_about.xml

@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".activity.AboutActivity">
+
+    <androidx.appcompat.widget.Toolbar
+        android:id="@+id/toolbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@color/white"
+        android:fitsSystemWindows="true"
+        app:layout_constraintTop_toTopOf="parent"
+        app:navigationIcon="?attr/homeAsUpIndicator"
+        app:title="@string/about"
+        app:titleTextColor="@color/black" />
+
+    <ImageView
+        android:id="@+id/logo"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@mipmap/ic_launcher"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="20dp"
+        android:text="@string/app_name"
+        android:textColor="@color/black"
+        android:textSize="28sp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/logo" />
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 1 - 0
app/src/main/res/layout/activity_permission_list.xml

@@ -25,4 +25,5 @@
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toBottomOf="@id/toolbar" />
+
 </androidx.constraintlayout.widget.ConstraintLayout>

+ 54 - 0
app/src/main/res/layout/activity_uninstall.xml

@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".activity.PermissionListActivity">
+
+    <androidx.appcompat.widget.Toolbar
+        android:id="@+id/toolbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@color/white"
+        android:fitsSystemWindows="true"
+        app:layout_constraintTop_toTopOf="parent"
+        app:navigationIcon="?attr/homeAsUpIndicator"
+        app:title="@string/uninstall_apps"
+        app:titleTextColor="@color/black" />
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/recycler_view"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:clipToPadding="false"
+        android:paddingBottom="86dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/toolbar" />
+
+    <ProgressBar
+        android:id="@+id/progress_bar"
+        android:layout_width="100dp"
+        android:layout_height="100dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <Button
+        android:id="@+id/btn_uninstall"
+        android:layout_width="match_parent"
+        android:layout_height="54dp"
+        android:layout_marginLeft="16dp"
+        android:layout_marginRight="16dp"
+        android:layout_marginBottom="16dp"
+        android:backgroundTint="@color/btn_uninstall_bg"
+        android:elevation="0dp"
+        android:enabled="false"
+        android:text="@string/uninstall"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 45 - 0
app/src/main/res/layout/item_app_uninstall.xml

@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="70dp"
+    android:background="@drawable/bg_cell_no_corner"
+    android:gravity="start|center">
+
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="40dp"
+        android:layout_height="40dp"
+        android:layout_marginStart="20dp"
+        android:scaleType="centerInside"
+        android:src="@mipmap/ic_launcher" />
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="15dp"
+        android:layout_weight="1"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/tv_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="YouTube"
+            android:textColor="@color/black"
+            android:textSize="15sp" />
+
+        <TextView
+            android:id="@+id/tv_date"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="2dp"
+            android:text="@string/number_of_sensitive_permissions"
+            android:textColor="#AAACAD"
+            android:textSize="13sp" />
+    </LinearLayout>
+
+    <com.google.android.material.checkbox.MaterialCheckBox
+        android:id="@+id/checkbox"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+</androidx.appcompat.widget.LinearLayoutCompat>

+ 1 - 0
app/src/main/res/layout/tool_fragment.xml

@@ -368,6 +368,7 @@
             </LinearLayout>
 
             <LinearLayout
+                android:id="@+id/cell_uninstall"
                 android:layout_width="match_parent"
                 android:layout_height="79dp"
                 android:layout_marginBottom="17dp"

+ 1 - 0
app/src/main/res/values/strings.xml

@@ -108,4 +108,5 @@
     <string name="per_record_audio_des">Allows the application to record audio</string>
     <string name="per_send_sms">SMS permissions</string>
     <string name="per_send_sms_des">Reading SMS / MMS, received messages, sent messages, etc</string>
+    <string name="uninstall">UNINSTALL</string>
 </resources>