debloat/Debloat.java
2023-07-07 22:47:41 -05:00

304 lines
9.9 KiB
Java

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.BevelBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
/**
* Universal Android Debloater GUI (2023)
*
*/
public class Debloat extends JFrame implements ActionListener {
public static final String ADB_BASE_URL = "./src/adb";
public static final String ADB_WINDOWS_URL = "/windows/adb.exe ";
public static final String ADB_MACOS_URL = "/macos/adb ";
public static final String ADB_LINUX_URL = "/linux/adb ";
public static final String ADB_START_SERVER = "start-server";
public static final String ADB_KILL_SERVER = "kill-server";
public static final int ADB_SERVER_REFRESH_MILSECOND = 5000;
public static final String ADB_HAS_DEVICE = "devices -l";
public static final String ADB_GET_DEVICE_MANUFACTURER = "shell getprop ro.product.manufacturer";
public static final String ADB_GET_DEVICE_MODEL = "shell getprop ro.product.model";
public static final String ADB_GET_PACKAGES = "shell pm list packages";
public static final String ADB_ENABLE_PACKAGE = "shell pm enable --user 0 ";
public static final String ADB_DISABLE_PACKAGE = "shell pm disable-user --user 0 ";
public static final String ADB_FORCE_DELETE_PACKAGE = "shell pm clear --user 0 ";
public static final String TITLE = "Debloat";
public static final Dimension WINDOW_SIZE = new Dimension(500, 800);
public static final Font DEFAULT_FONT = new Font("Arial", Font.PLAIN, 14);
public static final String DEFAULT_DEVICE_NAME = "Device: Not Found";
public static final String DEFAULT_STATUS_BAR = "Searching for Devices...";
public static final int PANE_CENTER_INSET = 15;
public static final int PANE_SOUTH_INSET = 10;
public static final int BTN_MARGIN = 5;
public static final String[] BTN_NAMES = { "Select All", "Unselect All", "Disable", "Enable" };
private JPanel pnlDeviceName;
private JPanel pnlCenterContainer;
private JPanel pnlPackages;
private JPanel pnlSouthContainer;
private JPanel pnlButtons;
private JPanel pnlStatus;
private JLabel lblDeviceName;
private JLabel lblStatus;
private HashMap<JCheckBox, Boolean> chkPackages;
/**
* Constructor that creates window
*
* @date 2022
*/
public Debloat() {
super(TITLE);
setLayout(new BorderLayout());
createDeviceNamePane();
createPackagesPane();
createButtonsPane();
createStatusBar();
adb(ADB_START_SERVER);
chkPackages = new HashMap<>();
// Detect devices
Timer t = new Timer();
t.schedule(new TimerTask() {
@Override
public void run() {
if (adb(ADB_HAS_DEVICE).size() > 2) {
lblStatus.setText("Device connected!");
lblDeviceName
.setText("Device: " + adb(ADB_GET_DEVICE_MANUFACTURER).get(0) + " " + adb(ADB_GET_DEVICE_MODEL).get(0));
if (chkPackages.size() == 0) {
for (String pkg : adb(ADB_GET_PACKAGES)) {
// Remove 'package:' prefix from package names
JCheckBox chk = new JCheckBox(pkg.substring(pkg.lastIndexOf(':') + 1));
chk.setFont(DEFAULT_FONT);
chk.setOpaque(false);
pnlPackages.add(chk);
chkPackages.put(chk, false);
pack();
}
}
} else {
// If device is disconnected
lblDeviceName.setText(DEFAULT_DEVICE_NAME);
lblStatus.setText(DEFAULT_STATUS_BAR);
pnlPackages.removeAll();
chkPackages.clear();
}
}
// Ensure device is connected
}, 0, ADB_SERVER_REFRESH_MILSECOND);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setPreferredSize(WINDOW_SIZE);
pack();
setLocationRelativeTo(null);
setVisible(true);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
dispose();
t.cancel();
t.purge();
adb(ADB_KILL_SERVER);
}
});
}
/**
* Execute adb shell command
*
* @param cmd adb shell command to execute
* @return command output line by line
*/
public static ArrayList<String> adb(String cmd) {
Process p;
String adbcmd = "";
// Check if OS is Windows
try {
p = Runtime.getRuntime().exec(ADB_BASE_URL + ADB_WINDOWS_URL + cmd);
if (p.waitFor() != 0) {
throw new IOException();
}
adbcmd = ADB_BASE_URL + ADB_WINDOWS_URL;
} catch (Exception e) {
}
// Check if OS is Mac OS
try {
p = Runtime.getRuntime().exec(ADB_BASE_URL + ADB_MACOS_URL + cmd);
if (p.waitFor() != 0) {
throw new IOException();
}
adbcmd = ADB_BASE_URL + ADB_MACOS_URL;
} catch (Exception e) {
}
// Check if OS is Linux
try {
p = Runtime.getRuntime().exec(ADB_BASE_URL + ADB_LINUX_URL + cmd);
if (p.waitFor() != 0) {
throw new IOException();
}
adbcmd = ADB_BASE_URL + ADB_LINUX_URL;
} catch (Exception e) {
}
// Read outputs from adb command
ArrayList<String> outputs = new ArrayList<>();
try (InputStream is = Runtime.getRuntime().exec(adbcmd + cmd).getInputStream();
Scanner s = new Scanner(is).useDelimiter("\\n")) {
while (s.hasNext()) {
outputs.add(s.next());
}
return outputs;
} catch (IOException e) {
}
return new ArrayList<>(Arrays.asList("Device is not configured correctly."));
}
/**
* Create a panel that contains device name
*
*/
protected void createDeviceNamePane() {
pnlDeviceName = new JPanel();
lblDeviceName = new JLabel(DEFAULT_DEVICE_NAME);
lblDeviceName.setFont(DEFAULT_FONT);
pnlDeviceName.add(lblDeviceName);
add(BorderLayout.NORTH, pnlDeviceName);
}
/**
* Create a panel that contains a list of package names
*
*/
protected void createPackagesPane() {
pnlCenterContainer = new JPanel();
pnlCenterContainer.setBorder(BorderFactory.createCompoundBorder(
new EmptyBorder(PANE_CENTER_INSET, PANE_CENTER_INSET, PANE_CENTER_INSET, PANE_CENTER_INSET),
new LineBorder(Color.LIGHT_GRAY)));
pnlCenterContainer.setLayout(new GridLayout(0, 1));
add(BorderLayout.CENTER, new JScrollPane(pnlCenterContainer));
pnlPackages = new JPanel();
pnlPackages.setBackground(Color.WHITE);
pnlPackages.setLayout(new GridLayout(0, 1));
pnlCenterContainer.add(pnlPackages);
}
/**
* Create a panel that contains various buttons
*
*/
protected void createButtonsPane() {
pnlSouthContainer = new JPanel();
pnlSouthContainer.setLayout(new BoxLayout(pnlSouthContainer, BoxLayout.Y_AXIS));
pnlButtons = new JPanel();
pnlButtons.setLayout(new GridLayout(0, BTN_NAMES.length / 2, BTN_MARGIN, BTN_MARGIN));
pnlSouthContainer.add(pnlButtons);
pnlButtons.setBorder(new EmptyBorder(PANE_SOUTH_INSET, PANE_SOUTH_INSET, PANE_SOUTH_INSET, PANE_SOUTH_INSET));
for (int i = 0; i < BTN_NAMES.length; i++) {
JButton btn = new JButton(BTN_NAMES[i]);
btn.setFont(DEFAULT_FONT);
btn.addActionListener(this);
pnlButtons.add(btn);
}
}
/**
* Create a panel that contains the status bar
*
*/
protected void createStatusBar() {
pnlStatus = new JPanel();
pnlStatus.setBorder(new BevelBorder(BevelBorder.LOWERED));
pnlSouthContainer.add(pnlStatus);
pnlStatus.setMaximumSize(getMaximumSize());
pnlStatus.setLayout(new BoxLayout(pnlStatus, BoxLayout.X_AXIS));
lblStatus = new JLabel(DEFAULT_STATUS_BAR);
lblStatus.setFont(DEFAULT_FONT);
pnlStatus.add(lblStatus);
add(BorderLayout.SOUTH, pnlSouthContainer);
}
/**
* Invoked when an action event occurs on a component
*
* @param ev received action event from component
*/
@Override
public void actionPerformed(ActionEvent ev) {
// Get name of the clicked button
String source = ev.getSource().toString();
int pkgCount = 1;
for (Map.Entry<JCheckBox, Boolean> entry : chkPackages.entrySet()) {
JCheckBox chk = entry.getKey();
// Determine which button is clicked using button name
if (source.contains("text=Select All")) {
chk.setSelected(true);
} else if (source.contains("text=Unselect All")) {
chk.setSelected(false);
} else if (source.contains("text=Enable")) {
if (chk.isSelected()) {
try {
adb(ADB_ENABLE_PACKAGE + chk.getText());
lblStatus.setText("Enabled " + pkgCount++ + " app(s) successfully.");
} catch (Exception e) {
lblStatus.setText("Failed enabling " + pkgCount + " app(s).");
}
}
} else if (source.contains("text=Disable")) {
if (chk.isSelected()) {
try {
adb(ADB_DISABLE_PACKAGE + chk.getText());
lblStatus.setText("Disabled " + pkgCount++ + " app(s) successfully.");
} catch (Exception e) {
try {
adb(ADB_FORCE_DELETE_PACKAGE + chk.getText());
lblStatus.setText("Disabled " + pkgCount + " app(s) successfully.");
} catch (Exception ex) {
lblStatus.setText("Failed disabling " + pkgCount + " app(s).");
}
}
}
}
}
}
/**
* Main
*
* @param args The command line arguments
**/
public static void main(String[] args) {
new Debloat();
}
}