mirror of
https://github.com/danog/termux-api.git
synced 2024-11-26 11:54:40 +01:00
Initial push
The project was just converted from Eclipse to Android Studio, so there may be some glitches.
This commit is contained in:
parent
9e043ef50d
commit
1f2e562ac7
39
.gitignore
vendored
Normal file
39
.gitignore
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
# .gitignore from https://gist.github.com/iainconnor/8605514:
|
||||
|
||||
# Built application files
|
||||
build/
|
||||
|
||||
# Crashlytics configuations
|
||||
com_crashlytics_export_strings.xml
|
||||
|
||||
# Local configuration file (sdk path, etc)
|
||||
local.properties
|
||||
|
||||
# Gradle generated files
|
||||
.gradle/
|
||||
|
||||
# Signing files
|
||||
.signing/
|
||||
|
||||
# User-specific configurations
|
||||
.idea/libraries/
|
||||
.idea/workspace.xml
|
||||
.idea/tasks.xml
|
||||
.idea/.name
|
||||
.idea/compiler.xml
|
||||
.idea/copyright/profiles_settings.xml
|
||||
.idea/encodings.xml
|
||||
.idea/misc.xml
|
||||
.idea/modules.xml
|
||||
.idea/scopes/scope_settings.xml
|
||||
.idea/vcs.xml
|
||||
*.iml
|
||||
|
||||
# OS-specific files
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
18
.idea/gradle.xml
Normal file
18
.idea/gradle.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="1.8" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
19
README.md
19
README.md
@ -1,2 +1,17 @@
|
||||
# termux-api
|
||||
Add-on app which exposes device functionality as API to command line programs.
|
||||
Termux API
|
||||
==========
|
||||
This is an app exposing Android API to command line usage and scripts or programs.
|
||||
|
||||
The termux-api client helper binary
|
||||
===================================
|
||||
The client helper binary ([termux-api.c](https://github.com/termux/termux-packages/blob/master/packages/termux-api/termux-api.c))
|
||||
generates two linux anonymous namespace sockets, and passes their address as in:
|
||||
- /system/bin/am broadcast ${SERVICE_CLASS} --es socket_input ${INPUT_SOCKET} --es socket_output ${OUTPUT_SOCKET}
|
||||
where the sockets are used to transfer:
|
||||
- input through stdin to the helper binary are forwarded to java code
|
||||
- java code may output feedback which are forwarded to the stdout of the helper binary
|
||||
|
||||
Client scripts
|
||||
==============
|
||||
Client scripts which processes command line arguments before calling the termux-api helper binary are available:
|
||||
- [The termux-api package](https://github.com/termux/termux-packages/tree/master/packages/termux-api)
|
||||
|
33
app/build.gradle
Normal file
33
app/build.gradle
Normal file
@ -0,0 +1,33 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 21
|
||||
buildToolsVersion "22.0.1"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.termux.api"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 22
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
}
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
storeFile new File(RELEASE_STORE_FILE)
|
||||
storePassword RELEASE_STORE_PASSWORD
|
||||
keyAlias RELEASE_KEY_ALIAS
|
||||
keyPassword RELEASE_KEY_PASSWORD
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
}
|
44
app/src/main/AndroidManifest.xml
Normal file
44
app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.termux.api"
|
||||
android:sharedUserId="com.termux"
|
||||
android:versionCode="2"
|
||||
android:versionName="0.2" >
|
||||
|
||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="21"
|
||||
android:targetSdkVersion="22" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_SMS" />
|
||||
<uses-permission android:name="android.permission.SEND_SMS" />
|
||||
<!-- Resolve phone numbers to contact names: -->
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
<!-- Some of the used permissions imply uses-feature, so we need to make it optional.
|
||||
See http://developer.android.com/guide/topics/manifest/uses-feature-element.html#permissions -->
|
||||
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.location" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.microphone" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.telephony" android:required="false" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@android:style/Theme.Material.Light" >
|
||||
<receiver android:name="com.termux.api.TermuxApiReceiver"/>
|
||||
<activity android:name="com.termux.api.PhotoActivity" android:theme="@android:style/Theme.Translucent.NoTitleBar" android:exported="false"/>
|
||||
<activity android:name="com.termux.api.DialogActivity" android:theme="@android:style/Theme.Material.Light.Dialog.Alert" android:noHistory="true" android:excludeFromRecents="true" android:exported="false"/>
|
||||
<service android:name="com.termux.api.SpeechToTextAPI$SpeechToTextService"/>
|
||||
<service android:name="com.termux.api.TextToSpeechAPI$TextToSpeechService" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
111
app/src/main/java/com/termux/api/BatteryStatusAPI.java
Normal file
111
app/src/main/java/com/termux/api/BatteryStatusAPI.java
Normal file
@ -0,0 +1,111 @@
|
||||
package com.termux.api;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.BatteryManager;
|
||||
import android.util.JsonWriter;
|
||||
|
||||
import com.termux.api.util.ResultReturner;
|
||||
import com.termux.api.util.TermuxApiLogger;
|
||||
import com.termux.api.util.ResultReturner.ResultJsonWriter;
|
||||
|
||||
public class BatteryStatusAPI {
|
||||
|
||||
public static void onReceive(TermuxApiReceiver apiReceiver, final Context context, Intent intent) {
|
||||
ResultReturner.returnData(apiReceiver, intent, new ResultJsonWriter() {
|
||||
@Override
|
||||
public void writeJson(JsonWriter out) throws Exception {
|
||||
Intent batteryStatus = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||
|
||||
int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
|
||||
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
|
||||
final int batteryPercentage = (level * 100) / scale;
|
||||
|
||||
int health = batteryStatus.getIntExtra(BatteryManager.EXTRA_HEALTH, -1);
|
||||
String batteryHealth;
|
||||
switch (health) {
|
||||
case BatteryManager.BATTERY_HEALTH_COLD:
|
||||
batteryHealth = "COLD";
|
||||
break;
|
||||
case BatteryManager.BATTERY_HEALTH_DEAD:
|
||||
batteryHealth = "DEAD";
|
||||
break;
|
||||
case BatteryManager.BATTERY_HEALTH_GOOD:
|
||||
batteryHealth = "GOOD";
|
||||
break;
|
||||
case BatteryManager.BATTERY_HEALTH_OVERHEAT:
|
||||
batteryHealth = "OVERHEAD";
|
||||
break;
|
||||
case BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE:
|
||||
batteryHealth = "OVER_VOLTAGE";
|
||||
break;
|
||||
case BatteryManager.BATTERY_HEALTH_UNKNOWN:
|
||||
batteryHealth = "UNKNOWN";
|
||||
break;
|
||||
case BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE:
|
||||
batteryHealth = "UNSPECIFIED_FAILURE";
|
||||
break;
|
||||
default:
|
||||
batteryHealth = Integer.toString(health);
|
||||
}
|
||||
|
||||
// BatteryManager.EXTRA_PLUGGED: "Extra for ACTION_BATTERY_CHANGED: integer indicating whether the
|
||||
// device is plugged in to a power source; 0 means it is on battery, other constants are different types
|
||||
// of power sources."
|
||||
int pluggedInt = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
|
||||
String batteryPlugged;
|
||||
switch (pluggedInt) {
|
||||
case 0:
|
||||
batteryPlugged = "UNPLUGGED";
|
||||
break;
|
||||
case BatteryManager.BATTERY_PLUGGED_AC:
|
||||
batteryPlugged = "PLUGGED_AC";
|
||||
break;
|
||||
case BatteryManager.BATTERY_PLUGGED_USB:
|
||||
batteryPlugged = "PLUGGED_USB";
|
||||
break;
|
||||
case BatteryManager.BATTERY_PLUGGED_WIRELESS:
|
||||
batteryPlugged = "PLUGGED_WIRELESS";
|
||||
break;
|
||||
default:
|
||||
batteryPlugged = "PLUGGED_" + pluggedInt;
|
||||
}
|
||||
|
||||
double batteryTemperature = batteryStatus.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1) / 10.f;
|
||||
|
||||
String batteryStatusString;
|
||||
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
|
||||
switch (status) {
|
||||
case BatteryManager.BATTERY_STATUS_CHARGING:
|
||||
batteryStatusString = "CHARGING";
|
||||
break;
|
||||
case BatteryManager.BATTERY_STATUS_DISCHARGING:
|
||||
batteryStatusString = "DISCHARGING";
|
||||
break;
|
||||
case BatteryManager.BATTERY_STATUS_FULL:
|
||||
batteryStatusString = "FULL";
|
||||
break;
|
||||
case BatteryManager.BATTERY_STATUS_NOT_CHARGING:
|
||||
batteryStatusString = "NOT_CHARGING";
|
||||
break;
|
||||
case BatteryManager.BATTERY_STATUS_UNKNOWN:
|
||||
batteryStatusString = "UNKNOWN";
|
||||
break;
|
||||
default:
|
||||
TermuxApiLogger.error("Invalid BatteryManager.EXTRA_STATUS value: " + status);
|
||||
batteryStatusString = "UNKNOWN";
|
||||
}
|
||||
|
||||
out.beginObject();
|
||||
out.name("health").value(batteryHealth);
|
||||
out.name("percentage").value(batteryPercentage);
|
||||
out.name("plugged").value(batteryPlugged);
|
||||
out.name("status").value(batteryStatusString);
|
||||
out.name("temperature").value(batteryTemperature);
|
||||
out.endObject();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
156
app/src/main/java/com/termux/api/CameraInfoAPI.java
Normal file
156
app/src/main/java/com/termux/api/CameraInfoAPI.java
Normal file
@ -0,0 +1,156 @@
|
||||
package com.termux.api;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.hardware.camera2.CameraCharacteristics;
|
||||
import android.hardware.camera2.CameraManager;
|
||||
import android.hardware.camera2.CameraMetadata;
|
||||
import android.util.JsonWriter;
|
||||
import android.util.SizeF;
|
||||
|
||||
import com.termux.api.util.ResultReturner;
|
||||
import com.termux.api.util.ResultReturner.ResultJsonWriter;
|
||||
|
||||
public class CameraInfoAPI {
|
||||
|
||||
static void onReceive(TermuxApiReceiver apiReceiver, final Context context, Intent intent) {
|
||||
ResultReturner.returnData(apiReceiver, intent, new ResultJsonWriter() {
|
||||
@Override
|
||||
public void writeJson(JsonWriter out) throws Exception {
|
||||
final CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
|
||||
|
||||
out.beginArray();
|
||||
for (String cameraId : manager.getCameraIdList()) {
|
||||
out.beginObject();
|
||||
out.name("id").value(cameraId);
|
||||
|
||||
CameraCharacteristics camera = manager.getCameraCharacteristics(cameraId);
|
||||
|
||||
out.name("facing");
|
||||
int lensFacing = camera.get(CameraCharacteristics.LENS_FACING);
|
||||
switch (lensFacing) {
|
||||
case CameraMetadata.LENS_FACING_FRONT:
|
||||
out.value("front");
|
||||
break;
|
||||
case CameraMetadata.LENS_FACING_BACK:
|
||||
out.value("back");
|
||||
break;
|
||||
default:
|
||||
out.value(lensFacing);
|
||||
}
|
||||
|
||||
out.name("focal_lengths").beginArray();
|
||||
for (float f : camera.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS))
|
||||
out.value(f);
|
||||
out.endArray();
|
||||
|
||||
out.name("auto_exposure_modes").beginArray();
|
||||
int[] flashModeValues = camera.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES);
|
||||
for (int flashMode : flashModeValues) {
|
||||
switch (flashMode) {
|
||||
case CameraMetadata.CONTROL_AE_MODE_OFF:
|
||||
out.value("CONTROL_AE_MODE_OFF");
|
||||
break;
|
||||
case CameraMetadata.CONTROL_AE_MODE_ON:
|
||||
out.value("CONTROL_AE_MODE_ON");
|
||||
break;
|
||||
case CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH:
|
||||
out.value("CONTROL_AE_MODE_ON_ALWAYS_FLASH");
|
||||
break;
|
||||
case CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH:
|
||||
out.value("CONTROL_AE_MODE_ON_AUTO_FLASH");
|
||||
break;
|
||||
case CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE:
|
||||
out.value("CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE");
|
||||
break;
|
||||
default:
|
||||
out.value(flashMode);
|
||||
}
|
||||
}
|
||||
out.endArray();
|
||||
|
||||
// out.write(" Focus modes: ");
|
||||
// boolean first = true;
|
||||
// // for (String mode : params.getSupportedFocusModes()) {
|
||||
// // if (first) {
|
||||
// // first = false;
|
||||
// // } else {
|
||||
// // out.write("/");
|
||||
// // }
|
||||
// // out.write(mode);
|
||||
// // }
|
||||
// out.write("\n");
|
||||
|
||||
SizeF physicalSize = camera.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE);
|
||||
out.name("physical_size").beginObject().name("width").value(physicalSize.getWidth()).name("height")
|
||||
.value(physicalSize.getHeight()).endObject();
|
||||
|
||||
out.name("capabilities").beginArray();
|
||||
for (int capability : camera.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)) {
|
||||
switch (capability) {
|
||||
case CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR:
|
||||
out.value("manual_sensor");
|
||||
break;
|
||||
case CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING:
|
||||
out.value("manual_post_processing");
|
||||
break;
|
||||
case CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE:
|
||||
out.value("backward_compatible");
|
||||
break;
|
||||
case CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_RAW:
|
||||
out.value("raw");
|
||||
break;
|
||||
default:
|
||||
out.value(capability);
|
||||
}
|
||||
}
|
||||
out.endArray();
|
||||
// out.write(" Picture formats: ");
|
||||
// first = true;
|
||||
// for (int i : params.getSupportedPictureFormats()) {
|
||||
// if (first) {
|
||||
// first = false;
|
||||
// } else {
|
||||
// out.write("/");
|
||||
// }
|
||||
// switch (i) {
|
||||
// case ImageFormat.JPEG:
|
||||
// out.write("JPEG");
|
||||
// break;
|
||||
// case ImageFormat.NV16:
|
||||
// out.write("NV16");
|
||||
// break;
|
||||
// case ImageFormat.NV21:
|
||||
// out.write("NV21");
|
||||
// break;
|
||||
// case ImageFormat.RGB_565:
|
||||
// out.write("RGB_565");
|
||||
// break;
|
||||
// case ImageFormat.YUV_420_888:
|
||||
// out.write("YUV_420_888");
|
||||
// break;
|
||||
// case ImageFormat.YUY2:
|
||||
// out.write("YUY2");
|
||||
// break;
|
||||
// case ImageFormat.YV12:
|
||||
// out.write("YV12");
|
||||
// break;
|
||||
// default:
|
||||
// out.write(i + " (no matching ImageFormat constant)");
|
||||
// }
|
||||
// }
|
||||
// out.write("\n");
|
||||
|
||||
// out.write(" Sizes:\n");
|
||||
|
||||
// for (Size size : params.getSupportedPictureSizes()) {
|
||||
// out.write(" [" + count + "]: " + size.width + "x" + size.height + "\n");
|
||||
// count++;
|
||||
// }
|
||||
out.endObject();
|
||||
}
|
||||
out.endArray();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
52
app/src/main/java/com/termux/api/ClipboardAPI.java
Normal file
52
app/src/main/java/com/termux/api/ClipboardAPI.java
Normal file
@ -0,0 +1,52 @@
|
||||
package com.termux.api;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipData.Item;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.termux.api.util.ResultReturner;
|
||||
import com.termux.api.util.ResultReturner.ResultWriter;
|
||||
|
||||
public class ClipboardAPI {
|
||||
|
||||
static void onReceive(TermuxApiReceiver apiReceiver, final Context context, Intent intent) {
|
||||
final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
final ClipData clipData = clipboard.getPrimaryClip();
|
||||
final String newClipText = intent.getStringExtra("text");
|
||||
if (newClipText != null) {
|
||||
// Set clip.
|
||||
clipboard.setPrimaryClip(ClipData.newPlainText("", newClipText));
|
||||
}
|
||||
|
||||
ResultReturner.returnData(apiReceiver, intent, new ResultWriter() {
|
||||
@Override
|
||||
public void writeResult(PrintWriter out) {
|
||||
if (newClipText == null) {
|
||||
// Get clip.
|
||||
if (clipData == null) {
|
||||
out.println();
|
||||
} else {
|
||||
int itemCount = clipData.getItemCount();
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
Item item = clipData.getItemAt(i);
|
||||
CharSequence text = item.coerceToText(context);
|
||||
if (text != null) {
|
||||
out.print(text);
|
||||
if (i + 1 != itemCount) {
|
||||
out.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Set clip - already done in main thread.
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
61
app/src/main/java/com/termux/api/ContactListAPI.java
Normal file
61
app/src/main/java/com/termux/api/ContactListAPI.java
Normal file
@ -0,0 +1,61 @@
|
||||
package com.termux.api;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.provider.BaseColumns;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Phone;
|
||||
import android.util.JsonWriter;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.termux.api.util.ResultReturner;
|
||||
import com.termux.api.util.ResultReturner.ResultJsonWriter;
|
||||
|
||||
public class ContactListAPI {
|
||||
|
||||
static void onReceive(TermuxApiReceiver apiReceiver, final Context context, Intent intent) {
|
||||
ResultReturner.returnData(apiReceiver, intent, new ResultJsonWriter() {
|
||||
@Override
|
||||
public void writeJson(JsonWriter out) throws Exception {
|
||||
listContacts(context, out);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void listContacts(Context context, JsonWriter out) throws Exception {
|
||||
ContentResolver cr = context.getContentResolver();
|
||||
|
||||
SparseArray<String> contactIdToNumberMap = new SparseArray<>();
|
||||
String[] projection = { Phone.NUMBER, Phone.CONTACT_ID };
|
||||
String selection = Phone.CONTACT_ID + " IS NOT NULL AND " + Phone.NUMBER + " IS NOT NULL";
|
||||
try (Cursor phones = cr.query(Phone.CONTENT_URI, projection, selection, null, null)) {
|
||||
int phoneNumberIdx = phones.getColumnIndexOrThrow(Phone.NUMBER);
|
||||
int phoneContactIdIdx = phones.getColumnIndexOrThrow(Phone.CONTACT_ID);
|
||||
while (phones.moveToNext()) {
|
||||
String number = phones.getString(phoneNumberIdx);
|
||||
int contactId = phones.getInt(phoneContactIdIdx);
|
||||
// int type = phones.getInt(phones.getColumnIndex(Phone.TYPE));
|
||||
contactIdToNumberMap.put(contactId, number);
|
||||
}
|
||||
}
|
||||
|
||||
out.beginArray();
|
||||
try (Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, ContactsContract.Contacts.DISPLAY_NAME)) {
|
||||
int contactDisplayNameIdx = cursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME);
|
||||
int contactIdIdx = cursor.getColumnIndex(BaseColumns._ID);
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
int contactId = cursor.getInt(contactIdIdx);
|
||||
String number = contactIdToNumberMap.get(contactId);
|
||||
if (number != null) {
|
||||
String contactName = cursor.getString(contactDisplayNameIdx);
|
||||
out.beginObject().name("name").value(contactName).name("number").value(number).endObject();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
out.endArray();
|
||||
}
|
||||
}
|
||||
}
|
77
app/src/main/java/com/termux/api/DialogActivity.java
Normal file
77
app/src/main/java/com/termux/api/DialogActivity.java
Normal file
@ -0,0 +1,77 @@
|
||||
package com.termux.api;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.Window;
|
||||
import android.widget.EditText;
|
||||
|
||||
import com.termux.api.util.ResultReturner;
|
||||
import com.termux.api.util.ResultReturner.ResultWriter;
|
||||
|
||||
public class DialogActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
setContentView(R.layout.dialog_textarea_input);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
setIntent(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
String inputHint = getIntent().getStringExtra("input_hint");
|
||||
if (inputHint != null) {
|
||||
((EditText) findViewById(R.id.text_input)).setHint(inputHint);
|
||||
}
|
||||
|
||||
findViewById(R.id.cancel_button).setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ResultReturner.returnData(DialogActivity.this, getIntent(), new ResultWriter() {
|
||||
@Override
|
||||
public void writeResult(PrintWriter out) throws Exception {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
findViewById(R.id.ok_button).setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ResultReturner.returnData(DialogActivity.this, getIntent(), new ResultWriter() {
|
||||
@Override
|
||||
public void writeResult(PrintWriter out) throws Exception {
|
||||
String text = ((EditText) findViewById(R.id.text_input)).getText().toString();
|
||||
out.println(text.trim());
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
44
app/src/main/java/com/termux/api/DownloadAPI.java
Normal file
44
app/src/main/java/com/termux/api/DownloadAPI.java
Normal file
@ -0,0 +1,44 @@
|
||||
package com.termux.api;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import android.app.DownloadManager;
|
||||
import android.app.DownloadManager.Request;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.termux.api.util.ResultReturner;
|
||||
import com.termux.api.util.ResultReturner.ResultWriter;
|
||||
|
||||
public class DownloadAPI {
|
||||
|
||||
static void onReceive(TermuxApiReceiver apiReceiver, final Context context, final Intent intent) {
|
||||
ResultReturner.returnData(apiReceiver, intent, new ResultWriter() {
|
||||
@Override
|
||||
public void writeResult(PrintWriter out) throws Exception {
|
||||
final Uri downloadUri = intent.getData();
|
||||
if (downloadUri == null) {
|
||||
out.println("No download URI specified");
|
||||
return;
|
||||
}
|
||||
|
||||
String title = intent.getStringExtra("title");
|
||||
String description = intent.getStringExtra("description");
|
||||
|
||||
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
Request req = new Request(downloadUri);
|
||||
req.setNotificationVisibility(Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
||||
req.setVisibleInDownloadsUi(true);
|
||||
|
||||
if (title != null)
|
||||
req.setTitle(title);
|
||||
|
||||
if (description != null)
|
||||
req.setDescription(description);
|
||||
|
||||
manager.enqueue(req);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
155
app/src/main/java/com/termux/api/LocationAPI.java
Normal file
155
app/src/main/java/com/termux/api/LocationAPI.java
Normal file
@ -0,0 +1,155 @@
|
||||
package com.termux.api;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.location.Location;
|
||||
import android.location.LocationListener;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Looper;
|
||||
import android.os.SystemClock;
|
||||
import android.util.JsonWriter;
|
||||
import android.util.Log;
|
||||
|
||||
import com.termux.api.util.ResultReturner;
|
||||
import com.termux.api.util.TermuxApiLogger;
|
||||
import com.termux.api.util.ResultReturner.ResultJsonWriter;
|
||||
|
||||
public class LocationAPI {
|
||||
|
||||
private static final String REQUEST_LAST_KNOWN = "last";
|
||||
private static final String REQUEST_ONCE = "once";
|
||||
private static final String REQUEST_UPDATES = "updates";
|
||||
|
||||
static void onReceive(TermuxApiReceiver apiReceiver, final Context context, final Intent intent) {
|
||||
ResultReturner.returnData(apiReceiver, intent, new ResultJsonWriter() {
|
||||
@Override
|
||||
public void writeJson(final JsonWriter out) throws Exception {
|
||||
LocationManager manager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
||||
|
||||
String provider = intent.getStringExtra("provider");
|
||||
if (provider == null)
|
||||
provider = LocationManager.GPS_PROVIDER;
|
||||
if (!(provider.equals(LocationManager.GPS_PROVIDER) || provider.equals(LocationManager.NETWORK_PROVIDER) || provider
|
||||
.equals(LocationManager.PASSIVE_PROVIDER))) {
|
||||
out.beginObject()
|
||||
.name("API_ERROR")
|
||||
.value("Unsupported provider '" + provider + "' - only '" + LocationManager.GPS_PROVIDER + "', '"
|
||||
+ LocationManager.NETWORK_PROVIDER + "' and '" + LocationManager.PASSIVE_PROVIDER + "' supported").endObject();
|
||||
return;
|
||||
}
|
||||
|
||||
String request = intent.getStringExtra("request");
|
||||
if (request == null)
|
||||
request = REQUEST_ONCE;
|
||||
switch (request) {
|
||||
case REQUEST_LAST_KNOWN:
|
||||
Location lastKnownLocation = manager.getLastKnownLocation(provider);
|
||||
locationToJson(lastKnownLocation, out);
|
||||
break;
|
||||
case REQUEST_ONCE:
|
||||
Looper.prepare();
|
||||
manager.requestSingleUpdate(provider, new LocationListener() {
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(String changedProvider, int status, Bundle extras) {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderEnabled(String changedProvider) {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderDisabled(String changedProvider) {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
try {
|
||||
locationToJson(location, out);
|
||||
} catch (IOException e) {
|
||||
TermuxApiLogger.error("Writing json", e);
|
||||
} finally {
|
||||
Looper.myLooper().quit();
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
Looper.loop();
|
||||
break;
|
||||
case REQUEST_UPDATES:
|
||||
Looper.prepare();
|
||||
manager.requestLocationUpdates(provider, 5000, 50.f, new LocationListener() {
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(String changedProvider, int status, Bundle extras) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderEnabled(String changedProvider) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderDisabled(String changedProvider) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
try {
|
||||
locationToJson(location, out);
|
||||
out.flush();
|
||||
} catch (IOException e) {
|
||||
TermuxApiLogger.error("Writing json", e);
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
final Looper looper = Looper.myLooper();
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(30 * 1000);
|
||||
} catch (InterruptedException e) {
|
||||
Log.e("termux", "INTER", e);
|
||||
}
|
||||
looper.quit();
|
||||
}
|
||||
}.start();
|
||||
Looper.loop();
|
||||
break;
|
||||
default:
|
||||
out.beginObject()
|
||||
.name("API_ERROR")
|
||||
.value("Unsupported request '" + request + "' - only '" + REQUEST_LAST_KNOWN + "', '" + REQUEST_ONCE + "' and '" + REQUEST_UPDATES
|
||||
+ "' supported").endObject();
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void locationToJson(Location lastKnownLocation, JsonWriter out) throws IOException {
|
||||
if (lastKnownLocation == null) {
|
||||
out.beginObject().name("API_ERROR").value("Failed to get location").endObject();
|
||||
return;
|
||||
}
|
||||
out.beginObject();
|
||||
out.name("latitude").value(lastKnownLocation.getLatitude());
|
||||
out.name("longitude").value(lastKnownLocation.getLongitude());
|
||||
out.name("altitude").value(lastKnownLocation.getAltitude());
|
||||
out.name("accuracy").value(lastKnownLocation.getAccuracy());
|
||||
out.name("bearing").value(lastKnownLocation.getBearing());
|
||||
out.name("speed").value(lastKnownLocation.getSpeed());
|
||||
long elapsedMs = (SystemClock.elapsedRealtimeNanos() - lastKnownLocation.getElapsedRealtimeNanos()) / 1000000;
|
||||
out.name("elapsedMs").value(elapsedMs);
|
||||
out.name("provider").value(lastKnownLocation.getProvider());
|
||||
out.endObject();
|
||||
}
|
||||
}
|
43
app/src/main/java/com/termux/api/NotificationAPI.java
Normal file
43
app/src/main/java/com/termux/api/NotificationAPI.java
Normal file
@ -0,0 +1,43 @@
|
||||
package com.termux.api;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.termux.api.util.ResultReturner;
|
||||
|
||||
/** Shows a notification. Driven by the termux-show-notification script. */
|
||||
public class NotificationAPI {
|
||||
|
||||
static void onReceive(TermuxApiReceiver apiReceiver, final Context context, Intent intent) {
|
||||
String content = intent.getStringExtra("content");
|
||||
String notificationId = intent.getStringExtra("id");
|
||||
if (notificationId == null) {
|
||||
notificationId = UUID.randomUUID().toString();
|
||||
}
|
||||
String title = intent.getStringExtra("title");
|
||||
|
||||
String url = intent.getStringExtra("url");
|
||||
PendingIntent pendingIntent = null;
|
||||
if (url != null) {
|
||||
Intent urlIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
pendingIntent = PendingIntent.getActivity(context, 0, urlIntent, 0);
|
||||
}
|
||||
|
||||
Notification.Builder notification = new Notification.Builder(context).setSmallIcon(android.R.drawable.ic_popup_reminder).setColor(0xFF000000)
|
||||
.setContentTitle(title).setContentText(content);
|
||||
if (pendingIntent != null)
|
||||
notification.setContentIntent(pendingIntent);
|
||||
|
||||
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
manager.notify(notificationId, 0, notification.build());
|
||||
|
||||
ResultReturner.noteDone(apiReceiver, intent);
|
||||
}
|
||||
|
||||
}
|
190
app/src/main/java/com/termux/api/PhotoActivity.java
Normal file
190
app/src/main/java/com/termux/api/PhotoActivity.java
Normal file
@ -0,0 +1,190 @@
|
||||
package com.termux.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.ImageFormat;
|
||||
import android.hardware.camera2.CameraCaptureSession;
|
||||
import android.hardware.camera2.CameraCharacteristics;
|
||||
import android.hardware.camera2.CameraDevice;
|
||||
import android.hardware.camera2.CameraManager;
|
||||
import android.hardware.camera2.CameraMetadata;
|
||||
import android.hardware.camera2.CaptureRequest;
|
||||
import android.hardware.camera2.TotalCaptureResult;
|
||||
import android.hardware.camera2.params.StreamConfigurationMap;
|
||||
import android.media.Image;
|
||||
import android.media.ImageReader;
|
||||
import android.os.Bundle;
|
||||
import android.util.Size;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceView;
|
||||
|
||||
import com.termux.api.util.TermuxApiLogger;
|
||||
|
||||
public class PhotoActivity extends Activity {
|
||||
|
||||
private SurfaceView surfaceView;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
surfaceView = new SurfaceView(this);
|
||||
setContentView(surfaceView);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
takePictureNoPreview();
|
||||
}
|
||||
|
||||
static class CompareSizesByArea implements Comparator<Size> {
|
||||
@Override
|
||||
public int compare(Size lhs, Size rhs) {
|
||||
// We cast here to ensure the multiplications won't overflow
|
||||
return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
setIntent(intent);
|
||||
}
|
||||
|
||||
public void takePictureNoPreview() {
|
||||
try {
|
||||
String filePath = getIntent().getStringExtra("file");
|
||||
final File tmpFile = new File(filePath);
|
||||
String cameraId = getIntent().getStringExtra("camera");
|
||||
if (cameraId == null) {
|
||||
cameraId = "0";
|
||||
}
|
||||
|
||||
final CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
|
||||
|
||||
TermuxApiLogger.info("cameraId=" + cameraId + ", filePath=" + tmpFile.getAbsolutePath());
|
||||
|
||||
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
|
||||
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
|
||||
|
||||
// For still image captures, we use the largest available size.
|
||||
Size largest = Collections.max(Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),
|
||||
new CompareSizesByArea());
|
||||
final ImageReader mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(),
|
||||
ImageFormat.JPEG, 2);
|
||||
mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
|
||||
@Override
|
||||
public void onImageAvailable(final ImageReader reader) {
|
||||
TermuxApiLogger.info("onImageAvailable() from mImageReader");
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try (final Image mImage = reader.acquireNextImage()) {
|
||||
ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
|
||||
byte[] bytes = new byte[buffer.remaining()];
|
||||
buffer.get(bytes);
|
||||
try (FileOutputStream output = new FileOutputStream(tmpFile)) {
|
||||
output.write(bytes);
|
||||
} catch (Exception e) {
|
||||
TermuxApiLogger.error("Error writing image", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
}, null);
|
||||
|
||||
manager.openCamera(cameraId, new CameraDevice.StateCallback() {
|
||||
@Override
|
||||
public void onOpened(final CameraDevice camera) {
|
||||
TermuxApiLogger.info("onOpened() from camera");
|
||||
try {
|
||||
final CaptureRequest.Builder captureBuilder = camera
|
||||
.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
|
||||
captureBuilder.addTarget(mImageReader.getSurface());
|
||||
|
||||
// Use the same AE and AF modes as the preview.
|
||||
captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,
|
||||
CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
|
||||
captureBuilder
|
||||
.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH);
|
||||
|
||||
// Orientation jpeg fix, from the Camera2BasicFragment example:
|
||||
int cameraJpegOrientation;
|
||||
switch (getWindowManager().getDefaultDisplay().getRotation()) {
|
||||
case Surface.ROTATION_0:
|
||||
cameraJpegOrientation = 90;
|
||||
break;
|
||||
case Surface.ROTATION_90:
|
||||
cameraJpegOrientation = 0;
|
||||
break;
|
||||
case Surface.ROTATION_180:
|
||||
cameraJpegOrientation = 270;
|
||||
break;
|
||||
case Surface.ROTATION_270:
|
||||
cameraJpegOrientation = 180;
|
||||
break;
|
||||
default:
|
||||
cameraJpegOrientation = 0;
|
||||
}
|
||||
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, cameraJpegOrientation);
|
||||
|
||||
List<Surface> outputSurfaces = Collections.singletonList(mImageReader.getSurface());
|
||||
camera.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {
|
||||
@Override
|
||||
public void onConfigured(CameraCaptureSession session) {
|
||||
TermuxApiLogger.info("onConfigured() from camera");
|
||||
try {
|
||||
session.stopRepeating();
|
||||
session.capture(captureBuilder.build(), new CameraCaptureSession.CaptureCallback() {
|
||||
@Override
|
||||
public void onCaptureCompleted(CameraCaptureSession completedSession,
|
||||
CaptureRequest request, TotalCaptureResult result) {
|
||||
TermuxApiLogger.info("onCaptureCompleted()");
|
||||
camera.close();
|
||||
finish();
|
||||
}
|
||||
}, null);
|
||||
} catch (Exception e) {
|
||||
TermuxApiLogger.error("onConfigured() error", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigureFailed(CameraCaptureSession session) {
|
||||
TermuxApiLogger.error("onConfigureFailed() error");
|
||||
}
|
||||
}, null);
|
||||
} catch (Exception e) {
|
||||
TermuxApiLogger.error("in onOpened", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnected(CameraDevice camera) {
|
||||
TermuxApiLogger.info("onDisconnected() from camera");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(CameraDevice camera, int error) {
|
||||
TermuxApiLogger.error("Failed opening camera: " + error);
|
||||
setResult(1);
|
||||
finish();
|
||||
}
|
||||
|
||||
}, null);
|
||||
} catch (Exception e) {
|
||||
TermuxApiLogger.error("Error getting camera", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
105
app/src/main/java/com/termux/api/SmsInboxAPI.java
Normal file
105
app/src/main/java/com/termux/api/SmsInboxAPI.java
Normal file
@ -0,0 +1,105 @@
|
||||
package com.termux.api;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.ContactsContract.PhoneLookup;
|
||||
import android.provider.Telephony;
|
||||
import android.provider.Telephony.TextBasedSmsColumns;
|
||||
import android.util.JsonWriter;
|
||||
|
||||
import com.termux.api.util.ResultReturner;
|
||||
import com.termux.api.util.ResultReturner.ResultJsonWriter;
|
||||
|
||||
/**
|
||||
* Call with
|
||||
*
|
||||
* <pre>
|
||||
* $ am broadcast --user 0 -n net.aterm.extras/.SmsLister
|
||||
*
|
||||
* Broadcasting: Intent { cmp=net.aterm.extras/.SmsLister }
|
||||
* Broadcast completed: result=13, data="http://fornwall.net"
|
||||
* </pre>
|
||||
*/
|
||||
public class SmsInboxAPI {
|
||||
|
||||
private static final String[] DISPLAY_NAME_PROJECTION = { PhoneLookup.DISPLAY_NAME };
|
||||
|
||||
static void onReceive(TermuxApiReceiver apiReceiver, final Context context, Intent intent) {
|
||||
final int offset = intent.getIntExtra("offset", 0);
|
||||
final int limit = intent.getIntExtra("limit", 50);
|
||||
|
||||
ResultReturner.returnData(apiReceiver, intent, new ResultJsonWriter() {
|
||||
@Override
|
||||
public void writeJson(JsonWriter out) throws Exception {
|
||||
getAllSms(context, out, offset, limit);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
public static void getAllSms(Context context, JsonWriter out, int offset, int limit) throws IOException {
|
||||
ContentResolver cr = context.getContentResolver();
|
||||
String sortOrder = "date DESC LIMIT + " + limit + " OFFSET " + offset;
|
||||
try (Cursor c = cr.query(Telephony.Sms.Inbox.CONTENT_URI, null, null, null, sortOrder)) {
|
||||
|
||||
c.moveToLast();
|
||||
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd kk:mm");
|
||||
Map<String, String> nameCache = new HashMap<>();
|
||||
|
||||
out.beginArray();
|
||||
for (int i = 0, count = c.getCount(); i < count; i++) {
|
||||
// String smsId = c.getString(c.getColumnIndexOrThrow(Telephony.Sms.Inbox._ID));
|
||||
String smsAddress = c.getString(c.getColumnIndexOrThrow(TextBasedSmsColumns.ADDRESS));
|
||||
String smsBody = c.getString(c.getColumnIndexOrThrow(TextBasedSmsColumns.BODY));
|
||||
boolean read = (c.getInt(c.getColumnIndex(TextBasedSmsColumns.READ)) != 0);
|
||||
long smsReceivedDate = c.getLong(c.getColumnIndexOrThrow(TextBasedSmsColumns.DATE));
|
||||
// long smsSentDate = c.getLong(c.getColumnIndexOrThrow(TextBasedSmsColumns.DATE_SENT));
|
||||
|
||||
String smsSenderName = getContactNameFromNumber(nameCache, context, smsAddress);
|
||||
|
||||
out.beginObject();
|
||||
out.name("read").value(read);
|
||||
|
||||
if (smsSenderName != null) {
|
||||
out.name("sender").value(smsSenderName);
|
||||
}
|
||||
out.name("number").value(smsAddress);
|
||||
|
||||
out.name("received").value(dateFormat.format(new Date(smsReceivedDate)));
|
||||
// if (Math.abs(smsReceivedDate - smsSentDate) >= 60000) {
|
||||
// out.write(" (sent ");
|
||||
// out.write(dateFormat.format(new Date(smsSentDate)));
|
||||
// out.write(")");
|
||||
// }
|
||||
out.name("body").value(smsBody);
|
||||
|
||||
c.moveToPrevious();
|
||||
out.endObject();
|
||||
}
|
||||
out.endArray();
|
||||
}
|
||||
}
|
||||
|
||||
private static String getContactNameFromNumber(Map<String, String> cache, Context context, String number) {
|
||||
if (cache.containsKey(number))
|
||||
return cache.get(number);
|
||||
Uri contactUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
|
||||
try (Cursor c = context.getContentResolver().query(contactUri, DISPLAY_NAME_PROJECTION, null, null, null)) {
|
||||
String name = c.moveToFirst() ? c.getString(c.getColumnIndex(PhoneLookup.DISPLAY_NAME)) : null;
|
||||
cache.put(number, name);
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
28
app/src/main/java/com/termux/api/SmsSendAPI.java
Normal file
28
app/src/main/java/com/termux/api/SmsSendAPI.java
Normal file
@ -0,0 +1,28 @@
|
||||
package com.termux.api;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.telephony.SmsManager;
|
||||
|
||||
import com.termux.api.util.ResultReturner;
|
||||
import com.termux.api.util.TermuxApiLogger;
|
||||
|
||||
public class SmsSendAPI {
|
||||
|
||||
static void onReceive(TermuxApiReceiver apiReceiver, final Intent intent) {
|
||||
ResultReturner.returnData(apiReceiver, intent, new ResultReturner.WithStringInput() {
|
||||
@Override
|
||||
public void writeResult(PrintWriter out) throws Exception {
|
||||
final SmsManager smsManager = SmsManager.getDefault();
|
||||
String recipientExtra = intent.getStringExtra("recipient");
|
||||
if (recipientExtra == null) {
|
||||
TermuxApiLogger.error("No 'recipient' extra");
|
||||
} else {
|
||||
smsManager.sendTextMessage(recipientExtra, null, inputString, null, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
208
app/src/main/java/com/termux/api/SpeechToTextAPI.java
Normal file
208
app/src/main/java/com/termux/api/SpeechToTextAPI.java
Normal file
@ -0,0 +1,208 @@
|
||||
package com.termux.api;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.IntentService;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.speech.RecognitionListener;
|
||||
import android.speech.RecognizerIntent;
|
||||
import android.speech.SpeechRecognizer;
|
||||
|
||||
import com.termux.api.util.ResultReturner;
|
||||
import com.termux.api.util.TermuxApiLogger;
|
||||
|
||||
public class SpeechToTextAPI {
|
||||
|
||||
public static class SpeechToTextService extends IntentService {
|
||||
|
||||
private static final String STOP_ELEMENT = "";
|
||||
|
||||
public SpeechToTextService() {
|
||||
this(SpeechToTextService.class.getSimpleName());
|
||||
}
|
||||
|
||||
public SpeechToTextService(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
protected SpeechRecognizer mSpeechRecognizer;
|
||||
final LinkedBlockingQueue<String> queueu = new LinkedBlockingQueue<>();
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
final Context context = this;
|
||||
|
||||
mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(this);
|
||||
|
||||
mSpeechRecognizer.setRecognitionListener(new RecognitionListener() {
|
||||
@Override
|
||||
public void onRmsChanged(float rmsdB) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResults(Bundle results) {
|
||||
List<String> recognitions = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
|
||||
TermuxApiLogger.error("RecognitionListener#onResults(" + recognitions + ")");
|
||||
queueu.addAll(recognitions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadyForSpeech(Bundle params) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPartialResults(Bundle partialResults) {
|
||||
// Do nothing.
|
||||
List<String> strings = partialResults.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
|
||||
TermuxApiLogger.error("RecognitionListener#onPartialResults(" + strings + ")");
|
||||
queueu.addAll(strings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(int eventType, Bundle params) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int error) {
|
||||
String description;
|
||||
switch (error) {
|
||||
case SpeechRecognizer.ERROR_CLIENT:
|
||||
description = "ERROR_CLIENT";
|
||||
break;
|
||||
case SpeechRecognizer.ERROR_SPEECH_TIMEOUT:
|
||||
description = "ERROR_SPEECH_TIMEOUT";
|
||||
break;
|
||||
case SpeechRecognizer.ERROR_RECOGNIZER_BUSY:
|
||||
description = "ERROR_RECOGNIZER_BUSY";
|
||||
break;
|
||||
case SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS:
|
||||
description = "ERROR_INSUFFICIENT_PERMISSIONS";
|
||||
break;
|
||||
default:
|
||||
description = Integer.toString(error);
|
||||
}
|
||||
TermuxApiLogger.error("RecognitionListener#onError(" + description + ")");
|
||||
queueu.add(STOP_ELEMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEndOfSpeech() {
|
||||
TermuxApiLogger.error("RecognitionListener#onEndOfSpeech()");
|
||||
queueu.add(STOP_ELEMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBufferReceived(byte[] buffer) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBeginningOfSpeech() {
|
||||
// Do nothing.
|
||||
}
|
||||
});
|
||||
|
||||
PackageManager pm = context.getPackageManager();
|
||||
List<ResolveInfo> installedList = pm.queryIntentActivities(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
|
||||
boolean speechRecognitionInstalled = !installedList.isEmpty();
|
||||
|
||||
if (!speechRecognitionInstalled) {
|
||||
new AlertDialog.Builder(context).setMessage("For recognition it’s necessary to install \"Google Voice Search\"")
|
||||
.setTitle("Install Voice Search from Google Play?").setPositiveButton("Install", new DialogInterface.OnClickListener() { // confirm
|
||||
// button
|
||||
// Install Button click handler
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Intent installIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.google.android.voicesearch"));
|
||||
// setting flags to avoid going in application history (Activity call
|
||||
// stack)
|
||||
installIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
|
||||
context.startActivity(installIntent);
|
||||
}
|
||||
}).setNegativeButton("Cancel", null) // cancel button
|
||||
.create().show();
|
||||
}
|
||||
|
||||
Intent recognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
|
||||
recognizerIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, "Enter shell command");
|
||||
recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
|
||||
recognizerIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 10);
|
||||
recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, "en-US");
|
||||
recognizerIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
|
||||
mSpeechRecognizer.startListening(recognizerIntent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
TermuxApiLogger.error("onDestroy");
|
||||
mSpeechRecognizer.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(final Intent intent) {
|
||||
TermuxApiLogger.error("onHandleIntent");
|
||||
ResultReturner.returnData(this, intent, new ResultReturner.WithInput() {
|
||||
@Override
|
||||
public void writeResult(PrintWriter out) throws Exception {
|
||||
while (true) {
|
||||
String s = queueu.take();
|
||||
if (s == STOP_ELEMENT) {
|
||||
return;
|
||||
} else {
|
||||
out.println(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static void onReceive(final Context context, Intent intent) {
|
||||
context.startService(new Intent(context, SpeechToTextService.class).putExtras(intent.getExtras()));
|
||||
}
|
||||
|
||||
public static void runFromActivity(final Activity context) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
List<ResolveInfo> installedList = pm.queryIntentActivities(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
|
||||
boolean speechRecognitionInstalled = !installedList.isEmpty();
|
||||
|
||||
if (speechRecognitionInstalled) {
|
||||
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
|
||||
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "Select an application"); // user hint
|
||||
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
|
||||
intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 1); // quantity of results we want to receive
|
||||
// context.startActivityForResult(intent, VOICE_RECOGNITION_REQUEST_CODE);
|
||||
} else {
|
||||
new AlertDialog.Builder(context).setMessage("For recognition it’s necessary to install \"Google Voice Search\"")
|
||||
.setTitle("Install Voice Search from Google Play?").setPositiveButton("Install", new DialogInterface.OnClickListener() { // confirm
|
||||
// button
|
||||
// Install Button click handler
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.google.android.voicesearch"));
|
||||
// setting flags to avoid going in application history (Activity call stack)
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}).setNegativeButton("Cancel", null) // cancel button
|
||||
.create().show();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
64
app/src/main/java/com/termux/api/TermuxApiReceiver.java
Normal file
64
app/src/main/java/com/termux/api/TermuxApiReceiver.java
Normal file
@ -0,0 +1,64 @@
|
||||
package com.termux.api;
|
||||
|
||||
import com.termux.api.util.TermuxApiLogger;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
public class TermuxApiReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String apiMethod = intent.getStringExtra("api_method");
|
||||
if (apiMethod == null) {
|
||||
TermuxApiLogger.error("Missing 'api_method' extra");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (apiMethod) {
|
||||
case "BatteryStatus":
|
||||
BatteryStatusAPI.onReceive(this, context, intent);
|
||||
break;
|
||||
case "CameraInfo":
|
||||
CameraInfoAPI.onReceive(this, context, intent);
|
||||
break;
|
||||
case "Clipboard":
|
||||
ClipboardAPI.onReceive(this, context, intent);
|
||||
break;
|
||||
case "ContactList":
|
||||
ContactListAPI.onReceive(this, context, intent);
|
||||
break;
|
||||
case "Dialog":
|
||||
context.startActivity(new Intent(context, DialogActivity.class).putExtras(intent.getExtras()).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
break;
|
||||
case "Download":
|
||||
DownloadAPI.onReceive(this, context, intent);
|
||||
break;
|
||||
case "Location":
|
||||
LocationAPI.onReceive(this, context, intent);
|
||||
break;
|
||||
case "Notification":
|
||||
NotificationAPI.onReceive(this, context, intent);
|
||||
break;
|
||||
case "SmsInbox":
|
||||
SmsInboxAPI.onReceive(this, context, intent);
|
||||
break;
|
||||
case "SmsSend":
|
||||
SmsSendAPI.onReceive(this, intent);
|
||||
break;
|
||||
case "SpeechToText":
|
||||
SpeechToTextAPI.onReceive(context, intent);
|
||||
break;
|
||||
case "TextToSpeech":
|
||||
TextToSpeechAPI.onReceive(context, intent);
|
||||
break;
|
||||
case "Vibrate":
|
||||
VibrateAPI.onReceive(this, context, intent);
|
||||
break;
|
||||
default:
|
||||
TermuxApiLogger.error("Unrecognized 'api_method' extra: '" + apiMethod + "'");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
164
app/src/main/java/com/termux/api/TextToSpeechAPI.java
Normal file
164
app/src/main/java/com/termux/api/TextToSpeechAPI.java
Normal file
@ -0,0 +1,164 @@
|
||||
package com.termux.api;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Bundle;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
import android.speech.tts.TextToSpeech.Engine;
|
||||
import android.speech.tts.TextToSpeech.EngineInfo;
|
||||
import android.speech.tts.TextToSpeech.OnInitListener;
|
||||
import android.speech.tts.UtteranceProgressListener;
|
||||
import android.util.JsonWriter;
|
||||
|
||||
import com.termux.api.util.ResultReturner;
|
||||
import com.termux.api.util.TermuxApiLogger;
|
||||
|
||||
public class TextToSpeechAPI {
|
||||
|
||||
public static void onReceive(final Context context, Intent intent) {
|
||||
context.startService(new Intent(context, TextToSpeechService.class).putExtras(intent.getExtras()));
|
||||
}
|
||||
|
||||
public static class TextToSpeechService extends IntentService {
|
||||
TextToSpeech mTts;
|
||||
final CountDownLatch mTtsLatch = new CountDownLatch(1);
|
||||
|
||||
public TextToSpeechService() {
|
||||
super(TextToSpeechService.class.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (mTts != null)
|
||||
mTts.shutdown();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(final Intent intent) {
|
||||
final String speechLanguage = intent.getStringExtra("language");
|
||||
final String speechEngine = intent.getStringExtra("engine");
|
||||
final float speechPitch = intent.getFloatExtra("pitch", 1.0f);
|
||||
|
||||
mTts = new TextToSpeech(this, new OnInitListener() {
|
||||
@Override
|
||||
public void onInit(int status) {
|
||||
if (status == TextToSpeech.SUCCESS) {
|
||||
mTtsLatch.countDown();
|
||||
} else {
|
||||
TermuxApiLogger.error("Failed tts initialization: status=" + status);
|
||||
stopSelf();
|
||||
}
|
||||
}
|
||||
}, speechEngine);
|
||||
|
||||
ResultReturner.returnData(this, intent, new ResultReturner.WithInput() {
|
||||
@Override
|
||||
public void writeResult(PrintWriter out) throws Exception {
|
||||
|
||||
try {
|
||||
try {
|
||||
if (!mTtsLatch.await(10, TimeUnit.SECONDS)) {
|
||||
TermuxApiLogger.error("Timeout waiting for TTS initialization");
|
||||
return;
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
TermuxApiLogger.error("Interrupted awaiting TTS initialization");
|
||||
return;
|
||||
}
|
||||
|
||||
if ("LIST_AVAILABLE".equals(speechEngine)) {
|
||||
try (JsonWriter writer = new JsonWriter(out)) {
|
||||
writer.setIndent(" ");
|
||||
String defaultEngineName = mTts.getDefaultEngine();
|
||||
writer.beginArray();
|
||||
for (EngineInfo info : mTts.getEngines()) {
|
||||
writer.beginObject();
|
||||
writer.name("name").value(info.name);
|
||||
writer.name("label").value(info.label);
|
||||
writer.name("default").value(defaultEngineName.equals(info.name));
|
||||
writer.endObject();
|
||||
}
|
||||
writer.endArray();
|
||||
}
|
||||
out.println();
|
||||
return;
|
||||
}
|
||||
|
||||
final AtomicInteger ttsDoneUtterancesCount = new AtomicInteger();
|
||||
|
||||
mTts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
|
||||
@Override
|
||||
public void onStart(String utteranceId) {
|
||||
// Ignore.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String utteranceId) {
|
||||
TermuxApiLogger.error("UtteranceProgressListener.onError() called");
|
||||
synchronized (ttsDoneUtterancesCount) {
|
||||
ttsDoneUtterancesCount.incrementAndGet();
|
||||
ttsDoneUtterancesCount.notify();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDone(String utteranceId) {
|
||||
synchronized (ttsDoneUtterancesCount) {
|
||||
ttsDoneUtterancesCount.incrementAndGet();
|
||||
ttsDoneUtterancesCount.notify();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (speechLanguage != null) {
|
||||
int setLanguageResult = mTts.setLanguage(new Locale(speechLanguage));
|
||||
if (setLanguageResult != TextToSpeech.LANG_AVAILABLE) {
|
||||
TermuxApiLogger.error("tts.setLanguage('" + speechLanguage + "') returned " + setLanguageResult);
|
||||
}
|
||||
}
|
||||
|
||||
mTts.setPitch(speechPitch);
|
||||
mTts.setSpeechRate(intent.getFloatExtra("rate", 1.0f));
|
||||
|
||||
String utteranceId = "utterance_id";
|
||||
Bundle params = new Bundle();
|
||||
params.putInt(Engine.KEY_PARAM_STREAM, AudioManager.STREAM_SYSTEM);
|
||||
params.putString(Engine.KEY_PARAM_UTTERANCE_ID, utteranceId);
|
||||
|
||||
int submittedUtterances = 0;
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (!line.isEmpty()) {
|
||||
submittedUtterances++;
|
||||
mTts.speak(line, TextToSpeech.QUEUE_ADD, params, utteranceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (ttsDoneUtterancesCount) {
|
||||
while (ttsDoneUtterancesCount.get() != submittedUtterances) {
|
||||
ttsDoneUtterancesCount.wait();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TermuxApiLogger.error("TTS error", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
18
app/src/main/java/com/termux/api/VibrateAPI.java
Normal file
18
app/src/main/java/com/termux/api/VibrateAPI.java
Normal file
@ -0,0 +1,18 @@
|
||||
package com.termux.api;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Vibrator;
|
||||
|
||||
import com.termux.api.util.ResultReturner;
|
||||
|
||||
public class VibrateAPI {
|
||||
|
||||
static void onReceive(TermuxApiReceiver apiReceiver, Context context, Intent intent) {
|
||||
Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
|
||||
int milliseconds = intent.getIntExtra("duration_ms", 1000);
|
||||
vibrator.vibrate(milliseconds);
|
||||
ResultReturner.noteDone(apiReceiver, intent);
|
||||
}
|
||||
|
||||
}
|
138
app/src/main/java/com/termux/api/util/ResultReturner.java
Normal file
138
app/src/main/java/com/termux/api/util/ResultReturner.java
Normal file
@ -0,0 +1,138 @@
|
||||
package com.termux.api.util;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.IntentService;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.BroadcastReceiver.PendingResult;
|
||||
import android.content.Intent;
|
||||
import android.net.LocalSocket;
|
||||
import android.net.LocalSocketAddress;
|
||||
import android.util.JsonWriter;
|
||||
|
||||
public class ResultReturner {
|
||||
|
||||
/**
|
||||
* An extra intent parameter which specifies a linux abstract namespace socket address where output from the API
|
||||
* call should be written.
|
||||
*/
|
||||
private static final String SOCKET_OUTPUT_EXTRA = "socket_output";
|
||||
|
||||
/**
|
||||
* An extra intent parameter which specifies a linux abstract namespace socket address where input to the API call
|
||||
* can be read from.
|
||||
*/
|
||||
private static final String SOCKET_INPUT_EXTRA = "socket_input";
|
||||
|
||||
public static interface ResultWriter {
|
||||
public void writeResult(PrintWriter out) throws Exception;
|
||||
}
|
||||
|
||||
/** Possible subclass of {@link ResultWriter} when input is to be read from stdin. */
|
||||
public static abstract class WithInput implements ResultWriter {
|
||||
protected InputStream in;
|
||||
|
||||
public void setInput(InputStream inputStream) throws Exception {
|
||||
this.in = inputStream;
|
||||
}
|
||||
}
|
||||
|
||||
/** Possible marker interface for a {@link ResultWriter} when input is to be read from stdin. */
|
||||
public static abstract class WithStringInput extends WithInput {
|
||||
protected String inputString;
|
||||
|
||||
@Override
|
||||
public final void setInput(InputStream inputStream) throws Exception {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int l;
|
||||
while ((l = inputStream.read(buffer)) > 0) {
|
||||
baos.write(buffer, 0, l);
|
||||
}
|
||||
inputString = new String(baos.toByteArray(), StandardCharsets.UTF_8).trim();
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class ResultJsonWriter implements ResultWriter {
|
||||
@Override
|
||||
public final void writeResult(PrintWriter out) throws Exception {
|
||||
JsonWriter writer = new JsonWriter(out);
|
||||
writer.setIndent(" ");
|
||||
writeJson(writer);
|
||||
out.println(); // To add trailing newline.
|
||||
}
|
||||
|
||||
public abstract void writeJson(JsonWriter out) throws Exception;
|
||||
}
|
||||
|
||||
/** Just tell termux-api.c that we are done. */
|
||||
@SuppressLint("SdCardPath")
|
||||
public static void noteDone(BroadcastReceiver receiver, final Intent intent) {
|
||||
returnData(receiver, intent, null);
|
||||
}
|
||||
|
||||
/** Run in a separate thread, unless the context is an IntentService. */
|
||||
@SuppressLint("SdCardPath")
|
||||
public static void returnData(Object context, final Intent intent, final ResultWriter resultWriter) {
|
||||
final PendingResult asyncResult = (context instanceof BroadcastReceiver) ? ((BroadcastReceiver) context)
|
||||
.goAsync() : null;
|
||||
final Activity activity = (Activity) ((context instanceof Activity) ? context : null);
|
||||
|
||||
final Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
try (LocalSocket outputSocket = new LocalSocket()) {
|
||||
String outputSocketAdress = intent.getStringExtra(SOCKET_OUTPUT_EXTRA);
|
||||
outputSocket.connect(new LocalSocketAddress(outputSocketAdress));
|
||||
try (PrintWriter writer = new PrintWriter(outputSocket.getOutputStream())) {
|
||||
if (resultWriter != null) {
|
||||
if (resultWriter instanceof WithInput) {
|
||||
try (LocalSocket inputSocket = new LocalSocket()) {
|
||||
String inputSocketAdress = intent.getStringExtra(SOCKET_INPUT_EXTRA);
|
||||
inputSocket.connect(new LocalSocketAddress(inputSocketAdress));
|
||||
((WithInput) resultWriter).setInput(inputSocket.getInputStream());
|
||||
resultWriter.writeResult(writer);
|
||||
}
|
||||
} else {
|
||||
resultWriter.writeResult(writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (asyncResult != null) {
|
||||
asyncResult.setResultCode(0);
|
||||
} else if (activity != null) {
|
||||
activity.setResult(0);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TermuxApiLogger.error("Error in ResultReturner", e);
|
||||
if (asyncResult != null) {
|
||||
asyncResult.setResultCode(1);
|
||||
} else if (activity != null) {
|
||||
activity.setResult(1);
|
||||
}
|
||||
} finally {
|
||||
if (asyncResult != null) {
|
||||
asyncResult.finish();
|
||||
} else if (activity != null) {
|
||||
activity.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (context instanceof IntentService) {
|
||||
runnable.run();
|
||||
} else {
|
||||
new Thread(runnable).start();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
21
app/src/main/java/com/termux/api/util/TermuxApiLogger.java
Normal file
21
app/src/main/java/com/termux/api/util/TermuxApiLogger.java
Normal file
@ -0,0 +1,21 @@
|
||||
package com.termux.api.util;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class TermuxApiLogger {
|
||||
|
||||
private static final String TAG = "termux-api";
|
||||
|
||||
public static void info(String message) {
|
||||
Log.i(TAG, message);
|
||||
}
|
||||
|
||||
public static void error(String message) {
|
||||
Log.e(TAG, message);
|
||||
}
|
||||
|
||||
public static void error(String message, Exception exception) {
|
||||
Log.e(TAG, message, exception);
|
||||
}
|
||||
|
||||
}
|
BIN
app/src/main/res/drawable-hdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/drawable-hdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
app/src/main/res/drawable-mdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/drawable-mdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 786 B |
BIN
app/src/main/res/drawable-xhdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/drawable-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
39
app/src/main/res/layout/dialog_textarea_input.xml
Normal file
39
app/src/main/res/layout/dialog_textarea_input.xml
Normal file
@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/RelativeLayout1"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<EditText
|
||||
android:id="@+id/text_input"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="top|start"
|
||||
android:inputType="textMultiLine"
|
||||
android:lines="10"
|
||||
android:scrollbars="vertical" />
|
||||
|
||||
<LinearLayout
|
||||
style="?android:attr/buttonBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<Button
|
||||
android:id="@+id/cancel_button"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@android:string/cancel" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/ok_button"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@android:string/ok" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
14
app/src/main/res/values/attrs.xml
Normal file
14
app/src/main/res/values/attrs.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<resources>
|
||||
|
||||
<!--
|
||||
Declare custom theme attributes that allow changing which styles are
|
||||
used for button bars depending on the API level.
|
||||
?android:attr/buttonBarStyle is new as of API 11 so this is
|
||||
necessary to support previous API levels.
|
||||
-->
|
||||
<declare-styleable name="ButtonBarContainerTheme">
|
||||
<attr name="metaButtonBarStyle" format="reference" />
|
||||
<attr name="metaButtonBarButtonStyle" format="reference" />
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
5
app/src/main/res/values/colors.xml
Normal file
5
app/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<resources>
|
||||
|
||||
<color name="black_overlay">#66000000</color>
|
||||
|
||||
</resources>
|
4
app/src/main/res/values/strings.xml
Normal file
4
app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Termux:API</string>
|
||||
</resources>
|
26
app/src/main/res/values/styles.xml
Normal file
26
app/src/main/res/values/styles.xml
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="FullscreenTheme" parent="android:Theme.Holo">
|
||||
<item name="android:actionBarStyle">@style/FullscreenActionBarStyle</item>
|
||||
<item name="android:windowActionBarOverlay">true</item>
|
||||
<item name="android:windowBackground">@null</item>
|
||||
<item name="metaButtonBarStyle">?android:attr/buttonBarStyle</item>
|
||||
<item name="metaButtonBarButtonStyle">?android:attr/buttonBarButtonStyle</item>
|
||||
</style>
|
||||
|
||||
<style name="FullscreenActionBarStyle" parent="android:Widget.Holo.ActionBar">
|
||||
<item name="android:background">@color/black_overlay</item>
|
||||
</style>
|
||||
|
||||
<style name="ButtonBar">
|
||||
<item name="android:paddingLeft">2dp</item>
|
||||
<item name="android:paddingTop">5dp</item>
|
||||
<item name="android:paddingRight">2dp</item>
|
||||
<item name="android:paddingBottom">0dp</item>
|
||||
<item name="android:background">@android:drawable/bottom_bar</item>
|
||||
</style>
|
||||
|
||||
<style name="ButtonBarButton" />
|
||||
|
||||
</resources>
|
15
build.gradle
Normal file
15
build.gradle
Normal file
@ -0,0 +1,15 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:1.2.3'
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
}
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
#Wed Apr 10 15:27:10 PDT 2013
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
|
164
gradlew
vendored
Executable file
164
gradlew
vendored
Executable file
@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
fi
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >&-
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >&-
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
90
gradlew.bat
vendored
Normal file
90
gradlew.bat
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
1
settings.gradle
Normal file
1
settings.gradle
Normal file
@ -0,0 +1 @@
|
||||
include ':app'
|
Loading…
Reference in New Issue
Block a user