[Open-Source project] TVRemoteIME interface for control applications of Smart TVs and TV boxes,
1. APP project introduction:
APP name:TVRemoteIME
Function Description:Android smart TV or android box control applications can be remotely input and remotely controlled across screens (instead of Remote Control) box, box application and file management, HTTP/RTMP/MMS network live video, ED2K/seed file video file playback
Project address:Https://github.com/kingthy/TVRemoteIME
APK package download:Https://github.com/kingthy/TVRemoteIME/raw/master/released/IMEService-release.apk
2. APP console interface
The control end does not need to install any APK application. It can be accessed directly by a mobile phone, computer, or PAD browser on the same LAN.
Input remote control:
Application Management:
File Management:
Livevideo:
Iii. APP core functions
1. IMEService:The main core portal of the APP, inherited from InputMethodService, is used to implement the input service and provides a simple QWERT keyboard UI. The core remote control function is also implemented through this service.
2. RemoteServer:The Web API service of the APP implements the Web service through NanoHTTPD. All operations on the control end are directly implemented by calling the WebAPI interface of the service.
3. ijkplayer project:The Video Player project is implemented by encapsulating the ijkplayer. It is closed for parsing and downloading the video source. It supports live video broadcast over HTTP/RTMP/MMS and live video files under ED2K/seed files.
4. thunder project:Third-party download service for downloading video sources
5366index.html and ime_core.js:Implement the functional page and script code of the control end. All operations on the control end are implemented through this page and script.
Iv. Introduction to WebAPI
1. "/text": Text input interface for cross-screen input
Code File: InputRequestProcesser. java
Parameters:
Text = the text to be input. It can contain any number of characters.
Call example:
POST/texttext = text to be input
Code implementation:
After receiving an Interface request, the Web Service notifies the input method service IMEService through the onTextReceived event Method of DataReceiver. The input method submits the remotely entered text to the corresponding input box through the commitText method.
private boolean commitText(String text){ InputConnection ic = getCurrentInputConnection(); boolean flag = false; if (ic != null){ Log.d(TAG, "commitText:" + text); if(text.length() > 1 && ic.beginBatchEdit()){ flag = ic.commitText(text, 1); ic.endBatchEdit(); }else{ flag = ic.commitText(text, 1); } } return flag; }
2. "/key", "/keydown", "/keyup":The three interfaces enable different input states of the buttons: "Press", "press", "Pop Up". It can be used for key operations, such as remote control.
Code File: InputRequestProcesser. java
Parameters:
Code = key code, which corresponds to the key code defined in the android KeyEvent. The key code "cls" defined in the special definition indicates that the text is cleared.
Call example:
POST /keycode=cls
POST /keydowncode=67
Code implementation:
After receiving the Interface request, the Web Service notifies the input method service IMEService through the onKeyEventReceived event Method of DataReceiver. The input method service then processes the request based on the key code and key actions.
@Override public void onKeyEventReceived(String keyCode, int keyAction) { if(keyCode != null) { if("cls".equalsIgnoreCase(keyCode)){ InputConnection ic = getCurrentInputConnection(); if(ic != null) { ic.performContextMenuAction(android.R.id.selectAll); ic.commitText("", 1); } }else { final int kc = KeyEvent.keyCodeFromString(keyCode); if(kc != KeyEvent.KEYCODE_UNKNOWN){ if(mInputView != null && KeyEventUtils.isKeyboardFocusEvent(kc) && mInputView.isShown()){ if(keyAction == KEY_ACTION_PRESSED || keyAction == KEY_ACTION_DOWN) { handler.post(new Runnable() { @Override public void run() { if (!handleKeyboardFocusEvent(kc)) { sendKeyCode(kc); } } }); } } else{ long eventTime = SystemClock.uptimeMillis(); InputConnection ic = getCurrentInputConnection(); switch (keyAction) { case KEY_ACTION_PRESSED: sendKeyCode(kc); break; case KEY_ACTION_DOWN: if(ic != null) { ic.sendKeyEvent(new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, kc, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE)); } break; case KEY_ACTION_UP: if(ic != null) { ic.sendKeyEvent(new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP, kc, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE)); } break; } } } } } }
3. "/apps ":Obtains the list of APP data installed on the box to manage the box APP.
Code File: AppRequestProcesser. java
Parameters:
System = system application included, true or false
Call example:
POST /appssystem=false
Code implementation:
You can use PackageManager to query the list of apps installed on the box and filter out the apps (not third-party) at the bottom of the system while processing the list data ).
Public static List <AppInfo> queryAppInfo (Context context, boolean containSysApp) {PackageManager pm = context. getPackageManager (); List <ApplicationInfo> listAppcations = pm. getInstalledApplications (PackageManager. MATCH_UNINSTALLED_PACKAGES); List <AppInfo> appInfos = new ArrayList <AppInfo> (); for (ApplicationInfo app: listAppcations) {if (containSysApp | (app. flags & ApplicationInfo. FLAG_SYSTEM) = 0) {boolean isSysApp = (app. flags & ApplicationInfo. FLAG_SYSTEM )! = 0; // filter out the app if (isSysApp & (app. packageName. startsWith ("com. android. ") | app. packageName. equals ("android") continue; AppInfo appInfo = new AppInfo (); appInfo. setLable (String) app. loadLabel (pm); appInfo. setPackageName (app. packageName); appInfo. setApkPath (app. sourceDir); appInfo. setSysApp (isSysApp); appInfos. add (appInfo) ;}} Collections. sort (appInfos, new Comparator <AppInfo> () {@ Override Public int compare (AppInfo o1, AppInfo o2) {int i1 = (o1.isSysApp? 2: 1); int i2 = (o2.isSysApp? 2: 1); if (i1 = i2) {return o1.getLable (). compareTo (o2.getLable ();} else {return Integer. compare (i1, i2) ;}}); return appInfos ;}
4. "/uninstall ":Uninstall application interface
Code File: AppRequestProcesser. java
Parameters:
PackageName = Name of the uninstalled application package
Call example:
POST /uninstallpackageName=com.test.app
Code implementation:
Directly call the system to uninstall the service.
Public static void uninstallPackage (final String packageName, final Context context) {if (getApplicationInfo (packageName, context) = null) return; try {Intent intent = new Intent (); intent. addFlags (Intent. FLAG_ACTIVITY_NEW_TASK); intent. setAction (Intent. ACTION_DELETE); intent. setData (Uri. parse ("package:" + packageName); context. startActivity (intent); Log. I (IMEService. TAG, String. format ("deleted application package [% s]", packageName);} catch (Exception ex) {Log. e (IMEService. TAG, String. format ("failed to delete application package [% s]", packageName), ex );}}
5. "/run", "/runSystem ":Run the application and or call the system application service. For example, the system settings page is displayed.
Code File: AppRequestProcesser. java
Parameters:
PackageName = Name of the application package to run
Call example:
POST /runpackageName=com.test.app
POST /runSystempackageName=android.settings.SETTINGS
Code implementation:
To run a third-party application, you can use PackageManager to obtain the startup page of the package and then run the application. To call the system application service, you can use Intent to directly start the application.
Public static void runPackage (final String packageName, final Context context) {if (getApplicationInfo (packageName, context) = null) return; try {PackageManager pm = context. getPackageManager (); Intent intent = pm. getLaunchIntentForPackage (packageName); if (intent! = Null) {context. startActivity (intent);} Log. I (IMEService. TAG, String. format ("running application package [% s]", packageName);} catch (Exception ex) {Log. e (IMEService. TAG, String. format ("error in running application package [% s]", packageName), ex) ;}} public static void runSystemPackage (final String packageName, final Context context) {try {Intent intent = new Intent (packageName); intent. addFlags (Intent. FLAG_ACTIVITY_NEW_TASK); context. startActivity (intent); Log. I (IMEService. TAG, String. format ("Running System Application Package [% s]", packageName);} catch (Exception ex) {Log. e (IMEService. TAG, String. format ("error in running system application package [% s]", packageName), ex );}}
6. "/upload ":File Transfer interface. The file transfer interface under the "input Remote Control" interface. If the APK package is transferred, it can be automatically installed. If it is a video file or a seed file, it can be automatically played.
Code File: UploadRequestProcesser. java
Parameters:
File = uploaded file
AutoPlay = whether to automatically install the APK package or play videos and seed files automatically, true or false
Call example:
POST /upload-------
Code implementation:
The file upload function of NanoHTTPD is used directly. To avoid moving files across "partitions", NanoHTTPD's TempFileManager is rewritten so that files can be uploaded directly to the files directory under the IMEService data directory.
For the code, see the RemoteServerFileManager. java file.
7. "/play ":Video Playback Service Interface
Code File: PlayRequestProcesser. java
Parameters:
PlayUrl = Source Address of the video to be played
Call example:
POST /playplayUrl=http://www.test.com/test.avi
Code implementation:
Directly transfer the video source address to the player of the ijkplayer project for playback.
XLVideoPlayActivity.intentTo(context, url, url);
8. "/file/dir /":The file management interface service enables you to view files and directories in a box.
Code File: FileRequestProcesser. java
Parameters:
None
Call example:
GET/file/dir/{view file or directory path}
Code implementation:
Obtains the list of subdirectories and files in the directory and returns JSON data.
private NanoHTTPD.Response responseDirData(String dirName) { File path = new File(Environment.getExternalStorageDirectory(), dirName); String root = Environment.getExternalStorageDirectory().getPath(); JSONArray dirs = new JSONArray(); JSONArray files = new JSONArray(); try { File[] subfiles = path.listFiles(); Arrays.sort(subfiles, new Comparator<File>() { @Override public int compare(File f1, File f2) { return f1.getName().toLowerCase().compareTo(f2.getName().toLowerCase()); } }); for (File file : subfiles) { if(file.isHidden()) continue; JSONObject item = new JSONObject(); item.put("name", file.getName()); item.put("path", file.getPath().substring(root.length())); if (file.isDirectory()) { //item.put("total", 0); dirs.put(item); }else { item.put("size", file.length()); files.put(item); } } JSONObject data = new JSONObject(); if(!TextUtils.isEmpty(dirName) && dirName != "/") data.put("parent", path.getParent().substring(root.length())); data.put("dirs", dirs); data.put("files", files); return RemoteServer.createJSONResponse(NanoHTTPD.Response.Status.OK, data.toString()); }catch (JSONException ex){ return RemoteServer.createPlainTextResponse(NanoHTTPD.Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: JSONException: " + ex.getMessage()); } }
9. "/file/download /":The file downloading Service implements the file downloading function of the box.
Code File: FileRequestProcesser. java
Parameters:
None
Call example:
GET/file/download/{path of the downloaded file}
Code implementation:
private NanoHTTPD.Response downloadFileData(String fileName){ File file = new File(Environment.getExternalStorageDirectory(), fileName); if(!file.exists()){ return RemoteServer.createPlainTextResponse(NanoHTTPD.Response.Status.NOT_FOUND, "Error 404, file not found."); } try{ InputStream inputStream = new FileInputStream(file); return RemoteServer.newFixedLengthResponse(NanoHTTPD.Response.Status.OK, NanoHTTPD.getMimeTypeForFile(file.getName()) + "; charset=utf-8", inputStream, (long)inputStream.available()); } catch (Exception e) { return RemoteServer.createPlainTextResponse(NanoHTTPD.Response.Status.NOT_FOUND, "Error 404, file not found."); } }
10. "/file/cut", "/file/copy ":File and directory cutting and copying services.
Code File: FileRequestProcesser. java
Parameters:
Paths = List of directories or files to be operated, separated by "|"
TargetPath = new target directory of the cut or move object
Call example:
POST /file/cut
paths=/Download/a.txt&targetPath=/Andorid/data/
POST /file/copy
paths=/Download/a.txt&targetPath=/Andorid/data/
Code implementation:
private void batchCopyFile(String targetPath, String paths){ File targetPathFile = new File(Environment.getExternalStorageDirectory(), targetPath); if(!targetPathFile.exists()) return; String[] pathData = paths.split("\\|"); for(String p : pathData){ if(!TextUtils.isEmpty(p)) { File source = new File(Environment.getExternalStorageDirectory(), p); RemoteServerFileManager.copyFile(source, targetPathFile); } } } private void batchCutFile(String targetPath, String paths){ File targetPathFile = new File(Environment.getExternalStorageDirectory(), targetPath); if(!targetPathFile.exists()) return; String[] pathData = paths.split("\\|"); for(String p : pathData){ if(!TextUtils.isEmpty(p)) { File source = new File(Environment.getExternalStorageDirectory(), p); RemoteServerFileManager.cutFile(source, targetPathFile); } } }
11. "/file/delete ":File or directory deletion service. If a directory is deleted, all subdirectories and files under the directory are deleted.
Code File: FileRequestProcesser. java
Parameters:
Paths = list of files or directories to be deleted
Call example:
POST /file/delete
paths=/Download/a.txt|/Download/test
Code implementation:
private void batchDeleteFile(String paths){ String[] pathData = paths.split("\\|"); for(String p : pathData){ if(!TextUtils.isEmpty(p)) { File path = new File(Environment.getExternalStorageDirectory(), p); RemoteServerFileManager.deleteFile(path); } } }
12. "/file/upload ":File Upload service. This interface is similar to the/upload interface, but this interface uploads files to the current directory on the file management interface, and does not install the APK package and play video files.
Code File: FileRequestProcesser. java
Parameters:
File = uploaded file data
Path = directory for storing uploaded files
Call example:
POST /file/upload
--------
Code implementation:
File Upload has been completed in NanoHTTPD, so you only need to move the uploaded file to the corresponding directory.
private NanoHTTPD.Response uploadFile(Map<String, String> params, Map<String, String> files){ String uploadFileName = params.get("file"); String uploadPathName = params.get("path"); String localFilename = files.get("file"); boolean r = false; if(!TextUtils.isEmpty(uploadFileName)) { if (!TextUtils.isEmpty(localFilename)) { File saveFilename = new File(Environment.getExternalStorageDirectory(), uploadPathName); File localFile = new File(localFilename); saveFilename = new File(saveFilename,localFile.getName()); r = localFile.renameTo(saveFilename); } } return RemoteServer.createJSONResponse(NanoHTTPD.Response.Status.OK, "{\"success\":" + (r ? "true": "false") + "}"); }
V. Notes
Project address:Https://github.com/kingthy/TVRemoteIME
Chat QQ group:7149348. You can join the QQ group to share the live video source, feedback, and suggestions.