Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 16 additions & 28 deletions COMMANDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,22 @@ adb install -r app/build/outputs/apk/fdroid/debug/app-fdroid-debug.apk

---

## 🐧 BusyBox Management

The launcher now features a built-in BusyBox manager to enable standard Linux commands.

### Installation
In the T-UI terminal, type:
```bash
bbman -install
```
This downloads the architecture-specific ELF binary, verifies its **SHA-256 hash**, and sets up the environment.

### Usage
Once installed, you can use any Linux command directly:
```bash
ls -la
grep "search_term" file.txt
busybox --list
```

### Removal
To clean up the environment:
```bash
bbman -remove
```
## ☀️ Weather Configuration

The `weather` command requires a personal API key from [OpenWeatherMap](https://openweathermap.org/) for manual updates.

### Setting your Key
1. Register for a free account on the OpenWeatherMap website.
2. Generate an API key in your account settings.
3. In the T-UI terminal, type:
```bash
weather -set_key [YOUR_API_KEY]
```
4. Enable weather updates:
```bash
weather -enable
```
*(If it shows a location error, ensure Location permission is granted in Android settings)*

---

Expand All @@ -73,7 +65,3 @@ adb uninstall ohi.andre.consolelauncher
adb push local_file.txt /data/user/0/ohi.andre.consolelauncher/files/
```

---

## 🛡 Security Note
All binaries are verified using hardcoded SHA-256 hashes found in `app/src/main/java/ohi/andre/consolelauncher/tuils/BusyBoxInstaller.java`. All network transport is forced over HTTPS.
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ android {
applicationId "ohi.andre.consolelauncher"
minSdk 21
targetSdk 34
versionCode 301
versionName "v6.15.1-updated-v2"
versionCode 700
versionName "v7.0.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />

<!-- features -->
<uses-feature
Expand Down
45 changes: 21 additions & 24 deletions app/src/main/java/ohi/andre/consolelauncher/LauncherActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
import ohi.andre.consolelauncher.managers.xml.options.Theme;
import ohi.andre.consolelauncher.managers.xml.options.Ui;
import ohi.andre.consolelauncher.tuils.Assist;
import ohi.andre.consolelauncher.tuils.BusyBoxInstaller;
import ohi.andre.consolelauncher.tuils.CustomExceptionHandler;
import ohi.andre.consolelauncher.tuils.LongClickableSpan;
import ohi.andre.consolelauncher.tuils.PrivateIOReceiver;
Expand Down Expand Up @@ -215,15 +214,6 @@ public void onCreate(Bundle savedInstanceState) {
}
}

if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
permissionsToRequest.add(Manifest.permission.READ_PHONE_STATE);
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
permissionsToRequest.add(Manifest.permission.ACCESS_FINE_LOCATION);
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
permissionsToRequest.add(Manifest.permission.ACCESS_COARSE_LOCATION);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
permissionsToRequest.add(Manifest.permission.POST_NOTIFICATIONS);
Expand Down Expand Up @@ -588,25 +578,32 @@ public void onRequestPermissionsResult(int requestCode, String permissions[], in
break;
case STARTING_PERMISSION:
int count = 0;
boolean storageDenied = false;
while(count < permissions.length && count < grantResults.length) {
if(grantResults[count] == PackageManager.PERMISSION_DENIED) {
Toast.makeText(this, R.string.permissions_toast, Toast.LENGTH_LONG).show();
new Thread() {
@Override
public void run() {
super.run();

try {
sleep(2000);
} catch (InterruptedException e) {}

runOnUiThread(stopActivity);
}
}.start();
return;
String p = permissions[count];
if (p.equals(Manifest.permission.READ_EXTERNAL_STORAGE) || p.equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
storageDenied = true;
}
}
count++;
}

if (storageDenied) {
Toast.makeText(this, R.string.permissions_toast, Toast.LENGTH_LONG).show();
new Thread() {
@Override
public void run() {
super.run();
try {
sleep(2000);
} catch (InterruptedException e) {}
runOnUiThread(stopActivity);
}
}.start();
return;
}

canApplyTheme = false;
finishOnCreate();
break;
Expand Down
26 changes: 15 additions & 11 deletions app/src/main/java/ohi/andre/consolelauncher/MainManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Parcelable;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
Expand Down Expand Up @@ -38,7 +39,7 @@
import ohi.andre.consolelauncher.managers.xml.XMLPrefsManager;
import ohi.andre.consolelauncher.managers.xml.options.Behavior;
import ohi.andre.consolelauncher.managers.xml.options.Theme;
import ohi.andre.consolelauncher.tuils.BusyBoxInstaller;
import ohi.andre.consolelauncher.managers.xml.options.Ui;
import ohi.andre.consolelauncher.tuils.PrivateIOReceiver;
import ohi.andre.consolelauncher.tuils.StoppableThread;
import ohi.andre.consolelauncher.tuils.Tuils;
Expand Down Expand Up @@ -248,6 +249,19 @@ public void onReceive(Context context, Intent intent) {
};

LocalBroadcastManager.getInstance(mContext.getApplicationContext()).registerReceiver(receiver, filter);

final SharedPreferences firstRunPrefs = mContext.getSharedPreferences("first_run", Context.MODE_PRIVATE);
if (firstRunPrefs.getBoolean("is_first_launch", true)) {
new android.os.Handler().postDelayed(new Runnable() {
@Override
public void run() {
Tuils.sendOutput(mContext, Tuils.span("--- Welcome to T-UI Launcher! ---", XMLPrefsManager.getColor(Theme.output_color)));
Tuils.sendOutput(mContext, "Use 'explain' to see a summary of the new modernized features.");
Tuils.sendOutput(mContext, "Type 'help' for a full list of commands.");
firstRunPrefs.edit().putBoolean("is_first_launch", false).apply();
}
}, 1000);
}
}

private void updateServices(String cmd, boolean wasMusicService) {
Expand Down Expand Up @@ -514,16 +528,6 @@ public boolean trigger(final MainPack info, final String input) throws Exception
final String trimmed = input.trim();
final String cmd = trimmed.split(" ")[0];

if (!BusyBoxInstaller.isInstalled(mContext)) {
String[] common = {"ping", "echo", "ls", "grep", "cat", "vi", "top", "ps", "ip"};
for (String c : common) {
if (cmd.equalsIgnoreCase(c)) {
Tuils.sendOutput(mContext, "Command not found. You can install BusyBox using: bbman -install", TerminalManager.CATEGORY_OUTPUT);
return true;
}
}
}

new StoppableThread() {
@Override
public void run() {
Expand Down
65 changes: 23 additions & 42 deletions app/src/main/java/ohi/andre/consolelauncher/UIManager.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ohi.andre.consolelauncher;

import android.Manifest;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.KeyguardManager;
Expand All @@ -10,6 +11,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Color;
Expand Down Expand Up @@ -644,7 +647,7 @@ private class WeatherRunnable implements Runnable {
String key;
String url;

public WeatherRunnable() {
public WeatherRunnable(boolean manual) {

if(XMLPrefsManager.wasChanged(Behavior.weather_key, false)) {
weatherDelay = XMLPrefsManager.getInt(Behavior.weather_update_time);
Expand All @@ -657,36 +660,18 @@ public WeatherRunnable() {

String where = XMLPrefsManager.get(Behavior.weather_location);
if(where == null || where.length() == 0 || (!Tuils.isNumber(where) && !where.contains(","))) {
// Tuils.location(mContext, new Tuils.ArgsRunnable() {
// @Override
// public void run() {
// setUrl(
// "lat=" + get(int.class, 0) + "&lon=" + get(int.class, 1),
// finalKey,
// XMLPrefsManager.get(Behavior.weather_temperature_measure));
// WeatherRunnable.this.run();
// }
// }, new Runnable() {
// @Override
// public void run() {
// updateText(Label.weather, Tuils.span(mContext, mContext.getString(R.string.location_error), XMLPrefsManager.getColor(Theme.weather_color), labelSizes[Label.weather.ordinal()]));
// }
// }, handler);

// Location l = Tuils.getLocation(mContext);
// if(l != null) {
// setUrl(
// "lat=" + l.getLatitude() + "&lon=" + l.getLongitude(),
// finalKey,
// XMLPrefsManager.get(Behavior.weather_temperature_measure));
// WeatherRunnable.this.run();
// } else {
// updateText(Label.weather, Tuils.span(mContext, mContext.getString(R.string.location_error), XMLPrefsManager.getColor(Theme.weather_color), labelSizes[Label.weather.ordinal()]));
// }

TuiLocationManager l = TuiLocationManager.instance(mContext);
l.add(ACTION_WEATHER_GOT_LOCATION);

if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
TuiLocationManager l = TuiLocationManager.instance(mContext);
l.add(ACTION_WEATHER_GOT_LOCATION);
} else {
if (manual) {
TuiLocationManager l = TuiLocationManager.instance(mContext);
l.add(ACTION_WEATHER_GOT_LOCATION);
} else {
updateText(Label.weather, Tuils.span(mContext, "Weather: Please enable Location permission in app settings.", weatherColor, labelSizes[Label.weather.ordinal()]));
}
}
} else {
fixedLocation = true;

Expand Down Expand Up @@ -835,18 +820,13 @@ public void onReceive(Context context, Intent intent) {
updateText(Label.weather, s);

if(showWeatherUpdate) {
String message = context.getString(R.string.weather_updated) + Tuils.SPACE + c.get(Calendar.HOUR_OF_DAY) + "." + c.get(Calendar.MINUTE) + Tuils.SPACE + "(" + lastLatitude + ", " + lastLongitude + ")";
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("HH:mm", java.util.Locale.getDefault());
String message = context.getString(R.string.weather_updated) + Tuils.SPACE + sdf.format(c.getTime());
Tuils.sendOutput(context, message, TerminalManager.CATEGORY_OUTPUT);
}
} else if(action.equals(ACTION_WEATHER_GOT_LOCATION)) {
// int result = intent.getIntExtra(XMLPrefsManager.VALUE_ATTRIBUTE, 0);
// if(result == PackageManager.PERMISSION_DENIED) {
// updateText(Label.weather, Tuils.span(context, context.getString(R.string.location_error), weatherColor, labelSizes[Label.weather.ordinal()]));
// } else handler.post(weatherRunnable);

if(intent.getBooleanExtra(TuiLocationManager.FAIL, false)) {
handler.removeCallbacks(weatherRunnable);
weatherRunnable = null;

CharSequence s = Tuils.span(context, context.getString(R.string.location_error), weatherColor, labelSizes[Label.weather.ordinal()]);

Expand All @@ -859,7 +839,7 @@ public void onReceive(Context context, Intent intent) {

if(!weatherPerformedStartupRun || XMLPrefsManager.wasChanged(Behavior.weather_key, false)) {
handler.removeCallbacks(weatherRunnable);
handler.post(weatherRunnable);
if (weatherRunnable != null) handler.post(weatherRunnable);
}
}
} else if(action.equals(ACTION_WEATHER_DELAY)) {
Expand All @@ -872,10 +852,11 @@ public void onReceive(Context context, Intent intent) {
}

handler.removeCallbacks(weatherRunnable);
handler.postDelayed(weatherRunnable, 1000 * 60);
if (weatherRunnable != null) handler.postDelayed(weatherRunnable, 1000 * 60);
} else if(action.equals(ACTION_WEATHER_MANUAL_UPDATE)) {
handler.removeCallbacks(weatherRunnable);
handler.post(weatherRunnable);
weatherRunnable = new WeatherRunnable(true);
if (weatherRunnable != null) handler.post(weatherRunnable);
}
}
};
Expand Down Expand Up @@ -1224,7 +1205,7 @@ public void onGlobalLayout() {
}

if(show[Label.weather.ordinal()]) {
weatherRunnable = new WeatherRunnable();
weatherRunnable = new WeatherRunnable(false);

weatherColor = XMLPrefsManager.getColor(Theme.weather_color);

Expand Down
Loading