mirror of
https://github.com/nix-community/nix-on-droid-app.git
synced 2025-11-08 19:46:10 +01:00
merge sources of Termux:Widget
This commit is contained in:
parent
865d38db11
commit
ef34333d4f
12 changed files with 643 additions and 43 deletions
|
|
@ -128,6 +128,35 @@
|
|||
android:name="com.termux.app.TermuxOpenReceiver$ContentProvider" />
|
||||
|
||||
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
|
||||
</application>
|
||||
|
||||
<receiver
|
||||
android:name="com.termux.widget.TermuxWidgetProvider"
|
||||
android:label="@string/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:name="com.termux.widget.TermuxCreateShortcutActivity"
|
||||
android:label="@string/single_shortcut_name"
|
||||
android:theme="@android:style/Theme.Material.Light.DarkActionBar">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.CREATE_SHORTCUT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.termux.widget.TermuxLaunchShortcutActivity"
|
||||
android:noHistory="true"
|
||||
android:theme="@android:style/Theme.NoDisplay"
|
||||
android:exported="true"/>
|
||||
|
||||
<service
|
||||
android:name="com.termux.widget.TermuxWidgetService"
|
||||
android:exported="false"
|
||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,98 @@
|
|||
package com.termux.widget;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
|
||||
import com.termux.R;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class TermuxCreateShortcutActivity extends Activity {
|
||||
|
||||
private ListView mListView;
|
||||
private File mCurrentDirectory;
|
||||
private File[] mCurrentFiles;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.shortcuts_listview);
|
||||
mListView = findViewById(R.id.list);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
updateListview(TermuxWidgetService.SHORTCUTS_DIR);
|
||||
|
||||
mListView.setOnItemClickListener((parent, view, position, id) -> {
|
||||
final Context context = TermuxCreateShortcutActivity.this;
|
||||
File clickedFile = mCurrentFiles[position];
|
||||
if (clickedFile.isDirectory()) {
|
||||
updateListview(clickedFile);
|
||||
return;
|
||||
}
|
||||
|
||||
Intent.ShortcutIconResource icon = Intent.ShortcutIconResource.fromContext(context, R.drawable.ic_launcher);
|
||||
|
||||
Uri scriptUri = new Uri.Builder().scheme("com.termux.file").path(clickedFile.getAbsolutePath()).build();
|
||||
Intent executeIntent = new Intent(context, TermuxLaunchShortcutActivity.class);
|
||||
executeIntent.setData(scriptUri);
|
||||
executeIntent.putExtra(TermuxLaunchShortcutActivity.TOKEN_NAME, TermuxLaunchShortcutActivity.getGeneratedToken(context));
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, executeIntent);
|
||||
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, clickedFile.getName());
|
||||
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, icon);
|
||||
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
private void updateListview(File directory) {
|
||||
mCurrentDirectory = directory;
|
||||
mCurrentFiles = directory.listFiles(pathname -> !pathname.getName().startsWith("."));
|
||||
if (mCurrentFiles == null) mCurrentFiles = new File[0];
|
||||
|
||||
Arrays.sort(mCurrentFiles, (f1, f2) -> f1.getName().compareTo(f2.getName()));
|
||||
|
||||
final boolean isTopDir = directory.equals(TermuxWidgetService.SHORTCUTS_DIR);
|
||||
getActionBar().setDisplayHomeAsUpEnabled(!isTopDir);
|
||||
|
||||
if (isTopDir && mCurrentFiles.length == 0) {
|
||||
// Create if necessary so user can more easily add.
|
||||
TermuxWidgetService.SHORTCUTS_DIR.mkdirs();
|
||||
new AlertDialog.Builder(this)
|
||||
.setMessage(R.string.no_shortcut_scripts)
|
||||
.setOnDismissListener(dialog -> finish()).show();
|
||||
return;
|
||||
}
|
||||
|
||||
final String[] values = new String[mCurrentFiles.length];
|
||||
for (int i = 0; i < values.length; i++)
|
||||
values[i] = mCurrentFiles[i].getName() +
|
||||
(mCurrentFiles[i].isDirectory() ? "/" : "");
|
||||
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, android.R.id.text1, values);
|
||||
mListView.setAdapter(adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
updateListview(mCurrentDirectory.getParentFile());
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package com.termux.widget;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.termux.R;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* An activity to launch a shortcut. We want to a launch a service directly, but a shortcut
|
||||
* cannot be used to launch a service, only activities, so have to go through this activity.
|
||||
*/
|
||||
public class TermuxLaunchShortcutActivity extends Activity {
|
||||
|
||||
static final String TOKEN_NAME = "com.termux.shortcut.token";
|
||||
|
||||
public static String getGeneratedToken(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences("token", Context.MODE_PRIVATE);
|
||||
String token = prefs.getString("token", null);
|
||||
if (token == null) {
|
||||
token = UUID.randomUUID().toString();
|
||||
prefs.edit().putString("token", token).apply();
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
Intent intent = getIntent();
|
||||
String token = intent.getStringExtra(TOKEN_NAME);
|
||||
if (token == null || !token.equals(getGeneratedToken(this))) {
|
||||
Log.w("termux", "Strange token: " + token);
|
||||
Toast.makeText(this, R.string.bad_token_message, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
File clickedFile = new File(intent.getData().getPath());
|
||||
TermuxWidgetProvider.ensureFileReadableAndExecutable(clickedFile);
|
||||
|
||||
// Do not use the intent data passed in, since that may be an old one with a file:// uri
|
||||
// which is not allowed starting with Android 7.
|
||||
Uri scriptUri = new Uri.Builder().scheme("com.termux.file").path(clickedFile.getAbsolutePath()).build();
|
||||
|
||||
Intent executeIntent = new Intent(TermuxWidgetProvider.ACTION_EXECUTE, scriptUri);
|
||||
executeIntent.setClassName("com.termux", TermuxWidgetProvider.TERMUX_SERVICE);
|
||||
if (clickedFile.getParentFile().getName().equals("tasks")) {
|
||||
executeIntent.putExtra("com.termux.execute.background", true);
|
||||
// Show feedback for executed background task.
|
||||
String message = "Task executed: " + clickedFile.getName();
|
||||
Toast toast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
|
||||
toast.setGravity(Gravity.CENTER, 0, 0);
|
||||
toast.show();
|
||||
}
|
||||
|
||||
TermuxWidgetProvider.startTermuxService(this, executeIntent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
132
app/src/main/java/com/termux/widget/TermuxWidgetProvider.java
Normal file
132
app/src/main/java/com/termux/widget/TermuxWidgetProvider.java
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
package com.termux.widget;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProvider;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.view.Gravity;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.termux.R;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Widget providing a list to launch scripts in $HOME/.termux/shortcuts/.
|
||||
* <p>
|
||||
* See https://developer.android.com/guide/topics/appwidgets/index.html
|
||||
*/
|
||||
public final class TermuxWidgetProvider extends AppWidgetProvider {
|
||||
|
||||
private static final String LIST_ITEM_CLICKED_ACTION = "com.termux.widgets.LIST_ITEM_CLICKED_ACTION";
|
||||
private static final String REFRESH_WIDGET_ACTION = "com.termux.widgets.REFRESH_WIDGET_ACTION";
|
||||
public static final String EXTRA_CLICKED_FILE = "com.termux.widgets.EXTRA_CLICKED_FILE";
|
||||
|
||||
public static final String TERMUX_SERVICE = "com.termux.app.TermuxService";
|
||||
public static final String ACTION_EXECUTE = "com.termux.service_execute";
|
||||
|
||||
|
||||
/**
|
||||
* "This is called to update the App Widget at intervals defined by the updatePeriodMillis attribute in the
|
||||
* AppWidgetProviderInfo (see Adding the AppWidgetProviderInfo Metadata above). This method is also called when the
|
||||
* user adds the App Widget, so it should perform the essential setup, such as define event handlers for Views and
|
||||
* start a temporary Service, if necessary. However, if you have declared a configuration Activity, this method is
|
||||
* not called when the user adds the App Widget, but is called for the subsequent updates. It is the responsibility
|
||||
* of the configuration Activity to perform the first update when configuration is done. (See Creating an App Widget
|
||||
* Configuration Activity below.)"
|
||||
*/
|
||||
@Override
|
||||
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
|
||||
for (int appWidgetId : appWidgetIds) {
|
||||
RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
|
||||
|
||||
// The empty view is displayed when the collection has no items. It should be a sibling
|
||||
// of the collection view:
|
||||
rv.setEmptyView(R.id.widget_list, R.id.empty_view);
|
||||
|
||||
// Setup intent which points to the TermuxWidgetService which will provide the views for this collection.
|
||||
Intent intent = new Intent(context, TermuxWidgetService.class);
|
||||
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
|
||||
// When intents are compared, the extras are ignored, so we need to embed the extras
|
||||
// into the data so that the extras will not be ignored.
|
||||
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
|
||||
rv.setRemoteAdapter(R.id.widget_list, intent);
|
||||
|
||||
// Setup refresh button:
|
||||
Intent refreshIntent = new Intent(context, TermuxWidgetProvider.class);
|
||||
refreshIntent.setAction(TermuxWidgetProvider.REFRESH_WIDGET_ACTION);
|
||||
refreshIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
|
||||
refreshIntent.setData(Uri.parse(refreshIntent.toUri(Intent.URI_INTENT_SCHEME)));
|
||||
PendingIntent refreshPendingIntent = PendingIntent.getBroadcast(context, 0, refreshIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
rv.setOnClickPendingIntent(R.id.refresh_button, refreshPendingIntent);
|
||||
|
||||
// Here we setup the a pending intent template. Individuals items of a collection
|
||||
// cannot setup their own pending intents, instead, the collection as a whole can
|
||||
// setup a pending intent template, and the individual items can set a fillInIntent
|
||||
// to create unique before on an item to item basis.
|
||||
Intent toastIntent = new Intent(context, TermuxWidgetProvider.class);
|
||||
toastIntent.setAction(TermuxWidgetProvider.LIST_ITEM_CLICKED_ACTION);
|
||||
toastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
|
||||
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
|
||||
PendingIntent toastPendingIntent = PendingIntent.getBroadcast(context, 0, toastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
rv.setPendingIntentTemplate(R.id.widget_list, toastPendingIntent);
|
||||
|
||||
appWidgetManager.updateAppWidget(appWidgetId, rv);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
super.onReceive(context, intent);
|
||||
|
||||
switch (intent.getAction()) {
|
||||
case LIST_ITEM_CLICKED_ACTION:
|
||||
String clickedFilePath = intent.getStringExtra(EXTRA_CLICKED_FILE);
|
||||
File clickedFile = new File(clickedFilePath);
|
||||
if (clickedFile.isDirectory()) return;
|
||||
ensureFileReadableAndExecutable(clickedFile);
|
||||
Uri scriptUri = new Uri.Builder().scheme("com.termux.file").path(clickedFilePath).build();
|
||||
|
||||
// Note: Must match TermuxService#ACTION_EXECUTE constant:
|
||||
Intent executeIntent = new Intent(ACTION_EXECUTE, scriptUri);
|
||||
executeIntent.setClassName("com.termux", TERMUX_SERVICE);
|
||||
if (clickedFile.getParentFile().getName().equals("tasks")) {
|
||||
executeIntent.putExtra("com.termux.execute.background", true);
|
||||
// Show feedback for executed background task.
|
||||
String message = "Task executed: " + clickedFile.getName();
|
||||
Toast toast = Toast.makeText(context, message, Toast.LENGTH_SHORT);
|
||||
toast.setGravity(Gravity.CENTER, 0, 0);
|
||||
toast.show();
|
||||
}
|
||||
startTermuxService(context, executeIntent);
|
||||
break;
|
||||
case REFRESH_WIDGET_ACTION:
|
||||
int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
|
||||
AppWidgetManager.getInstance(context).notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_list);
|
||||
|
||||
Toast toast = Toast.makeText(context, R.string.scripts_reloaded, Toast.LENGTH_SHORT);
|
||||
toast.setGravity(Gravity.CENTER, 0, 0);
|
||||
toast.show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void startTermuxService(Context context, Intent executeIntent) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
// https://developer.android.com/about/versions/oreo/background.html
|
||||
context.startForegroundService(executeIntent);
|
||||
} else {
|
||||
context.startService(executeIntent);
|
||||
}
|
||||
}
|
||||
|
||||
/** Ensure readable and executable file if user forgot to do so. */
|
||||
static void ensureFileReadableAndExecutable(File file) {
|
||||
if (!file.canRead()) file.setReadable(true);
|
||||
if (!file.canExecute()) file.setExecutable(true);
|
||||
}
|
||||
}
|
||||
149
app/src/main/java/com/termux/widget/TermuxWidgetService.java
Normal file
149
app/src/main/java/com/termux/widget/TermuxWidgetService.java
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
package com.termux.widget;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.RemoteViewsService;
|
||||
|
||||
import com.termux.R;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public final class TermuxWidgetService extends RemoteViewsService {
|
||||
|
||||
@SuppressLint("SdCardPath")
|
||||
public static final File SHORTCUTS_DIR = new File("/data/data/com.termux/files/home/.shortcuts");
|
||||
|
||||
public static final class TermuxWidgetItem {
|
||||
|
||||
/** Label to display in the list. */
|
||||
public final String mLabel;
|
||||
/** The file which this item represents, sent with the {@link TermuxWidgetProvider#EXTRA_CLICKED_FILE} extra. */
|
||||
public final String mFile;
|
||||
|
||||
public TermuxWidgetItem(File file, int depth) {
|
||||
this.mLabel = (depth > 0 ? (file.getParentFile().getName() + "/") : "")
|
||||
+ file.getName().replace('-', ' ');
|
||||
this.mFile = file.getAbsolutePath();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoteViewsFactory onGetViewFactory(Intent intent) {
|
||||
return new ListRemoteViewsFactory(getApplicationContext());
|
||||
}
|
||||
|
||||
public static class ListRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
|
||||
private final List<TermuxWidgetItem> mWidgetItems = new ArrayList<>();
|
||||
private final Context mContext;
|
||||
|
||||
public ListRemoteViewsFactory(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
// In onCreate() you setup any connections / cursors to your data source. Heavy lifting,
|
||||
// for example downloading or creating content etc, should be deferred to onDataSetChanged()
|
||||
// or getViewAt(). Taking more than 20 seconds in this call will result in an ANR.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
mWidgetItems.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mWidgetItems.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoteViews getViewAt(int position) {
|
||||
// Position will always range from 0 to getCount() - 1.
|
||||
TermuxWidgetItem widgetItem = mWidgetItems.get(position);
|
||||
|
||||
// Construct remote views item based on the item xml file and set text based on position.
|
||||
RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_item);
|
||||
rv.setTextViewText(R.id.widget_item, widgetItem.mLabel);
|
||||
|
||||
// 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().putExtra(TermuxWidgetProvider.EXTRA_CLICKED_FILE, widgetItem.mFile);
|
||||
rv.setOnClickFillInIntent(R.id.widget_item_layout, fillInIntent);
|
||||
|
||||
// You can do heaving lifting in here, synchronously. For example, if you need to
|
||||
// process an image, fetch something from the network, etc., it is ok to do it here,
|
||||
// synchronously. A loading view will show up in lieu of the actual contents in the
|
||||
// interim.
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoteViews getLoadingView() {
|
||||
// You can create a custom loading view (for instance when getViewAt() is slow.) If you
|
||||
// return null here, you will get the default loading view.
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewTypeCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStableIds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressLint("SdCardPath")
|
||||
@Override
|
||||
public void onDataSetChanged() {
|
||||
// This is triggered when you call AppWidgetManager notifyAppWidgetViewDataChanged
|
||||
// on the collection view corresponding to this factory. You can do heaving lifting in
|
||||
// here, synchronously. For example, if you need to process an image, fetch something
|
||||
// from the network, etc., it is ok to do it here, synchronously. The widget will remain
|
||||
// in its current state while work is being done here, so you don't need to worry about
|
||||
// locking up the widget.
|
||||
mWidgetItems.clear();
|
||||
// Create directory if necessary so user more easily finds where to put shortcuts:
|
||||
SHORTCUTS_DIR.mkdirs();
|
||||
|
||||
addFile(SHORTCUTS_DIR, mWidgetItems, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addFile(File dir, List<TermuxWidgetItem> widgetItems, int depth) {
|
||||
if (depth > 5) return;
|
||||
|
||||
File[] files = dir.listFiles(pathname -> !pathname.getName().startsWith("."));
|
||||
|
||||
if (files == null) return;
|
||||
Arrays.sort(files, (lhs, rhs) -> {
|
||||
if (lhs.isDirectory() != rhs.isDirectory()) {
|
||||
return lhs.isDirectory() ? 1 : -1;
|
||||
}
|
||||
return lhs.getName().compareToIgnoreCase(rhs.getName());
|
||||
});
|
||||
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
addFile(file, widgetItems, depth + 1);
|
||||
} else {
|
||||
widgetItems.add(new TermuxWidgetItem(file, depth));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
11
app/src/main/res/drawable/ripple_mask.xml
Normal file
11
app/src/main/res/drawable/ripple_mask.xml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@android:color/darker_gray" >
|
||||
|
||||
<item android:id="@android:id/mask">
|
||||
<shape android:shape="rectangle" >
|
||||
<solid android:color="#FF000000" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</ripple>
|
||||
BIN
app/src/main/res/drawable/widgetpreview.png
Normal file
BIN
app/src/main/res/drawable/widgetpreview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
6
app/src/main/res/layout/shortcuts_listview.xml
Normal file
6
app/src/main/res/layout/shortcuts_listview.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/list"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent">
|
||||
</ListView>
|
||||
20
app/src/main/res/layout/widget_item.xml
Normal file
20
app/src/main/res/layout/widget_item.xml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/widget_item_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/ripple_mask"
|
||||
android:padding="10dp" >
|
||||
|
||||
<!--
|
||||
See http://stackoverflow.com/questions/16278159/why-linearlayouts-margin-is-being-ignored-if-used-as-listview-row-view
|
||||
for the extra layout wrapper.
|
||||
-->
|
||||
|
||||
<TextView
|
||||
android:id="@+id/widget_item"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@android:color/primary_text_light" />
|
||||
|
||||
</FrameLayout>
|
||||
65
app/src/main/res/layout/widget_layout.xml
Normal file
65
app/src/main/res/layout/widget_layout.xml
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="0dp" >
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="0dp"
|
||||
android:paddingTop="6dp" >
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/top_row"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="#FF000000"
|
||||
android:orientation="horizontal"
|
||||
android:padding="6dp" >
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="2.0"
|
||||
android:text="@string/termux"
|
||||
android:textColor="@android:color/primary_text_dark"
|
||||
android:textSize="18sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<ListView
|
||||
android:id="@+id/widget_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/top_row"
|
||||
android:background="@android:color/background_light"
|
||||
android:divider="@android:color/darker_gray"
|
||||
android:dividerHeight="1px" >
|
||||
</ListView>
|
||||
|
||||
<!-- Shown for empty collection due to rv.setEmptyView(R.id.widget_list, R.id.empty_view) being called: -->
|
||||
|
||||
<TextView
|
||||
android:id="@+id/empty_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/top_row"
|
||||
android:background="@android:color/white"
|
||||
android:padding="6dp"
|
||||
android:text="@string/no_shortcut_scripts"
|
||||
android:textColor="@android:color/black" />
|
||||
</RelativeLayout>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/refresh_button"
|
||||
style="?android:attr/borderlessButtonStyle"
|
||||
android:layout_width="34dp"
|
||||
android:layout_height="34dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:contentDescription="@string/refresh"
|
||||
android:tint="#99000000"
|
||||
android:src="@android:drawable/stat_notify_sync" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
|
@ -1,56 +1,63 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="application_name">Termux</string>
|
||||
<string name="shared_user_label">Termux user</string>
|
||||
<string name="new_session">New session</string>
|
||||
<string name="new_session_failsafe">Failsafe</string>
|
||||
<string name="toggle_soft_keyboard">Keyboard</string>
|
||||
<string name="reset_terminal">Reset</string>
|
||||
<string name="style_terminal">Style</string>
|
||||
<string name="share_transcript_title">Terminal transcript</string>
|
||||
<string name="help">Help</string>
|
||||
<string name="toggle_keep_screen_on">Keep screen on</string>
|
||||
<string name="application_name">Termux</string>
|
||||
<string name="shared_user_label">Termux user</string>
|
||||
<string name="new_session">New session</string>
|
||||
<string name="new_session_failsafe">Failsafe</string>
|
||||
<string name="toggle_soft_keyboard">Keyboard</string>
|
||||
<string name="reset_terminal">Reset</string>
|
||||
<string name="style_terminal">Style</string>
|
||||
<string name="share_transcript_title">Terminal transcript</string>
|
||||
<string name="help">Help</string>
|
||||
<string name="toggle_keep_screen_on">Keep screen on</string>
|
||||
|
||||
<string name="bootstrap_installer_body">Installing…</string>
|
||||
<string name="bootstrap_error_title">Unable to install</string>
|
||||
<string name="bootstrap_error_body">Termux was unable to install the bootstrap packages.\n\nCheck your network connection and try again.</string>
|
||||
<string name="bootstrap_error_abort">Abort</string>
|
||||
<string name="bootstrap_error_try_again">Try again</string>
|
||||
<string name="bootstrap_error_not_primary_user_message">Termux can only be installed on the primary user account.</string>
|
||||
<string name="bootstrap_installer_body">Installing…</string>
|
||||
<string name="bootstrap_error_title">Unable to install</string>
|
||||
<string name="bootstrap_error_body">Termux was unable to install the bootstrap packages.\n\nCheck your network connection and try again.</string>
|
||||
<string name="bootstrap_error_abort">Abort</string>
|
||||
<string name="bootstrap_error_try_again">Try again</string>
|
||||
<string name="bootstrap_error_not_primary_user_message">Termux can only be installed on the primary user account.</string>
|
||||
|
||||
<string name="max_terminals_reached_title">Max terminals reached</string>
|
||||
<string name="max_terminals_reached_message">Close down existing ones before creating new.</string>
|
||||
<string name="max_terminals_reached_title">Max terminals reached</string>
|
||||
<string name="max_terminals_reached_message">Close down existing ones before creating new.</string>
|
||||
|
||||
<string name="reset_toast_notification">Terminal reset.</string>
|
||||
<string name="reset_toast_notification">Terminal reset.</string>
|
||||
|
||||
<string name="select_url">Select URL</string>
|
||||
<string name="select_url_dialog_title">Click URL to copy or long press to open</string>
|
||||
<string name="select_all_and_share">Share transcript</string>
|
||||
<string name="select_url_no_found">No URL found in the terminal.</string>
|
||||
<string name="select_url_copied_to_clipboard">URL copied to clipboard</string>
|
||||
<string name="share_transcript_chooser_title">Send text to:</string>
|
||||
<string name="select_url">Select URL</string>
|
||||
<string name="select_url_dialog_title">Click URL to copy or long press to open</string>
|
||||
<string name="select_all_and_share">Share transcript</string>
|
||||
<string name="select_url_no_found">No URL found in the terminal.</string>
|
||||
<string name="select_url_copied_to_clipboard">URL copied to clipboard</string>
|
||||
<string name="share_transcript_chooser_title">Send text to:</string>
|
||||
|
||||
<string name="kill_process">Kill process (%d)</string>
|
||||
<string name="confirm_kill_process">Really kill this session?</string>
|
||||
<string name="kill_process">Kill process (%d)</string>
|
||||
<string name="confirm_kill_process">Really kill this session?</string>
|
||||
|
||||
<string name="session_rename_title">Set session name</string>
|
||||
<string name="session_rename_positive_button">Set</string>
|
||||
<string name="session_new_named_title">New named session</string>
|
||||
<string name="session_new_named_positive_button">Create</string>
|
||||
<string name="session_rename_title">Set session name</string>
|
||||
<string name="session_rename_positive_button">Set</string>
|
||||
<string name="session_new_named_title">New named session</string>
|
||||
<string name="session_new_named_positive_button">Create</string>
|
||||
|
||||
<string name="styling_not_installed">The Termux:Style add-on is not installed.</string>
|
||||
<string name="styling_install">Install</string>
|
||||
<string name="styling_not_installed">The Termux:Style add-on is not installed.</string>
|
||||
<string name="styling_install">Install</string>
|
||||
|
||||
<string name="notification_action_exit">Exit</string>
|
||||
<string name="notification_action_wake_lock">Acquire wakelock</string>
|
||||
<string name="notification_action_wake_unlock">Release wakelock</string>
|
||||
<string name="notification_action_exit">Exit</string>
|
||||
<string name="notification_action_wake_lock">Acquire wakelock</string>
|
||||
<string name="notification_action_wake_unlock">Release wakelock</string>
|
||||
|
||||
<string name="file_received_title">Save file in ~/downloads/</string>
|
||||
<string name="file_received_edit_button">Edit</string>
|
||||
<string name="file_received_open_folder_button">Open folder</string>
|
||||
<string name="file_received_title">Save file in ~/downloads/</string>
|
||||
<string name="file_received_edit_button">Edit</string>
|
||||
<string name="file_received_open_folder_button">Open folder</string>
|
||||
|
||||
<string name="color_prompt">Choose color</string>
|
||||
<string name="font_prompt">Choose font</string>
|
||||
<string name="writing_failed">Failed to install file:\n\n</string>
|
||||
|
||||
<string name="color_prompt">Choose color</string>
|
||||
<string name="font_prompt">Choose font</string>
|
||||
<string name="writing_failed">Failed to install file:\n\n</string>
|
||||
<string name="shortcut_widget_name">All shortcuts</string>
|
||||
<string name="single_shortcut_name">Single shortcut</string>
|
||||
<string name="no_shortcut_scripts">No files in\n$HOME/.shortcuts/</string>
|
||||
<string name="refresh">Refresh</string>
|
||||
<string name="termux">Termux</string>
|
||||
<string name="scripts_reloaded">Termux shortcuts reloaded</string>
|
||||
<string name="bad_token_message">This shortcut has become invalid - remove and add again.</string>
|
||||
</resources>
|
||||
|
|
|
|||
14
app/src/main/res/xml/termux_appwidget_info.xml
Normal file
14
app/src/main/res/xml/termux_appwidget_info.xml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
See
|
||||
http://developer.android.com/guide/practices/ui_guidelines/widget_design.html#anatomy_determining_size
|
||||
for how to determine size:
|
||||
-->
|
||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:initialLayout="@layout/widget_layout"
|
||||
android:minHeight="110dp"
|
||||
android:minWidth="110dp"
|
||||
android:previewImage="@drawable/widgetpreview"
|
||||
android:resizeMode="horizontal|vertical"
|
||||
android:updatePeriodMillis="0"
|
||||
android:widgetCategory="home_screen|keyguard" />
|
||||
Loading…
Add table
Add a link
Reference in a new issue