diff --git a/build.gradle b/build.gradle index 084de23823a01a991e2433c7dfbf12e03175cc14..68ae8ba5397d2e4c6b3b09eea0104ff78fd3da0f 100644 --- a/build.gradle +++ b/build.gradle @@ -42,6 +42,6 @@ dependencies { api rootProject.ext.libSupportDesign //api rootProject.ext.libSupportV7 api rootProject.ext.libGoogleGuava - compile project(path: ':qbaselib') + api project(':qbaselib') } diff --git a/src/main/java/org/qpython/qsl4a/QSL4APP.java b/src/main/java/org/qpython/qsl4a/QSL4APP.java index 69e3533d74f4492e22abbd69c10680afdb1df38b..2c6d8a7172ca952f4cdcbf2308ec86e2524da8fc 100644 --- a/src/main/java/org/qpython/qsl4a/QSL4APP.java +++ b/src/main/java/org/qpython/qsl4a/QSL4APP.java @@ -2,7 +2,7 @@ package org.qpython.qsl4a; import com.quseit.base.MyApp; -import org.qpython.qsl4a.qsl4a.FutureActivityTaskExecutor; +import org.qpython.qsl4a.qsl4a.future.FutureActivityTaskExecutor; import org.qpython.qsl4a.qsl4a.LogUtil; import org.qpython.qsl4a.qsl4a.interpreter.InterpreterConfiguration; import org.qpython.qsl4a.qsl4a.interpreter.InterpreterConstants; diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/BaseApplication.java b/src/main/java/org/qpython/qsl4a/qsl4a/BaseApplication.java index 61f85c3a1dc4b50823ff614e375db5ec67f63ba2..174951b767f88fcc76a479a6d916fcdad40c6731 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/BaseApplication.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/BaseApplication.java @@ -20,6 +20,7 @@ import android.app.Application; import org.qpython.qsl4a.qsl4a.interpreter.InterpreterConfiguration; import org.qpython.qsl4a.qsl4a.trigger.TriggerRepository; +import org.qpython.qsl4a.qsl4a.future.FutureActivityTaskExecutor; public class BaseApplication extends Application { diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/IntentBuilders.java b/src/main/java/org/qpython/qsl4a/qsl4a/IntentBuilders.java index e8b1d15467e94b598ba456cf712403ad73a8a862..2640964f99e819d7f376d42d997a0ce085c9d2db 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/IntentBuilders.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/IntentBuilders.java @@ -152,6 +152,6 @@ public class IntentBuilders { public static PendingIntent buildTriggerServicePendingIntent(Context context) { final Intent intent = buildTriggerServiceIntent(); return PendingIntent.getService(context, EXECUTE_SCRIPT_REQUEST_CODE, intent, - PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } } diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/ScriptLauncher.java b/src/main/java/org/qpython/qsl4a/qsl4a/ScriptLauncher.java index 4bb294b27300fe1807fe0893b4720f0902b0f7b8..016acd979f6f0e2e9fb40b93e983c8fcb9499c24 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/ScriptLauncher.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/ScriptLauncher.java @@ -25,6 +25,7 @@ import java.io.File; import org.qpython.qsl4a.QSL4APP; import org.qpython.qsl4a.qsl4a.facade.FacadeConfiguration; import org.qpython.qsl4a.qsl4a.facade.FacadeManager; +import org.qpython.qsl4a.qsl4a.future.FutureActivityTaskExecutor; import org.qpython.qsl4a.qsl4a.interpreter.Interpreter; import org.qpython.qsl4a.qsl4a.interpreter.InterpreterConfiguration; import org.qpython.qsl4a.qsl4a.interpreter.InterpreterProcess; diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/AndroidFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/AndroidFacade.java index fb219a19fad3351d2bc2530d7fd47c7febabb98c..77871b53aeba1fde6d6af45e3b0481e38cc66bb7 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/AndroidFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/AndroidFacade.java @@ -43,7 +43,7 @@ import org.json.JSONException; import org.json.JSONObject; import org.qpython.qsl4a.QSL4APP; import org.qpython.qsl4a.qsl4a.util.FileUtils; -import org.qpython.qsl4a.qsl4a.FutureActivityTaskExecutor; +import org.qpython.qsl4a.qsl4a.future.FutureActivityTaskExecutor; import org.qpython.qsl4a.qsl4a.LogUtil; import org.qpython.qsl4a.qsl4a.NotificationIdFactory; import org.qpython.qsl4a.qsl4a.future.FutureActivityTask; @@ -456,6 +456,7 @@ public class AndroidFacade extends RpcReceiver { } public void doStartActivity(final Intent intent, Boolean wait) throws Exception { + Exception[] except = new Exception[1]; if (wait == null || !wait) { startActivity(intent); } else { @@ -466,7 +467,11 @@ public class AndroidFacade extends RpcReceiver { public void onCreate() { super.onCreate(); intent.setFlags(intent.getFlags() | intentFlags); - startActivity(intent); + try { + startActivity(intent); + } catch (Exception exception) { + except[0] = exception; + } } @Override @@ -490,10 +495,14 @@ public class AndroidFacade extends RpcReceiver { } catch (Exception e) { throw new RuntimeException(e); } + if(except[0]!=null) { + throw except[0]; + } } } public void doStartActivity(final Intent intent, Boolean wait,int flags) throws Exception { + Exception[] except = new Exception[1]; if (wait == null || !wait) { startActivity(intent); } else { @@ -504,7 +513,11 @@ public class AndroidFacade extends RpcReceiver { public void onCreate() { super.onCreate(); intent.setFlags(intent.getFlags() | intentFlags); - startActivity(intent); + try { + startActivity(intent); + } catch (Exception exception) { + except[0] = exception; + } } @Override @@ -528,6 +541,9 @@ public class AndroidFacade extends RpcReceiver { } catch (Exception e) { throw new RuntimeException(e); } + if(except[0]!=null) { + throw except[0]; + } } } @@ -708,7 +724,7 @@ public class AndroidFacade extends RpcReceiver { intent = new Intent (context,NotificationClickReceiver.class); intent.putExtra("path",uri); intent.putExtra("arg",arg); - contentIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + contentIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } catch (Exception e) { intent = new Intent(); intent.setClassName(mService.getPackageName(), "org.qpython.qpylib.MPyApi"); @@ -731,7 +747,7 @@ public class AndroidFacade extends RpcReceiver { intent = new Intent(); } if(contentIntent == null) - contentIntent = PendingIntent.getActivity(mService, NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT); + contentIntent = PendingIntent.getActivity(mService, NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); Notification notification = SPFUtils.getNotification(context, title, message, contentIntent, SPFUtils.getDrawableId(mService, "img_home_logo"), null); diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ApplicationManagerFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ApplicationManagerFacade.java index eefcf3626e647b8d3437275f1fd9ee5534721567..eb17fccf423401af8463fa305ca3e7ea6027471f 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ApplicationManagerFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ApplicationManagerFacade.java @@ -8,8 +8,10 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.os.Build; +import org.json.JSONObject; import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; import org.qpython.qsl4a.qsl4a.rpc.Rpc; import org.qpython.qsl4a.qsl4a.rpc.RpcDefault; @@ -62,41 +64,28 @@ public class ApplicationManagerFacade extends RpcReceiver { return applications; } - /*@Rpc(description = "get all packages") - public Map getAllPackages() { - Map packages = new HashMap<>(); - List packageInfos = context.getPackageManager().getInstalledPackages(PackageManager.GET_ACTIVITIES | - PackageManager.GET_SERVICES); - for (PackageInfo info : packageInfos) { - packages.put(info.packageName, info.applicationInfo.loadLabel(mPackageManager).toString()); - } - return packages; + @Rpc(description = "get Application Info") + public JSONObject getApplicationInfo( + @RpcParameter(name = "package name") @RpcOptional String packageName) throws Exception { + if(packageName == null) + packageName = context.getPackageName(); + ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES); + JSONObject result = new JSONObject(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + result.put("compileSdkVersion",appInfo.compileSdkVersion); + result.put("targetSdkVersion",appInfo.targetSdkVersion); + result.put("minSdkVersion",appInfo.minSdkVersion); + result.put("className",appInfo.className); + result.put("uid",appInfo.uid); + result.put("dataDir",appInfo.dataDir); + result.put("nativeLibraryDir",appInfo.nativeLibraryDir); + result.put("sourceDir",appInfo.sourceDir); + result.put("publicSourceDir",appInfo.publicSourceDir); + result.put("deviceProtectedDataDir",appInfo.deviceProtectedDataDir); + result.put("label",appInfo.loadLabel(mPackageManager)); + return result; } - @Rpc(description = "get system packages") - public Map getSystemPackages() { - Map packages = new HashMap<>(); - List packageInfos = context.getPackageManager().getInstalledPackages(PackageManager.GET_ACTIVITIES | - PackageManager.GET_SERVICES); - for (PackageInfo info : packageInfos) { - if(((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 1) || ((info.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 1)) - packages.put(info.packageName, info.applicationInfo.loadLabel(mPackageManager).toString()); - } - return packages; - } - - @Rpc(description = "get user packages") - public Map getUserPackages() { - Map packages = new HashMap<>(); - List packageInfos = context.getPackageManager().getInstalledPackages(PackageManager.GET_ACTIVITIES | - PackageManager.GET_SERVICES); - for (PackageInfo info : packageInfos) { - if(((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 1) && ((info.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 1)) - packages.put(info.packageName, info.applicationInfo.loadLabel(mPackageManager).toString()); - } - return packages; - }*/ - @Rpc(description = "Start activity with the given classname and/or packagename .") public void launch(@RpcParameter(name = "classname") @RpcOptional final String classname, @RpcParameter(name = "packagename") @RpcOptional String packagename, diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/CameraFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/CameraFacade.java index 806cdab8e98246cfa4ea8b57e4f00e52e0ea47d4..5afb66cb6a552b84b963d3b0dc22d636f8433e8b 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/CameraFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/CameraFacade.java @@ -38,7 +38,7 @@ import android.view.SurfaceHolder.Callback; import org.qpython.qsl4a.QSL4APP; import org.qpython.qsl4a.qsl4a.util.FileUtils; -import org.qpython.qsl4a.qsl4a.FutureActivityTaskExecutor; +import org.qpython.qsl4a.qsl4a.future.FutureActivityTaskExecutor; import org.qpython.qsl4a.qsl4a.LogUtil; diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/CommonIntentsFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/CommonIntentsFacade.java index 7cf9a36cb3c8c68528856fa40c1e40bb6ab86777..730819cfaa317d1d0d167806b5ecd013776a47a5 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/CommonIntentsFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/CommonIntentsFacade.java @@ -168,11 +168,7 @@ public class CommonIntentsFacade extends RpcReceiver { intent.setAction(android.content.Intent.ACTION_VIEW); File file = new File(path); Uri uri; - if (Build.VERSION.SDK_INT>=24) { - uri = FileProvider.getUriForFile(context,qpyProvider,file); - } else { - uri = Uri.fromFile(file); - } + uri = FileProvider.getUriForFile(context,qpyProvider,file); intent.setDataAndType(uri, type); try { mAndroidFacade.doStartActivity(intent,wait); diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/DocumentFileFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/DocumentFileFacade.java new file mode 100644 index 0000000000000000000000000000000000000000..184df028f2e48f8bc92708320f13e86a62a38270 --- /dev/null +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/DocumentFileFacade.java @@ -0,0 +1,292 @@ +package org.qpython.qsl4a.qsl4a.facade; +//by 乘着船 at 2021-2022 + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.net.Uri; +import android.os.Build; +import android.os.storage.StorageManager; +import android.os.storage.StorageVolume; +import android.preference.PreferenceManager; +import android.provider.DocumentsContract; +import android.support.annotation.RequiresApi; +import android.support.v4.provider.DocumentFile; +import android.util.Base64; + +import com.quseit.util.DocumentsUtils; + +import org.json.JSONArray; +import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; +import org.qpython.qsl4a.qsl4a.rpc.Rpc; +import org.qpython.qsl4a.qsl4a.rpc.RpcDefault; +import org.qpython.qsl4a.qsl4a.rpc.RpcOptional; +import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; + +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; + +public class DocumentFileFacade extends RpcReceiver { + + private final AndroidFacade mAndroidFacade; + private final Context context; + + public DocumentFileFacade(FacadeManager manager) { + super(manager); + mAndroidFacade = manager.getReceiver(AndroidFacade.class); + context = mAndroidFacade.context; + } + + @RequiresApi(api = Build.VERSION_CODES.N) + @Rpc(description = "Show Open Document Tree with RootPath .") + public Uri documentTreeShowOpen( + @RpcParameter(name = "rootPath") String rootPath + ) throws Exception { + File file = new File(rootPath); + if(file.canWrite()) + return Uri.fromFile(file); + String p = file.getAbsolutePath(); + if(p.startsWith(DocumentsUtils.ANDROID_PATH)) + return documentTreeAndroid(file); + // else next grade 1 + DocumentFile documentFile; + if (DocumentsUtils.isOnExtSdCard(file, context)) { + documentFile = DocumentsUtils.getDocumentFile(file, true, context); + if(documentFile != null && documentFile.canWrite()) + return documentFile.getUri(); + // else next grade 2 + } else { + p = PreferenceManager.getDefaultSharedPreferences(context).getString(rootPath, null); + if (p != null) { + documentFile = DocumentFile.fromTreeUri(context, Uri.parse(p)); + if(documentFile != null && documentFile.canWrite()) + return documentFile.getUri(); + // else next grade 3 + } // else next grade 2 + } + Intent intent = null; + StorageManager sm = context.getSystemService(StorageManager.class); + StorageVolume volume = sm.getStorageVolume(file); + if (volume != null) { + intent = volume.createAccessIntent(null); + } + if (intent == null) { + intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); + } + Intent intentR = mAndroidFacade.startActivityForResultCode(intent); + switch (intentR.getIntExtra("RESULT_CODE", -1025)) { + case -1025: + throw new Exception(intentR.getStringExtra("EXCEPTION")); + case Activity.RESULT_OK: + Uri uri = intentR.getData(); + DocumentsUtils.saveTreeUri(context,rootPath,uri); + return uri; + default: + return null; + } + } + + @Rpc(description = "Document File Rename .") + public boolean documentFileRenameTo ( + @RpcParameter(name = "src") String src, + @RpcParameter(name = "dest") String dest) throws Exception { + return DocumentsUtils.renameTo(context,new File(src),new File(dest)); + } + + @Rpc(description = "Document File ( or Tree ) Delete .") + public boolean documentFileDelete ( + @RpcParameter(name = "file or tree") String file) { + return DocumentsUtils.delete(context,new File(file)); + } + + @Rpc(description = "Document File Make Directorys .") + public boolean documentFileMkdir ( + @RpcParameter(name = "dir") String dir) { + return DocumentsUtils.mkdirs(context,new File(dir)); + } + + @Rpc(description = "Document File Input Stream .") + public String documentFileInputStream ( + @RpcParameter(name = "srcFile") String srcFile, + @RpcParameter(name = "encodingFormat") @RpcDefault("") String encodingFormat) + throws Exception{ + byte[] data; + InputStream fis=DocumentsUtils.getInputStream(context,new File(srcFile)); + int length = fis.available(); + data = new byte[length]; + fis.read(data); + fis.close(); + if (encodingFormat.equals("")) { + return Base64.encodeToString( data, Base64.DEFAULT ); + } else { + return new String(data, encodingFormat); + } + } + + @Rpc(description = "Document File Output Stream .") + public void documentFileOutputStream ( + @RpcParameter(name = "destFile") String destFile, + @RpcParameter(name = "srcString") @RpcDefault("") String srcString, + @RpcParameter(name = "encodingFormat") @RpcDefault("") String encodingFormat) + throws Exception{ + byte[] data; + if (encodingFormat.equals("")) { + data = Base64.decode( srcString, Base64.DEFAULT ); + } else { + data = srcString.getBytes( encodingFormat ); + } + OutputStream fos=DocumentsUtils.getOutputStream(context,new File(destFile)); + fos.write(data); + fos.flush(); + fos.close(); + } + + @Rpc(description = "Document File Copy .") + public void documentFileCopy ( + @RpcParameter(name = "src") String src, + @RpcParameter(name = "dest") String dest) + throws Exception{ + DocumentsUtils.copy(context,new File(src),new File(dest)); + } + + @Rpc(description = "Document File List Files .") + public JSONArray documentFileListFiles ( + @RpcParameter(name = "folder") String folder + ) throws Exception { + JSONArray jsonArray = new JSONArray(); + String[] S = DocumentsUtils.listFiles(context,new File(folder)); + if(S==null) return null; + for (String s : S) + jsonArray.put(s); + return jsonArray; + } + + @RequiresApi(api = Build.VERSION_CODES.N) + @Rpc(description = "The same as documentTreeShowOpen .") + public Uri documentFileShowOpen( + @RpcParameter(name = "rootPath") String rootPath + ) throws Exception { + return documentTreeShowOpen(rootPath); + } + + @Rpc(description = "The same as documentFileRenameTo .") + public boolean documentFileMoveTo ( + @RpcParameter(name = "src") String src, + @RpcParameter(name = "dest") String dest) throws Exception { + return documentFileRenameTo(src,dest); + } + + @Rpc(description = "The same as documentFileMkdir .") + public boolean documentFileMkdirs ( + @RpcParameter(name = "dir") String dir) { + return documentFileMkdir(dir); + } + + @Rpc(description = "Document File Get Uri .") + public Uri documentFileGetUri ( + @RpcParameter(name = "path") String path, + @RpcParameter(name = "isDirectory") @RpcOptional Boolean isDirectory) { + return DocumentsUtils.getUri(context,new File(path)); + } + + @Rpc(description = "Document File Is Directory .") + public Boolean documentFileIsDirectory ( + @RpcParameter(name = "path") String path + ) throws Exception { + return DocumentsUtils.isDirectory(context,new File(path)); + } + + @Rpc(description = "Document File Get Stat .") + public Map documentFileGetStat( + @RpcParameter(name = "path") String path + ){ + DocumentFile documentFile = DocumentsUtils.getDocumentFile(new File(path),null,context); + if(documentFile == null) return getFileStat(path); + Map map = new HashMap<>(); + map.put("length",documentFile.length()); + map.put("lastModified",documentFile.lastModified()); + map.put("isDirectory",documentFile.isDirectory()); + map.put("canRead",documentFile.canRead()); + map.put("canWrite",documentFile.canWrite());//外置卡此处为true + return map; + } + + @Rpc(description = "get file stat .") + public Map getFileStat( + @RpcParameter(name = "path") String path + ){ + File file = new File(path); + Map map = new HashMap<>(); + map.put("length",file.length()); + map.put("lastModified",file.lastModified()); + map.put("canRead",file.canRead()); + map.put("canWrite",file.canWrite());//外置卡此处为false + map.put("canExecute",file.canExecute()); + map.put("FreeSpace",file.getFreeSpace()); + map.put("TotalSpace",file.getTotalSpace()); + return map; + } + + public Uri documentTreeAndroid(File file) throws Exception { + String path = file.getAbsolutePath(); + String subPath = path.substring(DocumentsUtils.ANDROID_PATH.length()); + SharedPreferences perf = PreferenceManager.getDefaultSharedPreferences(context); + String uriStr = perf.getString(path,null); + Uri uri = null; + DocumentFile documentFile = null; + if(uriStr != null) + uri = Uri.parse(uriStr); + if(uri != null) + documentFile = DocumentFile.fromTreeUri(context, uri); + if(documentFile != null && documentFile.canWrite()) { + addOnSdCardList(path); + return uri; + } + String content = subPath.substring(0,subPath.indexOf("/")); + content = DocumentsUtils.ANDROID_CONTENT[0] + subPathToContent(content) + + DocumentsUtils.ANDROID_CONTENT[1] + subPathToContent(subPath); + uri = Uri.parse(content); + documentFile = DocumentFile.fromTreeUri(context, uri); + if(documentFile == null) + return null; + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); + intent.setFlags(DocumentsUtils.ANDROID_OPEN_INTENT); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, uri); + } + Intent intentR = mAndroidFacade.startActivityForResultCode(intent); + switch (intentR.getIntExtra("RESULT_CODE", -1025)) { + case -1025: + throw new Exception(intentR.getStringExtra("EXCEPTION")); + case Activity.RESULT_OK: + uri = intentR.getData(); + context.getContentResolver().takePersistableUriPermission(uri,DocumentsUtils.ANDROID_SAVE_INTENT); + addOnSdCardList(path); + perf.edit().putString(path, uri.toString()).apply(); + return uri; + default: + return null; + } + } + + public String subPathToContent(String subPath) { + if (subPath.endsWith("/")) { + subPath = subPath.substring(0, subPath.length() - 1); + } + return subPath.replace("%","%25").replace("/", "%2F"). + replace(" ","%20"); + } + + private void addOnSdCardList(String path){ + if(!DocumentsUtils.sExtSdCardPaths.contains(path)) + DocumentsUtils.sExtSdCardPaths.add(path); + } + + @Override + public void shutdown() { + } +} \ No newline at end of file diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/FacadeConfiguration.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/FacadeConfiguration.java index 7328ca5da33791e26928c469c9cfa79e3470b21d..2e5a4afb895db19e0d41532a18455e332e0ac352 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/FacadeConfiguration.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/FacadeConfiguration.java @@ -91,6 +91,7 @@ public class FacadeConfiguration { sFacadeClassList.add(VideoFacade.class); sFacadeClassList.add(FloatViewFacade.class); sFacadeClassList.add(CipherFacade.class); + sFacadeClassList.add(DocumentFileFacade.class); //if (sSdkLevel >= 4) { sFacadeClassList.add(TextToSpeechFacade.class); diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/MediaRecorderFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/MediaRecorderFacade.java index 5a9340472c4f7463b784e8714411b02fba3d8756..0eab0dc3d3e553f7d055cafa8952b77a9d19698d 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/MediaRecorderFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/MediaRecorderFacade.java @@ -27,7 +27,7 @@ import android.view.SurfaceView; import android.view.WindowManager; import org.qpython.qsl4a.QSL4APP; -import org.qpython.qsl4a.qsl4a.FutureActivityTaskExecutor; +import org.qpython.qsl4a.qsl4a.future.FutureActivityTaskExecutor; import org.qpython.qsl4a.qsl4a.LogUtil; diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/SettingsFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/SettingsFacade.java index 97a88d31b44e6ae190acb952973501a1efff25f9..151490eb301a406530dcb96a0fb2d86d0fba5b9b 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/SettingsFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/SettingsFacade.java @@ -34,7 +34,7 @@ import android.util.DisplayMetrics; import android.view.WindowManager; import org.qpython.qsl4a.QSL4APP; -import org.qpython.qsl4a.qsl4a.FutureActivityTaskExecutor; +import org.qpython.qsl4a.qsl4a.future.FutureActivityTaskExecutor; import org.qpython.qsl4a.qsl4a.LogUtil; import java.lang.reflect.Method; diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/SmsFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/SmsFacade.java index 472af8719d840e07f3387f0df608cfc5b9c0abcf..f83adee651a011710e9abac1f9d1c8b0ae35e0e5 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/SmsFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/SmsFacade.java @@ -27,7 +27,7 @@ import android.support.v4.content.ContextCompat; import android.telephony.gsm.SmsManager; import org.qpython.qsl4a.QSL4APP; -import org.qpython.qsl4a.qsl4a.FutureActivityTaskExecutor; +import org.qpython.qsl4a.qsl4a.future.FutureActivityTaskExecutor; import org.qpython.qsl4a.qsl4a.LogUtil; import org.qpython.qsl4a.qsl4a.facade.ui.AlertDialogTask; import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/WebCamFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/WebCamFacade.java index daf3282be4f3bbea6e88ceec0ffaee99e230b08f..372452dd983a21921d0cb6c184822480f946593a 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/WebCamFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/WebCamFacade.java @@ -48,7 +48,7 @@ import android.view.WindowManager; import android.view.SurfaceHolder.Callback; import org.qpython.qsl4a.QSL4APP; -import org.qpython.qsl4a.qsl4a.FutureActivityTaskExecutor; +import org.qpython.qsl4a.qsl4a.future.FutureActivityTaskExecutor; import org.qpython.qsl4a.qsl4a.LogUtil; import org.qpython.qsl4a.qsl4a.SingleThreadExecutor; import org.qpython.qsl4a.qsl4a.SimpleServer.SimpleServerObserver; diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/AlertDialogTask.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/AlertDialogTask.java index 8e4be374f6a0cc4ebe84125b650f639f6fd9b80f..d17a21f58235a358aaa8fa77efc2e3fef948bfba 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/AlertDialogTask.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/AlertDialogTask.java @@ -17,12 +17,20 @@ package org.qpython.qsl4a.qsl4a.facade.ui; import android.app.Activity; -//import android.support.v7.app.AlertDialog; -//import android.support.v7.app.AlertDialog.Builder; import android.app.AlertDialog; import android.content.DialogInterface; +import android.graphics.Color; +import android.graphics.Typeface; import android.text.method.PasswordTransformationMethod; import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; + +import org.json.JSONArray; +import org.json.JSONException; +import org.qpython.qsl4a.R; +import org.qpython.qsl4a.qsl4a.util.HtmlUtil; import java.util.ArrayList; import java.util.HashMap; @@ -31,19 +39,21 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; -import org.json.JSONArray; -import org.json.JSONException; -import org.qpython.qsl4a.R; +//import android.support.v7.app.AlertDialog; +//import android.support.v7.app.AlertDialog.Builder; /** * Wrapper class for alert dialog running in separate thread. * * @author MeanEYE.rcf (meaneye.rcf@gmail.com) + * + * Edit by 乘着船 at 2022 */ public class AlertDialogTask extends DialogTask { private final String mTitle; private final String mMessage; + private boolean mMessageIsHtml; private final List mItems; private final Set mSelectedItems; @@ -55,6 +65,7 @@ public class AlertDialogTask extends DialogTask { private String mNegativeButtonText; private String mNeutralButtonText; + private Activity mActivity; private EditText mEditText; private String mDefaultText; @@ -65,6 +76,7 @@ public class AlertDialogTask extends DialogTask { public AlertDialogTask(String title, String message) { mTitle = title; mMessage = message; + mMessageIsHtml = false; mInputType = InputType.DEFAULT; mItems = new ArrayList(); mSelectedItems = new TreeSet(); @@ -145,7 +157,7 @@ public class AlertDialogTask extends DialogTask { public void setTextInput(String defaultText) { mDefaultText = defaultText; mInputType = InputType.PLAIN_TEXT; - setEditInputType("text"); + //setEditInputType("textMultiLine"); } public void setEditInputType(String editInputType) { @@ -167,78 +179,105 @@ public class AlertDialogTask extends DialogTask { mInputType = InputType.PASSWORD; } + public void setMessageIsHtml(Boolean messageIsHtml){ + mMessageIsHtml = messageIsHtml; + } + + private void builderSetText(AlertDialog.Builder builder){ + ScrollView scrollView = new ScrollView(mActivity); + LinearLayout linearLayout = new LinearLayout(mActivity); + linearLayout.setOrientation(LinearLayout.VERTICAL); + if (mTitle != null){ + TextView textView = new TextView(mActivity); + textView.setText(mTitle); + textView.setTextSize(18); + textView.setTextColor(Color.GREEN); + textView.setTypeface(Typeface.DEFAULT_BOLD); + linearLayout.addView(textView); + } + if (mMessage != null) { + TextView textView = new TextView(mActivity); + if(mMessageIsHtml) + textView.setText(HtmlUtil.textToHtml(mMessage)); + else + textView.setText(mMessage); + textView.setTextColor(Color.WHITE); + linearLayout.addView(textView); + } + if(mEditText!=null) + linearLayout.addView(mEditText); + linearLayout.setPadding(30,30,30,30); + scrollView.addView(linearLayout); + builder.setView(scrollView); + } + + private void builderSetTitle(AlertDialog.Builder builder){ + if (mTitle != null) + builder.setTitle(mTitle); + } + @Override public void onCreate() { super.onCreate(); - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.MyDialog); - if (mTitle != null) { - builder.setTitle(mTitle); - } + mActivity = getActivity(); + AlertDialog.Builder builder = new AlertDialog.Builder(mActivity, R.style.MyDialog); // Can't display both a message and items. We'll elect to show the items instead. - if (mMessage != null && mItems.isEmpty()) { - builder.setMessage(mMessage); - } switch (mInputType) { // Add single choice menu items to dialog. case SINGLE_CHOICE: + builderSetTitle(builder); builder.setSingleChoiceItems(getItemsAsCharSequenceArray(), mSelectedItems.iterator().next(), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int item) { - mSelectedItems.clear(); - mSelectedItems.add(item); - } + (dialog, item) -> { + mSelectedItems.clear(); + mSelectedItems.add(item); }); break; // Add multiple choice items to the dialog. case MULTI_CHOICE: + builderSetTitle(builder); boolean[] selectedItems = new boolean[mItems.size()]; for (int i : mSelectedItems) { selectedItems[i] = true; } builder.setMultiChoiceItems(getItemsAsCharSequenceArray(), selectedItems, - new DialogInterface.OnMultiChoiceClickListener() { - @Override - public void onClick(DialogInterface dialog, int item, boolean isChecked) { - if (isChecked) { - mSelectedItems.add(item); - } else { - mSelectedItems.remove(item); - } + (dialog, item, isChecked) -> { + if (isChecked) { + mSelectedItems.add(item); + } else { + mSelectedItems.remove(item); } }); break; // Add standard, menu-like, items to dialog. case MENU: - builder.setItems(getItemsAsCharSequenceArray(), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int item) { - Map result = new HashMap(); - result.put("item", item); - dismissDialog(); - setResult(result); - } + builderSetTitle(builder); + builder.setItems(getItemsAsCharSequenceArray(), (dialog, item) -> { + Map result = new HashMap(); + result.put("item", item); + dismissDialog(); + setResult(result); }); break; case PLAIN_TEXT: - mEditText = new EditText(getActivity()); + mEditText = new EditText(mActivity); if (mDefaultText != null) { mEditText.setText(mDefaultText); } mEditText.setInputType(mEditInputType); - builder.setView(mEditText); - //builder.setView(mEditText,20,0,20,0); + builderSetText(builder); break; case PASSWORD: - mEditText = new EditText(getActivity()); + mEditText = new EditText(mActivity); mEditText.setInputType(android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD); mEditText.setTransformationMethod(new PasswordTransformationMethod()); - builder.setView(mEditText); + builderSetText(builder); break; default: - // No input type specified. + mEditText = null; + builderSetText(builder); } - configureButtons(builder, getActivity()); - addOnCancelListener(builder, getActivity()); + configureButtons(builder, mActivity); + addOnCancelListener(builder, mActivity); mDialog = builder.show(); mShowLatch.countDown(); } diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/DialogTask.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/DialogTask.java index 4285c647f7e7bd1f5d97ee59436bb368140325ce..6cfe5d21ddcb5c34e25576979654688f7c9237ab 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/DialogTask.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/DialogTask.java @@ -18,6 +18,7 @@ package org.qpython.qsl4a.qsl4a.facade.ui; import android.app.Dialog; +import org.qpython.qsl4a.R; import org.qpython.qsl4a.qsl4a.facade.EventFacade; import org.qpython.qsl4a.qsl4a.future.FutureActivityTask; @@ -28,6 +29,12 @@ abstract class DialogTask extends FutureActivityTask { protected Dialog mDialog; private EventFacade mEventFacade; + @Override + public void onCreate(){ + super.onCreate(); + setTaskDescription(R.string.dialog_activity); + } + public EventFacade getEventFacade() { return mEventFacade; } @@ -55,7 +62,7 @@ abstract class DialogTask extends FutureActivityTask { } /** - * Dismiss the {@link Dialog} and close {@link Sl4aActivity}. + * Dismiss the {@link Dialog} and close {#link Sl4aActivity}. */ public void dismissDialog() { if (mDialog != null) { diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/FullScreenTask.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/FullScreenTask.java index a49ba9c04698ccf3abeb541486a2b6aaac8ce575..19ef4c469e9022f758fe750bb59ae84b377df146 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/FullScreenTask.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/FullScreenTask.java @@ -17,13 +17,8 @@ package org.qpython.qsl4a.qsl4a.facade.ui; -import android.R; import android.app.Activity; -import android.app.ActivityManager; import android.graphics.Bitmap; -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.LayerDrawable; import android.os.Build; import android.os.Handler; import android.support.annotation.RequiresApi; @@ -41,6 +36,7 @@ import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import org.json.JSONArray; +import org.qpython.qsl4a.R; import org.qpython.qsl4a.qsl4a.facade.EventFacade; import org.qpython.qsl4a.qsl4a.future.FutureActivityTask; import org.xmlpull.v1.XmlPullParser; @@ -81,7 +77,7 @@ public class FullScreenTask extends FutureActivityTask implements OnClic @Override public void onCreate() { - // super.onCreate(); + //super.onCreate(); final Activity activity = getActivity(); if (mHandler == null) { mHandler = new Handler(); @@ -97,14 +93,16 @@ public class FullScreenTask extends FutureActivityTask implements OnClic String E = e.toString(); mInflater.getErrors().add(E); mView = defaultView(E); - mInflater.setIdList(R.id.class); + mInflater.setIdList(android.R.id.class); } //activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED); activity.setTheme(mTheme); activity.setContentView(mView); activity.setTitle(mTitle); - if (!mTitle.equals("")) - activity.setTaskDescription(new ActivityManager.TaskDescription(mTitle)); + if(mTitle.equals("")) + setTaskDescription(R.string.future_activity); + else + setTaskDescription(mTitle); mInflater.setClickListener(mView, this, this, this); mShowLatch.countDown(); } diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/NFCBeamTask.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/NFCBeamTask.java deleted file mode 100644 index 1d3d073d916afc48f4eeae6b8ebcbec8c3b4e412..0000000000000000000000000000000000000000 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/NFCBeamTask.java +++ /dev/null @@ -1,630 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a.facade.ui; - -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - - -import android.annotation.SuppressLint; -import android.annotation.TargetApi; -import android.app.Activity; -//import android.support.v7.app.AlertDialog; -import android.app.AlertDialog; -import android.app.PendingIntent; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.IntentFilter.MalformedMimeTypeException; -import android.nfc.NdefMessage; -import android.nfc.NdefRecord; -import android.nfc.NfcAdapter; -import android.nfc.NfcAdapter.CreateNdefMessageCallback; -import android.nfc.NfcAdapter.OnNdefPushCompleteCallback; -import android.nfc.NfcEvent; -import android.os.Build; -import android.os.Handler; -import android.os.Message; -import android.os.Parcelable; -import android.text.method.PasswordTransformationMethod; -import android.util.Log; -import android.view.KeyEvent; -import android.widget.EditText; -import android.widget.Toast; - -import org.qpython.qsl4a.qsl4a.activity.FutureActivity; - -/** - * Wrapper class for progress dialog running in separate thread - * - * @author MeanEYE.rcf (meaneye.rcf@gmail.com) - */ -class NFCBeamTask extends DialogTask implements CreateNdefMessageCallback, OnNdefPushCompleteCallback { - - private final String TAG = "NFCBeamTask"; - private final String mTitle; - private final String mMessage; - - private final List mItems; - private final Set mSelectedItems; - private final Map mResultMap; - //protected _WBase WBase; - protected int dialogIndex; - NfcAdapter _nfcAdapter; - IntentFilter[] _readTagFilters; - Handler mhandler; - private InputType mInputType; - private int mEditInputType = 0; - private String mPositiveButtonText; - private String mNegativeButtonText; - private String mNeutralButtonText; - private EditText mEditText; - private String mDefaultText; - private int beamStat=0; - private PendingIntent _nfcPendingIntent; - private String content = "QPython NFC"; - - public NFCBeamTask(String title, String message) { - mTitle = title; - mMessage = message; - mInputType = InputType.DEFAULT; - mItems = new ArrayList(); - mSelectedItems = new TreeSet(); - mResultMap = new HashMap(); - } - - public void setPositiveButtonText(String text) { - mPositiveButtonText = text; - } - - public void setNegativeButtonText(String text) { - mNegativeButtonText = text; - } - - public void setNeutralButtonText(String text) { - mNeutralButtonText = text; - } - - /** - * Set list items. - * - * @param items - */ - public void setItems(JSONArray items) { - mItems.clear(); - for (int i = 0; i < items.length(); i++) { - try { - mItems.add(items.getString(i)); - } catch (JSONException e) { - throw new RuntimeException(e); - } - } - mInputType = InputType.MENU; - } - - /** - * Set single choice items. - * - * @param items - * a list of items as {@link String}s to display - * @param selected - * the index of the item that is selected by default - */ - public void setSingleChoiceItems(JSONArray items, int selected) { - setItems(items); - mSelectedItems.clear(); - mSelectedItems.add(selected); - mInputType = InputType.SINGLE_CHOICE; - } - - /** - * Set multi choice items. - * - * @param items - * a list of items as {@link String}s to display - * @param selected - * a list of indices for items that should be selected by default - * @throws JSONException - */ - public void setMultiChoiceItems(JSONArray items, JSONArray selected) throws JSONException { - setItems(items); - mSelectedItems.clear(); - if (selected != null) { - for (int i = 0; i < selected.length(); i++) { - mSelectedItems.add(selected.getInt(i)); - } - } - mInputType = InputType.MULTI_CHOICE; - } - - /** - * Returns the list of selected items. - */ - public Set getSelectedItems() { - return mSelectedItems; - } - - public void setTextInput(String defaultText) { - mDefaultText = defaultText; - mInputType = InputType.PLAIN_TEXT; - setEditInputType("text"); - } - - public void setEditInputType(String editInputType) { - String[] list = editInputType.split("\\|"); - Map types = ViewInflater.getInputTypes(); - mEditInputType = 0; - for (String flag : list) { - Integer v = types.get(flag.trim()); - if (v != null) { - mEditInputType |= v; - } - } - if (mEditInputType == 0) { - mEditInputType = android.text.InputType.TYPE_CLASS_TEXT; - } - } - - public void setPasswordInput() { - mInputType = InputType.PASSWORD; - } - - @SuppressLint("NewApi") - public boolean initNFCBeam() { - Log.d(TAG, "initNFCBeam"); - mhandler = new Handler() { - @Override - public void handleMessage(Message msg) { - //Toast.makeText(getActivity().getApplicationContext(), "Master has sent content", Toast.LENGTH_SHORT).show(); - - setResult("master","ok"); - } - }; - -// WBase = new _WBase(getActivity().getApplicationContext(), getActivity()); - dialogIndex = 1; - - _nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity().getApplicationContext()); - - if (_nfcAdapter == null) { - Toast.makeText(getActivity().getApplicationContext(), "NFC is not available, please upgrade your mobile", Toast.LENGTH_SHORT).show(); - - /*WBase.setTxtDialogParam(R.drawable.alert_dialog_icon, - "NFC is not available, please update your mobile", - new OnClickListener() { - @Override - public void onClick(DialogInterface arg0, int arg1) { - } - }); - getActivity().showDialog(_WBase.DIALOG_NOTIFY_MESSAGE + dialogIndex); - dialogIndex++;*/ - } else { - - if (!_nfcAdapter.isEnabled()) { - Toast.makeText(getActivity().getApplicationContext(), "NFC is closed, please enable it with beam", Toast.LENGTH_SHORT).show(); - -/* WBase.setTxtDialogParam(R.drawable.alert_dialog_icon, - "NFC is closed, please enable it with beam", - new OnClickListener() { - @Override - public void onClick(DialogInterface arg0, int arg1) { - getActivity().startActivity(new Intent( - Settings.ACTION_NFC_SETTINGS)); - } - }); - getActivity().showDialog(_WBase.DIALOG_NOTIFY_MESSAGE + dialogIndex); - dialogIndex++;*/ - } else { - launchNFC(); - return true; - } - } - return false; - } - - public void setBeamType(int stat) { - beamStat = stat; - } - @SuppressLint("NewApi") - public void launchNFC() { - Log.d(TAG, "launchNFC:"+beamStat); - if (beamStat == 0) { - _nfcAdapter.setNdefPushMessageCallback(this, getActivity()); - _nfcAdapter.setOnNdefPushCompleteCallback(this, getActivity()); - - // start NFC Connection - /*_nfcPendingIntent = PendingIntent.getActivity(getActivity().getApplicationContext(), 0, - new Intent(getActivity().getApplicationContext(), FutureActivity.class) - .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0); - - IntentFilter ndefDetected = new IntentFilter( - NfcAdapter.ACTION_NDEF_DISCOVERED); - try { - ndefDetected.addDataType("application/com.hipipal.qpy.nfc"); - } catch (MalformedMimeTypeException e) { - throw new RuntimeException("Could not add MIME type.", e); - } - - _readTagFilters = new IntentFilter[] { ndefDetected };*/ - } else if (beamStat == 1) { - //_nfcAdapter.setNdefPushMessageCallback(this, getActivity()); - //_nfcAdapter.setOnNdefPushCompleteCallback(this, getActivity()); - - // start NFC Connection - _nfcPendingIntent = PendingIntent.getActivity(getActivity().getApplicationContext(), 0, - new Intent(getActivity().getApplicationContext(), FutureActivity.class) - .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0); - - IntentFilter ndefDetected = new IntentFilter( - NfcAdapter.ACTION_NDEF_DISCOVERED); - try { - ndefDetected.addDataType("application/com.hipipal.qpy.nfc"); - } catch (MalformedMimeTypeException e) { - throw new RuntimeException("Could not add MIME type.", e); - } - - _readTagFilters = new IntentFilter[] { ndefDetected }; - } - } - - - @TargetApi(Build.VERSION_CODES.GINGERBREAD) - @SuppressLint("NewApi") - @Override - public void onCreate() { - super.onCreate(); - if (initNFCBeam()) { - - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - if (mTitle != null) { - builder.setTitle(mTitle); - } - // Can't display both a message and items. We'll elect to show the items instead. - if (mMessage != null && mItems.isEmpty()) { - builder.setMessage(mMessage); - } - switch (mInputType) { - // Add single choice menu items to dialog. - case SINGLE_CHOICE: - builder.setSingleChoiceItems(getItemsAsCharSequenceArray(), mSelectedItems.iterator().next(), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int item) { - mSelectedItems.clear(); - mSelectedItems.add(item); - } - }); - break; - // Add multiple choice items to the dialog. - case MULTI_CHOICE: - boolean[] selectedItems = new boolean[mItems.size()]; - for (int i : mSelectedItems) { - selectedItems[i] = true; - } - builder.setMultiChoiceItems(getItemsAsCharSequenceArray(), selectedItems, - new DialogInterface.OnMultiChoiceClickListener() { - @Override - public void onClick(DialogInterface dialog, int item, boolean isChecked) { - if (isChecked) { - mSelectedItems.add(item); - } else { - mSelectedItems.remove(item); - } - } - }); - break; - // Add standard, menu-like, items to dialog. - case MENU: - builder.setItems(getItemsAsCharSequenceArray(), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int item) { - Map result = new HashMap(); - result.put("item", item); - dismissDialog(); - setResult(result); - } - }); - break; - case PLAIN_TEXT: - mEditText = new EditText(getActivity()); - if (mDefaultText != null) { - mEditText.setText(mDefaultText); - } - mEditText.setInputType(mEditInputType); - builder.setView(mEditText); - break; - case PASSWORD: - mEditText = new EditText(getActivity()); - mEditText.setInputType(android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD); - mEditText.setTransformationMethod(new PasswordTransformationMethod()); - builder.setView(mEditText); - break; - default: - // No input type specified. - } - configureButtons(builder, getActivity()); - addOnCancelListener(builder, getActivity()); - mDialog = builder.show(); - mShowLatch.countDown(); - } else { - - } - } - - private CharSequence[] getItemsAsCharSequenceArray() { - return mItems.toArray(new CharSequence[mItems.size()]); - } - - private AlertDialog.Builder addOnCancelListener(final AlertDialog.Builder builder, final Activity activity) { - return builder.setOnCancelListener(new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - mResultMap.put("canceled", true); - setResult("self","canceled"); - } - }); - } - - private void configureButtons(final AlertDialog.Builder builder, final Activity activity) { - DialogInterface.OnClickListener buttonListener = new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - switch (which) { - case DialogInterface.BUTTON_POSITIVE: - mResultMap.put("which", "positive"); - break; - case DialogInterface.BUTTON_NEGATIVE: - mResultMap.put("which", "negative"); - break; - case DialogInterface.BUTTON_NEUTRAL: - mResultMap.put("which", "neutral"); - - break; - } - setResult("self","ok"); - } - }; - if (mNegativeButtonText != null) { - builder.setNegativeButton(mNegativeButtonText, buttonListener); - } - if (mPositiveButtonText != null) { - builder.setPositiveButton(mPositiveButtonText, buttonListener); - } - if (mNeutralButtonText != null) { - builder.setNeutralButton(mNeutralButtonText, buttonListener); - } - } - - private void setResult(String ms, String stat) { - dismissDialog(); - String msg = ""; - if (mInputType == InputType.PLAIN_TEXT || mInputType == InputType.PASSWORD) { - msg = mEditText.getText().toString(); - } else { - msg = content; - } - Log.d(TAG, "setResult:"+mResultMap); - JSONObject ret2 = new JSONObject(); - try { - ret2.put("role", ms); - ret2.put("stat", stat); - ret2.put("message", msg); - mResultMap.put("value", ret2.toString()); - - } catch (JSONException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - setResult(mResultMap); - } - - @SuppressLint("NewApi") - @Override - public void onPause() { - Log.d(TAG, "onPause"); - - super.onPause(); - if (_nfcAdapter!=null && beamStat==1) { - _nfcAdapter.disableForegroundDispatch(getActivity()); - } - } - - @SuppressLint("NewApi") - public void onResume() { - Log.d(TAG, "onResume"); - super.onResume(); - if (_nfcAdapter!=null && beamStat==1) { - recvMsg(); - - _nfcAdapter.enableForegroundDispatch(getActivity(), _nfcPendingIntent, _readTagFilters, null); - } - } - - @SuppressLint("NewApi") - public void recvMsg() { - Log.d(TAG, "recvMsg"); - if (_nfcAdapter == null) { - - Toast.makeText(getActivity().getApplicationContext(), "NFC is not available, please upgrade your mobile", Toast.LENGTH_SHORT).show(); - - /*WBase.setTxtDialogParam(R.drawable.alert_dialog_icon, - "NFC is not available, please update your mobile", - new OnClickListener() { - @Override - public void onClick(DialogInterface arg0, int arg1) { - } - }); - getActivity().showDialog(_WBase.DIALOG_NOTIFY_MESSAGE + dialogIndex); - dialogIndex++;*/ - } else { - - if (!_nfcAdapter.isEnabled()) { - Toast.makeText(getActivity().getApplicationContext(), "NFC is closed, please enable it with beam", Toast.LENGTH_SHORT).show(); - - /*WBase.setTxtDialogParam(R.drawable.alert_dialog_icon, - "NFC is closed, please enable it with beam", - new OnClickListener() { - @Override - public void onClick(DialogInterface arg0, int arg1) { - startActivity(new Intent( - Settings.ACTION_NFC_SETTINGS)); - } - }); - getActivity().showDialog(_WBase.DIALOG_NOTIFY_MESSAGE + dialogIndex); - dialogIndex++;*/ - } else { - // OK - if (getActivity().getIntent().getAction() != null) { - - if (getActivity().getIntent().getAction().equals( - NfcAdapter.ACTION_NDEF_DISCOVERED)) { - NdefMessage[] msgs = getNdefMessagesFromIntent(getActivity().getIntent()); - NdefRecord record = msgs[0].getRecords()[0]; - byte[] payload = record.getPayload(); - - String payloadString = new String(payload); - - //Toast.makeText(getActivity().getApplicationContext(), payloadString, Toast.LENGTH_SHORT).show(); - - this.content = payloadString; - setResult("slave","ok"); - - } else { - - } - } - - _nfcAdapter.enableForegroundDispatch(getActivity(), _nfcPendingIntent, _readTagFilters, null); - - } - } - } - - @SuppressLint("NewApi") - NdefMessage[] getNdefMessagesFromIntent(Intent intent) { - // Parse the intent - NdefMessage[] msgs = null; - String action = intent.getAction(); - if (action.equals(NfcAdapter.ACTION_TAG_DISCOVERED) - || action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED)) { - Parcelable[] rawMsgs = intent - .getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); - if (rawMsgs != null) { - msgs = new NdefMessage[rawMsgs.length]; - for (int i = 0; i < rawMsgs.length; i++) { - msgs[i] = (NdefMessage) rawMsgs[i]; - } - - } else { - // Unknown tag type - byte[] empty = new byte[] {}; - NdefRecord record = new NdefRecord(NdefRecord.TNF_UNKNOWN, - empty, empty, empty); - NdefMessage msg = new NdefMessage(new NdefRecord[] { record }); - msgs = new NdefMessage[] { msg }; - } - - } else { - Log.e(TAG, "Unknown intent."); - finish(); - } - return msgs; - } - - @SuppressLint("NewApi") - @Override - public void onNewIntent(Intent intent) { - Log.d(TAG, "onNewIntent"); - - if (intent.getAction().equals(NfcAdapter.ACTION_NDEF_DISCOVERED)) { - NdefMessage[] msgs = getNdefMessagesFromIntent(intent); - NdefRecord record = msgs[0].getRecords()[0]; - byte[] payload = record.getPayload(); - - String payloadString = new String(payload); - - //Toast.makeText(getActivity().getApplicationContext(), payloadString, Toast.LENGTH_SHORT).show(); - - this.content = payloadString; - setResult("slave","ok"); - - - } else if (intent.getAction().equals(NfcAdapter.ACTION_TAG_DISCOVERED)) { - Toast.makeText(getActivity().getApplicationContext(), "This NFC tag has no NDEF data.", - Toast.LENGTH_LONG).show(); - } - } - - /*public String getResult() { - return content; - }*/ - - public void setContent(String content) { - this.content = content; - } - - @SuppressLint("NewApi") - @Override - public void onNdefPushComplete(NfcEvent arg0) { - Log.d(TAG, "onNdefPushComplete"); - //Toast.makeText(getActivity().getApplicationContext(), "Master", Toast.LENGTH_SHORT).show(); - - mhandler.sendEmptyMessage(0); - } - - @SuppressLint("NewApi") - @Override - public NdefMessage createNdefMessage(NfcEvent arg0) { - String data; - if (mEditText!=null) { - data = mEditText.getText().toString(); - } else { - data = content; - } - - String mimeType = "application/com.hipipal.qpy.nfc"; - - byte[] mimeBytes = mimeType.getBytes(Charset.forName("UTF-8")); - byte[] dataBytes = data.getBytes(Charset.forName("UTF-8")); - byte[] id = new byte[0]; - - NdefRecord record = new NdefRecord(NdefRecord.TNF_MIME_MEDIA, - mimeBytes, id, dataBytes); - - NdefMessage message = new NdefMessage(new NdefRecord[] { record }); - - return message; - } - - public boolean onKeyDown(int keyCode, KeyEvent event) { - getActivity().finish(); - return true; - } - - private enum InputType { - DEFAULT, MENU, SINGLE_CHOICE, MULTI_CHOICE, PLAIN_TEXT, PASSWORD; - } -} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/SeekBarDialogTask.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/SeekBarDialogTask.java index fa07716312381acf41f0921dbd62a6df4361d4ee..59d806d58d5b5fa4ccdf97a258559b9c589a2051 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/SeekBarDialogTask.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/SeekBarDialogTask.java @@ -17,17 +17,15 @@ package org.qpython.qsl4a.qsl4a.facade.ui; import android.app.Activity; -//import android.support.v7.app.AlertDialog; import android.app.AlertDialog; import android.content.DialogInterface; import android.util.AndroidRuntimeException; import android.widget.SeekBar; -import org.qpython.qsl4a.qsl4a.LogUtil; -import org.qpython.qsl4a.qsl4a.facade.EventFacade; - import org.json.JSONException; import org.json.JSONObject; +import org.qpython.qsl4a.qsl4a.LogUtil; +import org.qpython.qsl4a.qsl4a.facade.EventFacade; /** * Wrapper class for dialog box with seek bar. diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/UiConfig.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/UiConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..c5e92a621e3df8122729209d8831d42540df0dad --- /dev/null +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/UiConfig.java @@ -0,0 +1,57 @@ +package org.qpython.qsl4a.qsl4a.facade.ui; + +import org.qpython.qsl4a.qsl4a.facade.FacadeManager; +import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; +import org.qpython.qsl4a.qsl4a.rpc.Rpc; +import org.qpython.qsl4a.qsl4a.rpc.RpcOptional; +import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; +import org.qpython.qsl4a.qsl4a.util.HtmlUtil; + +import java.util.HashMap; +import java.util.Map; + + +public class UiConfig extends RpcReceiver { + // This value should not be used for menu groups outside this class. + + public UiConfig(FacadeManager manager) { + super(manager); + } + + @Rpc(description = "set html picture size") + public static void htmlPictureSetSize( + // widthFixed or heightFixed = 0 means original picture size + @RpcParameter(name = "width fixed") @RpcOptional Integer widthFixed, + @RpcParameter(name = "height fixed") @RpcOptional Integer heightFixed, + @RpcParameter(name = "width ratio") @RpcOptional Double widthRatio, + @RpcParameter(name = "height ratio") @RpcOptional Double heightRatio + ){ + if(widthFixed == null) + widthFixed = 0; + HtmlUtil.widthFixed = widthFixed; + if(heightFixed == null) + heightFixed = 0; + HtmlUtil.heightFixed = heightFixed; + if(widthRatio == null) + widthRatio = 1.0; + HtmlUtil.widthRatio = widthRatio; + if(heightRatio == null) + heightRatio = 1.0; + HtmlUtil.heightRatio = heightRatio; + } + + @Rpc(description = "get html picture size") + public static Map htmlPictureGetSize(){ + Map map = new HashMap<>(); + map.put("widthFixed",HtmlUtil.widthFixed); + map.put("heightFixed",HtmlUtil.heightFixed); + map.put("widthRatio",HtmlUtil.widthRatio); + map.put("heightRatio",HtmlUtil.heightRatio); + return map; + } + + @Override + public void shutdown() { + + } +} \ No newline at end of file diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/UiFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/UiFacade.java index d4050dfa6ec2974416f93ced06e348601c690a91..f1c7ca78482236f548e4aa24121fc75611044bd7 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/UiFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/UiFacade.java @@ -16,8 +16,10 @@ package org.qpython.qsl4a.qsl4a.facade.ui; +import android.annotation.SuppressLint; import android.app.ProgressDialog; import android.app.Service; +import android.content.Context; import android.graphics.Bitmap; import android.os.Environment; import android.util.AndroidRuntimeException; @@ -27,10 +29,13 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; +import org.json.JSONArray; +import org.json.JSONException; import org.qpython.qsl4a.QSL4APP; -import org.qpython.qsl4a.qsl4a.FutureActivityTaskExecutor; +import org.qpython.qsl4a.R; import org.qpython.qsl4a.qsl4a.facade.EventFacade; import org.qpython.qsl4a.qsl4a.facade.FacadeManager; +import org.qpython.qsl4a.qsl4a.future.FutureActivityTaskExecutor; import org.qpython.qsl4a.qsl4a.interpreter.html.HtmlActivityTask; import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; import org.qpython.qsl4a.qsl4a.rpc.Rpc; @@ -44,23 +49,18 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; -import org.json.JSONArray; -import org.json.JSONException; - /** * User Interface Facade.
*
* Usage Notes
*
- * The UI facade provides access to a selection of dialog boxes for general user interaction, and - * also hosts the {@link #webViewShow} call which allows interactive use of html pages.
+ * The UI facade provides access to a selection of dialog boxes for general user interaction
* The general use of the dialog functions is as follows:
*
    *
  1. Create a dialog using one of the following calls: @@ -103,8 +103,8 @@ import org.json.JSONException; * You can also manipulate menu options. The menu options are available for both {@link #dialogShow} * and {@link #fullShow}. *
      - *
    • {@link #clearOptionsMenu} - *
    • {@link #addOptionsMenuItem} + *
    • {link clearOptionsMenu} + *
    • {link addOptionsMenuItem} *
    *
    * Some notes:
    @@ -135,6 +135,7 @@ public class UiFacade extends RpcReceiver { private List mOverrideKeys = Collections.synchronizedList(new ArrayList()); private final String sdcard; + private final Context context; public UiFacade(FacadeManager manager) { super(manager); @@ -145,6 +146,7 @@ public class UiFacade extends RpcReceiver { mEventFacade = manager.getReceiver(EventFacade.class); mMenuUpdated = new AtomicBoolean(false); sdcard = Environment.getExternalStorageDirectory().toString(); + context = mService.getApplicationContext(); } /** @@ -158,8 +160,7 @@ public class UiFacade extends RpcReceiver { @RpcParameter(name = "title", description = "title of the input box") @RpcDefault("Value") final String title, @RpcParameter(name = "message", description = "message to display above the input box") @RpcDefault("Please enter value:") final String message, @RpcParameter(name = "defaultText", description = "text to insert into the input box") @RpcOptional final String text, - @RpcParameter(name = "inputType", description = "type of input data, ie number or text") @RpcOptional final String inputType) - throws InterruptedException { + @RpcParameter(name = "inputType", description = "type of input data, ie number or text") @RpcOptional final String inputType) { dialogDismiss(); mDialogTask = new AlertDialogTask(title, message); ((AlertDialogTask) mDialogTask).setTextInput(text); @@ -177,6 +178,13 @@ public class UiFacade extends RpcReceiver { ((AlertDialogTask) mDialogTask).setPasswordInput(); } + @Rpc(description = "set dialog message is a html .") + public void dialogSetMessageIsHtml( + @RpcParameter(name = "messageIsHtml") @RpcDefault("true") Boolean messageIsHtml + ){ + ((AlertDialogTask) mDialogTask).setMessageIsHtml(messageIsHtml); + } + /** * The result is the user's input, or None (null) if cancel was hit.
    * Example (python) @@ -196,9 +204,9 @@ public class UiFacade extends RpcReceiver { @RpcParameter(name = "message", description = "message to display above the input box") @RpcDefault("Please enter value:") final String message, @RpcParameter(name = "defaultText", description = "text to insert into the input box") @RpcOptional final String text) throws InterruptedException { - dialogCreateInput(title, message, text, "text"); - dialogSetNegativeButtonText("Cancel"); - dialogSetPositiveButtonText("Ok"); + dialogCreateInput(title, message, text, "textMultiLine"); + dialogSetNegativeButtonText(context.getString(R.string.cancel)); + dialogSetPositiveButtonText(context.getString(R.string.ok)); dialogShow(); Map response = (Map) dialogGetResponse(); if ("positive".equals(response.get("which"))) { @@ -215,8 +223,8 @@ public class UiFacade extends RpcReceiver { @RpcParameter(name = "message", description = "message to display above the input box") @RpcDefault("Please enter password:") final String message) throws InterruptedException { dialogCreatePassword(title, message); - dialogSetNegativeButtonText("Cancel"); - dialogSetPositiveButtonText("Ok"); + dialogSetNegativeButtonText(context.getString(R.string.cancel)); + dialogSetPositiveButtonText(context.getString(R.string.ok)); dialogShow(); Map response = (Map) dialogGetResponse(); if ("positive".equals(response.get("which"))) { @@ -457,11 +465,11 @@ public class UiFacade extends RpcReceiver { } } - /** + /* * * See wiki page for more * detail. - */ - /*@Rpc(description = "Display a WebView with the given URL.") + * / + @Rpc(description = "Display a WebView with the given URL.") public void webViewShow( @RpcParameter(name = "url") String url, @RpcParameter(name = "wait", description = "block until the user exits the WebView") @RpcOptional Boolean wait) @@ -479,11 +487,11 @@ public class UiFacade extends RpcReceiver { throw new RuntimeException(e); } } - }*/ + } - /** - * Context menus are used primarily with {@link #webViewShow} - */ + /* * + * Context menus are used primarily with {webViewShow} + * / @Rpc(description = "Adds a new item to context menu.") public void addContextMenuItem( @RpcParameter(name = "label", description = "label for this menu item") String label, @@ -516,7 +524,7 @@ public class UiFacade extends RpcReceiver { * print "And done." * * - */ + * / @Rpc(description = "Adds a new item to options menu.") public void addOptionsMenuItem( @RpcParameter(name = "label", description = "label for this menu item") String label, @@ -536,7 +544,7 @@ public class UiFacade extends RpcReceiver { public void clearOptionsMenu() { mOptionsMenuItems.clear(); mMenuUpdated.set(true); - } + }*/ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { for (UiMenuItem item : mContextMenuItems) { @@ -635,13 +643,9 @@ public class UiFacade extends RpcReceiver { if (mFullScreenTask == null) { throw new RuntimeException("No screen displayed."); } - List Ids = new ArrayList(); - for (int i = 0; i < ids.length(); i++) { - Ids.add(ids.get(i).toString()); - } JSONArray rst = new JSONArray(); - for(String id:Ids) { - rst.put(mFullScreenTask.getViewPropery(id,property)); + for (int i = 0; i < ids.length(); i++) { + rst.put(mFullScreenTask.getViewPropery(ids.get(i).toString(),property)); } return rst; } @@ -666,13 +670,9 @@ public class UiFacade extends RpcReceiver { if (mFullScreenTask == null) { throw new RuntimeException("No screen displayed."); } - List Ids = new ArrayList(); - for (int i = 0; i < ids.length(); i++) { - Ids.add(ids.get(i).toString()); - } String rst; - for(String id:Ids) { - rst = mFullScreenTask.setViewProperty(id, property, value); + for (int i = 0; i < ids.length(); i++) { + rst = mFullScreenTask.setViewProperty(ids.get(i).toString(), property, value); if (!rst.equals("OK")) return rst; } @@ -786,6 +786,7 @@ public class UiFacade extends RpcReceiver { } } + @SuppressLint("SimpleDateFormat") @Rpc(description = "Get the Full Screen Activity ScreenShot to path .") public String fullGetScreenShot( @RpcParameter(name = "path") @RpcOptional String path) throws Exception { diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/ViewInflater.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/ViewInflater.java index d0aa0df8f89ef49b74fb89f37b01d804fab36571..6819fa14b45b15607f61ed5360053b41fbfc6058 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/ViewInflater.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ui/ViewInflater.java @@ -75,8 +75,8 @@ import java.util.Map.Entry; public class ViewInflater { private static XmlPullParserFactory mFactory; - public static final String ANDROID = "http://schemas.android.com/apk/res/android"; - public static final String QPYTHON = "http://www.qpython.org"; + //public static final String ANDROID = "http://schemas.android.com/apk/res/android"; + //public static final String QPYTHON = "http://www.qpython.org"; public static final int BASESEQ = 0x7f0f0000; private int mNextSeq = BASESEQ; private final Map mIdList = new HashMap(); @@ -302,7 +302,7 @@ public class ViewInflater { } @SuppressWarnings("rawtypes") - public void setClickListener(View v, android.view.View.OnClickListener listener, + public void setClickListener(View v, View.OnClickListener listener, OnItemClickListener itemListener, SeekBar.OnSeekBarChangeListener seekbarListener) { if (v.isClickable()) { @@ -370,13 +370,10 @@ public class ViewInflater { if (view != null) { getLayoutParams(view, root); // Make quite sure every view has a layout param. for (int i = 0; i < xml.getAttributeCount(); i++) { - String ns = xml.getAttributeNamespace(i); + //String ns = xml.getAttributeNamespace(i); String attr = xml.getAttributeName(i); - if (ANDROID.equals(ns)) { + //if (ANDROID.equals(ns) || QPYTHON.equals(ns)) setProperty(view, root, attr, xml.getAttributeValue(i)); - } else if (QPYTHON.equals(ns)) { - setProperty(view, root, attr, xml.getAttributeValue(i)); - } } if (root != null) { root.addView(view); diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/usb/USBHostSerialFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/usb/USBHostSerialFacade.java index 756c1c7264cfa90d743d5b2b40d49abe5141b30a..afa11c3848b9409bfabb23d4c5bbd0e29bcf3283 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/usb/USBHostSerialFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/usb/USBHostSerialFacade.java @@ -150,7 +150,7 @@ public class USBHostSerialFacade extends RpcReceiver { boolean deviceFound = false; PendingIntent mPermissionIntent = - PendingIntent.getBroadcast(mService, 0, new Intent(ACTION_USB_PERMISSION), 0); + PendingIntent.getBroadcast(mService, 0, new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE); Map map = mUsbManager.getDeviceList(); if (hash.equals("")) { diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/activity/FutureActivity.java b/src/main/java/org/qpython/qsl4a/qsl4a/future/FutureActivity.java similarity index 91% rename from src/main/java/org/qpython/qsl4a/qsl4a/activity/FutureActivity.java rename to src/main/java/org/qpython/qsl4a/qsl4a/future/FutureActivity.java index 945d32f713b988e8caeb594d99faab0bd38b1577..8491cdefcf3cb9f5576337c232f1228f444714d5 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/activity/FutureActivity.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/future/FutureActivity.java @@ -14,14 +14,13 @@ * the License. */ -package org.qpython.qsl4a.qsl4a.activity; +package org.qpython.qsl4a.qsl4a.future; import android.app.Activity; import android.app.Service; import android.content.Intent; import android.content.pm.ResolveInfo; import android.os.Bundle; -//import android.support.v7.app.AppCompatActivity; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.KeyEvent; @@ -30,9 +29,7 @@ import android.view.View; import org.qpython.qsl4a.QSL4APP; import org.qpython.qsl4a.qsl4a.Constants; -import org.qpython.qsl4a.qsl4a.FutureActivityTaskExecutor; import org.qpython.qsl4a.qsl4a.LogUtil; -import org.qpython.qsl4a.qsl4a.future.FutureActivityTask; /** * This {@link Activity} is launched by {@link }s in order to perform operations that a @@ -44,12 +41,12 @@ import org.qpython.qsl4a.qsl4a.future.FutureActivityTask; public class FutureActivity extends Activity { private FutureActivityTask mTask; - private int count = 0; + //private int count = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - LogUtil.v("FutureActivity created."); + //LogUtil.v("FutureActivity created."); int id = getIntent().getIntExtra(Constants.EXTRA_TASK_ID, 0); if (id == 0) { throw new RuntimeException("FutureActivityTask ID is not specified."); @@ -65,7 +62,7 @@ public class FutureActivity extends Activity { intent.addCategory(Intent.CATEGORY_LAUNCHER); String packageName = getPackageName(); for (ResolveInfo resolve : getPackageManager().queryIntentActivities(intent, 0)) { - LogUtil.d("resolve.activityInfo.name:"+resolve.activityInfo.name); + //LogUtil.d("resolve.activityInfo.name:"+resolve.activityInfo.name); if (resolve.activityInfo.packageName.equals(packageName)) { intent.setClassName(packageName, resolve.activityInfo.name); break; @@ -73,7 +70,7 @@ public class FutureActivity extends Activity { } startActivity(intent); } catch (Exception e) { - LogUtil.e("Can't find main activity."); + //LogUtil.e("Can't find main activity."); } } else { mTask.setActivity(this); diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/future/FutureActivityTask.java b/src/main/java/org/qpython/qsl4a/qsl4a/future/FutureActivityTask.java index 7d53ca028b14359be07d249765dc266e54b0e522..1369cd40a5f5fd77b50915bc38a4a4ad14f70ea7 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/future/FutureActivityTask.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/future/FutureActivityTask.java @@ -17,8 +17,8 @@ package org.qpython.qsl4a.qsl4a.future; import android.app.Activity; +import android.app.ActivityManager; import android.content.Intent; -import android.util.Log; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.KeyEvent; @@ -26,56 +26,63 @@ import android.view.Menu; import android.view.View; import android.view.Window; -import org.qpython.qsl4a.R; - /** - * Encapsulates an {@link Activity} and a {@link FutureObject}. + * Encapsulates an {@link Activity} and a {#link FutureObject}. * * @author Damon Kohler (damonkohler@gmail.com) + * + * 乘着船 修改 2022 */ public abstract class FutureActivityTask { - final private String TAG = "FutureActivityTask"; + //final private String TAG = "FutureActivityTask"; private final FutureResult mResult = new FutureResult(); private Activity mActivity; - public void setActivity(Activity activity) { - mActivity = activity; + public void setActivity(Activity activity) { + mActivity = activity; } public Activity getActivity() { - return mActivity; + return mActivity; } - public void onCreate() { - Log.d(TAG, "onCreate"); - mActivity.getWindow().requestFeature(Window.FEATURE_NO_TITLE); - mActivity.setContentView(R.layout.activity_run_splash); + public void setTaskDescription(String title){ + mActivity.setTaskDescription(new ActivityManager.TaskDescription(title)); + } + public void setTaskDescription(int title){ + setTaskDescription(mActivity.getString(title)); + } + + public void onCreate() { + //Log.d(TAG, "onCreate"); + mActivity.getWindow().requestFeature(Window.FEATURE_NO_TITLE); + //mActivity.setContentView(R.layout.activity_run_splash); } public void onStart() { - Log.d(TAG, "onStart"); + //Log.d(TAG, "onStart"); } public void onResume() { - Log.d(TAG, "onResume"); + //Log.d(TAG, "onResume"); } public void onPause() { - Log.d(TAG, "onPause"); + //Log.d(TAG, "onPause"); } public void onStop() { - Log.d(TAG, "onStop"); + //Log.d(TAG, "onStop"); } public void onDestroy() { - Log.d(TAG, "onDestroy"); + //Log.d(TAG, "onDestroy"); } @@ -103,12 +110,22 @@ public abstract class FutureActivityTask { mActivity.finish(); } - public void startActivity(Intent intent) { - mActivity.startActivity(intent); + public void startActivity(Intent intent) throws Exception{ + try { + mActivity.startActivity(intent); + } catch (Exception e){ + mActivity.finish(); + throw e; + } } - public void startActivityForResult(Intent intent, int requestCode) { - mActivity.startActivityForResult(intent, requestCode); + public void startActivityForResult(Intent intent, int requestCode) throws Exception{ + try { + mActivity.startActivityForResult(intent, requestCode); + } catch (Exception e) { + mActivity.finish(); + throw e; + } } public boolean onKeyDown(int keyCode, KeyEvent event) { diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/FutureActivityTaskExecutor.java b/src/main/java/org/qpython/qsl4a/qsl4a/future/FutureActivityTaskExecutor.java similarity index 93% rename from src/main/java/org/qpython/qsl4a/qsl4a/FutureActivityTaskExecutor.java rename to src/main/java/org/qpython/qsl4a/qsl4a/future/FutureActivityTaskExecutor.java index 24c2f48b4b15416d21591589f1469dcfd2691539..7e4290a3125f2e06b46ce41bd9beef3f933d8d4e 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/FutureActivityTaskExecutor.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/future/FutureActivityTaskExecutor.java @@ -14,14 +14,12 @@ * the License. */ -package org.qpython.qsl4a.qsl4a; +package org.qpython.qsl4a.qsl4a.future; import android.content.Context; import android.content.Intent; - -import org.qpython.qsl4a.qsl4a.activity.FutureActivity; -import org.qpython.qsl4a.qsl4a.future.FutureActivityTask; +import org.qpython.qsl4a.qsl4a.Constants; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -33,7 +31,6 @@ public class FutureActivityTaskExecutor { private final Map> mTaskMap = new ConcurrentHashMap>(); private final AtomicInteger mIdGenerator = new AtomicInteger(0); - private final int intentFlags = Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK; public FutureActivityTaskExecutor(Context context) { diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/util/HtmlUtil.java b/src/main/java/org/qpython/qsl4a/qsl4a/util/HtmlUtil.java index bae5c6d0525c2ac701bdb210e4eef47218e2c98b..723aab2c08f461f94c9ae0467c165778bab89f8d 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/util/HtmlUtil.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/util/HtmlUtil.java @@ -8,12 +8,29 @@ import java.io.IOException; import java.net.URL; public class HtmlUtil { + public static int widthFixed = 0; + public static int heightFixed = 0; + public static double widthRatio = 1.0; + public static double heightRatio = 1.0; public static final Html.ImageGetter imageGetter = source -> { Drawable drawable = null; + int width,height; try { drawable = Drawable.createFromStream(new URL(source).openStream(), null); - drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + if(widthFixed <= 0) + width = drawable.getIntrinsicWidth(); + else + width = widthFixed; + if(heightFixed <= 0) + height = drawable.getIntrinsicHeight(); + else + height = heightFixed; + if(widthRatio != 1.0) + width *= widthRatio; + if(heightRatio != 1.0) + height *= heightRatio; + drawable.setBounds(0, 0, width, height); } catch (IOException e) { e.printStackTrace(); } diff --git a/src/main/res/values-zh/strings.xml b/src/main/res/values-zh/strings.xml index f36a02e8509659f7c8d57d94a9851e81d9bdfe53..bc7216cb7062dbfa152f0f60ccced178b1e2c9e9 100644 --- a/src/main/res/values-zh/strings.xml +++ b/src/main/res/values-zh/strings.xml @@ -2,4 +2,18 @@ QSL4A 悬浮窗索引超出范围。 + 悬浮窗索引不存在。 + 确认 + 取消 + 守护 + 小程序 + QSL4A对话小程序 + QSL4A未来小程序 + 分享 + 视频 + 浏览器 + 启动 + 打开 + 一般 + 点击多任务列表的“守护+数字”解锁进程 \ No newline at end of file diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 55c9b6cbfecb48fe0a55c87abf3db17fd7df31cd..b0f44ad1cad5886263af8fe1b2b4e9250c83d606 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -2,4 +2,18 @@ QSL4A Float View Index Out of Range . + Float View Index Not Exist . + OK + Cancel + Daemon + Activity + QSL4A DialogActivity + QSL4A FutureActivity + Share + Video + Browser + Launch + Open + Normal + Click on "Daemon+Number" in the multitask list to unlock the process \ No newline at end of file