Skip to content

Commit

Permalink
Inline Termux:Widget into the main Termux app
Browse files Browse the repository at this point in the history
  • Loading branch information
fornwall committed Oct 22, 2024
1 parent 8727d59 commit 23dd2d7
Show file tree
Hide file tree
Showing 31 changed files with 133 additions and 755 deletions.
2 changes: 1 addition & 1 deletion settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
include(":termux-api", ":termux-app", ":terminal-emulator", ":terminal-view", ":termux-style", ":termux-widget")
include(":termux-api", ":termux-app", ":terminal-emulator", ":terminal-view", ":termux-style")
29 changes: 29 additions & 0 deletions termux-app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,35 @@
</intent-filter>
</activity>


<!-- Widgets -->
<receiver
android:name=".widget.TermuxWidgetProvider"
android:exported="false"
android:label="@string/title_shortcut_widget_name" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/termux_appwidget_info" />
</receiver>

<activity
android:exported="true"
android:name=".widget.TermuxCreateShortcutActivity"
android:label="@string/title_single_shortcut_name">
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<service
android:name=".widget.TermuxWidgetService"
android:exported="false"
android:permission="android.permission.BIND_REMOTEVIEWS" />


<activity android:name=".app.api.DialogAPI$DialogActivity"
android:theme="@style/DialogTheme"
android:noHistory="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
- Make the comparison case insensitive.
- Remove the main() and associated methods.
*/
public class NaturalOrderComparator {
final class NaturalOrderComparator {

private static int compareRight(String a, String b) {
int bias = 0, ia = 0, ib = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,35 @@
import android.content.pm.ShortcutManager;
import android.os.Bundle;
import android.util.Log;
import android.view.WindowInsets;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

import com.termux.R;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class TermuxCreateShortcutActivity extends Activity {

private ListView mListView;
private final List<ShortcutFile> mAllFiles = new ArrayList<>();
private final List<TermuxWidgetShortcutFile> mAllFiles = new ArrayList<>();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.shortcuts_listview);
mListView = findViewById(R.id.list);

TermuxPathLister.listPaths(this, TermuxWidgetConstants.TERMUX_SHORTCUT_SCRIPTS_DIR_PATH, path -> mAllFiles.add(new ShortcutFile(new File(path))));
mListView.setOnApplyWindowInsetsListener((v, insets) -> {
var bars = insets.getInsets(WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
v.setPadding(bars.left, bars.top, bars.right, bars.bottom);
return WindowInsets.CONSUMED;
});

TermuxWidgetPathLister.listPaths(path -> mAllFiles.add(new TermuxWidgetShortcutFile(new File(path))));
}

@Override
Expand All @@ -43,7 +52,7 @@ protected void onResume() {
}

private void updateListview() {
ShortcutFile.sort(mAllFiles);
TermuxWidgetShortcutFile.sort(mAllFiles);

if (mAllFiles.isEmpty()) {
new AlertDialog.Builder(this)
Expand All @@ -61,7 +70,7 @@ private void updateListview() {
mListView.setAdapter(adapter);
}

private void createShortcut(Context context, ShortcutFile shortcutFile) {
private void createShortcut(Context context, TermuxWidgetShortcutFile shortcutFile) {
var shortcutManager = context.getSystemService(ShortcutManager.class);

if (shortcutManager.isRequestPinShortcutSupported()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.termux.widget;

import android.annotation.SuppressLint;
import com.termux.app.TermuxConstants;

public class TermuxWidgetConstants {

Expand All @@ -12,11 +12,10 @@ public class TermuxWidgetConstants {

public static final String EXTRA_FILE_CLICKED = "com.termux.widget.EXTRA_FILE_CLICKED";

public static final String SHORTCUTS_DIR_NAME = ".shortcuts";

public static final String TASKS_DIR_NAME = "tasks";

@SuppressLint("SdCardPath")
public static final String TERMUX_SHORTCUT_SCRIPTS_DIR_PATH = "/data/data/com.termux/files/home/" + SHORTCUTS_DIR_NAME;
public static final String TERMUX_SHORTCUT_SCRIPTS_DIR_PATH = TermuxConstants.HOME_PATH + "/.config/termux/shortcuts";

public static final String TERMUX_SHORTCUT_SCRIPTS_DIR_PATH_LEGACY = TermuxConstants.HOME_PATH + "/.shortcuts";

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.termux.widget;

import android.util.Log;

import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;

public class TermuxWidgetPathLister {

public static void listPaths(Consumer<String> onFile) {
var visited = new HashSet<String>();
for (var parentPath : new String[]{
TermuxWidgetConstants.TERMUX_SHORTCUT_SCRIPTS_DIR_PATH,
TermuxWidgetConstants.TERMUX_SHORTCUT_SCRIPTS_DIR_PATH_LEGACY,
}) {
var parentDir = new File(parentPath);
listPathsInternal(parentDir, visited, onFile);
}
}

private static void listPathsInternal(File directory, Set<String> visited, Consumer<String> onFile) {
try {
var canonicalPath = directory.getCanonicalPath();
if (!visited.add(canonicalPath)) {
Log.w(TermuxWidgetConstants.LOG_TAG, "Avoiding visiting directory again: " + directory.getAbsolutePath());
return;
}
} catch (IOException e) {
Log.e(TermuxWidgetConstants.LOG_TAG, "Exception resolving canonical path for: " + directory.getAbsolutePath(), e);
return;
}

var children = directory.listFiles();

if (children == null) {
return;
}

for (var child : children) {
if (child.isFile()) {
if (!child.canExecute()) {
if (!child.setExecutable(true)) {
Log.e(TermuxWidgetConstants.LOG_TAG, "Unable to set file to executable: " + child.getAbsolutePath());
}
}
onFile.accept(child.getAbsolutePath());
} else if (child.isDirectory()) {
listPathsInternal(child, visited, onFile);
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

import androidx.annotation.NonNull;

import com.termux.R;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -101,7 +103,7 @@ public void onReceive(Context context, Intent intent) {
return;
}

var shortcut = new ShortcutFile(new File(clickedFilePath));
var shortcut = new TermuxWidgetShortcutFile(new File(clickedFilePath));

var executionIntent = shortcut.getExecutionIntent(false);
if (shortcut.mIsTask) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public RemoteViewsFactory onGetViewFactory(Intent intent) {

public static class ListRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {

private final List<ShortcutFile> shortcutFiles = new ArrayList<>();
private final List<TermuxWidgetShortcutFile> shortcutFiles = new ArrayList<>();
private final Context mContext;
private final int mAppWidgetId;

Expand Down Expand Up @@ -84,11 +84,9 @@ public void onDataSetChanged() {
// locking up the widget.
shortcutFiles.clear();

TermuxPathLister.listPaths(mContext, TermuxWidgetConstants.TERMUX_SHORTCUT_SCRIPTS_DIR_PATH, path -> {
shortcutFiles.add(new ShortcutFile(new File(path)));
});
TermuxWidgetPathLister.listPaths(path -> shortcutFiles.add(new TermuxWidgetShortcutFile(new File(path))));

ShortcutFile.sort(shortcutFiles);
TermuxWidgetShortcutFile.sort(shortcutFiles);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,17 @@

import androidx.annotation.NonNull;

import com.termux.R;

import java.io.File;
import java.util.Comparator;
import java.util.List;

public final class ShortcutFile {
final class TermuxWidgetShortcutFile {

public static void sort(List<ShortcutFile> shortcutFiles) {
public static void sort(List<TermuxWidgetShortcutFile> shortcutFiles) {
shortcutFiles.sort(Comparator
.comparingInt((ShortcutFile a) -> (a.mDisplaysDirectory ? 1 : -1))
.comparingInt((TermuxWidgetShortcutFile a) -> (a.mDisplaysDirectory ? 1 : -1))
.thenComparing((a, b) -> NaturalOrderComparator.compare(a.mLabel, b.mLabel))
);
}
Expand All @@ -27,10 +29,12 @@ public static void sort(List<ShortcutFile> shortcutFiles) {
public final boolean mDisplaysDirectory;
public final boolean mIsTask;

public ShortcutFile(@NonNull File file) {
public TermuxWidgetShortcutFile(@NonNull File file) {
mPath = file.getAbsolutePath();
var parentDirName = file.getParentFile().getName();
mDisplaysDirectory = !parentDirName.equals(TermuxWidgetConstants.SHORTCUTS_DIR_NAME);
var parentAbsolutePath = file.getParentFile().getAbsolutePath();
mDisplaysDirectory = !(TermuxWidgetConstants.TERMUX_SHORTCUT_SCRIPTS_DIR_PATH.equals(parentAbsolutePath)
|| TermuxWidgetConstants.TERMUX_SHORTCUT_SCRIPTS_DIR_PATH_LEGACY.equals(parentAbsolutePath));
mIsTask = parentDirName.equals(TermuxWidgetConstants.TASKS_DIR_NAME);
mLabel = mDisplaysDirectory ? (parentDirName + "/" + file.getName()) : file.getName();
}
Expand Down Expand Up @@ -73,12 +77,12 @@ public ShortcutInfo getShortcutInfo(Context context) {
public RemoteViews getListWidgetView(Context context) {
// Position will always range from 0 to getCount() - 1.
// Construct remote views item based on the item xml file and set text based on position.
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_item);
var remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_item);
remoteViews.setTextViewText(R.id.widget_item, getLabel());

// Next, we set a fill-intent which will be used to fill-in the pending intent template
// which is set on the collection view in TermuxAppWidgetProvider.
Intent fillInIntent = new Intent()
var fillInIntent = new Intent()
.putExtra(TermuxWidgetConstants.EXTRA_FILE_CLICKED, getPath());

remoteViews.setOnClickFillInIntent(R.id.widget_item_layout, fillInIntent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="2.0"
android:text="@string/title_widget"
android:text="@string/application_name"
android:textColor="@android:color/primary_text_dark"
android:textSize="18sp" />
</LinearLayout>
Expand Down Expand Up @@ -61,4 +61,4 @@
android:contentDescription="@string/action_refresh"
android:src="@android:drawable/stat_notify_sync" />

</RelativeLayout>
</RelativeLayout>
10 changes: 10 additions & 0 deletions termux-app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@
<string name="action_file_received_edit">Edit</string>
<string name="action_file_received_open_directory">Open directory</string>

<!-- Widgets -->
<string name="title_shortcut_widget_name">Termux widget</string>
<string name="title_single_shortcut_name">Termux shortcut</string>
<string name="title_termux_shortcuts">Termux Shortcuts</string>
<string name="msg_no_shortcut_scripts">No files in $HOME/.shortcuts/</string>
<string name="action_refresh">Refresh</string>
<string name="msg_pinned_shortcuts_not_supported">The current launcher does not support pinned shortcuts.</string>
<string name="msg_widgets_reloaded">Termux widget reloaded</string>
<string name="msg_executing_task">Executing task: \"%1$s\"</string>

<!-- Miscellaneous -->
<string name="hint_file_name">File name</string>
<string name="cancel">Cancel</string>
Expand Down
Loading

0 comments on commit 23dd2d7

Please sign in to comment.